mirror of
https://github.com/temetro/temetro.git
synced 2026-07-26 11:58:14 +00:00
bffed5525d
Bump root/backend/frontend to 0.12.1 and move the CHANGELOG notes under a dated 0.12.1 heading (centered wallet-sync stepper, timeline record history, and the landing-page globe deferral). Adds the radix-ui dependency pulled in by the Origin UI stepper/timeline primitives. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
23 KiB
23 KiB
Changelog
All notable changes to temetro are recorded here. The format is loosely based on Keep a Changelog, and the project follows Semantic Versioning. See RELEASING.md for how releases are cut and published.
[Unreleased]
[0.12.1] — 2026-07-10
Changed
- Centered wallet-sync stepper. The in-dialog "Sync to wallet" stepper (
DialogStepperinfrontend/components/wallet/wallet-sync-step.tsx) now uses a proper Stepper primitive (components/ui/stepper.tsx), so the numbered indicators sit centered inline with their labels instead of the previous left-aligned look. - Record history is now a timeline. The patient sheet's Record history section
(
RecordHistoryinfrontend/components/patients/patient-detail.tsx) renders as a vertical timeline (components/ui/timeline.tsx) — who made the change, an entity-type icon, what happened, and when — replacing the flat avatar list.
Performance
- Landing page: defer the 3D globe. The Temetro Network globe (three.js) on the marketing site
now mounts only when its section scrolls near the viewport (IntersectionObserver), instead of on
hydration — removing ~730 KB of JS and its main-thread execution from initial page load. (Landing
page lives in the sibling
temetro/landing-pagerepo.)
[0.12.0] — 2026-07-09
Added
- In-dialog "Sync to wallet" stepper. When a clinician adds or edits a record for a
wallet-linked patient — invoice, appointment, prescription, patient demographics, or an AI
scribe note — the create/edit dialog shows a two-step stepper: after saving, step 2 offers to
push the change to the patient's wallet (reusing
pushWalletUpdate+ approval polling). ShareduseWalletSynchook andDialogStepper/WalletSyncStepcomponents undercomponents/wallet/. Patients without a linked wallet see the old close-on-save behaviour.
Changed
- Appointment & invoice date pickers block past dates. The new-appointment date picker disables days before today; the invoice issue-date picker does too, with an opt-in Back-date checkbox for recording genuinely older invoices (edit mode keeps existing past dates).
- Patient Portal wallet link is identified by wallet number only. The portal
linkaction no longer asks the wallet app for a name + file number — it resolves the file the clinic already paired the wallet number to (services/portal.ts#linkWallet), returning a friendly 404 when the wallet isn't paired yet.
Removed
- Stale Settings "Features" section. Dropped the inert "patient-owned storage" / "require signed records" toggles (and their unused i18n keys) that did nothing.
[0.11.0] — 2026-07-09
Added
- Patient Portal over the Temetro Network relay + wallet linking. The wallet app now reaches a
clinic's Patient Portal through the relay instead of a direct HTTP API, so it works from a real
phone. New
services/portal.ts(clinic info, doctors, availability, wallet linking, conflict-aware booking, results, downloadable lab files) runs behind aportal:requesthub handler (backend/src/services/relay-client.ts). A new nullablepatients.wallet_numbercolumn stores the link, andwalletNumberForPatientresolves through it so clinic→wallet pushes work after a portal link (not only after a permanent share).GET /api/portal/:clinic/linkreturns the relay-based pairing descriptor (clinic signing key + relay URL). - Portal "Link my wallet" option. The Patient Portal kiosk adds a third card that shows a QR the
wallet app scans to link over the relay (
components/portal/portal-kiosk.tsx). - Appointments & invoices reach the wallet. Clinic→wallet pushes now include the patient's appointments and invoices in the sealed bundle, so they show up in the wallet app.
- Clinic location reverse-geocoding. "Use my current location" now fills address / city /
country (OpenStreetMap Nominatim), not just latitude / longitude (
lib/geocode.ts).
Fixed
- Patient Portal QR was unreachable from a phone. Settings → Signing now encodes a
temetro-portal:pairing URI (relay URL + clinic signing key) instead of alocalhostAPI URL, so the wallet app can actually connect (components/settings/settings-portal.tsx). - Arabic (RTL) sidebar. The collapse arrow and notification bell now stack above the nav
icons instead of being pinned to the opposite edge; the toggle glyph mirrors and the notifications
popover opens toward the content side (
components/sidebar-02/app-sidebar.tsx,components/ui/sidebar.tsx,components/sidebar-02/nav-notifications.tsx).
[0.10.0] — 2026-07-07
Added
- Patient Portal doctor picker & availability. The portal now lists the clinic's doctors and
books against a chosen provider, showing only free slots. New public endpoints
GET /api/portal/:clinic/doctorsandGET /api/portal/:clinic/availability?provider=&date=(display-safe fields only), andPOST /api/portal/:clinic/appointmentsaccepts an optionalprovider(backend/src/routes/portal.ts). The existing 409 conflict check stays authoritative. - Patient Portal links in Settings. Settings → Signing → Patient Portal adds open, copy
link, and QR code actions (
components/settings/settings-portal.tsx); the QR carries the backend base (?api=) so the patient wallet app can book natively when it scans it. - Clinic location "Use my current location". The location editor fills map coordinates from the
browser's geolocation (
components/settings/settings-location.tsx). - Wallet app native Patient Portal. Scanning a clinic's portal QR opens a native booking screen (doctor list → free-slot picker → confirm) in the patient wallet app.
Fixed
- Arabic (RTL) layout. The sidebar now anchors to the right for RTL locales and the toggle
switch mirrors correctly, instead of leaving the shell misaligned
(
components/sidebar-02/app-sidebar.tsx,components/ui/switch.tsx). - Wallet app: record-card bottom sheet no longer freezes the app (dropped the per-frame animated blur overlay for HeroUI's built-in overlay); Reset wallet now confirms in a native HeroUI dialog with Liquid Glass actions; fixed the white edge flash on screen transitions in dark mode; the home/onboarding/register logo is now the Temetro mark.
Changed
- New i18n keys (
settings.portal.*, geolocation strings) are translated into all shipped locales (en, de, fr, ar, so).
[0.9.0] — 2026-07-06
Added
- Patient blood type & phone number. The patient record now carries a
bloodType(e.g.O+) and aphonenumber. Both are shown in the record sheet and chat summary card and are editable in the add/edit patient form.phoneis a demographic/contact field (visible to and editable by the reception role);bloodTypeis treated as clinical PHI and is redacted for reception (like allergies/vitals). New columnspatients.phone/patients.blood_type(migration0033). - Clinic location setting. A new org-scoped
clinic_settingstable (migration0034) stores the clinic's address (address / city / country) plus optional map coordinates (latitude / longitude), set in Settings → Signing → Clinic location (owner/admin only). New endpointsGET /api/clinic/settings(any clinician) andPUT /api/clinic/location(owner/admin). This will be surfaced in the patient wallet app to show a clinic's location.
Changed
- New i18n keys for the above are translated into all shipped locales (en, de, fr, ar, so), per
the coverage rule now documented in
frontend/CLAUDE.md.
[0.8.2] — 2026-07-05
Fixed
RELAY_URLnow defaults to the hosted relay (https://network.temetro.com) instead ofhttp://localhost:8080. The old default silently failed for anyone who joined the network without explicitly settingRELAY_URL— the backend's hub connection could never reach the relay (inside Dockerlocalhostis the container itself), so it never authenticated and QR pairing generated a QR pointing at an unreachablelocalhost. Self-hosters running their own relay still overrideRELAY_URL. Updated.env.exampleaccordingly.
Changed
- Generating a pairing QR (
POST /api/patients/wallet/pair) now ensures the clinic's relay hub is connected before pre-registering the request, so the routing is set up even if the connection was opened lazily.
Fixed
- QR "scan to connect" pairing was broken by the multi-clinic relay routing (v0.8.0): pairing
has no wallet number, so the clinic never sent a
wallet:sendto register the request, and the relay rejected the scanning device's response as "unknown or expired". The clinic now pre-registers the pairing request with the relay (a newhub:expect { requestId }event onPOST /api/patients/wallet/pair), so the device's response routes back correctly. On hub (re)connect the backend re-registers its still-pending requests, so routing also survives a relay restart.POST /pairnow also requires the clinic to have joined the network (clear 409 instead of a dead QR), surfaced in the import dialog.
Added
- Multi-clinic Temetro Network. The relay now serves many self-hosted clinics at once. Each
clinic authenticates to the
/hubnamespace by signing a challenge with its own Ed25519 clinic signing key (services/signing.ts) — a per-clinic identity, not a shared password — and the relay routes every device response back to only the clinic that originated the request (keyed byrequestId), so clinics never see each other's traffic.wallet:onlineis fanned out only to clinics with pending work for that wallet. - "Join Temetro Network" opt-in. A per-clinic toggle in Settings → Signing (backed by
clinic_signing_keys.network_enabled,GET/PUT /api/signing/network, owner/admin only). Off by default; enabling opens the clinic's relay connection, disabling tears it down. Wallet import/push endpoints return 409 while a clinic hasn't joined. Localised in all five languages.
Changed
- The backend keeps one authenticated relay connection per network-enabled org
(
services/relay-client.ts—connectOrg/disconnectOrg, ahubsmap keyed byorgId), instead of a single shared-token connection.emitToWallet/sendToWalletnow take anorgId, and the offline-flush (pendingUpdatesForWallet) is org-scoped. RELAY_TOKENis now optional/legacy. Clinics authenticate with their signing key, so an open relay needs no shared secret;RELAY_TOKENonly gates an optional private relay.
[0.7.0] — 2026-07-05
Added
- Temetro Network — a standalone, high-performance relay (Rust + Axum + socketioxide) that
connects the backend to patient wallet apps, in its own repo
(github.com/temetro/temetro-network) and deployable
on Railway. It replaces the flaky Cloudflare quick-tunnel that used to expose the backend's
embedded
/walletSocket.io namespace to phones. The relay is a dumb, stateless pipe: a/walletnamespace for devices (challenge/Ed25519-signature auth, room keyed by wallet number) and aRELAY_TOKEN-authenticated/hubnamespace for the backend. It forwards sealed ciphertext verbatim, keeps no database, and its only crypto is verifying a device's auth signature (proven byte-for-byte compatible withwallet-crypto.ts).
Changed
- The backend is now a client of the relay, not the wallet server. The
/walletSocket.io namespace was removed fromsrc/realtime.ts; a newsrc/services/relay-client.tsconnects to the relay's/hub(emitToWalletdelegates to itssendToWallet), handles device responses (wallet:share-response/wallet:update-response/wallet:revoke) and flushes missed updates onwallet:online— calling the samewallet-share/wallet-updatesservices as before. NewRELAY_URL+RELAY_TOKENenv vars; the wallet-import QR now points atRELAY_URL.
[0.6.0] — 2026-07-04
Added
- Read-only FHIR R4 server — temetro can now be a FHIR server, not just a client.
A new endpoint tree at
/fhir(mounted outside/api, bearer-only) exposes each clinic's records as FHIR R4: Patient, Observation (labs + synthesized vital signs), AllergyIntolerance, Condition, MedicationRequest, Encounter and Appointment, plus an unauthenticatedGET /fhir/metadataCapabilityStatement. Searches return searchsetBundles with_count/_offsetpagination and self/next/prev links. Because temetro stores free-text clinical values, everyCodeableConceptis text-only (no SNOMED/LOINC) and patients carry an age extension rather than abirthDate— documented in the CapabilityStatement and API docs. - Per-clinic FHIR API keys — machine-to-machine auth via
Authorization: Bearer tmf_…. Keys are created/revoked under Settings → Integrations → FHIR server (owner/admin), SHA-256-hashed at rest, and shown once at creation. Every FHIR request is org-scoped (no cross-clinic reads) and written to the activity log with the key name and result count. Newfhir_api_keystable,middleware/fhir-auth.ts, theservices/fhir-server/mapping module (queries, resources, bundle, capability, keys), the/fhirrouter, andGET/POST/DELETE /api/integrations/fhir-server/keys. NewfhirServerlocale namespace across all five languages.
[0.5.0] — 2026-07-03
Added
- Clinic → wallet record-update push — a clinician can push an updated record to a
wallet-linked patient (a permanent, approved share). The record snapshot is signed
with the clinic's 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, and delivered over the/walletrelay live and on the wallet's next authenticated connect (so an offline phone catches up). The patient reviews it in a pending-updates inbox and approves/denies; the wallet signs its decision, the backend verifies it, and the on-device record is replaced only on approval. The wallet pins the clinic key (TOFU) and warns on a key change. NewPOST /api/patients/wallet/push,GET /api/patients/wallet/{link/:fileNumber,updates,updates/:id},walletRecordUpdatestable + service,wallet:update-request/wallet:update-responserelay events, a "Push to wallet" dialog with live status, and a "Sent updates" list under Settings → Signing. NewwalletPush/walletUpdatesListlocale namespaces across all five languages. (The wallet app half ships in the siblingtemetro-apprepo.)
[0.4.0] — 2026-07-03
Added
- Ambient AI visit scribe — a Record visit action on the patient sheet turns a
clinician↔patient conversation into a draft SOAP encounter note. Record with the
microphone (
MediaRecorder, stored as an auditable patient attachment) or paste a transcript; the backend transcribes via the user's OpenAI (Whisper) or Gemini key, de-identifies the transcript + patient context through Veil, and the model drafts a structured note that the clinician reviews and edits before saving — the same write-approval gate as the chat agent. NewPOST /api/scribe/{transcribe,draft,save}(backend/src/routes/scribe.ts,services/ai/transcribe.ts), aveil.redactText()free-text redactor, and anappendEncounterservice that adds one note without touching the rest of the record. Gated bypatient:write+ the clinic AI policy (reception and disabled-AI accounts don't see it). Newscribelocale namespace across all five languages. Drafting also works with local Ollama from a pasted transcript.
[0.3.0] — 2026-07-02
Added
- Three new interface languages — Somali (
so), Arabic (ar) and German (de) join English and French, selectable in Settings → Profile → Language. Each locale carries a full translation of the ~1,660 UI strings, with native names shown in the selector (Soomaali, العربية, Deutsch). - Right-to-left (RTL) support — selecting Arabic sets
dir="rtl"on the document (applied before first paint via an inline script, so no flash), flips physical spacing/alignment to logical CSS utilities, mirrors directional icons, and loads an Arabic-capable typeface (IBM Plex Sans Arabic) appended to the font stack for per-character fallback. - Language roams across devices — the chosen language is persisted to the
per-user
user_settingspreferences and re-applied on sign-in, with localStorage remaining the offline source of truth. frontend/scripts/check-locales.mjs(+npm run check-locales) — a parity check that fails on missing/extra keys or{{placeholder}}mismatches across locales and warns when Arabic count-keys lack the full CLDR plural forms.
[0.2.5] — 2026-07-01
Fixed
- Multi-arch Docker images — the
releaseworkflow now builds and publisheskhalidxv/temetro-backendandkhalidxv/temetro-frontendfor bothlinux/amd64andlinux/arm64. Previously the images were amd64-only, sodocker compose pullon Apple Silicon failed with no matching manifest for linux/arm64/v8 and fell back to building from source.
Changed
- Language switcher in Settings → Profile is now a select that asks for confirmation before switching the interface language, instead of applying the change instantly on a button tap.
[0.2.4] — 2026-06-29
Added
- Pagination on the Activity and Invoices pages (10 per page), matching the
Patients page, via a shared
ListPaginationcomponent. - French (Français) interface language, with a language switcher in Settings → Profile. The choice persists on the device.
- "Check for updates" button in Settings → About & updates that forces a fresh check.
Changed
- Update detection now reads the latest version from Docker Hub image tags (the channel clinics actually pull), falling back to the GitHub release if Docker Hub is unreachable. This fixes "About & updates" showing Up to date when a newer image was already published.
- docker compose host ports are now configurable (
BACKEND_PORT,FRONTEND_PORT,ADMINER_PORT, alongsidePOSTGRES_PORT) so a port clash ondocker compose up -dcan be resolved from.envwithout editing the file.
[0.2.3] — 2026-06-29
Fixed
- AI chat patient record cards now render on Google Gemini for name
lookups. "Show me 's medical record" relied on the model chaining
searchPatients→getPatient, but Gemini often calledsearchPatientsand then emitted only a canned closing line ("Here's the record.") without the second tool call — so no card was ever drawn.searchPatientsnow displays the record card directly when exactly one patient matches, so the flow no longer depends on a follow-up tool call. (The previous Gemini fix in 0.2.2 only covered the empty-schema list tools.)
[0.2.2] — 2026-06-28
Fixed
- AI chat record cards now render on Google Gemini. The card-emitting
chat tools (
listAppointments,listTasks,listPrescriptions,getClinicInfo,getAnalytics,listInventory) used an empty parameter schema; Gemini can't emit a function call for a schema with no properties, so it printed the call astool_codetext instead of invoking the tool — leaving replies as plain text (e.g. "Show today's schedule" leaked a raw<tool_code>block) with no cards. The tools now share a non-empty schema so Gemini calls them; other providers are unaffected.
[0.2.1] — 2026-06-27
Fixed
- Patients pagination controls now render as proper buttons — the prev/next
and page-number controls were unstyled and wrapping (the COSS
PaginationLinkdrops its button styling when given arenderprop). - Patient detail sheet header reflowed: actions (Download summary / Transfer / Edit / Delete) moved to their own wrapping row so the patient name is no longer truncated.
- Messages thread now shows sender and recipient avatars alongside the chat bubbles.
- Release notes — the
releaseworkflow now publishes the matchingCHANGELOG.mdsection as the GitHub Release body (instead of only the auto-generated "Full Changelog" link).
[0.2.0] — 2026-06-27
Added
- Patients table pagination. The Patients list now paginates at 10 rows per
page (COSS
Pagination), so large clinics no longer scroll endlessly. - Per-patient record history. The patient detail sheet shows an audit
timeline of every add/change on that chart.
GET /api/activity/patient/:fileNumber. - Patient summary PDF. A Download summary action on the patient sheet produces a clean, printable one-page clinical summary (browser "Save as PDF").
- AI setup notice. A single, dismissible heads-up appears above the chat input on a fresh chat when no AI provider (API key or local Ollama) is configured; it clears itself once you send a message.
- Version & update awareness.
GET /api/versionreports the running version and checks GitHub Releases for a newer one; Settings → About & updates shows the current/latest version, and an optional, dismissible banner appears when an update is available. - LAN access. The frontend now resolves the backend URL from the host the
browser is using, so other departments can reach temetro at
http://<server-LAN-IP>:3000with no rebuild. A Settings panel surfaces the shareable network address.GET /api/networkreports detected LAN addresses. - Prebuilt Docker images published to Docker Hub (
khalidxv/temetro-backend,khalidxv/temetro-frontend) via a tag-triggered GitHub Actions release workflow. - Voice dictation on the AI chat input (Web Speech API), with graceful fallback where the browser doesn't support it.
Changed
- Messages thread rebuilt on the shadcn
Message/Bubble/Attachmentcomponents (COSS colour tokens preserved) for a cleaner conversation surface. - Patient status badges now use semantic colours (active → success, inpatient → info) instead of a flat secondary badge.
docker-compose.ymlreferences the published images (with a build fallback) and no longer bakes a fixed API URL into the frontend.
Fixed
- AI import approval card.
previewImportnow declares a concrete record schema so Google Gemini emits a real tool call (and the approval card renders) instead of dumping atool_code/JSON wall as text. The system prompt also forbids printing tool calls and re-listing fields. - Settings network address no longer shows a bogus container IP / "Error"
under Docker — the
/api/networkendpoint now skips container-internal interfaces, and the panel falls back to the helpful LAN hint.
[0.1.0] — 2026-06-26
Initial baseline: clinician AI-chat UI wired to the TypeScript/Express/Postgres API (Better Auth, multi-tenant clinics, org-scoped patient records), the patient wallet encrypted-share flow, and Dockerised local run.