Commit Graph

35 Commits

Author SHA1 Message Date
Khalid Abdi dd64d689c8 backend: Patient Portal over the relay + wallet linking
- services/portal.ts: clinic-scoped portal actions (clinic info, doctors,
  availability, linkWallet, conflict-checked booking, results, downloadable lab
  files) plus handlePortalRequest to dispatch a relayed portal:request.
- relay-client.ts: handle portal:request on the hub and ack the result back down
  the relay (device path identifies the patient by verified wallet number).
- patients: new nullable wallet_number column (+ migration); linking stores it,
  and walletNumberForPatient now resolves via it so pushes work after a portal
  link, not only after a permanent share.
- routes/portal.ts: GET /:clinic/link returns the relay-based pairing descriptor
  (clinic signing key + relay URL) for the QR — no more localhost-baked API URL.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-08 20:08:36 +03:00
Khalid Abdi 36461a5498 feat: patient blood type & phone + clinic location setting (v0.9.0)
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>
2026-07-06 22:07:27 +03:00
Khalid Abdi 99aa534e88 feat: multi-clinic Temetro Network with per-clinic identity (v0.8.0)
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>
2026-07-05 18:30:19 +03:00
Khalid Abdi 0d2494d67a feat: read-only FHIR R4 server (share records over /fhir)
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>
2026-07-04 01:27:32 +03:00
Khalid Abdi bb536ba6da feat: clinic→wallet record-update push
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>
2026-07-03 18:57:38 +03:00
Khalid Abdi 7a359d4911 feat: QR pairing for wallet import (backend + website)
- Backend: nullable wallet_number on wallet_share_requests (migration 0029);
  createPairingRequest + POST /api/patients/wallet/pair; applyShareResponse
  binds the authenticated wallet on QR (pairing) requests
- Frontend: Import dialog gains a "Show QR" mode rendering a temetro-pair QR
  (react-qr-code) the patient scans; requestWalletPairing helper; i18n keys

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 21:31:51 +03:00
Khalid Abdi 2d47abcc42 feat: patient wallet — real Signing, encrypted share relay, import-from-app
Backend
- clinic Ed25519 signing key (services/signing.ts, routes/signing.ts,
  clinic_signing_keys table) — Settings → Signing is now real
- @noble wallet-crypto (lib/wallet-crypto.ts): ed25519 identity, base58check
  wallet numbers, sealed-box (x25519 + xchacha20poly1305)
- /wallet Socket.io relay namespace (challenge-signed device auth) forwarding
  only ciphertext; emitToWallet helper
- import-from-app flow (routes/patients-wallet.ts, services/wallet-share.ts):
  request-share → patient approval → decrypt + verify → review draft → commit
- temporary shares: patients.share_expires_at + 5-min auto-delete sweep; revoke

Frontend
- SigningPanel wired to live key/fingerprint/rotate + shared-records list
- "Import from a patient app" dialog (lib/signing.ts, import-from-wallet-dialog)
  reusing the draft-review path; temporary badge on the patient list

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-21 18:19:57 +03:00
Khalid Abdi 90e6ec4cc0 feat: email provider, admin password reset, portal new-patient, chat pill
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>
2026-06-20 19:52:55 +03:00
Khalid Abdi bbc6869745 tasks: assign a task to a specific person
Backend: add tasks.assignee_user_id (nullable, fk user) + migration; persist
it through create/update and surface tasks to the assigned person in listTasks.

Frontend: New Task dialog gains a three-way Assignee control
(Myself / Department / Person) with a provider picker from /api/staff/providers;
task card + detail show the assignee's name when set.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 18:40:56 +03:00
Khalid Abdi 913a217e1d feat(meetings): live invite, scheduling calendar, Discord-style redesign
- Live invite: ring a clinic member into a room (socket call:invite + a bell
  notification); the invitee gets a toast with a Join action. Notifications of
  type "meeting" deep-link to /messages/meetings?room=, which auto-joins.
- Scheduling: new scheduled_meetings table + /api/meetings/events (list mine,
  create, delete); a Calendar tab on the Meetings page with a month picker
  (meeting-day dots), the day's agenda, and a Schedule-meeting dialog
  (title/date/time/participants).
- Redesign: rounded control bar with tooltips (mic/cam/screen/invite + separated
  red Leave), speaking ring on tiles (Web Audio), and live room-occupancy counts
  via call:presence broadcasts. Migration 0025.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-20 02:07:41 +03:00
Khalid Abdi c88b674196 feat(meetings): Discord-style staff voice/video calls under Messages
Backend: meeting_rooms table + /api/meetings (list/create/delete, org-scoped),
and WebRTC mesh signaling over the existing authed Socket.io (call:join with a
≤4 cap and org authorization, call:signal relay, peer-joined/left, disconnect
cleanup). Migration 0024.

Frontend: Messages nav is now expandable (Inbox + Meetings); new
/messages/meetings page with a room list and a Discord-style call UI — a
useWebRtcMesh hook (camera/mic, screen share via replaceTrack, ≤4 mesh) and a
tile grid with a mic/camera/screen/leave control bar. New lib/meetings + i18n.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 20:48:26 +03:00
Khalid Abdi 2f36875d37 backend: add per-clinic staff specialty (Care Team -> patient sheet)
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>
2026-06-19 20:28:52 +03:00
Khalid Abdi 43eaccb97e feat: HL7/FHIR, e-prescribing & insurance claims integrations
Real, standards-compliant integration clients that the clinic points at
its own (sandbox or production) endpoints — no mock data.

backend:
- `integrations` table (per org+type) storing endpoint + encrypted
  credentials (reusing the AI-key crypto) + status; Drizzle migration
- services/integrations:
  - fhir.ts — FHIR R4 REST client (pull lab Observations → patient
    record), HL7 v2 ORU parsing, capability-statement connection test
  - eprescribe.ts — NCPDP SCRIPT NewRx message build + transmit
  - claims.ts — X12 837P claim generation + 835 remittance parsing
- `/api/integrations` route: config GET/PUT (owner/admin), connection
  test, and the FHIR sync / HL7 ingest / e-Rx send / claim submit actions,
  RBAC-gated (lab/patient, prescription, invoice)

frontend:
- lib/integrations.ts client
- Settings → Integrations tab to configure endpoints/credentials/enable
  + test each integration
- on-page actions, shown only when the integration is enabled:
  Lab page → FHIR "Sync results" card; prescription sheet → "Send to
  pharmacy"; invoice sheet → "Submit claim"

Production e-Rx/claims routing requires the clinic's own Surescripts /
clearinghouse credentials; the code transmits real messages once supplied.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 20:15:00 +03:00
Khalid Abdi b1abb29108 feat: patient & lab file attachments (real backend storage)
Add a real file-storage layer to the backend and wire upload UI into the
frontend.

backend:
- new `attachments` table (org-scoped, links to a patient file number and
  optionally a lab result) + Drizzle migration
- `/api/attachments` route: upload (multer → disk under UPLOAD_DIR),
  list, stream/download, delete; gated by patient:write OR lab:write via
  a new requireAnyPermission helper so lab staff can attach analyses
- UPLOAD_DIR env (default ./uploads) + a persistent docker volume

frontend:
- lib/attachments.ts client (multipart upload, list, delete, preview URL)
- staged file picker in the patient Add/Edit dialog (uploaded after save)
  and the lab Add-result dialog (linked to the result)
- a Files section in the patient sheet that lists attachments and opens
  them in a preview dialog (images inline, others via download)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-18 20:04:03 +03:00
Khalid Abdi f0fe501d62 backend: prescription start/end dates + dispense ledger
Add optional startDate/endDate columns to prescriptions (schema,
validation, types, service) — when set, endDate drives expiry.

Add a new append-only `dispenses` resource (schema, types, validation,
service, route at /api/dispenses) recording who received which medication,
gated on the existing inventory RBAC statement. Migration 0020.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 20:44:47 +03:00
Khalid Abdi 15fc7fdf26 feat(messages): attach files and share appointments
- new message_attachments table + POST/GET /api/conversations/attachments
  (base64 upload, capped 10MB; authed clinic-scoped download)
- messages carry an attachments JSON column (file refs or appointment
  snapshots); realtime + REST send accept attachments and allow
  attachment-only messages; migration 0019
- composer "+" menu (Files / Appointments): file picker uploads and
  stages chips; appointment picker searches by patient and attaches a
  snapshot; both render in the thread (download chip / appointment card)
- raise express json limit to 15mb for base64 uploads

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 18:58:11 +03:00
Khalid Abdi addddc8972 feat(ai): clinic-wide AI kill-switch (admin-controlled)
- 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>
2026-06-15 18:42:50 +03:00
Khalid Abdi 6fdb7bdb46 feat(tasks): kanban board with To Do / In Progress / Done columns
- add a `status` field to tasks (backend schema/validation/service/types
  + frontend types), kept in sync with the legacy `done` flag
- migration 0017 adds the column and backfills done tasks to "done"
- rewrite the tasks page as a three-column board: per-column add buttons,
  draggable cards, and a status mover in the detail sheet

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 18:35:48 +03:00
Khalid Abdi 3aa699aefe feat: clinic-wide AI (analytics/earnings/inventory + invoice-from-file),
real Live card, persistent chat history

Analytics & earnings:
- Analytics now carries real money computed from invoices (billed/paid/
  outstanding + by-month); new Earnings section on the Analysis page drawn with
  the project's Bklit chart components (shared EarningsChart).

AI agent reaches the whole clinic:
- new read tools getClinicInfo / getAnalytics / listInventory render clinic,
  analytics (with a Bklit earnings chart) and inventory cards in chat
- proposeInvoice turns an uploaded purchase/medication list into an invoice
  (new "invoice" action-preview kind → createInvoice); invoices/appointments
  auto-create/link a patient (ensurePatient) so they hit the Patients page

Live card:
- plots real data — patients checked in today — via GET /api/analytics/live
  (polled); value pill clamped and margins widened so nothing spills the card

Persistent AI chat history (Claude-style):
- ai_chat_threads + ai_chat_messages (migration 0016); per-user, org-scoped
  thread CRUD under /api/chat/threads
- chat panel owns a thread id, loads /?thread=<id>, and auto-saves after each
  exchange; sidebar lists past chats (open/delete), "New chat" starts fresh

Verified with backend typecheck + frontend tsc + next build.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14 22:25:15 +03:00
Khalid Abdi d096c4fe9d feat: invoices — patient billing with installments + PDF export
Backend (new `invoice` RBAC resource, granted to clinicians + reception):
- invoices table (line items + installments as JSONB), types, zod validation,
  service (CRUD + splitIntoInstallments + auto invoice numbers), org-scoped
  REST routes mounted at /api/invoices, activity logging (migration 0015)

Frontend:
- lib/invoices.ts API client + money/date helpers
- /invoices page: list with KPIs and search, create/edit dialog (searchable
  patient combobox, inline line-item editor, live total), detail sheet to split
  a bill into equal monthly installments, delete, and Download PDF
- dependency-free PDF via a print-styled window (browser "Save as PDF")
- sidebar "Invoices" entry under the Patients group; "Added by AI" badge honored

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14 19:58:08 +03:00
Khalid Abdi 929bec8f31 feat: AI-added records save with placeholders + "Added by AI" provenance
Stop blocking AI imports/proposals on missing non-critical fields. Records
the chat agent drafts now save with safe placeholders, auto-generated file
numbers, and a source="ai" marker that surfaces an "Added by AI" badge so a
clinician can review/edit them later.

Backend:
- add `source` (manual|ai) column to patients/appointments/prescriptions
  (migration 0014) + canonical types, services, validation schemas
- relax patient/appointment validation: empty file number allowed, demographic
  + type/provider/initials fall back to placeholders (initials derived from name)
- patients.generateFileNumber() auto-assigns an MRN when one is missing
- proposeAppointment accepts a name when no file number resolves; AI commits +
  /api/ai/import stamp source="ai"

Frontend:
- `source` on Appointment/Patient/Prescription types; AI commits send source="ai"
- reusable <AiBadge> shown on the Patients table/detail and prescriptions list

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-14 19:27:17 +03:00
Khalid Abdi 3a7378e00d backend: AI chat agent with Veil PHI safeguard (providers, /chat, import)
Real LLM chat replacing the mock, backend-centric per the plan:

- Multi-provider API-key mode (OpenAI / Anthropic / Gemini via the AI SDK) plus
  local Ollama (OpenAI-compatible endpoint). Provider is derived from the
  picked model id; the matching stored key is used. New user_ai_settings table
  holds per-user config with provider API keys encrypted at rest (AES-256-GCM,
  src/lib/crypto.ts, keyed by AI_CREDENTIALS_KEY).
- POST /api/ai/config (get/put, secrets never returned), POST /api/ai/test
  (Ollama ping / key presence), POST /api/ai/import (approved migration commit,
  re-validated server-side, reuses the audited patient service).
- POST /api/chat: streamText agent with tools (getPatient, getPatientLabs,
  searchPatients, previewImport). Real record data streams to the clinician as
  custom data parts (cards) while the model sees only Veil-redacted results.
- Veil (src/services/ai/veil.ts): de-identifies patient identifiers to tokens
  before external calls, resolves tokens on tool args, and rehydrates the final
  answer. Bypassed for local Ollama. External mode runs non-streamed so the
  rehydrated text is correct. Every call is audited (provider + Veil level).
- Shared role-scoping helpers extracted to src/lib/role-scope.ts (reused by the
  patient routes and chat tools so visibility rules match).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 18:35:16 +03:00
Khalid Abdi 67fdf15de4 backend: org-scoped pharmacy inventory model
Add an `inventory` resource mirroring the prescriptions feature end-to-end:
Drizzle table (org-scoped, indexed), domain type, zod validation, service
(list/get/create/update/delete), and an RBAC-gated CRUD router mounted at
/api/inventory. Grant the new `inventory` statement to roles — pharmacy gets
read/write, full clinicians read/write/delete, reception/lab none — in both the
backend access control and (mirrored) the frontend. Record writes in the
activity log via a new `inventory` entity type. Includes the migration and an
idempotent demo seed script.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-12 19:16:28 +03:00
Khalid Abdi e37d131800 backend: replace viewer role with pharmacy and lab department roles
Drop the unused read-only "viewer" role and remap any members (and pending
invitations) holding it to "member" via a custom migration. Add a "lab"
statement (read/write) granted to all full clinicians, plus two new
provisionable department roles: "pharmacy" (patient/appointment read,
prescription read+write — no delete, which doubles as the full-clinician
marker the frontend probes) and "lab" (patient/appointment read, task queue,
lab results via the new statement). Both join TASK_DEPARTMENTS so tasks can
be assigned to them.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 21:03:15 +03:00
Khalid Abdi 50fbb6cd40 backend: per-user settings store (/api/settings) + enable account deletion
- user_settings table (userId PK -> user, jsonb preferences) + migration
- GET/PUT /api/settings (requireAuth, user-scoped; zod-validated flat map)
- Better Auth user.deleteUser enabled so users can delete their own account

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 20:05:34 +03:00
Khalid Abdi 6a0fab97ae backend: per-doctor patient visibility, transfer, provider picker & scoped activity
- Add patients.primary_provider_id (FK to user) + migration; persist it through
  create/update and surface it on the Patient shape.
- Scope patient list/get for the `doctor` role to their own panel (with a
  createdBy fallback for legacy rows); admin/owner/member/reception/viewer keep
  seeing every patient.
- Add POST /api/patients/:fileNumber/transfer to reassign a chart (updates the
  provider link + PCP label, records activity, notifies the clinic).
- Add GET /api/staff/providers (any member) listing clinical-capable members for
  the PCP picker and transfer dialog.
- Scope the activity feed: non-admins see only their own actions; owners/admins
  see the whole clinic.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-09 19:51:17 +03:00
Khalid Abdi 4f8793c765 fix: clinic creation gating, analysis line charts, username + task assignee, delete confirm
- 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>
2026-06-08 19:52:12 +03:00
Khalid Abdi 6213da9477 feat: admin-provisioned staff, username login & role-based access
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>
2026-06-08 19:12:07 +03:00
Khalid Abdi 730e07fcfc feat: real-time staff messaging over Socket.io
Add conversations/participants/messages tables, a participant-scoped REST
API (/api/conversations) and a Socket.io server (session-authenticated
handshake; per-user + per-conversation rooms) sharing the HTTP port. New
messages broadcast live and create per-recipient notifications. Also lands
the notifications table + service + routes (used by the message flow). The
Messages page is rewritten: live threads, unread state, and a compose
dialog to start a conversation with a clinic member.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 21:26:28 +03:00
Khalid Abdi 75940313a4 feat: activity audit log written from all resource routes
Add the activity_log table, a best-effort recordActivity() service and a
GET /api/activity feed, and write entries on create/update/delete of
patients, notes, appointments, prescriptions and tasks. The Activity page
now shows the real audit trail (actor, action, patient context, time);
the fabricated signing hashes / approval badges are gone — that vision
stays deferred.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 19:46:22 +03:00
Khalid Abdi 25254dd4c1 feat: org-scoped tasks backend, wire tasks page
Add the tasks table, validation, service and routes (/api/tasks with a
PATCH for partial updates / the done toggle, RBAC-gated) and the frontend
data module. The tasks board now loads, creates and toggles real data
(optimistic toggle with rollback).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 19:41:10 +03:00
Khalid Abdi 7ef9da59bd feat: org-scoped prescriptions backend, wire prescriptions page
Add the prescriptions table, validation, service and CRUD routes
(/api/prescriptions, RBAC-gated; prescriber defaults to the signed-in
clinician, prescribedAt to today) and the frontend data module. The page
now loads/persists real data and computes its status KPIs.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 19:37:46 +03:00
Khalid Abdi dec04ec506 feat: org-scoped appointments backend, wire appointments page
Add the appointments table, validation, service and CRUD routes
(/api/appointments, RBAC-gated) and the matching frontend data module.
The appointments page now loads and persists real data; KPIs are computed
from it and the schedule/calendar anchor to the real current date.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 19:33:37 +03:00
Khalid Abdi 7d6641ef59 Add notes API: clinic + author-scoped CRUD
New `note` table (src/db/schema/notes.ts) referencing organization + user,
with org+author scoping so a doctor only sees their own notes within the active
clinic. Adds:

- src/types/note.ts + src/lib/note-validation.ts (zod)
- src/services/notes.ts (CRUD, treats non-uuid ids as not-found)
- src/routes/notes.ts mounted at /api/notes, gated requireAuth → requireOrg
- schema barrel + generated migration drizzle/0001_*.sql (applied on startup
  by the runtime migrator)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 19:48:39 +03:00
Claude 9dabe2f5d2 Add TypeScript + Express + Postgres backend with Better Auth
Implements the first temetro backend: an Express 5 API on Postgres via
Drizzle ORM, with authentication and multi-tenant clinics powered by
Better Auth.

- Auth: email/password with required email verification, password reset,
  rate limiting, CSRF/trusted-origins, secure cookies, session audit hook.
- Organizations (clinics) with RBAC (owner/admin/member/viewer) and an
  extended `patient` permission set; member invitations by email.
- Org-scoped patient records mirroring the frontend Patient shape, with
  CRUD endpoints gated by permission (read/write/delete).
- Email helper logs links to the console when SMTP is unset (zero-setup
  local dev); Dockerfile + docker-compose (db + backend + frontend) with
  migrations applied on startup and a configurable Postgres host port.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-02 21:27:32 +03:00