How it’s built
Students, teachers, batch scheduling and the entire money workflow — multi-tender payments, allocations, discounts and receipts — with role-based access enforced down to the database row.
Built on Next.js 15 / React 19 / Supabase + RLS
50/51
Pages are Server Components
25
Server-action modules
67
Database migrations
4
Tender types per payment
Explore the system
Pick a pillar to see how Centrely runs a multi-branch coaching business — from enrolment to receipt to row-level access.
Everything a coaching centre runs on
One data-heavy internal tool, no separate API layer — every domain from enrolment to receipt in the same system.
Students & teachers
Auto-generated student IDs, teacher profiles with subject specialisations and availability.
Batch scheduling
Flexible batches with weekly time slots, per-batch calendars and attendance tracking.
Enrolments
Regular and holiday-course enrolments, with multiple enrolments per subject supported.
Pricing & discounts
Year/class and subject-level pricing, plus per-student recurring or one-off discounts.
Reporting suite
Collection, enrolment, workload, utilisation, credit-due, overdue and transaction reports.
Progress & audit
Printable report cards per batch, and database triggers that log every data change.
The payments engine is the heart of the app
Hourly-rate pricing by year, class and study mode. A full invoice → allocation model with proportional discounts, grace authorisation, overdue flows — and a printable receipt at the end.
- Multi-tender — cash, bank, card & direct debit in one payment
- Proportional discount application across allocations
- Money math unit-tested — proration, stacking, DST-safe dates
- Invoice
- Allocate
- Tender
- Receipt
One super-admin over every branch, enforced down to the row
Branch admins are scoped to their own branch in middleware, inside every server action, and by Postgres Row Level Security — defence in depth, not a single gate. Teachers sign in to a separate portal and only ever see their own batches, enforced by RLS as well.
| Capability | Super | Branch | Teacher |
|---|---|---|---|
| Branch access | All | Own | Own batches |
| Manage pricing & discounts | Yes | No | No |
| Validate electronic payments | Yes | No | No |
| Validate cash payments | Yes | Yes | No |
| Manage admin users | Yes | No | No |
| Run reports | Yes | Yes | No |
Defence in depth — every request passes all three
Middleware
Refreshes the session and gates every route.
Server action
Re-verifies role via verify_admin_user — never trusts the client.
Postgres RLS
Row Level Security — the final backstop, safe even if a check is missed.
Under the hood
Pages fetch data by calling server-action functions directly. Every action returns a uniform { success, data | error } contract, with auth and role checks inside — not just at the route.
- Next.js 15 · App Router
- React 19
- TypeScript · strict
- Supabase · Postgres + RLS
- Tailwind v4
- shadcn/ui · Radix
- TanStack Table
- Recharts
- react-hook-form + Zod
- Vitest
- Sentry
Server-first
Interactivity isolated to client components; pages stay thin and data-driven.
Type-safe end-to-end
DB types generated from the live schema; forms and actions infer from one source.
Tested where it counts
Money math and access control unit-tested — pure logic factored out of actions.