Apply the same COSS Separated Panels treatment used in the other settings
frames (Preferences, AI) to the Care team section: pass separated to its
SettingsSection so the cards render as distinct panels on a muted tray.
The members list keeps p-0/overflow-hidden so its divided rows sit flush
inside their panel.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replace the gap-4-on-CardFrame hack with a faithful COSS Frame: a muted
tray (bg-muted/72) holding distinct bordered FramePanels spaced by mt-1,
matching coss.com/ui/docs/components/frame. Adds components/ui/frame.tsx
and switches SettingsFrame/SettingsCard to render Frame/FramePanel when
separated (via context), keeping joined frames on CardFrame.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add an opt-in `separated` prop to SettingsFrame/SettingsSection that adds the
COSS "Separated Panels" gap-4 between sibling panels (matches CardFrame's
built-in -1rem clip vars so rounded corners clip across the gap). Turn it on
for the multi-toggle frames — AI availability and the two notification
sections — so their panels no longer stack flush. Joined divide-y lists stay
flush.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
`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>
The Clinician profile frame inset its content 40px and drew a second
border around it — a box inside a box. Both came from CardFramePanel,
added in 44d653f. It doesn't exist in the COSS registry: upstream
@coss/card ships Card, CardFrame, CardFrameHeader/Title/Description/
Action/Footer and nothing else. There is no body part because the frame
isn't meant to pad its body.
CardFrame styles its cards through *direct-child* selectors — -m-px,
the clip-path, rounded-t/b-xl, shadow-none, before:hidden — pulling each
card out by a pixel so it sits flush inside the frame's own border. The
panel wrapper made every card a grandchild, so none of those selectors
matched: the 20px came from the panel's p-5, the second border came from
the card keeping the treatment the frame was supposed to suppress. The
commit that added it claimed the cards would "pick up the frame's card
treatment"; they never did.
Drop the panel and render children directly inside the frame, which is
the COSS default. Body padding goes on the Card, where COSS puts it, so
content is inset once rather than twice. Card-bearing bodies now get the
frame treatment they were meant to have, and the divide-y lists in
Records, Care team and Signing stack flush, which is what -m-px and
rounded-t-xl were for. The notification ToggleRows lose their space-y
wrapper and read as one list.
Frames holding raw form content (the AI panel, Care team's error and
toolbar) get an explicit padded Card instead of leaning on the frame.
card.tsx is now upstream @coss/card verbatim, minus font-heading on
CardTitle.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
SettingsFrame used a hand-rolled <div className="p-5"> body and
SettingsCard was a plain div (no data-slot=card), so the frame's card
styling never applied.
- Add CardFramePanel (data-slot="card-frame-panel") as the padded frame
body, mirroring the other CardFrame* subcomponents.
- SettingsFrame now composes CardFrameHeader + CardFramePanel (same p-5
padding as before) instead of a raw div.
- SettingsCard renders a real COSS Card (data-slot=card). Since Card is
flex flex-col, the horizontal-row call sites (ToggleRow, billing and
preferences rows) now pass flex-row.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- patients: move status filter to its own "Filter" row above the table
- patient sheet: name + ⋯ menu on one left row; Edit moved into the ⋯ menu
- settings: redesign sections with the COSS CardFrame surface (SettingsFrame)
- AI: add Automatic (auto-pick provider) and Off modes; default to Automatic
so a fresh install shows the setup banner until a provider is configured
- AI: fix the setup banner never showing (defaulted Ollama URL counted as
configured); add an "AI off" notice; add a fallback render for unknown chat
data parts so cards never silently vanish
- backend: include patient attachment metadata in the wallet record-update
bundle so pushed documents reach the wallet
- i18n: add mode/off/card keys across all five locales
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Record history: replace fragile COSS Timeline separator math with a
continuous-rail list so every audited change renders in full (RTL-safe).
- Prescriptions: new reusable COSS DatePicker (Popover + Calendar) replaces
raw <input type="date"> for Start/End date.
- Update banner: rebuilt with COSS Alert variant="warning"; pinned physical
bottom-right so it stays bottom-right under Arabic RTL.
- AI setup notice: thin attached warning banner with clearer copy; now shown
above the input in active chats too, not just the empty state.
- Settings profile: wire the previously dead Specialty (Select) and
Professional links (editable rows) into the persisted preferences.
- Patients: add a status filter and broaden search to conditions/allergies.
- Sidebar user menu: quick language switch submenu (RTL already wired).
- ai-elements: fix a subset of Base UI type drift (22 -> 12; remainder is
cmdk->Autocomplete migration drift kept behind ignoreBuildErrors).
- i18n: new keys added across all five locales (en/de/fr/ar/so).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Appointment date picker disables days before today.
- Invoice issue-date picker disables past days by default, with an opt-in
"Back-date" checkbox for recording older invoices (edit mode keeps past
dates). Added invoices.dialog.backdate to all locales.
- Removed the inert, outdated "Features" section (patient-owned storage /
require signed toggles) from Settings and its now-unused i18n keys.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- lib/portal.ts: getPortalLink + portalPairingUri build a temetro-portal: URI
(relay URL + clinic signing key) instead of a localhost API URL.
- Signing settings QR now encodes that pairing URI, so a real phone can reach
the clinic over the Temetro Network relay (fixes "server cannot be accessed").
- Portal kiosk gains a third "Link my wallet" option that shows the same QR.
- New portal.choose.wallet* / portal.wallet.* keys in all 5 locales.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Arabic RTL: stack the collapse arrow/bell above the nav icons instead of
pinning them to the opposite edge; mirror the panel-toggle glyph; flip the
notifications popover to open toward the content side.
- "Use my current location" now reverse-geocodes (OpenStreetMap Nominatim) to
fill address/city/country, not just latitude/longitude, with a graceful
coordinates-only fallback. New settings.location.geoPartial key in all 5 locales.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
backend: public GET /api/portal/:clinic/doctors and /availability, and
thread a chosen provider into portal bookings (conflict check unchanged).
frontend: fix site-wide Arabic RTL — anchor the sidebar right for RTL and
mirror the Switch thumb. Add a Patient Portal section (open/copy/QR) to
Settings → Signing and a "Use my current location" GPS button to the clinic
location editor. New i18n keys across all five locales.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Patient record:
- Add `bloodType` and `phone` to the patient model (schema, canonical types on
both backend + frontend, zod validation). `phone` is a demographic field
(reception can read/write); `bloodType` is clinical PHI, redacted for the
reception role. Surface both in the record sheet, chat summary card, and the
add/edit patient form. Migration 0033.
Clinic location:
- New org-scoped `clinic_settings` table (address/city/country + optional
lat/long), service, and routes: GET /api/clinic/settings (any clinician) and
PUT /api/clinic/location (owner/admin). Edited in Settings → Signing → Clinic
location. Consumed later by the wallet app. Migration 0034.
i18n:
- Translate all new keys into every shipped locale (en/de/fr/ar/so) and document
the "translate into every locale" rule in frontend/CLAUDE.md.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The relay is now multi-clinic. Each clinic authenticates to the /hub
namespace by signing a challenge with its own Ed25519 clinic signing key
(a per-clinic identity, not a shared RELAY_TOKEN), and the relay routes
every device response back to only the clinic that originated the request
(keyed by requestId) — so clinics never see each other's traffic.
Backend:
- clinic_signing_keys.network_enabled + GET/PUT /api/signing/network
(owner/admin) to join/leave the network.
- relay-client keeps one authenticated hub connection per network-enabled
org (connectOrg/disconnectOrg, hubs map keyed by orgId); emitToWallet/
sendToWallet take orgId; offline flush is org-scoped.
- Wallet import/push return 409 until a clinic joins.
- RELAY_TOKEN is now optional/legacy (open relay needs no shared secret).
Frontend:
- "Join Temetro Network" toggle in Settings → Signing, localized in all
five languages (en, fr, de, so, ar).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Expose temetro's own records as a read-only FHIR R4 server at /fhir,
authenticated with per-clinic API keys (tmf_… bearer tokens, SHA-256
hashed, shown once). Serves Patient, Observation (labs + vitals),
AllergyIntolerance, Condition, MedicationRequest, Encounter and
Appointment as text-only CodeableConcepts (temetro stores free-text
clinical values); CapabilityStatement at /fhir/metadata (unauth).
Searchset Bundles with _count/_offset pagination and self/next/prev
links; every request is org-scoped and written to the activity log.
Keys are created/revoked under Settings → Integrations (owner/admin).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A clinician can push an updated record to a wallet-linked patient (permanent
share). The snapshot is signed with the clinic Ed25519 key and sealed to the
wallet's X25519 key — derived from its Ed25519 wallet number via the birational
map, verified byte-for-byte against the wallet's own derivation. Stored pending,
delivered over the /wallet relay live and on the wallet's next authenticated
connect (offline catch-up). The patient approves/denies in-app; the wallet signs
its decision, the backend verifies it, and the record is replaced only on
approval. Wallet pins the clinic key (TOFU) and warns on change.
Backend: walletRecordUpdates table + service, ed25519PubToX25519Hex helper,
POST /api/patients/wallet/push, GET .../link/:fileNumber|updates|updates/:id,
wallet:update-request / wallet:update-response relay events.
Frontend: "Push to wallet" dialog with live status, wallet-link gating on the
patient sheet, "Sent updates" list under Settings → Signing, walletPush /
walletUpdatesList locale namespaces across all five languages. Bumps to v0.5.0.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add three UI locales (so/ar/de) with full ~1,660-key translations alongside
en/fr, selectable in Settings → Profile. Arabic gets full right-to-left support:
- config.ts registers the locales and exports a `dirFor` helper; an inline
<head> script in layout.tsx sets <html dir/lang> before first paint (no RTL
flash), and i18n-provider keeps them in sync on language change.
- ~160 physical direction utilities converted to logical (ms/me/ps/pe/
start/end/text-start/text-end); directional chevrons/arrows get rtl:rotate-180;
chat-bubble align variants fixed to logical.
- IBM Plex Sans Arabic appended to the sans/heading font stacks for
per-character Arabic fallback.
- Language persists to the backend user_settings and re-applies on sign-in so it
roams across devices (localStorage stays the offline source of truth).
- New scripts/check-locales.mjs (npm run check-locales) enforces key/placeholder
parity and Arabic CLDR plural completeness.
Bump to 0.3.0.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- release.yml: add QEMU + platforms: linux/amd64,linux/arm64 to both
build-push steps so the published images work on Apple Silicon (fixes
'no matching manifest for linux/arm64/v8' on docker compose pull).
- Settings -> Profile language picker is now a select that opens a
confirmation dialog before applying, instead of switching instantly.
- CLAUDE.md: require a dated docs changelog entry for every release.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
GET /api/version now reads the latest version from Docker Hub image tags (the
actual update channel clinics pull), falling back to the GitHub release if
Docker Hub is unreachable, with a shorter 1h cache and a `?refresh=1` bypass.
Settings → About & updates gains a "Check for updates" button that forces a
fresh, cache-bypassing lookup.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A Language section in the Profile panel offers English / Français, wired to
i18n.changeLanguage (persisted to localStorage via the detector cache).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
/api/network reported the container's bridge IP (172.x) when running in
Docker, which surfaced as a broken/"Error" value in Settings → About &
updates. Skip container-internal interfaces (detected via /.dockerenv) so the
endpoint returns no address inside a container, and guard the panel against an
unexpected response shape — both paths fall back to the helpful
"open via the server's IP" hint.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Resolve the backend URL from the current host at runtime (lib/backend-url.ts)
so one build works on localhost and any clinic LAN IP without a rebuild; used
by the API client, auth client, and socket. Wire the AI-chat mic to the Web
Speech API with graceful fallback. Add a Settings "About & updates" panel
(current/latest version, update command, shareable network address) and an
optional dismissible update banner.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Chat: history pill now shows a History icon + a Start-new-chat (SquarePen)
button; removed the duplicate chat-history list from the sidebar.
Email: deployment-wide email provider config (Resend/Postmark/SendGrid/SMTP) in
Settings → Developers, with encrypted API key and a Send-test action. sendEmail
dispatches via the chosen provider (REST via fetch; SMTP via nodemailer).
Forgot password with no provider: alert the clinic admin(s) via a "System"
message card in Messages + a bell notification (seeded system user + per-clinic
System conversation); clicking deep-links to /settings?tab=careTeam&member=<id>.
Admins can set a member's password directly from the employee dialog
(PATCH /api/staff/:id/password via Better Auth's internal context — no admin
plugin needed).
Patient Portal: "New patient" booking path registers a demographics-only patient
then books; bookings reject double-booked slots (409).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Backend: GET /api/settings/records/export downloads the clinic's full patient
archive as JSON; POST /api/settings/records/import creates new patients (skips
existing file numbers, drops cross-clinic provider links). Both admin-gated.
Frontend: Records settings now has a working Export button and an Import flow
(choose file -> preview count -> apply) wired to the new endpoints.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- settings: keep tab nav on its own row so "Developers" no longer wraps
- chat: show the "Veil active" chip once per conversation, not every turn
- chat: record cards only offer "Click for more" when they have detail
- chat: chat-history panel (pill + sheet) top-left of the AI chat with
"Start new chat"; rename sidebar "New chat" -> "Ask temetro" (Sparkles)
- meetings: disable past dates, add an Upcoming Meetings list, and use the
Empty component for empty days; scheduler can be pre-targeted via ?with
- messages: add a call button left of each inbox row -> Meetings (?with)
- toast: add a dismiss (x) button; call invites now ring 30s with "Accept"
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
New staff_profile table (org + user, unique) holding a clinician's clinical
specialty. GET /api/staff and /api/staff/providers now include specialty; new
PATCH /api/staff/:userId (member:update) upserts it. Migration 0023.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
CLINICAL_RESOURCES includes "lab" but the employee detail dialog only
mapped patient/appointment/prescription/task, so the lab row rendered as
the raw i18n key with no icon. Add a FlaskConical icon and a
"Lab results" label.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- new org_ai_policy table + GET/PUT /api/ai/policy (read for any member,
write owner/admin only); migration 0018
- /api/chat hard-blocks (403) when AI is off for the caller
- Settings → AI "Availability" section: enable AI, or disable for
employees only (owners/admins keep access); read-only for non-admins
- sidebar, command palette and route guard hide/redirect the AI chat
when it's disabled for the current user
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The chat defaulted to a Claude model, so a user who saved only a Gemini key got
a silent failure (backend derived Anthropic, found no key, errored — and the UI
showed nothing).
- Backend: resolveModel now falls back to whichever provider actually has a key
(preferring the configured one), so a Gemini key just works regardless of the
picked model. Clear 400 if no provider is configured at all.
- Frontend: the chat seeds its model/effort from the saved AI config, and now
surfaces request failures as both a persistent alert banner and a toast —
never silent.
- Settings: switching provider auto-selects that provider's default model.
- Refreshed the model catalog to current ids (Gemini 2.5 Pro/Flash + 2.0 Flash;
dropped the retired gemini-1.5-pro); per-provider default model updated.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
New per-user AI settings section (visible to every clinician, not just admins):
- Inference mode: cloud API key vs local Ollama.
- Provider + key (OpenAI / Anthropic / Gemini) via a select; the key field is
write-only and shows a "key set" indicator from the backend's apiKeySet,
never echoing the stored secret. Default model + effort per provider.
- Local Ollama: base URL + model name with a "Test connection" probe.
- Veil: de-identification level (full / names / off) with mode-aware copy.
Talks to the new /api/ai/config + /api/ai/test endpoints via lib/ai-settings.ts.
The old mock clinical selects (specialty/facility/time range/access/response)
are dropped entirely rather than relocated — they were placeholder knobs.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Records tab: data sources, import/export, retention (replaces placeholder)
- Developers tab: API base URL, access-tokens empty state, resources
- Signing tab: how-it-works steps + backup key entries
- Team member dialog: header band, per-resource permission rows with icons
and action chips, COSS Select for role change
- Care team: hint that clicking a member shows their permissions
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- ToggleRow supports controlled checked/onCheckedChange; CopyField copy works
- ProfilePanel loads/saves preferences via /api/settings, name via updateUser
- dirty tracking with a sticky save bar that follows the scroll
- Delete account: password-confirmed dialog wired to authClient.deleteUser
- clinician ID / handle now show the real session user
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Messages: search the inbox and the compose member picker.
- Care Team: clickable member rows open an employee dialog showing role +
permissions, with change-role (updateMemberRole) and remove.
- Patients: Primary Care is now a provider dropdown (defaults to self for a
doctor); add a Transfer action + dialog wired to the transfer API.
- Activity: entries are clickable, opening a detail dialog.
- Analytics: Section takes a columns prop so each row fills evenly (no orphan
card in Appointments).
- Add lib/staff.ts (listProviders), transferPatient client, rolePermissionSummary
helper, and i18n keys for all new strings.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Hide "Create clinic" (sidebar footer) for non-admins; only owner/admin can
spin up additional clinics. Onboarding for brand-new users is unaffected.
- Analysis: drop the bar charts; show line charts (Sparkline) inside KPI cards
that open a detail dialog with the full chart + per-point breakdown.
- Add Team Member: validate the username client-side (no spaces; letters,
numbers, dots, underscores) with a clear warning + field hint.
- Tasks: New Task now has an Assignee selector (Myself / Other → department).
Tasks are visible to the department they're assigned to (or the creator), and
show who created them. Backend adds assignee_role + created_by_name with
visibility filtering in listTasks; owners/admins see all.
- Care team: removing a member now asks for confirmation first (dialog) and
surfaces success/failure + refreshes.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Replace the email-invitation flow with admin-provisioned staff accounts and
add role-based access that changes what each member sees.
Backend:
- Enable Better Auth `username` plugin (staff sign in by username); regenerate
auth schema (+ username/displayUsername on user) and migration 0007.
- Add `doctor` and `reception` roles to the access-control RBAC. `reception` is
scoped to scheduling + registration (no `prescription` statement).
- New `/api/staff` route: POST creates a user (auth.api.signUpEmail) and adds
them to the active clinic (auth.api.addMember); GET lists members + usernames.
Gated by requirePermission({ member: ["create"] }).
- Redact clinical PHI for the reception role in the patients service (read,
create and update) so demographics-only is enforced server-side.
Frontend:
- usernameClient + Email|Username tabs on the login form.
- lib/roles.ts: useActiveRole + Better-Auth-permission-driven nav visibility,
default landing, and a route guard (reception -> /appointments, blocked from
clinical routes). Applied to the sidebar, command palette and auth guard.
- Care team page now provisions staff via a two-step Add-team-member dialog
(details -> username/password) hitting /api/staff; removes the email-invite
and pending-invitation UI. New members are contactable from Messages
automatically (they become org members).
- Hide clinical sections of the patient form and the admin-only settings tabs
for non-clinical/non-admin roles.
All permission management stays in Better Auth (per the better-auth skills now
referenced in backend/CLAUDE.md).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Finish the i18n pass: settings panels (profile/care-team/signing), the
chat heading + input, the patient cards / detail / create-edit form, and
the notes page + rich-text editor are all keyed in en/translation.json.
All 526 static t() keys resolve. Document the new backend resources +
Socket.io realtime (backend README/CLAUDE) and the i18n coverage
(frontend CLAUDE).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- MenuGroupLabel must live inside a MenuGroup (Base UI context) — wrap the
label in nav-user, team-switcher, and nav-notifications. Fixes the
'MenuGroupContext is missing' runtime error.
- Patient form/detail dialogs: COSS DialogPopup has no inner padding; move the
body into DialogPanel (form as 'contents') so fields get p-6 instead of being
flush to the edges.
- Settings 'Delete' button used the default variant + bg override, keeping a
near-white border-primary in dark mode. Use variant='destructive'.
- Auth forms: COSS Field is items-start (no stretch). Make the submit button and
the password-row/grid w-full, and underline the bottom sign-in/up link.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Components:
- Replace components/ui primitives with COSS equivalents from the @coss/* shadcn
registry. Add menu/preview-card/group; remove the superseded dropdown-menu,
hover-card, button-group; keep carousel (no COSS equivalent).
- Migrate live call sites to canonical COSS APIs preserving layout/behavior:
sidebar menus (nav-user, team-switcher, nav-notifications), chat-input radio
menu, patient dialogs/cards, auth forms (FieldGroup -> flex column), settings.
- ai-elements: migrate the live conversation/message with behavior parity
(Tooltip via render, Group/GroupText); repoint dormant files (hover-card ->
preview-card, dropdown-menu -> menu, button-group -> group, InputGroupButton
-> Button). Residual pre-existing Base UI type drift stays behind
ignoreBuildErrors.
Theme:
- Adopt COSS default neutral tokens in globals.css with light + dark palettes.
- Add next-themes (defaultTheme=dark, enableSystem) and drop the forced dark
class. Align font variables to the COSS contract (--font-sans/-heading/-mono).
Make scrollbars theme-aware.
i18n:
- Add i18next + react-i18next (lib/i18n/config.ts, locales/en/translation.json,
components/i18n-provider.tsx mounted in layout). Convert auth forms, sidebar
nav, and settings tabs to useTranslation() as the reference pattern.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Connects the UI-only app to the new backend over Better Auth + a small
patient API client, replacing the in-memory fixture and placeholder identity.
- Better Auth React client (lib/auth-client.ts) + shared access-control
roles; API client (lib/api-client.ts) sending credentials cross-origin.
- Designed (auth) route group: login, signup, verify-email, forgot/reset
password, clinic onboarding, and accept-invite.
- proxy.ts (this Next's renamed middleware) optimistic redirect + an
authoritative client AppAuthGuard requiring a session and active clinic.
- Real identity in nav-user (useSession + sign out); the unused team
switcher repurposed as an organization (clinic) switcher; Care-team
settings tab manages members and invitations.
- lib/patients.ts now calls the org-scoped API (types unchanged); patients
table and create/edit dialog updated for async create/update.
- next.config: standalone output + Dockerfile for containerized runs.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Patients: new /patients directory table (name, MRN, age·sex, status,
last seen, allergies) with search + Add patient; clicking a row opens
the record in the chat via /?patient=<#>. chat-panel reads that search
param (Suspense-wrapped) and runs the lookup once on arrival. Sidebar
"Patients" now links to /patients; lib/patients gains listPatients().
- Settings: re-themed to clinical tabs (Profile · Records · Signing ·
Care team · Developers) — clinician profile, patient notifications,
patient-owned-storage/signed-records features, and a Signing key panel.
Same layout/components, only content changed.
- Sidebar footer: Polar-style NavUser (avatar + name + chevron) opening a
menu upward — Settings · Docs & GitHub · Theme · Log out. Collapsed
("locked"): avatar only, name on hover (tooltip), menu opens to the
right. Placeholder identity (no auth yet).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>