~510
Commits over ~9 months of active development
~390
Modules — ~180 Python, ~210 TypeScript/React
16
Core data models across 4 domain apps
12
GitHub Actions workflows
The problem
Every new clinic multiplied the admin load.
Clinic groups ran on paper intake forms, shared spreadsheets and manual reminder emails. There was no reliable record of who accessed or changed what — which becomes a problem the moment a compliance review happens.
The solution
One multi-tenant platform where the clinic is the unit of isolation. Admins onboard a clinic, build their own forms and assign them; clinicians work their patient list; patients complete everything in their own portal. Passwordless login removes password-reset support load, and every mutation is audit-logged automatically.
Role hierarchy
Four portals, four sets of permissions.
Each role gets its own dashboard, and the hierarchy controls exactly what that user can see and do. Full dark mode across all four.
What the platform does.
Multi-tenant by design
The clinic is the root tenant. Every queryset is role-scoped, so data cannot leak across clinics.
Dynamic form builder
Forms, sections, typed fields, conditional content and document uploads. Adding a new intake form needs no developer.
Form assignment and tracking
Assign to an individual patient or a patient group, then monitor submission status from a live dashboard.
Patient records and tasks
Profiles, task lists and grouping, so bulk operations run against a group rather than one record at a time.
Passwordless OTP access
A six-digit code by email, JWT access and refresh tokens, lockout after three failed attempts, and a 15-minute inactivity auto-lock.
Immutable audit log
Every create, update and delete is recorded with actor, entity and action — written, never edited.
Consent tracking
SMS and email consent changes are their own events, each stamped with actor, timestamp and IP address.
Automated email
Reminders and notifications are queued and sent asynchronously, so nothing blocks a clinician mid-workflow.
Architecture
Thin views, thick services.
Feature-module frontend
Each features/<domain> owns its api, types, hooks and components, so a domain can be read and changed in one place.
One API layer
Every HTTP call goes through a single central ApiService with automatic token refresh and retry on 401 — auth handling exists once, not per call site.
Strict service layer
Views stay thin and all business logic lives in services.py, keeping the domain testable independently of HTTP. OpenAPI/Swagger docs are generated, with an MkDocs site alongside.
Frontend
- Next.js 16 (App Router)
- React 19
- TypeScript
- Tailwind CSS v4
- Radix UI
Backend
- Django 5.1
- Django REST Framework
- JWT (simplejwt) + email OTP
Data
- PostgreSQL 16
- Redis 7
- Celery 5 (worker + beat)
Platform
- AWS S3
- AWS SES
- Docker Compose
- GitHub Actions CI/CD
Engineering practice
Security scanning on every change.
Five scan classes run in CI on every change, with results published to GitHub’s Security tab as SARIF rather than sitting in a log nobody opens.
Multi-environment pipeline
Separate QA, staging and production deploys, with a promote-to-staging flow and a dedicated hotfix path for changes that cannot wait for the queue.
Tests and quality gates
- 54Backend pytest modules
- 19Playwright end-to-end specs
- 4Role portals covered with authenticated fixtures
Ruff, ESLint and Prettier run pre-commit, and builds are type-checked, so style and type failures never reach review.
What changed for the group.
- Onboarding a new clinic is a configuration task, not a deployment.
- New intake forms ship without engineering involvement.
- Compliance questions are answered from the audit log rather than reconstructed from memory.