Thera.Net is an offline-first patient visit ledger and practice management app for independent clinics.
HealthTypeScript · PLpgSQLGitHub @prem-anand89OnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
Loading the full README. The saved excerpt is shown below.
Thera.Net — Patient Visit Ledger & Revenue Tracking
Offline-first visit ledger, revenue-split tracker, and invoice book for
physiotherapy clinics operating inside a partner hospital. Structured for
multi-clinic operations from day one, with configurable revenue-split models
(simple or hospital partnership).
Stack: React + Vite + TypeScript · Supabase (Postgres/Auth/Realtime/Storage)
· Dexie (IndexedDB) local-first store with outbox sync · Tailwind CSS.Read the full README ↗Offline-first visit ledger, revenue-split tracker, and invoice book for physiotherapy clinics operating inside a partner hospital. Structured for multi-clinic operations from day one, with configurable revenue-split models (simple or hospital partnership).
Stack: React + Vite + TypeScript · Supabase (Postgres/Auth/Realtime/Storage) · Dexie (IndexedDB) local-first store with outbox sync · Tailwind CSS.
Current scope: the visit ledger (visits, invoices, reports, dashboard),
revenue tracking, multi-clinic isolation, and Core Assessment (clinical
consultation notes with comprehensive pain profiling, functional status tracking,
and objective neurological examination). The database already has schema and
RLS scaffolding for optional Region Modules (FaCE Scale, Facial Palsy, and
others) as future extensions of Core Assessment, but none of them has any
client-side implementation yet — no UI, no Dexie/sync integration. See
FEATURES_AND_SCHEMA.md for the current state of that scaffolding.
PREFIXYY-NNNN, e.g. W26-0001 for the first walk-in of 2026, resetting yearly and auto-widening past 9999), visit entry with catalog price autofill, several services per visit (service rows, each optionally tagged with an assisting therapist, with a read-only revenue-share line; a saved visit's shares are corrected in the revenue share editor, by admin, front desk or the visit's therapist), one overall discount or extra with a mandatory reason, package sessions chosen in the service picker (1/3, 2/3 … as ₹0 continuation rows), and a phone save bar that states the payment choice. A therapist who only assisted on a colleague's visit sees it in their Workspace, in the Ledger when filtered to them, and in patient history as an "Assisted" row with just their own share (never the bill or Collect), and the Monthly statement adds an Assisted count. Once a patient's confirmed, a reference panel shows their last visit and open-package progress alongside the form./ledger?tab=daybook); the Invoices and Daybook sub-tabs only appear for roles with billing access. The Daybook provides an end-of-day cash reconciliation log that correctly prevents double-counting by excluding advance drawdowns, while making staff accountable for given discounts by displaying their adjustment reasons. Invoices are only ever issued against a real visit — there's no standalone "manual invoice" path.BM/26-27/0001), immutable once issued (DB triggers), printable A4/A5 with clinic letterhead + optional partner-hospital branding. Titled "BILL" while outstanding and "BILL CUM RECEIPT" once paid, rather than "Invoice"/"Tax Invoice" — matches how insurers/TPAs expect these documents to read for GST-exempt healthcare services. TPA-facing print layout: dates-of-service and per-session rate get their own columns, a treatment period line, and a caption whenever a package's real total and its printed rate don't multiply out exactly — plus an optional "Clinical details" block (diagnosis, referring physician, place of service) pre-filled from the patient's note at issue time and then frozen with the rest of the bill. Several independently-logged visits of the same service now collapse onto one "bill by service" line instead of a package-only grouping.Collect ₹X action wherever money can still be taken; a "Needs receipt" queue on the Invoices tab surfaces every visit collected but never invoiced. Monthly report shows HV settlement card for variance tracking.issue_invoice(), not just hidden in the UI./schedule: therapist columns with appointments sized by length, a current-time line, click-to-book free time, a mini month, closed days and holidays, and a details panel for arrive / reschedule / cancel / no-show / create visit. Phones get a Day view (cards for one therapist, a column per therapist for several) and a 3-Day view, with visible free time and a Now line. Staff start times can be every 15 or 30 minutes independent of the appointment length (online booking keeps the appointment length). Drag to move (15-minute steps; on phones and iPad press and hold, then drag, with edge auto-scroll) or resize, repeat bookings tied to packages, per-therapist working hours with breaks, WhatsApp reminders and therapist notifications, and a no-show history badge. Therapists book and manage their own schedule and set their own hours, and can view colleagues' booked slots read-only. Front desk confirms public booking requests from an inline inbox. Therapists can start notes before choosing a service (a visit in progress), and complete the visit with the service and payment later./book/$clinicSlug) where patients pick a day and a free time (closed days and booked times are hidden); front desk confirms each request into an appointment.src/domain/ pure business logic (money, splits, fiscal year, clinical assessments) — no framework imports, unit-tested
src/repositories/ data-access interfaces + Dexie implementations (UI reads/writes local only)
src/sync/ outbox push / delta pull engine against Supabase
src/services/ visit/invoice/report/patient/dashboard/consultation-note/advance orchestration — no React imports
src/features/ UI pages and components (React + TanStack Router)
├── workspace/ WorkspacePage (default landing: header stats, Today, Open Packages, therapist comparison)
├── visits/ LedgerPage at /ledger (Visits/Patients/Invoices/Daybook sub-tabs); DaybookPage; NewVisitPage
├── patients/ PatientsList (embedded in /ledger), PatientProfilePage, NoteEditorPage (Core Assessment)
├── reports/ ReportsPage at /insights (Trends + monthly statement; nav label is "Reports")
├── settings/ SettingsPage at /settings (nav label: "Settings", in main header nav for admins); CreateClinicForm
├── requests/ /schedule — the calendar (schedule/), Requests, History, and Feedback (admin)
├── publicFeedback/ Public, no-login feedback form at /f/$token
└── publicBooking/ Public, no-login booking request form at /book/$clinicSlug
src/components/ Shared UI components (BodyChart, ScaleWidget, TreatmentNote, ColumnsPicker, etc.)
supabase/ SQL migrations (schema, RLS, RPCs, realtime publications), seed
App Routes:
/workspace (default, / redirects here) — today's work, recent history, open packages, pending items/ledger — historical visit records, patients list, invoices, and daybook as URL-addressable sub-tabs (?tab=visits|patients|invoices|daybook); /patients redirects to /ledger?tab=patients. The Invoices/Daybook tabs are hidden without billing access/patients/$patientId — individual patient profile with clinical notes history (Back returns to /ledger?tab=patients)/patients/$patientId/notes/$noteId — Core Assessment note editor (Initial/Follow-up consultation notes)/insights — Dashboard + monthly per-therapist statement (nav label: "Reports"; ?tab=monthly for the statement). Hidden from plain therapists — a colleague's individual earnings stay admin/front_desk-only, same as the monthly statement always has been. The one exception, the therapist comparison chart, surfaces on /workspace instead so therapists can still reach it./settings — clinic configuration, MRNO settings, billing mode, rate setup, feature toggles (nav label: "Settings", promoted to header nav for admins; removed from AccountMenu)/schedule — clinic calendar for every role (therapists see their own), booking requests (admin + front_desk), Feedback (admin)/f/$token, /book/$clinicSlug — public, no-login, no-Shell forms for patient feedback and booking requests/archive, /setup, /invoices, /reports — legacy paths, kept as redirects to the routes above for old bookmarksBusiness logic never imports Supabase or Dexie — swapping the backend means reimplementing the repository interfaces, nothing above them.
Three roles per clinic membership, enforced server-side via Postgres RLS (not just hidden in the UI):
useWorkspaceScope() / usePermissions() centralize these checks
client-side for UI framing; the RLS policies and SECURITY DEFINER RPCs
in supabase/migrations/ are the actual enforcement boundary.
Two clinic-level toggles (Settings → Features/Billing) layer on top of the
role model: billing access (billingEnabled + who's allowed to issue
invoices — everyone or billing staff only) and therapist comparison
(off by default; widens the dashboard's revenue/visit charts to
therapists, not just admins).
Onboarding — inviting a therapist from Settings → Team creates their
login and their service-roster entry (linked to that login) in one step;
admin/front_desk invites only need the login. A therapist can always be
deactivated (keeps history intact) and, once they have zero visits
(including visits they assisted on or hold a revenue share of), notes, or
invoices on record, permanently deleted — matching how patients work.
Phase 1 (Ledger & Revenue): Complete ✅ Merged to main and deployed. Phase 2 (Core Assessment): Complete ✅ Merged to main and deployed. Phase 3 (Role model, billing access & nav overhaul): Complete ✅ See below.
WorkspacePage (New Default Landing)
/ now redirects to /workspace as the primary entry pointArchive Page (/archive, since renamed to Ledger at /ledger — see Phase 3)
DatePresetBar (This week, This month, Last month, All time, Custom), patient search (MRNO prefix + name substring)Services Layer (dashboardService.ts)
todayWorklist(clinicId) — today's visits with derived payment states (paid/outstanding/uninvoiced/zero_session)pendingWork(clinicId) — aggregated unresolved items (stale packages, outstanding invoice totals, incomplete notes)recentVisitsWindow(clinicId, days) — rolling window query excluding todayopenPackages(clinicId) — active packages with days-since-last-visit calculationweeklySummary(clinicId) — weekly visit counts and collected revenueAuth & Account
Database Fix
20260713000002_fix_clinic_creation_rls.sql resolves clinic creation RLS erroradd_creator_as_admin() trigger now SECURITY DEFINER to bypass RLS during clinic founder onboardingCore Assessment (NoteEditorPage)
patient_module_enrollments (Supabase RLS + Dexie sync)nrsScore, psfsMean, redFlagCount) extracted at save time for filtering/reportingcomputeBmi, computeWaistToHeightRatio, outcomeTrend, computeDerivedFields)Patient Profile Integration
New Visit Form Enhancements
patientId or prefillName search params15 PRs across two build plans (docs/BUILD-PLAN-compiled-changes.md,
docs/BUILD-PLAN-role-model-and-billing.md):
front_desk) and RLS
policies that actually scope writes (previously any clinic member could
write any other therapist's visits/roster/pricing; see Roles &
permissions above)./archive → /ledger, /setup → /settings,
"Insights" → "Reports" in the nav (path stays /insights); Ledger's
Visits/Invoices/Reports sub-tabs became URL-addressable.invoiceId-only check that had
independently drifted wrong in five places across the codebase.issue_invoice() RPC.tab: (744px) Tailwind
token so iPad Mini gets the tablet layout instead of the phone layout.Sequential Patient ID Format
W-260816-K3F) to sequential per-clinic-per-year format (PREFIXYY-NNNN, e.g., W26-0001, W26-0002).W26-10000); no configuration needed, leveraging the existing walkInMrnoPrefix field in Setup.Schema & Sync Fixes
pending_payment_note text column to visits table — this nullable field (set from NewVisitPage, displayed on Workspace's Needs-attention list) was blocking all new visit creates with schema-cache 400 errors, preventing visit logging entirely.walk_in_mrno_prefix text column to clinics table — SetupPage's clinic profile form always includes this field, so every clinic profile save was failing the same way.useClinicRole querying with empty clinicId during Shell render before active clinic resolves — the hook now skips the query until it has a real clinic ID, eliminating wasted 400 errors on invalid uuid parameters.All original features remain: offline-first sync, revenue split tracking, invoice issuance, monthly reports, historical import, payment status, dashboard analytics, clinical documentation (consultation notes, consent ledger, AI generation log).
supabase/migrations/ into the SQL editor (or supabase db push with the
CLI), then run supabase/seed.sql. For an already-live project, only the
migration file(s) not yet applied need to be run.supabase/setup_members.sql with their real emails to grant clinic access.cp .env.example .env and fill in the project URL + anon key
(Project Settings → API).npm install && npm run dev| Command | What |
|---|---|
npm run dev | dev server on :5173 |
npm test | unit tests (domain + services) |
npm run typecheck | strict TS |
npm run lint | eslint |
npm run build | production build |
npm run e2e | Playwright: boot smoke always; login→visit→sync when .env has Supabase (local defaults to [email protected]) |
clinic_id; RLS restricts all access to clinic members.
Patient data is health data — there is no anonymous read path.