Commit Graph

21 Commits

Author SHA1 Message Date
Khalid Abdi d237504af9 frontend: add Somali, Arabic (RTL) & German languages
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>
2026-07-02 23:03:13 +03:00
Khalid Abdi 403e0e38e9 frontend: runtime backend URL (LAN), voice dictation, version + update UI
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>
2026-06-26 21:34:30 +03:00
Khalid Abdi d30c310336 feat(chat): replace model picker with situation modes (Chat/Analysis/Graph)
The composer's SELECT is now a clinician-facing mode picker instead of a list
of LLM vendors (the model still comes from Settings → AI and continues to drive
the Veil consent gate). The mode travels with each send:

- backend appends an Analysis/Graph directive to the agent's system prompt
- Graph mode renders an Obsidian-style RecordGraph for the /patient fast-path
  (new data-recordGraph message part reusing the shared graph component)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 19:30:12 +03:00
Khalid Abdi 705d4b5719 frontend: fix file attach dropping the picked file (live FileList race)
The file pickers read event.target.files but reset event.target.value = ""
before the file was actually used: in the AI composer the value reset ran
before React's deferred setState updater read the (now-empty) live FileList,
so no chip appeared; in Messages the reset ran before the length check, so it
returned early. Snapshot the files into a plain array synchronously, before the
reset. This is why attaching seemed to fail (no badge) — especially while typing.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 21:09:15 +03:00
Khalid Abdi 700b461618 frontend: fix composer attach while typing via native label
The "+" attach control called fileInputRef.current.click() programmatically.
When the textarea is focused with text, clicking "+" blurs it first and the
browser drops the programmatic picker-open (user-activation gating), so no file
could be attached while typing. Replace it with a real <label> wrapping the
file input — native label activation always opens the picker, regardless of
focus/text state.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 21:07:27 +03:00
Khalid Abdi 307cc3cc64 frontend: clinical UI fixes + chat/composer polish
- chat composer: attach works with text (sr-only file input); lighter
  bordered input box
- messages: defer "+" menu actions past close so file/appointment pickers fire
- tasks: top-level New task button
- chat: condensed appointment card with "View in calendar" deep-link;
  appointments page auto-opens the month calendar from ?calendar=&date=
- lab: work-queue rows expand to show task detail + complete
- inventory: clickable item detail dialog
- pharmacy: Dispense action records a dispense + "Recently dispensed" feed;
  expiring badge uses endDate and only flags <=2 days left
- prescriptions: keyboard nav in patient search, inventory-backed medication
  combobox (free-text fallback), optional start/end dates; thread dates through
- sidebar: whole nav scrolls as one
- i18n keys for all new strings

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 20:45:30 +03:00
Khalid Abdi 6e5598262e feat(chat): file attachments as chips, CoT closed, AI add-to-inventory
Chat fixes (#1/#6/#7):
- chat-input passes raw File[] up instead of inlining file text
- chat-panel sends files as FileUIPart and renders them with the
  ai-elements Attachments component (no more raw text dumps)
- backend extracts text-like file content for the model in routes/chat
- Chain-of-Thought now defaults to collapsed
- file upload works regardless of whether the input has text

AI add-to-inventory (#2):
- new proposeInventory tool (validates items, streams an approval card)
- system prompt distinguishes stocking inventory vs. billing a patient
- ActionPreviewCard commits inventory via POST /api/inventory

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 18:28:05 +03:00
Khalid Abdi b946dc9226 fix: chat input clipping, AI appointments create patients, batched approval
1. Chat input toolbar was clipped in the empty state: the form's overflow-hidden
   made its flex min-height resolve to 0, so the vertically-centered layout
   squeezed it and hid the bottom toolbar (attach/add-patient/model/mic/send).
   Add `shrink-0` to the form; let the empty-state column scroll.
2. AI-imported appointments now create/link a patient: services.ensurePatient
   reuses a same-name patient or creates one (auto file number, source "ai");
   appointments.createAppointment calls it when the booking has no file number,
   so imported people appear on the Patients page.
3. Many proposals collapse into one BatchActionPreviewCard → a review dialog
   with per-row remove and "Add all" (commits sequentially), instead of one
   Add/Discard card per record.

Plus: widen the Live chart right margin so the value pill stays inside the card.
Verified with `next build`.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14 21:23:25 +03:00
Khalid Abdi 9086867782 frontend: Claude-style AI chat (tool cards, reasoning, queue, suggestions)
Wire the existing ai-elements into the chat:
- collapsible tool-call cards (name, params, result, status) for native tool
  parts on the streamed path
- a reasoning block (shimmer while thinking, "Thought for Ns") when the model
  emits reasoning; backend forwards it via toUIMessageStream({ sendReasoning })
- a message queue: typing + Enter while the assistant is responding queues the
  message (shown above the input, removable) and auto-sends when it goes idle
- starter suggestion chips on the empty state, each tied to a tool

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14 20:06:57 +03:00
Khalid Abdi fa2e499440 feat: live AI chat (thinking + inline Veil) and display/add actions
Make the chat feel alive and let the agent act on the clinic — safely.

UX (frontend):
- Stream `data-step` parts from each tool into an inline Chain-of-Thought
  trace, plus a "Thinking…" shimmer while a request is in flight (works even
  on the non-streamed external+Veil path).
- Replace the modal Veil consent Dialog with an inline, once-per-session
  "Veil" confirmation above the input (with a "Use local model" option).
- Render new list + action-preview cards; chat-input now uses COSS tokens.

Agent (backend):
- Add display tools (listAppointments / listTasks / listPrescriptions) and
  propose tools (proposeAppointment / proposeTask / proposePrescription) that
  validate as a dry run and stream an approval card — nothing is written until
  the clinician approves, via the existing RBAC-gated create endpoints.
- previewImport now also covers single-patient add + migration.
- System prompt: display + add only, never edit/delete or alter the schema;
  stronger migration guidance. ToolContext carries the viewer for task scoping.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 23:49:18 +03:00
Khalid Abdi 31d86bb5dd frontend: wire chat to the real agent + lab charts + import approval
Replace the mock chat with the live backend agent:
- chat-panel uses @ai-sdk/react useChat against /api/chat (credentials included),
  passing the selected model + effort per send. The `/patient <file#>` fast-path
  is kept as an instant client-side shortcut.
- Renders the agent's streamed data parts: patientCard → record cards
  (PatientResult), labCard → new LabChartCard (visx area chart with a high/low
  flag badge in the top-right corner + recent values), importPreview →
  ImportPreviewCard (the human approval gate: review counts/issues, then commit
  via POST /api/ai/import — nothing is written until approved).
- Veil consent: a one-time dialog before the first send to a cloud model,
  explaining that identifiers are de-identified before leaving the clinic.
- Attached text files (csv/json/txt…) now include their content in the message
  so the agent can parse an export for import.

Shared chat message/data-part types in lib/ai-chat.ts.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 18:44:20 +03:00
Khalid Abdi c6a1b98427 frontend: declutter chat input to a single model+effort picker
Replace the five clinical context selects (access, response mode, specialty,
facility, time range) with one model picker styled like the reference design:
a primary model list with descriptions, an Effort submenu, and a "More models"
submenu. Move the Add-patient pill up into the toolbar and drop the now-empty
bottom context-selector card so the input is a single clean rounded surface.

Model/effort selection is lifted to ChatPanel so it can travel with each send
(wired to the backend agent in a later phase). Adds a shared model catalog
(lib/ai-models.ts) reused by the picker, Settings, and the chat wiring.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 18:23:45 +03:00
Khalid Abdi ab2f10bffc i18n: convert settings, chat, patient records and notes; update docs
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>
2026-06-08 02:23:20 +03:00
Khalid Abdi a837e52263 Lighten chat input via COSS tokens; enable sidebar theme toggle
- chat-input: replace the two hardcoded oklch backgrounds (leftover Linear
  palette) with COSS surface tokens — outer form bg-muted, inner card bg-input.
  Lighter in dark mode and theme-aware (no longer near-black in light mode),
  preserving the two-tone layered look.
- nav-user: wire the previously-static Theme menu item to next-themes
  (useTheme/setTheme) — toggles light/dark, with the icon (Sun/Moon) and
  shortcut label reflecting the current theme.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-03 18:59:31 +03:00
Khalid Abdi 4d6a5dc008 Migrate UI to COSS components + COSS neutral theme; add i18next
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>
2026-06-03 18:39:20 +03:00
Khalid Abdi 750622e0db Edit patient records + fix Add-patient dialog scrollbar
- Generalize add-patient-dialog → shared PatientFormDialog (create | edit).
  Edit mode prefills from the patient; both modes now cover all sections
  with add/remove rows: identity, vitals, allergies, meds, problems, labs,
  visits. Save writes to the store (addPatient overwrites by file #).
- Fix the dialog scrollbar: fixed header + footer (Save/Cancel pinned),
  only the field body scrolls, scrollbar hidden (no-scrollbar).
- patient-cards: "Edit record" button on the Summary card opens the editor
  (stopPropagation so it doesn't open the detail dialog); PatientResult
  holds the editor state and exposes onPatientUpdated.
- chat-panel: onPatientUpdated replaces the message's patient in place, so
  saved edits update the on-screen cards (no duplicate message, no requery).
- chat-input: Add pill now uses PatientFormDialog mode="create".

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-01 21:17:43 +03:00
Khalid Abdi bb7478433c Add patient: intake form in the input that creates + opens a new chart
- chat-input: new "Add patient" pill (UserPlus) opens an intake dialog;
  remounts it via a key each open for a fresh file # / cleared form. On
  create it runs /patient <new#> so the record's cards open in the chat.
- add-patient-dialog: controlled Dialog + form — generated file # (with
  regenerate), identity (name/age/sex/status/PCP), current vitals, and
  repeatable allergies & medications. Save builds a Patient and adds it
  to the store. Save disabled until a name is entered.
- lib/patients: generateFileNumber() (unique) + addPatient() (session
  store; swap point for a real create API).
- patient-cards: graceful empty states (no labs / meds / problems /
  visits / trend) so a brand-new sparse chart renders cleanly.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-01 21:00:12 +03:00
Khalid Abdi 9a7e694d5a Redesign patient cards: 7 focused cards + sparklines; lighten input
- Split the overloaded card into 7 single-topic cards (Summary, Vitals,
  Labs, Medications, Problems, Allergies & alerts, Visits) so the
  equal-height row is evenly filled instead of stretched-and-empty.
- Add a dependency-free inline-SVG shaded-area Sparkline; Vitals shows a
  heart-rate trend and Labs a headline-lab trend, fed by new vitalsTrend/
  labTrend series in the fixture.
- Lighten the chat input's two surfaces so it reads as a raised layer
  above the new (lighter) page background.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-01 19:42:30 +03:00
Khalid Abdi fa32346d2f Horizontal patient cards, dark scrollbar, functional input controls
- globals.css: thin dark-themed scrollbar (Firefox + WebKit) so scroll
  areas no longer show the white default; `no-scrollbar` regions still win.
- patient-cards.tsx: render the cards in a horizontal scroll row (each
  w-80 shrink-0 snap-start), loading skeletons included.
- chat-input.tsx: wire the controls — Attach opens a file picker with
  removable chips; Access / response mode / specialty / facility / time
  range are real DropdownMenu radio selectors (via a SelectPill helper)
  that remember the choice. Mic stays visual-only for now. Layout/styling
  unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-01 19:22:17 +03:00
Khalid Abdi 88a45cf19d Add /patient lookup with patient-info cards, clinical chat input
Type `/patient <fileNumber>` to pull up a patient: a skeleton card set
appears, then fills in with summary, allergies & alerts, medications &
problems, and vitals/labs/visits — composed from existing Card/Badge/
Avatar/Separator/Skeleton primitives (no new design).

- lib/patients.ts: typed mock fixture + async getPatient() (swap point
  for a real API later).
- components/chat/patient-cards.tsx: PatientResult (loading/ready/
  not-found).
- chat-panel.tsx: discriminated-union message model, strict /patient
  parse, async lookup with update-by-id, clinical heading.
- chat-input.tsx: relabel leftover coding-assistant controls to clinical
  copy + clinical icons; layout/styling unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-01 18:56:56 +03:00
Khalid Abdi 97bf9ed8cc Build temetro app: dark theme, clinical sidebar, AI chat, settings page
- Linear-inspired dark theme (app/globals.css), forced dark in layout
- Clinical sidebar (components/sidebar-02) with temetro brand
- AI chat UI (components/chat): Cursor-style input, UI-only mock replies
- Settings page (components/settings): Polar-style tabs/sections, UI only
- Route-group app shell: app/(app)/{layout,page,settings}
- shadcn (Base UI) + ai-elements component libraries
- CLAUDE.md: architecture notes + per-folder commit policy

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-31 20:25:19 +03:00