In production · 2026
Rebote — Basketball club management
Multi-club SaaS for youth basketball: teams, call-ups, attendance, fees, treasury and public sign-ups.
- Role
- Design and development, end to end
- Dashboard sections
- 20
- Entities in the model
- 13
Try it yourself
LiveWorth a look inside
- The club dashboard: teams, players, call-ups and attendance
- Treasury: fees, payments and transaction categories
- The family portal, which is the same application seen through a different role
- The club’s public sign-up page
It is a fake club with fake data. Poke at whatever you like — it gets reseeded.
Problem
A youth basketball club is usually run on a WhatsApp group per team, a spreadsheet for the fees and the coach’s memory for the call-ups. That holds until the club grows.
Decision
Multi-tenant in the data model, not in the interface: everything hangs off the club identifier, and each person’s role is defined per club through a memberships table.
That lets the same user be a coach at one club and a parent at another without duplicate accounts. Isolating per query is what lets one small server serve several clubs — at the cost of never writing a query without its scope.
Implementation
Seasons, teams, players, guardians, events, call-ups, attendance, fees, payments, documents and messaging, across thirteen entities.
Each club gets a public page with its standings and its own sign-up form. Families log into their own portal to see call-ups, documents and where their fees stand.
Authentication runs on Sanctum tokens rather than cookie sessions: the front end is a separate application, and talking in tokens avoids the whole shared-domain and cross-origin CSRF tangle.
Treasury covers fees, payments, transaction categories and reconciliation, with optional integration into GoCardless’ bank data API.
Deployment
Three containers on Coolify: the Next.js front end compiled in standalone mode, the Laravel back end served by FrankenPHP, and MySQL on a private network with no route out to the internet.
The back end does get a public domain, because the caller is the visitor’s browser, not the front-end server.