Next.js 15 · React 19 · Supabase · TypeScript
A clinic management system where the safety rules are tested.
Appointments, consultations, prescribing, laboratory, pharmacy, billing and a patient portal. 6 roles, 53 screens, 61 tables. The parts that can hurt somebody are pure modules with 343 tests around them, and the permissions are proven against real Postgres rather than asserted in a README.

- 343
- Unit tests
- 200
- RLS assertions
- 15
- Money parity checks
- 134
- Row level policies
Scheduling, dosing, money, triage
Run against real Postgres
Screen totals match the database
Across 61 tables
Most clinic templates are screens. This one is mostly the reasoning underneath them.
11 pure, dependency-free modules hold the decisions that matter. They have no database, no framework and no network, so they can be read in one sitting and tested directly. Every one of them is the answer to something that goes wrong in real clinics.
Drug allergy checking
Catches amoxicillin prescribed to a penicillin-allergic patient, which a name comparison misses entirely. It warns and demands a written reason; it never blocks, because software that refuses gets worked around.
Result flagging
Picks the reference range by sex and age before comparing, so 13 g/dL of haemoglobin is normal for a woman and low for a man. Critical beats abnormal. No range on file means no flag rather than a guess.
Invoice arithmetic
Ported from the generated columns and triggers, then proven against the real migrations in Postgres. It found a schema bug where an insurance-settled invoice stayed unpaid forever.
Batch dispensing
Picks first-expiry-first-out, not first-in-first-out, and flags a batch that expires partway through a 30-day course. Nothing else in a clinic system knows both those facts at once.
Scheduling
Slot generation in the clinic's own timezone, tested through daylight saving in both directions, because an appointment that moves an hour twice a year is a patient who missed it.
Statistics
Medians with a 90th percentile rather than means, and any rate built on fewer than ten observations refuses to print a percentage instead of publishing noise a manager will act on.
Pharmacy
The warning names the reason, not just the risk.
- A class match, not a name match, amoxicillin and penicillin share no letters, so a string comparison misses it entirely.
- It asks rather than refuses, the pharmacist can go ahead with a written reason, which is stored on the dispense.
- The batch is picked by expiry, not by arrival, and the server picks again at the counter rather than trusting the page.
- Stock is never written directly, every change is a ledger row, so a batch and its history cannot disagree.

Analytics
Every percentage arrives with its denominator.
- No bare percentages, 90% always appears as 90% (735 of 818), because the first is an opinion and the second is a fact.
- Under ten observations it will not give a figure, it says there are too few to judge, and means it.
- Medians with a 90th percentile, a mean waiting time hides the afternoon that went wrong.
- Empty days stay in the series, dropping them turns a quiet week into a flattering line.

Front desk
A day that survives daylight saving.
- Arithmetic in the clinic's timezone, never the viewer's, tested through the clock going forward and back.
- Walk-ins and appointments in one queue, because that is how a waiting room actually works.
- Waiting time is measured, not estimated, from the moment of arrival, and it is on the screen for the person who can act on it.
- Cancellations are excluded from no-shows, a patient who rang ahead is not the same as one who did not turn up.

The assistant
Half of the AI features deliberately use no AI.
A patient asking what a haemoglobin of 9.1 means wants what the test measures, where their number sits, and what happens next. All three are already in the record. A language model adds nothing and risks the one thing that matters most: producing a number the record does not contain, fluently. So the result explainer is a curated glossary and a template, and a test asserts that no figure can appear in an explanation that was not in the result row.
Symptom triage is an explicit rule table a clinician can read and correct. It answers how soon somebody should be seen, which is routing rather than diagnosis, and a test walks every rule to prove none of them names a condition. Red flags cannot be talked down: chest pain reported as one out of ten is still an emergency.
Only note drafting uses a model, and it is fenced on both sides. Identifiers are stripped on the way out. On the way back, every number in the draft is checked against the transcript, because an invented dose reads exactly like a real one.
What that buys the buyer
- Two of the four features cost nothing to run and cannot hallucinate.
- With no API key set, the product still works. Drafting says so plainly instead of failing.
- Every run is recorded with its model, its latency and whether a clinician accepted it.
- Nothing generated becomes part of a record until a person signs it. There is no button that skips that.

Six roles. Six different products.
The navigation, the data and the permissions all change with who is signed in. Open the demo and switch role from the bar at the top; it is the fastest way to see how much is here.

Reception
Arrivals, walk-ins, the queue, payments and the day's schedule.

Consulting room
SOAP notes with a sign-and-lock flow, ICD-10 diagnoses, prescribing with allergy checks.

Laboratory
Bench worklist, chain of custody, result entry flagged live, approve then release.

Pharmacy
Dispensing by expiry, batches, the stock ledger, purchase orders and suppliers.

Billing
Invoices, payments, refunds, insurance claims and day-end reconciliation.

Patient portal
Appointments, results in plain language, medication, bills and the assistant.
Permissions
Hiding something in the interface is not a permission.
Three independent layers, none of them trusted to be the only one: middleware gates navigation, pages re-check the role next to the data, and row level security decides what the database will return. All 61 tables have RLS enabled and at least one policy.
You can run the proof yourself with npm run db:test. It applies all 13 migrations to Postgres compiled to WASM and impersonates each role in turn. No Docker, no Supabase project, about twenty seconds.
A few of the 200 assertions
- Reception sees demographics and money, and no clinical note.
- A patient sees a lab result only once it is approved and separately released.
- Signing a note makes it immutable; corrections are amendments.
- Nobody can promote themselves. Only an administrator changes a role.
- A clinician cannot file a note under a colleague’s name.
- The stock ledger and the audit log are append-only.
- A refund cannot exceed what was paid.
Buy it once. It is yours.
The complete source, the migrations, the test suites and the setup guide. No subscription, no hosted dependency on us, nothing phoning home.
Single licence
One clinic, one deployment. Yours to modify and run.
- The complete source, all 10 phases
- Use in one commercial project
- 12 months of updates
- The full test suite and the migrations
- Deployment and Supabase setup guide
Unlimited licence
Every project you build, including work you deliver to clients.
- Everything in the single licence
- Unlimited commercial projects
- Use in client work you are paid for
- Lifetime updates
- Priority email support
Launch pricing runs until Fri, 07 Aug 2026 23:59:59 GMT. After that both licences return to $499 and $999. The timer at the top of this page counts to that instant and the prices change when it reaches it.
Before you buy
What this is not, and what is still yours to do.
Written plainly, because you will find all of it in the first hour and it is cheaper for both of us if you find it now.
Regulatory compliance is yours
If real patient records go into your deployment, HIPAA, GDPR or your local equivalent applies to you, not to this repository. That means a data processing agreement with your host, a retention policy, and a security review. The audit log, the RLS and the encryption in transit are groundwork, not a certification.
It has not been used on real patients
It is a complete, carefully built system with a large test suite. It has not run a clinic. Anything clinical, especially the triage rule table and the drug class list, should be reviewed by a practising clinician before it touches a real person.
Database types need one command
`src/types/database.ts` is a hand-written stand-in covering seven of 61 tables, so services query through a deliberately untyped client. One supabase gen types run against your project makes the whole service layer type-safe.
Some things are scaffolded, not finished
Purchase orders can be received but not raised. Voice-to-text is not built. There is no analytics export, no online patient payments, and medication reminders have their tables but no delivery. The README lists every gap by name.
The demo is the whole product. Go and break it.
Every screen, every role, real fixtures. Nothing is saved and nothing is hidden behind a form.