Healthcare / scheduling
Patient scheduling platform
A full-stack scheduling platform for a therapy practice — booking patients against the intersection of therapist, room, device and time-off availability, with role-based access from front desk to therapist.
Overview
A scheduling platform for a therapy practice. On the surface it books patients into appointments; underneath, every booking is a small constraint-satisfaction problem — the right therapist, a free room, sometimes a specific device, and never on someone’s day off. It is a product I am building end to end, from the Prisma schema to the responsive UI.
Problem
Scheduling in a practice looks trivial until you list what a valid appointment actually requires. It needs an available therapist who isn’t already booked and isn’t on leave. It needs a room that isn’t over capacity — and if a therapist is marked as working alone, a room nobody else is using. It may need a particular device that lives in that room. A paper diary or a shared spreadsheet can’t enforce any of that, so the failures are the expensive kind: double-booked staff, over-full rooms, patients turned away, resources sitting idle.
Solution
A full-stack application where a booking is only allowed if it satisfies every constraint at once. Front desk and managers schedule; therapists see their own day; each role sees exactly what it should. Around the calendar sit the CRUD surfaces the schedule depends on — employees and their working hours, rooms and their capacity, the devices in each room, and a click-to-toggle day-off calendar. The whole thing is responsive from a phone at the desk to a desktop in the back office.
Architecture
A TypeScript monorepo: an Express + Prisma backend over PostgreSQL, and a React
(Vite) frontend on Material UI. Prisma’s schema is the single source of truth for
the domain — Employee, Room, Device, DayOff, Appointment, and the
EmployeeRoom link between staff and the rooms they can work in — so the model
is expressed once and the types flow out to both ends.
Auth is JWT with a role hierarchy (root, admin, manager, therapist, client). One
detail I’m oddly fond of: a legacy EMPLOYEE role is kept as an alias while
every row migrates to THERAPIST, so the rename ships without a flag day. The
whole stack runs on Docker Compose with four env-driven modes — local dev with
hot reload and a mail catcher, staging, and two production shapes (external or
bundled database) — so “how do I run it” has one honest answer for every
environment.
Technical challenges
The booking validator is the heart of it, and it’s where the interesting bugs live. A single “can I book this?” check has to run four independent guards together — the therapist’s day-off calendar, a time-conflict check against their existing appointments, the room’s occupancy limit, and the room-exclusivity flag for staff who must work alone — and it has to do it atomically, because two receptionists booking the last slot at the same moment is a real race, not a hypothetical. Getting those rules to live in one place, server-side and authoritative, rather than smeared across the UI, was the work that mattered.
Lessons learned
Model the resources and their constraints before you draw a single calendar. Once “an appointment is the intersection of an available therapist, room, device and time” was written down as an explicit rule set, the UI became a thin thing on top and every new constraint had an obvious home. The scheduling grid is the part users see; the validator is the part that makes it trustworthy.