chore: import upstream snapshot with attribution
CI / Migrate Dev DB (push) Has been skipped
CI / Detect Version (push) Has been cancelled
CI / Migrate DB (push) Has been cancelled
CI / Build Dev ECR (./docker/app.Dockerfile, ECR_APP) (push) Has been cancelled
CI / Build Dev ECR (./docker/db.Dockerfile, ECR_MIGRATIONS) (push) Has been cancelled
CI / Build Dev ECR (./docker/pii.Dockerfile, ECR_PII) (push) Has been cancelled
CI / Build Dev ECR (./docker/realtime.Dockerfile, ECR_REALTIME) (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
CI / Build AMD64 (./docker/app.Dockerfile, ECR_APP, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build AMD64 (./docker/db.Dockerfile, ECR_MIGRATIONS, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (./docker/pii.Dockerfile, ECR_PII, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (./docker/realtime.Dockerfile, ECR_REALTIME, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/app.Dockerfile, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/db.Dockerfile, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/pii.Dockerfile, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/realtime.Dockerfile, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Test and Build (push) Has been cancelled
Publish CLI Package / publish-npm (push) Has been cancelled
Publish Python SDK / publish-pypi (push) Has been cancelled
Publish TypeScript SDK / publish-npm (push) Has been cancelled

This commit is contained in:
wehub-resource-sync
2026-07-13 13:20:55 +08:00
commit d25d482dc2
13754 changed files with 4996608 additions and 0 deletions
@@ -0,0 +1,104 @@
'use client'
import { type CSSProperties, useEffect, useRef, useState } from 'react'
import { chipBorderShadowRing, cn } from '@sim/emcn'
import dynamic from 'next/dynamic'
import { preconnect } from 'react-dom'
import { DemoForm, type DemoLead } from '@/app/(landing)/demo/components/demo-form'
const importScheduler = () => import('@/app/(landing)/demo/components/demo-scheduler')
/**
* Warm the entire booking path while the visitor fills the form: preconnect to
* app.cal.com, then load the scheduler chunk, Cal.com's embed.js, and the
* booker iframe assets (via the embed's `preload` instruction). Fired on first
* form focus so nothing Cal.com-related competes with initial page load — the
* connection handshake overlaps the chunk import, and it all finishes long
* before the visitor submits.
*/
function preloadScheduler() {
preconnect('https://app.cal.com')
return importScheduler().then((m) => m.preloadCalEmbed())
}
/**
* Lazy-loaded so the Cal.com embed never enters the initial landing bundle - it
* loads only once a visitor reaches the booking step. `loading: () => null` (no
* skeleton): the panel is already sized and the slide covers the brief load, so
* there is no flash-then-resize.
*/
const DemoScheduler = dynamic(() => importScheduler().then((m) => m.DemoScheduler), {
ssr: false,
loading: () => null,
})
interface DemoBookingProps {
/** Layout/placement classes (grid cell). Never chrome. */
className?: string
}
/**
* The demo page's right column: a two-step booking card and the only client
* island on the page. Owns the card chrome and the step transition.
*
* The form (panel 1) and scheduler (panel 2) sit side by side in a sliding
* track; submitting slides one-way to the scheduler at `duration-200 ease-out`.
* The form stays mounted and drives the card height, so the card never resizes
* across the transition; a `ResizeObserver` keeps the pinned height in sync as
* the form grows (inline error, phone breakpoint). The off-screen panel is
* `inert` (out of tab/AT order) and the scheduler lazy-mounts on submit,
* preloaded on first form focus.
*
* The pin is published as the `--demo-card-h` CSS var (not an inline `height`)
* so a `max-sm:h-[80svh]` class can override it once the scheduler shows — the
* Cal booker needs a real viewport on phones instead of being crammed into the
* short form height. `svh` keeps tap targets from shifting as the mobile URL bar
* hides/shows.
*/
export function DemoBooking({ className }: DemoBookingProps) {
const [lead, setLead] = useState<DemoLead | null>(null)
const [formHeight, setFormHeight] = useState<number>()
const formRef = useRef<HTMLDivElement>(null)
const showScheduler = lead !== null
useEffect(() => {
const node = formRef.current
if (!node) return
const measure = () => setFormHeight(node.offsetHeight)
measure()
const observer = new ResizeObserver(measure)
observer.observe(node)
return () => observer.disconnect()
}, [])
return (
<div
className={cn(
'relative min-w-0 overflow-hidden rounded-lg bg-[var(--surface-2)]',
chipBorderShadowRing,
'h-[var(--demo-card-h)]',
showScheduler && 'max-sm:h-[80svh]',
className
)}
style={{ '--demo-card-h': formHeight ? `${formHeight}px` : undefined } as CSSProperties}
>
<div
className='flex h-full w-full transition-transform duration-200 ease-out motion-reduce:transition-none'
style={{ transform: showScheduler ? 'translateX(-100%)' : undefined }}
>
<div
className='w-full min-w-0 shrink-0'
inert={showScheduler}
onFocusCapture={() => void preloadScheduler()}
>
<div ref={formRef} className='p-6 max-sm:p-5'>
<DemoForm onComplete={setLead} />
</div>
</div>
<div className='h-full w-full min-w-0 shrink-0' inert={!showScheduler}>
{lead ? <DemoScheduler lead={lead} /> : null}
</div>
</div>
</div>
)
}
@@ -0,0 +1 @@
export { DemoBooking } from './demo-booking'
@@ -0,0 +1,350 @@
'use client'
import { type ReactNode, useEffect, useId, useState } from 'react'
import { Chip, ChipDropdown, ChipInput, ChipTextarea, Label } from '@sim/emcn'
import {
DEMO_REQUEST_COMPANY_SIZE_OPTIONS,
type DemoRequestBody,
} from '@/lib/api/contracts/demo-requests'
import { isFreeEmailDomain } from '@/lib/messaging/email/free-email'
import { quickValidateEmail } from '@/lib/messaging/email/validation'
import { useSubmitDemoRequest } from '@/hooks/queries/demo-requests'
/** Options for the "What can we help you with?" select; the first is the default. */
const TOPIC_OPTIONS = [
{ value: 'demo', label: 'Book a demo' },
{ value: 'other', label: 'Other' },
] as const
/**
* Field control height - slightly taller than the 30px in-app chip default and
* just under the 36px auth field, so the booking form reads as a roomy landing
* surface. Applied to each control's `className`, the sanctioned way to own only
* a chip field's height (mirrors `AuthInput`).
*/
const FIELD_HEIGHT = 'h-[34px]'
/**
* The form's working state. On submit it maps onto the `demo-requests` contract
* payload (the sales notification) and the {@link DemoLead} handed to the
* scheduler - kept as one object so both mappings read from a single source.
*/
interface DemoFormState {
firstName: string
lastName: string
email: string
phone: string
company: string
companySize: string
topic: string
message: string
}
const INITIAL_STATE: DemoFormState = {
firstName: '',
lastName: '',
email: '',
phone: '',
company: '',
companySize: '',
topic: TOPIC_OPTIONS[0].value,
message: '',
}
/** The captured lead handed to the Cal.com scheduler to prefill the booking. */
export interface DemoLead {
/** Full name - `${firstName} ${lastName}`. */
name: string
/** Work email. */
email: string
/** A readable summary of the company/role/topic, shown on the booking. */
notes: string
}
interface DemoFormProps {
/** Called with the captured {@link DemoLead} when a valid form is submitted. */
onComplete: (lead: DemoLead) => void
}
/** Resolve an option's display label from its value, falling back to the value. */
const labelFor = (options: ReadonlyArray<{ value: string; label: string }>, value: string) =>
options.find((option) => option.value === value)?.label ?? value
/** Compose the booking notes from the structured fields, skipping empty optionals. */
function buildNotes(form: DemoFormState): string {
return [
`Company: ${form.company}`,
`Company size: ${labelFor(DEMO_REQUEST_COMPANY_SIZE_OPTIONS, form.companySize)}`,
form.phone && `Phone: ${form.phone}`,
`Topic: ${labelFor(TOPIC_OPTIONS, form.topic)}`,
form.message && `Notes: ${form.message}`,
]
.filter(Boolean)
.join('\n')
}
/**
* Compose the sales-notification `details` from the free-form fields the typed
* payload doesn't carry on its own - the company name, the topic, and any
* message. Always non-empty (company and topic are required), satisfying the
* contract's `details` minimum.
*/
function buildDetails(form: DemoFormState): string {
return [
`Company: ${form.company}`,
`Topic: ${labelFor(TOPIC_OPTIONS, form.topic)}`,
form.message && `\nNotes:\n${form.message}`,
]
.filter(Boolean)
.join('\n')
}
interface DemoFieldProps {
label: string
/** Set for native controls (inputs/textarea) to associate the label by `id`. */
htmlFor?: string
required?: boolean
error?: string
/** The control. Dropdowns (no `htmlFor`) are wrapped in a labeled group. */
children: ReactNode
}
/**
* A labeled field row matching the chip field rhythm (`gap-[9px]`, muted label,
* caption-sized error). Native controls associate via `htmlFor`/`id`; controls
* that can't take a label `id` (the dropdowns) become a `role='group'` named by
* the label instead, so every field has an accessible name.
*/
function DemoField({ label, htmlFor, required, error, children }: DemoFieldProps) {
const labelId = useId()
const isGroup = htmlFor === undefined
return (
<div
className='flex flex-col gap-[9px]'
role={isGroup ? 'group' : undefined}
aria-labelledby={isGroup ? labelId : undefined}
>
<Label id={labelId} htmlFor={htmlFor} className='pl-0.5 font-normal text-[var(--text-muted)]'>
{label}
{required ? (
<span aria-hidden className='ml-0.5 text-[var(--text-error)]'>
*
</span>
) : null}
</Label>
{children}
{error ? <p className='pl-0.5 text-[var(--text-error)] text-caption'>{error}</p> : null}
</div>
)
}
/**
* Step 1 of the booking card - the demo-request form. Rendered inside the card
* chrome owned by {@link DemoBooking}, so it returns just its heading and fields.
*
* Fields are hand-composed at the slightly-taller {@link FIELD_HEIGHT} (the
* sanctioned standalone-field pattern - `Label` + a height-raised chip control),
* stacked at the platform `gap-4` rhythm with no divider lines. Optional fields
* carry an `(optional)` suffix; the "What can we help you with?" select defaults
* to its first option ("Book a demo"); every other field is required. The email
* runs through {@link quickValidateEmail}, its error surfaces only once the value
* looks like an address attempt, and the primary {@link Chip} stays disabled until
* every required field is valid.
*
* On a valid submit it fires the inbound-demo notification to sales (via
* {@link useSubmitDemoRequest}, best-effort - never blocking) and composes a
* {@link DemoLead} (name, email, and a notes summary) handed to `onComplete`,
* which advances the card to the scheduler.
*/
export function DemoForm({ onComplete }: DemoFormProps) {
const submitDemoRequest = useSubmitDemoRequest()
const [form, setForm] = useState<DemoFormState>(INITIAL_STATE)
const setField = (key: keyof DemoFormState) => (value: string) =>
setForm((prev) => ({ ...prev, [key]: value }))
/**
* Prefill the email from the `?email=` param the landing hero's CTA forwards
* (its "Book a demo" GET form). Read once on mount from `window.location` -
* not `useSearchParams` - so the page stays statically rendered with no
* Suspense bailout; server and first client render are both empty, so there is
* no hydration mismatch. Only seeds when the field is still untouched.
*/
useEffect(() => {
const prefill = new URLSearchParams(window.location.search).get('email')?.trim()
if (prefill) setForm((prev) => (prev.email ? prev : { ...prev, email: prefill }))
}, [])
const trimmedEmail = form.email.trim()
const emailFormatValid = trimmedEmail.length > 0 && quickValidateEmail(trimmedEmail).isValid
const emailIsFreeDomain = isFreeEmailDomain(trimmedEmail)
const emailIsValid = emailFormatValid && !emailIsFreeDomain
const canSubmit =
emailIsValid &&
form.firstName.trim().length > 0 &&
form.lastName.trim().length > 0 &&
form.company.trim().length > 0 &&
form.companySize.length > 0
/**
* Surface an error only once the value looks like an address attempt (contains
* `@`) so the field doesn't flash on the first keystroke, and distinguish a
* malformed address from a personal one so the visitor knows to switch to a
* work email — matching the server's work-email requirement.
*/
const emailError = !form.email.includes('@')
? undefined
: !emailFormatValid
? 'Enter a valid work email address.'
: emailIsFreeDomain
? 'Please use your work email address.'
: undefined
const handleSubmit = () => {
if (!canSubmit) return
// Best-effort sales notification — fire-and-forget so it never blocks scheduling.
submitDemoRequest.mutate({
firstName: form.firstName.trim(),
lastName: form.lastName.trim(),
companyEmail: trimmedEmail,
phoneNumber: form.phone.trim() || undefined,
companySize: form.companySize as DemoRequestBody['companySize'],
details: buildDetails(form),
})
onComplete({
name: `${form.firstName} ${form.lastName}`.trim(),
email: trimmedEmail,
notes: buildNotes(form),
})
}
return (
<>
<h2 id='demo-form-heading' className='text-[var(--text-primary)] text-xl leading-[1.2]'>
Book a demo now
</h2>
<p className='mt-1.5 text-[var(--text-muted)] text-sm'>
Tell us about your team and we'll tailor your demo to what you're building.
</p>
<form
onSubmit={(event) => {
event.preventDefault()
handleSubmit()
}}
aria-labelledby='demo-form-heading'
className='mt-5 flex flex-col gap-4'
noValidate
>
<div className='grid grid-cols-2 gap-3 max-sm:grid-cols-1'>
<DemoField label='First name' htmlFor='demo-first-name' required>
<ChipInput
id='demo-first-name'
className={FIELD_HEIGHT}
value={form.firstName}
onChange={(event) => setField('firstName')(event.target.value)}
placeholder='Jane'
autoComplete='given-name'
/>
</DemoField>
<DemoField label='Last name' htmlFor='demo-last-name' required>
<ChipInput
id='demo-last-name'
className={FIELD_HEIGHT}
value={form.lastName}
onChange={(event) => setField('lastName')(event.target.value)}
placeholder='Doe'
autoComplete='family-name'
/>
</DemoField>
</div>
<DemoField label='Work email' htmlFor='demo-email' required error={emailError}>
<ChipInput
id='demo-email'
type='email'
className={FIELD_HEIGHT}
value={form.email}
onChange={(event) => setField('email')(event.target.value)}
error={Boolean(emailError)}
placeholder='jane@acme.co'
autoComplete='email'
/>
</DemoField>
<DemoField label='Phone number (optional)' htmlFor='demo-phone'>
<ChipInput
id='demo-phone'
type='tel'
className={FIELD_HEIGHT}
value={form.phone}
onChange={(event) => setField('phone')(event.target.value)}
placeholder='+1 (555) 000-0000'
autoComplete='tel'
/>
</DemoField>
<DemoField label='Company' htmlFor='demo-company' required>
<ChipInput
id='demo-company'
className={FIELD_HEIGHT}
value={form.company}
onChange={(event) => setField('company')(event.target.value)}
placeholder='Acme Inc.'
autoComplete='organization'
/>
</DemoField>
<DemoField label='Company size' required>
<ChipDropdown
fullWidth
flush
className={FIELD_HEIGHT}
value={form.companySize || undefined}
onChange={setField('companySize')}
options={DEMO_REQUEST_COMPANY_SIZE_OPTIONS}
placeholder='Select one'
/>
</DemoField>
<DemoField label='What can we help you with?'>
<ChipDropdown
fullWidth
flush
className={FIELD_HEIGHT}
value={form.topic}
onChange={setField('topic')}
options={TOPIC_OPTIONS}
/>
</DemoField>
<DemoField label='Anything else we should know? (optional)' htmlFor='demo-message'>
<ChipTextarea
id='demo-message'
value={form.message}
onChange={(event) => setField('message')(event.target.value)}
placeholder='What are you hoping to build with Sim?'
rows={3}
/>
</DemoField>
{/*
`Chip` gives its label span `flex-1`; under `fullWidth` that left-aligns the
label, so center it with `justify-center` + a `flex-none` span override.
*/}
<Chip
type='submit'
variant='primary'
flush
fullWidth
disabled={!canSubmit}
className='mt-1 justify-center [&>span]:flex-none'
>
Continue
</Chip>
</form>
</>
)
}
@@ -0,0 +1 @@
export { DemoForm, type DemoLead } from './demo-form'
@@ -0,0 +1,92 @@
'use client'
import { useEffect } from 'react'
import Cal, { getCalApi } from '@calcom/embed-react'
import type { DemoLead } from '@/app/(landing)/demo/components/demo-form'
/** The Cal.com event the demo books - set `NEXT_PUBLIC_CAL_LINK` to override. */
const CAL_NAMESPACE = 'demo'
const CAL_LINK = process.env.NEXT_PUBLIC_CAL_LINK ?? 'team/sim/demo'
/**
* Sim's brand color, matching the `--brand-agent` token. The embed renders in a
* cross-origin iframe, so it can't read our CSS vars - it needs the literal hex.
*/
const CAL_BRAND_COLOR = '#6f3dfa'
interface DemoSchedulerProps {
/** The captured lead used to prefill the Cal.com booking. */
lead: DemoLead
}
let calEmbedPreloaded = false
/**
* Warm the Cal.com embed before the scheduler mounts. Loads `embed.js` and
* issues the embed's `preload` instruction, which fetches the booker in a
* hidden `?preload=true` iframe so its assets are already cached when the real
* embed renders on submit. Without this, nothing Cal.com-related starts
* downloading until the visitor presses Continue, which is why the calendar
* used to take several seconds to appear. Idempotent — repeat calls no-op
* while a warm-up is in flight or done, but a failed embed.js load resets the
* flag so a later focus can retry.
*/
export function preloadCalEmbed(): void {
if (calEmbedPreloaded) return
calEmbedPreloaded = true
getCalApi({ namespace: CAL_NAMESPACE })
.then((cal) => {
cal('preload', { calLink: CAL_LINK })
})
.catch(() => {
calEmbedPreloaded = false
})
}
/**
* Step 2 of the booking card - the Cal.com scheduler, prefilled from the form's
* {@link DemoLead}. Rendered inside the card chrome owned by {@link DemoBooking}
* and lazy-loaded, so the embed script never touches the initial landing bundle.
*
* The embed is pinned to the page's light theme and Sim's brand color, and the
* captured name/email/notes prefill the booking so the visitor never retypes. It
* fills the panel (`flex-1`), which the parent sizes to the form's height, so the
* card stays the same height across the form→calendar transition.
*/
export function DemoScheduler({ lead }: DemoSchedulerProps) {
useEffect(() => {
getCalApi({ namespace: CAL_NAMESPACE }).then((cal) => {
cal('ui', {
hideEventTypeDetails: true,
styles: { branding: { brandColor: CAL_BRAND_COLOR } },
})
})
}, [])
return (
<div className='flex h-full min-w-0 flex-col p-6 max-sm:p-5'>
<h2 className='text-[var(--text-primary)] text-xl leading-[1.2]'>
Pick a time{lead.name ? `, ${lead.name}` : ''}
</h2>
<p className='mt-1.5 text-[var(--text-muted)] text-sm'>
Choose a slot that works for your team and we'll send a calendar invite.
</p>
<div className='mt-5 min-h-0 flex-1'>
<Cal
namespace={CAL_NAMESPACE}
calLink={CAL_LINK}
style={{ width: '100%', height: '100%', overflow: 'auto' }}
config={{
name: lead.name,
email: lead.email,
notes: lead.notes,
theme: 'light',
'ui.color-scheme': 'light',
layout: 'month_view',
useSlotsViewOnSmallScreen: 'true',
}}
/>
</div>
</div>
)
}
@@ -0,0 +1 @@
export { DemoScheduler, preloadCalEmbed } from './demo-scheduler'