The aescolab showcase adds an at-home blood test flow to the template: a patient orders a package, books a blood draw at a Partner Practice, the lab releases a Result Set, and the patient reads an Energy Score with a plan. Every person, practice, order and value is fictional and marked is_demo. The domain terms are in CONTEXT.md; the tenancy decision is ADR-0014.
Data
| Kind | Tables | Who reads, who writes |
|---|---|---|
| Global catalogs | biomarker, biomarker_range, biomarker_alias, lab_package, practice, practice_hours, postcode_centroid | Every trusted session reads (signed out too); only the seed writes |
| Health data (personal Organization) | health_profile, lab_result_set, lab_measurement, recommendation, plan_summary, goal | The patient; a Result Set is invisible until released_at is set (RLS) |
| Bookings | appointment, appointment_hold | The patient; every tenant sees which slots are taken, never who holds them (column grant) |
| Orders | lab_order, lab_order_event, result_notice | The patient reads and creates; status changes run on the system path |
Pure lab logic lives in packages/db/src/lab/ so the seed and the API share one implementation: scoring.ts (Marker Status, Energy Score, grades, retest direction; re-exported to clients as @template-cloudflare/api/lab-results/scoring), labels.ts (alias normalisation), plan-rules.ts (the rules table and summary), demo-values.ts (deterministic fictional values), result-sets.ts (insert and release) and order-pipeline.ts (one forward step, release on results_ready).
Procedures
| Router | Procedures | Access |
|---|---|---|
biomarkers | list | public |
practices | search (postcode or point, radius 10/25/50/100 km), get, availability (14 days, 24 h lead time, Europe/Berlin, DST-safe) | public |
labOrders | packages | public |
healthProfile | get, update | Organization |
labResults | overview, sets, list, biomarker, compare | Organization |
healthPlan | get, setDone, goals.create, goals.update | Organization |
bookings | book (audited, one slot once), list, cancel (until 12 h before) | Organization |
labOrders | list, get, create, notices, markSeen, demoAdvance | Organization; demoAdvance only in Demo Mode |
admin.labOrders | list, board, get, advance (audited), resetDemo | Admin; metadata only, never a measurement value; resetDemo only in Demo Mode |
Advancing an order to results_ready releases its Result Set, or generates a deterministic fictional one when the lab attached none, then writes the rules plan, the summary and exactly one Result Notice, in one transaction.
Demo Mode
DEMO_MODE=true (a Worker var; off by default) enables the demo controls: the credential card on /login, labOrders.demoAdvance for the patient's own demo order (system reason demo.advance) and admin.labOrders.resetDemo. The DEMO label in the UI is always shown.
Seed
pnpm --filter @template-cloudflare/db db:seed # local database
SEED_ALLOW_REMOTE=true pnpm --filter @template-cloudflare/db db:seed # a hosted demo database
SEED_ALLOW_REMOTE=true pnpm --filter @template-cloudflare/db db:seed:reset-demo # pipeline onlyThe seed is idempotent and dates everything relative to the run. It loads 61 Biomarkers with demo reference ranges, about 400 aliases, the three packages, 58 fictional practices on real postcode centroids (GeoNames, CC BY 4.0, in packages/db/seed-data/postcodes-de.csv), exactly two sign-in accounts (a patient and an admin, printed at the end), the patient's history (C 64 → B 74 → B 82), an open 360° retest waiting at the lab with an unreleased A-grade set, a fresh order for the booking demo, and 14 fictional pipeline patients without a password.
Two ways back to a known state:
db:seedis the clean baseline. It rewrites the history and also deletes the patient's Lab Report Imports and their Result Sets.db:seed:reset-demoand the admin's "Demo zurücksetzen" (admin.labOrders.resetDemo) restore only the open order, appointments, notices and the 14 pipeline patients. The history sets and the patient's imports are kept; the only Result Set removed is the lab retest that a live "advance" released, so the open order waits at the lab again with its unreleased A-grade set.
Web screens
German is the default; every string is in apps/web/src/locales/{portal,public,admin-orders,lab-import}.{de,en,fr}.ts, spread into the main catalogs.
| Route | Feature | What it shows |
|---|---|---|
/ , /pakete, /fuer-unternehmen | landing | Landing, package comparison, B2B page; every number from landing.facts.ts or the packages API |
/praxen, /praxen/$slug | practices | Postcode + radius finder with an SVG map (no tiles, no third-party requests), one page per practice |
/praxen/$slug/termin | appointments | Day strip and slot radio group; booking is persisted, a taken slot refreshes availability. Signed out, /login?redirect= brings the User back |
/dashboard | lab-results | Energy Score gauge (A–E), change since the previous set, category grades, top measures, order tracker, next appointment. Admins are sent to /admin/bestellungen |
/werte, /werte/$code, /werte/vergleich | lab-results | Values with reference vs optimal range bars, Biomarker detail with trend chart and table, retest comparison |
/plan | plan | Summary with its engine badge (and "Mit KI neu schreiben" when a model is configured), measures by kind with done toggles, goals |
/termine, /bestellungen, /bestellen | appointments, lab-orders | Appointments with cancel, orders with the pipeline tracker and the demo advance, demo checkout |
/import, /import/$importId | lab-import | PDF → text in the browser, engine choice ("KI (OpenAI …)" or "Regelbasiert (Demo-Fallback)"), two sample DEMO PDFs, row-by-row review |
/admin/bestellungen, /admin/bestellungen/$orderId | lab-orders | Pipeline board with button-based advancing, table view, order detail, "Demo zurücksetzen" |
The bell in the top bar polls Result Notices every 30 seconds while the tab has focus; a new notice refreshes the scores and the plan, so an admin's release shows on the patient's dashboard without a reload. Status is never shown by colour alone: every chip has a label and an icon, charts have a title, a description and a table.