pediatric-ai-scribe-v3/public/app/assets/VaxSchedule-WVnLWGX9.js
Daniel c157c709c8 feat: React auth screen + SPA now serves every route (vanilla retired)
The final big piece of "everything in React + Tailwind". Login,
register, forgot-password, reset-password, and email-verification all
render from the React bundle now. The root path / serves the SPA,
vanilla index.html + public/js/* are no longer served by the server.

BACKEND — src/routes/auth.ts
  New GET /api/auth/public-config (public — no auth required) returns
  { registrationEnabled, turnstileSiteKey, oidcEnabled,
    disableLocalAuth, ssoButtonLabel }.
  Single round-trip the React auth screen needs on mount. Reuses
  existing DB settings; no new tables.

BACKEND — server.ts
  • / and /index.html now send public/app/index.html (React SPA),
    not public/index.html (vanilla).
  • /auth, /reset-password, /verify-email explicitly route to the SPA
    so the email links land on the React router.
  • /app/*splat preserved as an alias so old bookmarks keep working.
  • SPA fallback added after express.static so hard-refresh on
    /encounter / /bedside / /settings etc. serves the React index
    instead of 404ing. API paths and static-file extensions still
    fall through to their existing handlers.
  • The dead app.get('/') duplicate that also pointed at the vanilla
    index is removed.

CLIENT — React auth flow
  client/src/pages/Auth.tsx (new)
    Login / register / forgot sub-forms with a single useQuery on
    ['public-config'] driving Turnstile + SSO button visibility.
    Login flow handles all three vanilla-equivalent responses
    (token / requires2FA / needsVerification). 2FA field reveals
    inline when the server asks for it; resend-verification link
    appears when needsVerification fires. SSO button renders
    whenever oidcEnabled is true, even if local auth is disabled
    (disableLocalAuth hides the login/register/forgot forms
    entirely). HIPAA notice + APK download link preserved.

  client/src/pages/ResetPassword.tsx (new)
    Reads ?token=xxx from the URL, POSTs /api/auth/reset-password.
    Confirm-password match, 8+ char validation, server
    passwordWarning (pwned password) surfaces as an amber info box.
    Redirects to /auth 2.5 s after success.

  client/src/components/Turnstile.tsx (new)
    Loads the challenges.cloudflare.com/turnstile script once,
    renders a widget per form, calls onToken(token) on success and
    onToken('') on error / expiry. If siteKey is null/empty (e2e
    container with TURNSTILE_SITE_KEY="") renders nothing and
    auto-reports empty — matches the vanilla no-key-no-widget
    behaviour.

  client/src/components/AuthGuard.tsx (new)
    useQuery(['auth-me']) with retry: false. On 401/error redirects
    to /auth?next=<current-url> so the deep link survives sign-in.
    Used as a parent route in App.tsx wrapping every private page.

  client/src/components/Layout.tsx
    "← back to legacy app" link replaced with "Sign out" — calls
    POST /api/auth/logout then window.location = /auth.

  client/src/App.tsx
    BrowserRouter no longer has basename (was "/app"). Public
    routes: /auth, /reset-password. Everything else lives under
    <AuthGuard> → <Layout>. Lazy-loaded Auth + ResetPassword join
    the existing heavy-route code-split.

  client/vite.config.ts
    base stays "/app/" so hashed asset URLs resolve to
    /app/assets/... (served unchanged by express.static).

shared/types.ts + client/src/shared/types.ts — additive:
  PublicConfigOk { registrationEnabled, turnstileSiteKey,
    oidcEnabled, disableLocalAuth, ssoButtonLabel }.

Bundle — Auth chunk splits out at 10.87 kB / 3.35 kB gz, lazy-loaded
only on the sign-in path; initial bundle unchanged at 343.97 kB /
106.59 kB gz.

Backend tsc + client tsc + vite build + 136/136 vitest all green.
2026-04-24 02:45:21 +02:00

1 line
No EOL
2.2 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import{t as e}from"./jsx-runtime-ByY1xr43.js";import{i as t,l as n}from"./index-BuU36CvS.js";var r=e();function i(){let{data:e,isLoading:i,error:a}=n({queryKey:[`schedule-data`],queryFn:()=>t.get(`/api/schedule-data`)});if(i)return(0,r.jsx)(`div`,{className:`p-6 text-sm text-muted-foreground`,children:`Loading schedule…`});if(a)return(0,r.jsx)(`div`,{className:`p-6 text-sm text-destructive`,children:a.message});if(!e)return null;let o=e.visitAges.filter(t=>e.periodicity[t.id]?.vaccines?.length),s=[],c=new Set;return o.forEach(t=>{e.periodicity[t.id].vaccines.forEach(e=>{c.has(e.vaccine)||(c.add(e.vaccine),s.push(e.vaccine))})}),(0,r.jsxs)(`div`,{className:`max-w-full mx-auto p-6 space-y-4`,children:[(0,r.jsxs)(`header`,{children:[(0,r.jsx)(`h1`,{className:`text-2xl font-semibold`,children:`Vaccine Schedule`}),(0,r.jsx)(`p`,{className:`text-sm text-muted-foreground`,children:`AAP/ACIP 2025 complete immunization schedule (018 years).`})]}),(0,r.jsx)(`div`,{className:`rounded-lg border border-border overflow-auto bg-card`,children:(0,r.jsxs)(`table`,{className:`text-xs`,children:[(0,r.jsx)(`thead`,{className:`sticky top-0 bg-muted`,children:(0,r.jsxs)(`tr`,{children:[(0,r.jsx)(`th`,{className:`text-left font-semibold px-3 py-2 border-b border-border min-w-[180px] sticky left-0 bg-muted`,children:`Vaccine`}),o.map(e=>(0,r.jsx)(`th`,{className:`px-2 py-2 border-b border-border text-center whitespace-nowrap`,children:e.label},e.id))]})}),(0,r.jsx)(`tbody`,{children:s.map(t=>(0,r.jsxs)(`tr`,{className:`even:bg-muted/20`,children:[(0,r.jsx)(`td`,{className:`px-3 py-2 border-b border-border font-medium sticky left-0 bg-card`,children:e.vaccineFullNames[t]||t}),o.map(n=>{let i=(e.periodicity[n.id].vaccines||[]).find(e=>e.vaccine===t);if(!i)return(0,r.jsx)(`td`,{className:`border-b border-border`},n.id);let a=typeof i.dose==`number`?`#`+i.dose:i.dose||``;return(0,r.jsx)(`td`,{className:`border-b border-border text-center bg-primary/10 font-mono text-[11px]`,title:i.notes||`${t} dose ${i.dose}`,children:a},n.id)})]},t))})]})}),(0,r.jsx)(`p`,{className:`text-xs text-muted-foreground`,children:`Hover any filled cell for notes. Sources: AAP/Bright Futures (Feb 2025), CDC Child & Adolescent Immunization Schedule (2025).`})]})}export{i as default};