Skip to content

Mock vs real

The demo runs in the browser. Every server-side concern is mocked or stubbed. This page walks the seam between demo and production line by line so a future build can replace each piece independently.

LayerDemoProduction
HostingVercel static + client-only routesVercel with Node runtime for API routes
StateZustand persist to localStoragePostgres (Neon or Vercel Postgres) + Vercel KV
AuthNone (rep-name in localStorage, no admin login)Signed retailer URL, magic link for customer, Auth.js session for admin
EmailNonePostmark (preferred) or SES
SMSNoneMessageBird or Twilio (optional)
PDFStyled HTML accordion@react-pdf/renderer, stored on Vercel Blob
Audit logHardcoded fixtures in lib/fixtures.tsaudit_events table, append-only
Rate limitingNoneVercel KV counters per resource
Observabilityconsole.logVercel Analytics + structured logs to Logflare or Axiom
ConcernDemoProduction
ContentStatic MDX/TSX in app/page.tsx and components/marketing/Same. No backend interaction.
Embedded previewLive React component at 0.7 scaleSame
FormsNone (the “Open the demo” CTA is a link)Optional: a “request a demo” form posting to a CRM. Out of scope for v1.

The marketing surface is real in both modes. The only delta is whether a “request a demo” form exists.

ConcernDemoProduction
BootstrapReads skin from URL, looks up SKINS[skinId]GET /api/retailer/me from signed URL, returns {retailer, brandKit, catalogue}
Rep identityFree-text name in localStorageSame. Signed URL identifies the retailer; rep name is captured per device, not authenticated.
Form stateZustand inFlightQuoteSame on the client.
Send to customersetInFlightQuote(...) then route to /demo/customer/[token]POST /api/quote returns 201, then redirect rep to a “sent” confirmation. Token is generated server-side.
In-store fallbackSame surface re-renders customer view inlinePOST /api/quote with inStoreFallback: true returns the validated token; the rep tablet renders the customer view inline.
Rate limitNone10 quote-creates/min per signed retailer URL
ConcernDemoProduction
Token validationNone (any string accepted)HMAC-SHA256 + nonce blocklist + expiry check
Quote sourceReads inFlightQuote from ZustandGET /api/quote/by-token/{token} returns the quote row
Catalogue sourcegetCatalogue(skinId) from lib/catalogue.tsSame shape, joined on the server side, returned with the quote
Comparison mathslib/finance-math.ts, in-browserSame code, same module. Math runs client-side; the server stores raw inputs only.
Budget calculatorPure clientSame
Acknowledgement submitLocal setCustomerAck, then route to /demo/adminPOST /api/quote/{id}/acknowledge with AcknowledgementSchema body. Server appends option-picked and acknowledgements-confirmed events, transitions status to acknowledged.
PDF previewStyled HTML accordion in components/customer/pdf-preview.tsx@react-pdf/renderer generates a PDF on acknowledgements-confirmed, uploads to Vercel Blob, stores the URL in the quote row. The customer sees a “download your quote” link.
Confirmation receiptNoneEmail receipt to customer with PDF attached
Rate limitNone5 magic-link opens/min per token
ConcernDemoProduction
AuthNoneAuth.js with passwordless email or SSO
RBACNoneadmin, auditor, read-only (see permissions and contracts)
Dashboard datacomputeKpis(skinId) over fixturesGET /api/admin/kpis with date range, returns same shape
Quote listgetQuotesForSkin(skinId)GET /api/admin/quotes?status=&rep=&from=&to=&page=
Quote detailgetQuoteById(skinId, id)GET /api/admin/quote/{id}
Resend magic linkn/aPOST /api/admin/quote/{id}/resend (admin only)
CSV exportStatic fixture downloadReal query, streamed CSV
PDF downloadStatic placeholderSigned URL to Vercel Blob
Rate limitNone30 admin reads/min per session
ConcernDemoProduction
Coach markslib/walkthrough.ts step machine, rendered by components/shell/walkthrough-overlay.tsxRemoved. The walkthrough is a marketing-and-demo affordance, not a production feature.
Free-explore modePersona switcher, skin switcher, surface tabsRemoved

The walkthrough is demo-only.

ConcernDemoProduction
SwitchingURL param + localStorage. All three skins live in the bundle.Removed. Each retailer deploys with their own brand kit; no cross-retailer skin switcher.

What is identical between demo and production

Section titled “What is identical between demo and production”

A surprising amount.

  • The four surfaces (marketing, rep, customer, admin) live at the same routes.
  • The TypeScript types in lib/skins.ts, lib/catalogue.ts, lib/finance-math.ts, and lib/state.ts are unchanged.
  • The finance maths in lib/finance-math.ts is reused verbatim. Same monthly payment, same target-payment binary search, same badge logic.
  • The Tailwind tokens, the shadcn components, the Geist font, the motion transitions: all identical.
  • The AdminQuote shape, the AuditEvent shape, the QuoteStatus enum: identical.

The boundary is narrow. It is concentrated at three points: token signing, persistence, and email delivery.