Skip to content

feat(ui): add request a demo modal - #3766

Merged
waleedlatif1 merged 16 commits into
stagingfrom
feat/demo-modal
Mar 25, 2026
Merged

feat(ui): add request a demo modal#3766
waleedlatif1 merged 16 commits into
stagingfrom
feat/demo-modal

Conversation

@TheodoreSpeaks

@TheodoreSpeaksTheodoreSpeaks commented Mar 25, 2026

Copy link
Copy Markdown
Collaborator

Summary

Migrated over to sim native modal for requesting demos.
Added this modal to pricing and hero pages. I skipped the enterprise section in the top app bar and footer because we're going to have an enterprise page anyways.

  • Created demo-request-modal using EMCN components for use in pricing and hero sections of landing page
  • Created /demo-request endpoint to handle sending emails to enterprise@sim.ai

Type of Change

  • Bug fix
  • New feature
  • Breaking change
  • Documentation
  • Other: ___________

Testing

  • Tested with invalid inputs in hero and pricing sections
  • Tested with valid inputs in hero and pricing sections. Validated email send arguments are correct, but don't have email creds locally so didn't test emails landed in enterprise inbox.

Checklist

  • Code follows project style guidelines
  • Self-reviewed my changes
  • Tests added/updated and passing
  • No new warnings introduced
  • I confirm that I have read and agree to the terms outlined in the Contributor License Agreement (CLA)

Screenshots/Videos

hero page screenshot
image

Success:
image

Error:
image

Enterprise "Get a demo" in pricing section
image

Success:
image

Error:
image

@vercel

vercelBot commented Mar 25, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

1 Skipped Deployment
ProjectDeploymentActionsUpdated (UTC)
docsSkippedSkippedMar 25, 2026 10:29pm

Request Review

@TheodoreSpeaks

Copy link
Copy Markdown
CollaboratorAuthor

@cursor review

@cursor

cursorBot commented Mar 25, 2026

Copy link
Copy Markdown

PR Summary

Medium Risk
Adds a new public-facing form + API endpoint that sends email (with rate limiting) and changes email header handling to prevent injection; errors here could impact lead capture or outbound email delivery. Remaining changes are mostly SEO/docs metadata and UI polish, lower risk.

Overview
Adds a new “Request/Book a demo” flow: a DemoRequestModal with zod validation is wired into the landing Hero, Pricing, and Enterprise CTA, and a new POST /api/demo-requests endpoint validates input, rate-limits by IP, and sends a transactional email to enterprise@….

Hardens outbound email handling by adding email-header control character detection and subject sanitization in mailer.ts, reusing the same constraints in help/integration-request APIs; accompanying tests are updated/expanded.

Improves integrations SEO and rendering by switching metadata/canonicals to getBaseUrl(), adding integrations to sitemap.ts, using next/image in dropdowns/integration pages, and ensuring related-integrations CTA renders consistently.

Updates docs/data generation: expands HubSpot tool docs (deals/tickets/line items/quotes/appointments/carts/owners/marketing events/lists + pagination fields), tweaks Rippling paging wording, enriches many integration operation descriptions in integrations.json, improves generate-docs.ts tool-id mapping via switch/case parsing, and fixes Upstash tool-id selection mapping.

Written by Cursor Bugbot for commit 769a804. Configure here.

Comment threadapps/sim/app/(home)/components/navbar/navbar.tsx Outdated
Comment threadapps/sim/components/emcn/components/modal/modal.tsx Outdated
@TheodoreSpeaks
TheodoreSpeaks marked this pull request as ready for review March 25, 2026 19:44
@greptile-apps

greptile-appsBot commented Mar 25, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR replaces external Typeform links with a native DemoRequestModal component across the hero and pricing sections of the landing page, and introduces a new /api/demo-requests endpoint that sends an internal email notification to enterprise@sim.ai.

  • New DemoRequestModal — A fully controlled, client-rendered form with inline Zod validation (field-level errors, control-character guards on name fields, email format validation) and distinct success/error states. The modal is wired to both the hero page and the Enterprise pricing card.
  • New /api/demo-requests route — Rate-limited per IP (10 tokens, 5 refill/min), server-side Zod validation with demoRequestSchema, and sendEmail with the validated payload sent to the enterprise inbox.
  • Email header injection hardening — Addresses prior review feedback by adding NO_EMAIL_HEADER_CONTROL_CHARS_REGEX to firstName/lastName schema constraints and adding a prepareEmailHeaders function in mailer.ts that validates recipients/sender/reply-to for control characters and sanitizes subjects.
  • Accessibility and SEO improvements — Adds a skip-to-main-content link, removes userScalable: false from the global viewport, replaces decorative <h1> tags inside preview components with <p>, and adds integration pages to the sitemap.
  • blog-dropdown and docs-dropdown — Migrated from plain <img> to next/image with fill and explicit sizes props for better LCP optimization.

Confidence Score: 5/5

  • Safe to merge — all prior security concerns have been addressed and no new critical issues were found.
  • The PR is well-structured: the email header injection concern from prior review is fully resolved (both at the schema level and in prepareEmailHeaders), the useMemo nit and the resetForm/submitSuccess ordering concern are gone, client- and server-side validation are consistent, rate limiting is in place, and the new EMCN FormField component follows existing patterns. The only remaining comments are minor style suggestions (unused dep in useCallback, redundant role='presentation', and a silent default in the Upstash switch) that do not affect correctness or security.
  • No files require special attention.

Important Files Changed

FilenameOverview
apps/sim/app/api/demo-requests/route.tsNew POST endpoint to handle demo requests: rate-limited per IP, validates input with demoRequestSchema (including control-char guards on name fields), sanitizes the email subject at the mailer layer, and sends an internal notification to enterprise@sim.ai.
apps/sim/app/(home)/components/demo-request/demo-request-modal.tsxNew client-side modal component for demo requests. Well-structured with inline Zod validation, clear success/error states, and proper aria attributes. Minor nit: resetForm is listed in handleSubmit's dependency array but never called inside it.
apps/sim/app/(home)/components/demo-request/consts.tsDefines the shared Zod schema, enums, and label-lookup helpers for demo requests. Correctly applies NO_EMAIL_HEADER_CONTROL_CHARS_REGEX on name fields and uses quickValidateEmail for the email field.
apps/sim/lib/messaging/email/mailer.tsAdds prepareEmailHeaders function that validates recipients, sender, and reply-to for control characters and sanitizes the subject by replacing CRLF sequences with spaces. Addresses the email header injection concern raised in prior review.
apps/sim/lib/messaging/email/utils.tsExports EMAIL_HEADER_CONTROL_CHARS_REGEX, NO_EMAIL_HEADER_CONTROL_CHARS_REGEX, and hasEmailHeaderControlChars — reusable primitives for email header injection prevention used across the new endpoint and existing routes.
apps/sim/app/(home)/components/pricing/pricing.tsxReplaces external Typeform link with the native DemoRequestModal for the Enterprise tier CTA. Uses an action discriminator on the tier config and falls back to /signup when href is absent for non-demo tiers.
apps/sim/blocks/blocks/upstash.tsConverts the tool-ID selector from string interpolation to an explicit switch statement for clarity. The default case silently falls back to upstash_redis_get, which could mask future unhandled operations.
apps/sim/app/layout.tsxRemoves maximumScale: 1 / userScalable: false from the viewport config (accessibility improvement) and removes the globally-injected structured data script (likely now emitted per-page instead).

Sequence Diagram

sequenceDiagram
participant User
participant DemoRequestModal
participant API as /api/demo-requests
participant Mailer as mailer.ts
participant Email as Email Provider
User->>DemoRequestModal: Click "Get a demo" / "Book a demo"
DemoRequestModal->>DemoRequestModal: Client-side Zod validation (demoRequestSchema)
alt Validation fails
DemoRequestModal-->>User: Show inline field errors
else Validation passes
DemoRequestModal->>API: POST /api/demo-requests (JSON body)
API->>API: IP rate-limit check (10 tokens/min)
alt Rate limit exceeded
API-->>DemoRequestModal: 429 Too Many Requests
DemoRequestModal-->>User: Show error message
else Within limit
API->>API: Server-side Zod validation + control-char check
API->>Mailer: sendEmail({ to: enterprise@sim.ai, replyTo: companyEmail })
Mailer->>Mailer: prepareEmailHeaders() — validate recipients, sanitize subject
Mailer->>Email: Send transactional email
Email-->>Mailer: Success / Failure
Mailer-->>API: { success: true }
API-->>DemoRequestModal: 201 { success: true }
DemoRequestModal-->>User: Show success screen
end
end
Loading

Reviews (3): Last reviewed commit: "fix(upstash): throw on unknown operation..." | Re-trigger Greptile

Comment threadapps/sim/components/emcn/components/form-field/form-field.tsx Outdated
Comment threadapps/sim/app/api/demo-requests/route.ts
Comment threadapps/sim/app/(home)/components/demo-request/demo-request-modal.tsx Outdated
Comment threadapps/sim/app/(home)/components/demo-request/demo-request-modal.tsx Outdated
@TheodoreSpeaks

Copy link
Copy Markdown
CollaboratorAuthor

@greptile review

Comment threadapps/sim/lib/marketing/demo-request.ts Outdated
Comment threadapps/sim/app/(home)/components/pricing/pricing.tsx
Co-authored-by: Theodore Li <TheodoreSpeaks@users.noreply.github.com>
Co-authored-by: Theodore Li <TheodoreSpeaks@users.noreply.github.com>
Co-authored-by: Theodore Li <TheodoreSpeaks@users.noreply.github.com>
cursoragentand others added 2 commits March 25, 2026 20:31
Co-authored-by: Theodore Li <TheodoreSpeaks@users.noreply.github.com>
Co-authored-by: Theodore Li <TheodoreSpeaks@users.noreply.github.com>
Comment threadapps/sim/app/api/demo-request/route.ts Outdated
Comment threadapps/sim/app/(home)/components/demo-request/consts.ts
Comment threadapps/sim/lib/messaging/email/header-safety.ts Outdated
- Fix endsWith over-matching: basename === 'index.ts'/'types.ts' instead
of endsWith(), which was silently skipping valid tool files like
list_leave_types.ts, delete_index.ts, etc.
- Add extractSwitchCaseToolMapping() to resolve op ID → tool ID mismatches
where block switch statements map differently (e.g. HubSpot get_carts →
hubspot_list_carts)
- Fix double fs.readFileSync in writeIntegrationsJson — reuse existing
fileContent variable instead of re-reading the file
- Remove 5 dead functions superseded by *FromContent variants
- Simplify extractToolsAccessFromContent to use matchAll
- fix(upstash): replace template literal tool ID with explicit switch cases
@waleedlatif1

Copy link
Copy Markdown
Collaborator

@cursor review

@waleedlatif1

Copy link
Copy Markdown
Collaborator

@greptile

@cursorcursorBot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Comment threadapps/sim/blocks/blocks/upstash.ts Outdated
@waleedlatif1
waleedlatif1 merged commit be6b00d into stagingMar 25, 2026
6 checks passed
@waleedlatif1
waleedlatif1 deleted the feat/demo-modal branch March 25, 2026 22:30
waleedlatif1 added a commit that referenced this pull request Mar 27, 2026
* fix(ui): add request a demo modal
* Remove dead code
* Remove footer modal
* Address greptile comments
* Sanatize CRLF characters from emails
* extract shared email header safety regex
Co-authored-by: Theodore Li <TheodoreSpeaks@users.noreply.github.com>
* Use pricing CTA action for demo modal
Co-authored-by: Theodore Li <TheodoreSpeaks@users.noreply.github.com>
* fix demo request import ordering
Co-authored-by: Theodore Li <TheodoreSpeaks@users.noreply.github.com>
* merge staging and fix hubspot list formatting
Co-authored-by: Theodore Li <TheodoreSpeaks@users.noreply.github.com>
* fix(generate-docs): fix tool description extraction and simplify script
- Fix endsWith over-matching: basename === 'index.ts'/'types.ts' instead
of endsWith(), which was silently skipping valid tool files like
list_leave_types.ts, delete_index.ts, etc.
- Add extractSwitchCaseToolMapping() to resolve op ID → tool ID mismatches
where block switch statements map differently (e.g. HubSpot get_carts →
hubspot_list_carts)
- Fix double fs.readFileSync in writeIntegrationsJson — reuse existing
fileContent variable instead of re-reading the file
- Remove 5 dead functions superseded by *FromContent variants
- Simplify extractToolsAccessFromContent to use matchAll
- fix(upstash): replace template literal tool ID with explicit switch cases
* fix(generate-docs): restore extractIconName by aliasing to extractIconNameFromContent
* restore
* fix(demo-modal): reset form on open to prevent stale success state on reopen
* undo hardcoded ff
* fix(upstash): throw on unknown operation instead of silently falling back to get
---------
Co-authored-by: Theodore Li <teddy@zenobiapay.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Theodore Li <TheodoreSpeaks@users.noreply.github.com>
Co-authored-by: waleed <walif6@gmail.com>
waleedlatif1 added a commit that referenced this pull request Apr 4, 2026
* fix(ui): add request a demo modal
* Remove dead code
* Remove footer modal
* Address greptile comments
* Sanatize CRLF characters from emails
* extract shared email header safety regex
Co-authored-by: Theodore Li <TheodoreSpeaks@users.noreply.github.com>
* Use pricing CTA action for demo modal
Co-authored-by: Theodore Li <TheodoreSpeaks@users.noreply.github.com>
* fix demo request import ordering
Co-authored-by: Theodore Li <TheodoreSpeaks@users.noreply.github.com>
* merge staging and fix hubspot list formatting
Co-authored-by: Theodore Li <TheodoreSpeaks@users.noreply.github.com>
* fix(generate-docs): fix tool description extraction and simplify script
- Fix endsWith over-matching: basename === 'index.ts'/'types.ts' instead
of endsWith(), which was silently skipping valid tool files like
list_leave_types.ts, delete_index.ts, etc.
- Add extractSwitchCaseToolMapping() to resolve op ID → tool ID mismatches
where block switch statements map differently (e.g. HubSpot get_carts →
hubspot_list_carts)
- Fix double fs.readFileSync in writeIntegrationsJson — reuse existing
fileContent variable instead of re-reading the file
- Remove 5 dead functions superseded by *FromContent variants
- Simplify extractToolsAccessFromContent to use matchAll
- fix(upstash): replace template literal tool ID with explicit switch cases
* fix(generate-docs): restore extractIconName by aliasing to extractIconNameFromContent
* restore
* fix(demo-modal): reset form on open to prevent stale success state on reopen
* undo hardcoded ff
* fix(upstash): throw on unknown operation instead of silently falling back to get
---------
Co-authored-by: Theodore Li <teddy@zenobiapay.com>
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Theodore Li <TheodoreSpeaks@users.noreply.github.com>
Co-authored-by: waleed <walif6@gmail.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

@TheodoreSpeaks@waleedlatif1@cursoragent