`npm run lint` reported 99 errors and 26 warnings across 65 files and had presumably been failing for a while — next.config.ts sets eslint.ignoreDuringBuilds, so the build never surfaced it. 68 were in vendored code: components/charts and components/ai-elements, pulled from upstream registries. Re-linting those reports upstream's style back at us, and "fixing" them means diverging and eating conflicts on every update — ai-elements already has exactly this carve-out on the TypeScript side via ignoreBuildErrors. Ignore both, plus the two registry files in components/ui (carousel publishes its api from an effect; the sidebar skeleton picks a random width). The other 31 were ours, nearly all react-hooks/set-state-in-effect on the same shape: an effect that re-seeds form state when a dialog opens or a selection changes. Moved to render-phase adjustment, which is both what React recommends and a real fix — the effect version paints one frame of the *previous* record's values before correcting itself. Two carried sharper bugs: the employee dialog could keep a typed password across a switch to another member, and use-wallet-sync could carry `linked` over to a newly-selected patient, briefly offering to push a record to someone else's wallet. The rest: useIsMobile and speech-support detection become useSyncExternalStore (correct on first paint, no mount flash); refs mirroring state are written in effects rather than during render; the care-team fetch moves into its effect behind a reload key, dropping an exhaustive-deps suppression. Two effects in chat-panel keep the rule disabled with a reason. Both are what effects are for: draining the queued-message buffer when the transport goes idle, and resolving ?thread from the URL — the latter mints an id with nanoid(), so moving it into render would just trade this error for a purity one. Also removes a dead /explore-era import and unused directives found on the way. lint now exits 0, with the ai-elements tsc carve-out unchanged. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
temetro frontend
The clinician-facing AI chat UI for temetro — a Next.js 16 app where
clinicians retrieve and organize patient data in natural language, rendered as
rich record cards. It's wired to the ../backend for real auth,
multi-tenant clinics, and live patient data.
Stack
Next.js 16 (App Router) · React 19 · TypeScript · Tailwind CSS v4 · COSS UI components (Base UI) · i18next · Socket.io client.
This app runs a customized Next.js 16 whose conventions differ from the public docs (e.g. route protection lives in
proxy.ts, notmiddleware.ts). SeeCLAUDE.mdandnode_modules/next/dist/docs/before writing Next.js code.
Develop
npm install
npm run dev # Next dev server (Turbopack) on http://localhost:3000
Other scripts: npm run build (production build), npm run start (serve the
build), npm run lint. There is no test runner — verify changes by running the
dev server.
Talking to the backend
The frontend needs the API running (see ../backend). It resolves
the backend URL from the host you open the app on — so it works on
localhost and across the clinic LAN (http://<server-IP>:3000) without a
rebuild. Set NEXT_PUBLIC_API_URL only to pin a fixed or reverse-proxied URL;
see lib/backend-url.ts.
Architecture
app/— App Router.app/(app)/is the authenticated product shell;app/(auth)/holds the login / signup / onboarding pages.components/chat/— the chat UI (input, message state, patient cards).components/settings/— settings panels, including About & updates (version + LAN access).components/ui/— COSS primitives (Base UI, added via the shadcn CLI).lib/— API + auth clients, i18n, and data helpers.
See CLAUDE.md for the full architecture, theming, and gotchas.
License
MIT.
