mirror of
https://github.com/temetro/temetro.git
synced 2026-07-26 11:58:14 +00:00
c5812511b5
Hardware barcode scanner in the pharmacy Add-item dialog, Separated Panels in Care team settings, and a pointer cursor on Activity rows. Also fixes the README changelog badge, which was stuck at 0.14.0. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
581 lines
36 KiB
Markdown
581 lines
36 KiB
Markdown
# Changelog
|
|
|
|
All notable changes to temetro are recorded here. The format is loosely based on
|
|
[Keep a Changelog](https://keepachangelog.com/), and the project follows
|
|
[Semantic Versioning](https://semver.org/). See [RELEASING.md](./RELEASING.md)
|
|
for how releases are cut and published.
|
|
|
|
## [Unreleased]
|
|
|
|
## [0.17.0] — 2026-07-19
|
|
|
|
### Changed
|
|
- **Pharmacy Add-item now uses a hardware barcode scanner.** The inventory Add-item dialog is
|
|
built for a USB barcode scanner connected to the computer (a keyboard wedge): scanning a
|
|
medication types the code as a fast keystroke burst ending in Enter, which is parsed and
|
|
auto-fills the Barcode/NDC, expiry (AI 17), and lot (AI 10) fields — no need to focus any input
|
|
first. The barcode field also parses on Enter for manual entry. This replaces the camera scanner
|
|
in this dialog (the camera scanner stays for importing a patient's wallet code)
|
|
(`frontend/components/pharmacy/add-inventory-dialog.tsx`).
|
|
- **Care team settings use Separated Panels.** The Care team section now renders as distinct
|
|
bordered panels on a muted tray, matching the Preferences and AI settings frames
|
|
(`frontend/components/settings/settings-care-team.tsx`).
|
|
|
|
### Fixed
|
|
- **Pointer cursor on Activity rows.** Hovering an entry in the Activity feed now shows the pointer
|
|
cursor (`frontend/components/activity/activity-view.tsx`).
|
|
|
|
## [0.16.0] — 2026-07-18
|
|
|
|
### Added
|
|
- **Show / Add tabs on the patient record dialog.** Editing a record now opens on a read-only
|
|
"Show" view (the existing `PatientDetail`), with an "Add" tab for the editable form and its Add
|
|
buttons. Saving from the Add tab still offers the existing "send to the patient's wallet" step
|
|
when the patient is wallet-linked (`frontend/components/chat/patient-form-dialog.tsx`).
|
|
- **Barcode scanning in the pharmacy.** The inventory Add-item dialog can now scan a medication
|
|
barcode with the camera (`@zxing/browser`, native `BarcodeDetector` where available) into a new
|
|
Barcode/NDC field, auto-filling expiry (AI 17) and lot (AI 10) from a GS1 DataMatrix. Adds a
|
|
nullable `inventory.barcode` column (migration `0036`).
|
|
- **Scan a patient's wallet code when importing.** "Import from a patient app" gains a Scan button
|
|
that reads the QR or 2D barcode shown in the patient's wallet app and fills the wallet number
|
|
(`frontend/components/patients/import-from-wallet-dialog.tsx`).
|
|
|
|
### Changed
|
|
- **Settings "separated panels" now match COSS exactly.** The opt-in `separated` settings frames
|
|
render a real COSS Frame — a muted tray holding distinct bordered panels spaced apart — instead
|
|
of a `gap-4` bolted onto the panel-fusing `CardFrame`. New `frontend/components/ui/frame.tsx`.
|
|
|
|
## [0.15.1] — 2026-07-17
|
|
|
|
### Fixed
|
|
- **Settings frames with more than one panel now breathe.** When a settings section stacked
|
|
several cards — the AI availability toggles and the patient/account notification lists — the
|
|
panels sat flush against each other with no gap, reading as one joined block. Those frames now
|
|
use the COSS "separated panels" spacing (a 1rem gap that keeps each card's rounded corners),
|
|
while intentional joined lists (care team, records) stay flush. Opt-in via a new `separated`
|
|
prop on `SettingsFrame`/`SettingsSection` (`frontend/components/settings/settings-parts.tsx`).
|
|
|
|
## [0.15.0] — 2026-07-15
|
|
|
|
### Fixed
|
|
- **Prescriptions pushed to a wallet now actually arrive.** `createPrescription` writes to the
|
|
`prescriptions` table, but the sealed bundle only carried `patient.medications`, which comes from
|
|
the separate `patient_medications` table and never grows when a prescription is written. The
|
|
patient approved a bundle identical to what they already had, so nothing appeared in the app's
|
|
Prescriptions section — the "New prescription: X" line in `changes` is display text, not data.
|
|
The bundle now ships `prescriptions` alongside the appointments and invoices that were already
|
|
handled this way, keeping prescriber/status/dates (`backend/src/services/wallet-updates.ts`).
|
|
- **No more bogus "this clinic's key changed" warnings.** Update events now carry a stable
|
|
`clinicId`. Wallets pinned a clinic's signing key against its display *name*, which is mutable
|
|
and falls back to the literal "A clinic" for unnamed orgs — so two unnamed clinics collided on
|
|
one pin and the second always warned.
|
|
- **Silent drops on the wallet-push path are now logged.** `sendToWallet` no-ops when an org has no
|
|
live relay hub, and `applyUpdateResponse` returned `null` without a word when a response arrived
|
|
unsigned or already resolved. From the clinic side both looked exactly like a patient who never
|
|
tapped Approve.
|
|
- **Windows checkouts no longer break the backend image.** The repo had no `.gitattributes`, so Git
|
|
for Windows' default `core.autocrlf=true` rewrote `docker-entrypoint.sh` to CRLF; the container
|
|
then died with `exec /usr/local/bin/docker-entrypoint.sh: no such file or directory`. Line
|
|
endings are pinned, and the image strips CR before `chmod` so existing checkouts are fixed too.
|
|
- **Settings frames use the COSS default padding.** `CardFramePanel` (added in 0.14.0) isn't part of
|
|
the COSS registry: its `p-5` inset content twice over, and wrapping the body made every card a
|
|
grandchild of `CardFrame`, so the frame's direct-child selectors never applied and each card drew
|
|
its own border — a box inside a box. Body padding now sits on the card, where COSS puts it.
|
|
- **99 lint errors cleared.** `npm run lint` had been failing (the build hides it via
|
|
`eslint.ignoreDuringBuilds`). Vendored `components/charts` and `components/ai-elements` are now
|
|
ignored; the 31 in our own code are fixed. Two were real bugs: the employee dialog could keep a
|
|
typed password when switching to another member, and the wallet-sync hook could carry a patient's
|
|
`linked` state to a newly-selected patient, briefly offering to push a record to someone else's
|
|
wallet.
|
|
|
|
### Changed
|
|
- The clinic→wallet update event carries `clinicId`, and the sealed bundle carries `prescriptions`.
|
|
Both are additive; older wallets ignore them. See the
|
|
[signing API docs](https://docs.temetro.com/docs/api/signing#clinic--wallet-record-updates).
|
|
|
|
## [0.14.2] — 2026-07-15
|
|
|
|
### Fixed
|
|
- **Docker images build on ARM64 again.** Next 16's default Turbopack production build has no native
|
|
bindings for `linux/arm64` in the Alpine image, so `docker compose up --build` failed with
|
|
"Turbopack is not supported on this platform". The frontend now builds with Webpack
|
|
(`next build --webpack`), which builds on every architecture (`frontend/package.json`).
|
|
|
|
## [0.14.1] — 2026-07-15
|
|
|
|
### Fixed
|
|
- **"Send to wallet" now works from every dialog, not just the patient sheet.** The wallet step in
|
|
the appointment, invoice, prescription, patient-edit, and scribe dialogs was gated on a wallet-link
|
|
check that resolved asynchronously; if the clinician saved before it resolved (or it briefly
|
|
failed), the dialog silently closed without pushing. The dialogs now await the link check before
|
|
deciding, and an empty change summary can no longer be sent
|
|
(`frontend/components/wallet/use-wallet-sync.ts`, `wallet-sync-step.tsx`).
|
|
|
|
### Changed
|
|
- **Settings sections composed from `CardFrame` primitives.** Each settings section now builds on
|
|
`CardFrameHeader`/`CardFrameTitle`/`CardFrameDescription` + a new `CardFramePanel` body, and the
|
|
per-row `SettingsCard` renders a real `Card`, giving a consistent framed surface
|
|
(`frontend/components/ui/card.tsx`, `frontend/components/settings/settings-parts.tsx`).
|
|
|
|
## [0.14.0] — 2026-07-13
|
|
|
|
### Changed
|
|
- **Patients page filter moved to its own row.** The status filter left the toolbar and now sits on a
|
|
dedicated **"Filter"** row directly above the table, so the header stays a clean title + search +
|
|
"Add patient" + `⋯` cluster (`frontend/components/patients/patients-view.tsx`).
|
|
- **Patient detail sheet header.** The patient name, status badge, and the `⋯` menu now share one
|
|
left-aligned row, and the standalone **Edit** button moved to be the first item inside the `⋯` menu
|
|
(`frontend/components/patients/patient-detail.tsx`).
|
|
- **Settings redesigned with the COSS frame surface.** Every settings section now renders in a
|
|
`CardFrame` (header + body) via a new `SettingsFrame` part, replacing the flat `SettingsCard` divs
|
|
(`frontend/components/settings/settings-parts.tsx`, `settings-ai.tsx`).
|
|
|
|
### Added
|
|
- **AI Mode: Automatic and Off.** Settings → AI gains two modes beyond API / Local: **Automatic**
|
|
(use a cloud API key when set, else fall back to local Ollama) and **Off** (assistant disabled).
|
|
Automatic is the new default, so a fresh install shows the setup banner until a provider is wired
|
|
(`frontend/lib/ai-settings.ts`, `backend/src/services/ai/{config,provider}.ts`, `types/ai.ts`).
|
|
|
|
### Fixed
|
|
- **AI setup banner now appears when no provider is configured.** Previously the defaulted Ollama URL
|
|
counted as "configured," so the "connect an AI model" banner never showed; it now reflects the
|
|
actual mode (`frontend/components/chat/ai-setup-notice.tsx`).
|
|
- **Chat cards no longer silently vanish.** An unrecognized/renamed streamed data part now renders a
|
|
small placeholder instead of nothing (`frontend/components/chat/chat-panel.tsx`).
|
|
|
|
### Wallet app
|
|
- **Documents/files reach the wallet.** Clinic record-update pushes now include attachment metadata,
|
|
which the wallet folds into the record so the **Documents** tile counts them and the Documents
|
|
screen lists them (`backend/src/services/wallet-updates.ts`, `temetro-app` types + home/documents).
|
|
- **Failed pushes are no longer swallowed.** A record update that fails signature verification or
|
|
decoding is now logged (and a "couldn't verify" notice is raised) instead of disappearing silently.
|
|
- **i18next translation.** The wallet app now uses `i18next` + `react-i18next` + `expo-localization`
|
|
(English shipped; other locales can be added later), with the home, record-update inbox, navigation
|
|
titles, and the visits/prescriptions-adjacent detail screens extracted to translation keys.
|
|
|
|
## [0.13.1] — 2026-07-13
|
|
|
|
### Changed
|
|
- **New Invoice checkboxes.** The Back-date / Due-date toggles now use the COSS `Checkbox`
|
|
(`frontend/components/ui/checkbox.tsx`) instead of raw, misshapen native checkboxes.
|
|
- **Patients page toolbar & table.** The list is now a COSS **Table in a `CardFrame`**
|
|
(`frontend/components/ui/table.tsx`), and the secondary "Import from a patient app" action moved
|
|
into a `⋯` overflow menu so the toolbar keeps a single primary "Add patient" CTA.
|
|
- **Patient detail sheet header.** The five action buttons plus delete collapse into a primary
|
|
**Edit** button and a `⋯ More` menu (Download summary, Record visit, Transfer, Push to wallet, and
|
|
a destructive Delete).
|
|
|
|
### Wallet app
|
|
- **Home quick actions open sheets.** "Share record" now opens a bottom sheet with a scannable **QR**
|
|
of the wallet number (`react-native-qrcode-svg`); "My wallet" opens a bottom sheet with copyable
|
|
wallet number / fingerprint / algorithm; and the duplicate "Notifications" action (the header
|
|
already has a bell) is replaced with **Scan**.
|
|
|
|
## [0.13.0] — 2026-07-12
|
|
|
|
### Added
|
|
- **Prescription date pickers.** The New Prescription dialog's Start/End date now use a proper COSS
|
|
date picker (`frontend/components/ui/date-picker.tsx`, Popover + Calendar) instead of the raw
|
|
native date input.
|
|
- **Working profile fields.** Settings → Profile **Specialty** (a Select) and **Professional links**
|
|
(editable rows) are now wired to persisted preferences — previously they were inert stubs.
|
|
- **Patient filters.** The Patients page gains a status filter (all / active / inpatient /
|
|
discharged) and its search now also matches conditions and allergies.
|
|
- **Language quick-switch.** A language submenu in the sidebar user menu (alongside Theme), applied
|
|
immediately with Arabic RTL.
|
|
- **Wallet app — home & settings.** The patient app home screen adds a quick-action row (Share
|
|
record / My wallet / Notifications) and a recent-activity feed; the settings screen adds an About
|
|
section (version, docs, blog) and a privacy footer.
|
|
|
|
### Changed
|
|
- **Record history renders in full.** The patient sheet's Record history replaces the fragile
|
|
timeline-separator layout with a continuous-rail list, so every audited change shows completely
|
|
(and mirrors correctly under RTL).
|
|
- **Update banner + AI setup notice.** The "update available" banner now uses the COSS warning Alert
|
|
and stays bottom-right under Arabic; the "connect an AI provider" notice is a clearer, thinner bar
|
|
shown above the chat input in active conversations too.
|
|
- **Wallet app — bottom sheets.** Polished the shared sheet building blocks (spacing, radii, close
|
|
and action affordances).
|
|
|
|
### Fixed
|
|
- **Blog reachable.** `blog.temetro.com` was returning 502 due to a custom-domain target-port
|
|
mismatch (Ghost listens on 2368); documented and corrected.
|
|
- **Docs freshness.** Corrected stale `CLAUDE.md` claims (the AI chat is real and `@ai-sdk/react` is
|
|
installed; the signing/approval flow is built) and reduced the vendored `ai-elements` Base UI
|
|
type-drift errors.
|
|
|
|
## [0.12.1] — 2026-07-10
|
|
|
|
### Changed
|
|
- **Centered wallet-sync stepper.** The in-dialog "Sync to wallet" stepper (`DialogStepper` in
|
|
`frontend/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
|
|
(`RecordHistory` in `frontend/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-page` repo.)
|
|
|
|
## [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). Shared
|
|
`useWalletSync` hook and `DialogStepper` / `WalletSyncStep` components under `components/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 `link` action 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 a `portal:request` hub handler
|
|
(`backend/src/services/relay-client.ts`). A new nullable `patients.wallet_number` column stores the
|
|
link, and `walletNumberForPatient` resolves through it so clinic→wallet pushes work after a portal
|
|
link (not only after a permanent share). `GET /api/portal/:clinic/link` returns 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 a `localhost` API 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/doctors` and `GET /api/portal/:clinic/availability?provider=&date=`
|
|
(display-safe fields only), and `POST /api/portal/:clinic/appointments` accepts an optional
|
|
`provider` (`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 a `phone` number. Both are shown in the record sheet and chat summary card and are editable in
|
|
the add/edit patient form. `phone` is a demographic/contact field (visible to and editable by the
|
|
**reception** role); `bloodType` is treated as clinical PHI and is **redacted for reception** (like
|
|
allergies/vitals). New columns `patients.phone` / `patients.blood_type` (migration `0033`).
|
|
- **Clinic location setting.** A new org-scoped `clinic_settings` table (migration `0034`) stores the
|
|
clinic's address (address / city / country) plus optional map coordinates (latitude / longitude),
|
|
set in **Settings → Signing → Clinic location** (owner/admin only). New endpoints
|
|
`GET /api/clinic/settings` (any clinician) and `PUT /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_URL` now defaults to the hosted relay** (`https://network.temetro.com`) instead of
|
|
`http://localhost:8080`. The old default silently failed for anyone who joined the network without
|
|
explicitly setting `RELAY_URL` — the backend's hub connection could never reach the relay (inside
|
|
Docker `localhost` is the container itself), so it never authenticated and QR pairing generated a
|
|
QR pointing at an unreachable `localhost`. Self-hosters running their own relay still override
|
|
`RELAY_URL`. Updated `.env.example` accordingly.
|
|
|
|
### 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:send` to 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 new `hub:expect { requestId }` event on
|
|
`POST /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 /pair` now 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 `/hub` namespace 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 by
|
|
`requestId`), so clinics never see each other's traffic. `wallet:online` is 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`, a `hubs` map keyed by `orgId`), instead
|
|
of a single shared-token connection. `emitToWallet`/`sendToWallet` now take an `orgId`, and the
|
|
offline-flush (`pendingUpdatesForWallet`) is org-scoped.
|
|
- **`RELAY_TOKEN` is now optional/legacy.** Clinics authenticate with their signing key, so an open
|
|
relay needs no shared secret; `RELAY_TOKEN` only 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](https://github.com/temetro/temetro-network)) and deployable
|
|
on Railway. It replaces the flaky Cloudflare quick-tunnel that used to expose the backend's
|
|
embedded `/wallet` Socket.io namespace to phones. The relay is a **dumb, stateless pipe**: a
|
|
`/wallet` namespace for devices (challenge/Ed25519-signature auth, room keyed by wallet number)
|
|
and a `RELAY_TOKEN`-authenticated `/hub` namespace 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 with `wallet-crypto.ts`).
|
|
|
|
### Changed
|
|
- **The backend is now a client of the relay, not the wallet server.** The `/wallet` Socket.io
|
|
namespace was removed from `src/realtime.ts`; a new `src/services/relay-client.ts` connects to the
|
|
relay's `/hub` (`emitToWallet` delegates to its `sendToWallet`), handles device responses
|
|
(`wallet:share-response` / `wallet:update-response` / `wallet:revoke`) and flushes missed updates on
|
|
`wallet:online` — calling the same `wallet-share` / `wallet-updates` services as before. New
|
|
`RELAY_URL` + `RELAY_TOKEN` env vars; the wallet-import QR now points at `RELAY_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 unauthenticated **`GET /fhir/metadata`** CapabilityStatement.
|
|
Searches return searchset `Bundle`s with `_count`/`_offset` pagination and self/next/prev
|
|
links. Because temetro stores free-text clinical values, every `CodeableConcept` is
|
|
**text-only** (no SNOMED/LOINC) and patients carry an **age** extension rather than a
|
|
`birthDate` — 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. New `fhir_api_keys` table, `middleware/fhir-auth.ts`, the
|
|
`services/fhir-server/` mapping module (queries, resources, bundle, capability, keys), the
|
|
`/fhir` router, and `GET/POST/DELETE /api/integrations/fhir-server/keys`. New `fhirServer`
|
|
locale 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 `/wallet` relay 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. New
|
|
`POST /api/patients/wallet/push`, `GET /api/patients/wallet/{link/:fileNumber,updates,updates/:id}`,
|
|
`walletRecordUpdates` table + service, `wallet:update-request` / `wallet:update-response`
|
|
relay events, a "Push to wallet" dialog with live status, and a "Sent updates" list under
|
|
Settings → Signing. New `walletPush` / `walletUpdatesList` locale namespaces across all five
|
|
languages. (The wallet app half ships in the sibling `temetro-app` repo.)
|
|
|
|
## [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. New `POST /api/scribe/{transcribe,draft,save}`
|
|
(`backend/src/routes/scribe.ts`, `services/ai/transcribe.ts`), a `veil.redactText()`
|
|
free-text redactor, and an `appendEncounter` service that adds one note without touching
|
|
the rest of the record. Gated by `patient:write` + the clinic AI policy (reception and
|
|
disabled-AI accounts don't see it). New `scribe` locale 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_settings` preferences 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 `release` workflow now builds and publishes
|
|
`khalidxv/temetro-backend` and `khalidxv/temetro-frontend` for both
|
|
`linux/amd64` and `linux/arm64`. Previously the images were amd64-only, so
|
|
`docker compose pull` on 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 `ListPagination` component.
|
|
- **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`, alongside `POSTGRES_PORT`) so a port clash on
|
|
`docker compose up -d` can be resolved from `.env` without 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 <name>'s medical record" relied on the model chaining
|
|
`searchPatients` → `getPatient`, but Gemini often called `searchPatients`
|
|
and then emitted only a canned closing line ("Here's the record.") without
|
|
the second tool call — so no card was ever drawn. `searchPatients` now
|
|
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 as `tool_code` text 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 `PaginationLink`
|
|
drops its button styling when given a `render` prop).
|
|
- **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 `release` workflow now publishes the matching
|
|
`CHANGELOG.md` section 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/version` reports 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>:3000` with no rebuild. A Settings panel surfaces the
|
|
shareable network address. `GET /api/network` reports 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` / `Attachment`
|
|
components (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.yml` references the published images (with a build fallback) and
|
|
no longer bakes a fixed API URL into the frontend.
|
|
|
|
### Fixed
|
|
- **AI import approval card.** `previewImport` now declares a concrete record
|
|
schema so Google Gemini emits a real tool call (and the approval card renders)
|
|
instead of dumping a `tool_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/network` endpoint 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.
|