Skip to content

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.

Client
Personal project
Role
Product, Design, Full-stack development
Stack
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
Year
2026
Vires sign-in screen: a card reading 'Clear plans, progress every session' beside a Google sign-in card, over a blurred gym

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.

Vires sign-in screen with a Vietnamese headline about clear plans and steady progress, and a Sign in with Google button
The sign-in screen: one Google sign-in for PTs and clients

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.

PartResponsibility
apps/portalPT portal and client portal
apps/adminAdmin portal: approve PTs, review exercises, manage clients
packages/uiShared components and design tokens
packages/schemasZod schemas and types shared by the browser and the server
packages/firebaseClient and admin initialisation, typed converters
packages/storageBackblaze 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.
Vires Admin exercise library: a filterable table of exercises with primary muscles, equipment, suggested sets and status
Admin portal: the shared exercise library, filterable by muscle group and equipment
Roles
3
Clients per PT
10
Months, max plan length
3
Responsive breakpoints
3

To be updated.

Next project

Human Resources Management

View case study

Get in touch

Curious how something here was built? Let's talk.

Say hello