Case study
Vires | PT & Client Fitness Tracker
A platform where personal trainers build weekly plans by drag and drop, and clients log every set on their phone, even with weak gym signal.
- Personal project
- Product, Design, Full-stack development
- Next.js · React · TypeScript · Tailwind CSS · Flowbite React · dnd-kit · React Hook Form · Zod · Firebase Auth · Cloud Firestore · Backblaze B2 · Turborepo · pnpm · Vitest · Playwright · Vercel
- 2026

Overview
Vires is a web platform for personal trainers (PTs) and their clients. A PT builds an individual weekly training plan for each client and shares it as a link. The client opens the link on a phone, signs in with Google and logs kilograms and reps after every set. The PT follows the results week by week.
It replaces a single-user HTML tracking page. That page worked for one person, but the plan was hard-coded, PTs had nowhere to build a plan per client, and clients had nowhere to review their progress and body metrics.

Challenge
Two facts about the users drove most decisions:
- Clients train in a gym with unstable signal. Logging a set had to respond instantly and never wait for the network.
- PTs build plans on a laptop, but check on clients from a phone. The plan builder had to be fast with a mouse and still work on a small screen and with a keyboard.
On top of that, the data is personal and partly sensitive: body metrics, InBody images, injury notes. A PT must see only their own clients, and a client only their own data. The platform needed to get that right from the first sprint, not at the end.
Approach
Spec first, then sprints. I wrote a full spec before any code: roles and permissions, user flows, data model, security, non-functional requirements and open questions, each with a proposed default. The build then ran as short sprints (foundation, sign-in, clients, exercises, plan builder, client portal, hardening), each ending in a working build that I reviewed before moving on.
Three roles, one sign-in. Everyone signs in with Google through Firebase Auth. The server matches the verified email against stored data and grants roles as custom claims (PT, client, admin). Firestore Security Rules enforce those claims, and the UI only hides buttons. A PT whose email is not yet known can send a sign-up request, which I approve in an Admin portal.
A plan builder that works without a mouse. A plan is a repeating weekly schedule of seven columns. Each day can be split into parts, and each part is a list of exercises. On a laptop, the PT drags exercises from the library into a day with dnd-kit. On a phone there is no drag and drop: an "Add to day" button and up/down buttons do the same job. Everything is keyboard operable.
Offline-tolerant logging. The client's tracking screen writes straight to Firestore with the client SDK, so entries are stored locally and synced when the connection returns. A status line shows "Saved" or "Waiting to sync", so the client always knows where they stand.
A tracking link that is not a key. Activating a plan creates a link the PT sends over Zalo or text. The link only points to the right plan. Someone who holds it without signing in with the matching email still cannot read the plan, because the rules compare the client's claim with the plan.
An exercise library with muscle maps. The shared library is seeded from earlier plans and shows a red-highlighted muscle figure drawn from each exercise's primary and secondary muscle groups. PTs can create their own exercises, usable at once, and an admin reviews them before they join the shared library.
Design system before screens. Components live in a shared @vires/ui package built on Flowbite React, and the apps never import the library directly. Colours, type and spacing are semantic tokens in a red, gold, white and black palette, with light and dark themes. Pages do not define their own colours, radii or font sizes.
Accessibility as a requirement. The target is WCAG 2.2 AA: keyboard operation including drag and drop, touch targets of at least 44 px, sufficient contrast and no horizontal scroll at 320 px. Each screen is checked at mobile, tablet and desktop widths.
Architecture
One monorepo with two Next.js apps deployed on Vercel, sharing packages and a Firebase backend.
| Part | Responsibility |
|---|---|
apps/portal | PT portal and client portal |
apps/admin | Admin portal: approve PTs, review exercises, manage clients |
packages/ui | Shared components and design tokens |
packages/schemas | Zod schemas and types shared by the browser and the server |
packages/firebase | Client and admin initialisation, typed converters |
packages/storage | Backblaze B2 access through the S3 API, server only |
Data model. Cloud Firestore holds structured data. Every client record carries a ptId, so the rules can check ownership without reading another document. A plan is one embedded document, so it can be edited atomically. Training results are stored per day, separate from the plan, and each log records the plan version it was trained on.
Server-only privileged work. Granting claims, creating clients, activating plans and every upload go through Server Actions or route handlers using the Admin SDK. The browser always goes through the client SDK and is always subject to the rules.
Private file storage. InBody images and PDFs live in a private Backblaze B2 bucket. The browser never holds a storage credential. The server checks permissions, then issues short-lived presigned URLs to upload and to view. Uploaded SVGs are sanitised before storage.
Health data protection. A client's phone number lives in a server-only document and is shown masked to everyone. Clients see a consent screen on first sign-in, and can export their data or request deletion. Audit events record ids and field names, never values.
Quality gates. Vitest covers units, Playwright covers end-to-end flows, and the Firebase Emulator runs a test suite for the Security Rules. Nothing merges while the rule tests fail. GitLab CI verifies every change before Vercel deploys.
Outcome
The PT and client portals are built and reviewed sprint by sprint, and the Admin portal followed on the same design system. The platform is live at its own domain and is being tried by 3 PTs and 10 users ahead of the public launch on 1 December 2026:
- PTs sign in, create clients, record metrics and InBody images, and build weekly plans with drag and drop.
- Clients open a plan link, accept the consent screen, log kg and reps, and view their own profile and charts.
- The shared library ships with 84 exercises, seeded from the original plan and two earlier plans, and managed in the Admin portal.

- Roles
- 3
- Clients per PT
- 10
- Months, max plan length
- 3
- Responsive breakpoints
- 3
To be updated.
