Apr 2026 – present · Built and hosted, schema to deploy
Athlete platform
One record per athlete, shared by everyone who works with them: injuries, testing and performance plans in one place. Each role on the staff sees exactly what it should, in English or French.
Open the demo ↗Source is private. Happy to walk through it.
- Backend
- Java · Spring Boot
- Frontend
- React 19 · TypeScript
- Data
- PostgreSQL · 19 migrations
- Languages
- English · French
- Deploy
- Docker · GHCR · CI/CD
Who it's for
A sports-science team looks at the same athlete through many lenses. The platform gives each staff role its own view of one shared record, and gives athletes a dashboard of their own.
The public demo has a role switcher, so you can see how the same athlete looks to each member of staff.
What's in it
Triage, injury cards with typed updates, and expected against actual return dates.
Physical and technical performance, with trends over time and cohort comparisons.
Individual plans broken into domains, action items and a worklist of what's open.
Legacy workbooks parsed on upload, so existing data comes in without retyping.
Engineering notes
The demo can't leak into production
The demo's login panel, role switcher and published password exist only in a separate frontend image. The production image tree-shakes them away, and CI greps the built bundle to prove the demo credentials aren't in it.
Test once, deploy everywhere
Tests run on every pull request, and main only accepts branches that are up to date. A push to main builds each image once per commit, then fans out to every server as parallel jobs. Server secrets live on deploy environments, where a pull request can't reach them.
Invitations double as email verification
Athletes arrive through Excel import without accounts. An admin invites one by email and gets a one-time link that expires after 48 hours. Only a SHA-256 hash of the token is stored, and re-inviting supersedes the old link.
Bilingual from the schema up
Labels are translated in the database as well as the interface, messages use ICU formatting, and a key checker and lint rules catch any string that's missing its French.
How it's built
| Backend | Java, Spring Boot, Flyway migrations, Apache POI for workbook parsing |
| Frontend | React 19, TypeScript, Vite, Tailwind, Chart.js, i18next with ICU |
| Data | PostgreSQL; integration tests run against a real Postgres in Testcontainers |
| Infra | Docker Compose, nginx, TLS, GitHub Actions, GitHub Container Registry |
| Process | Non-technical stakeholders file issues in a separate repository, so nobody outside engineering is near the deploy secrets |