All work
TGMulti-Tenant SaaS · FitnessEnterprise SaaSCase study

Technaut Gym — Gym Management Platform

Runs a gym from the front desk — memberships, check-ins, dues and stock — with a separate app for members, trainers and the owner.

Gym management for Indian gyms: memberships and plans, attendance, billing and point of sale, inventory, expenses and reporting, served from a NestJS API to a Next.js dashboard and three role-specific Flutter apps.

Source private
Technaut Gym — Gym Management Platform interface
Industry
Fitness & Wellness
Role
Architect & Full-Stack Engineer
Type
SaaS Platform
Year
2026
Status
Delivered
3 + web
Client Apps
1 package
Shared Core
Multi-gym
Tenancy

Overview

Technaut Gym covers what a gym owner actually watches day to day: who is active, whose membership expires this week, what came in today, and who still owes. The dashboard leads with those numbers — total and active members, expiries inside seven days, today's attendance and revenue, pending payments and low stock — because they are the questions asked before anything else.

Three Flutter apps sit beside the web dashboard, each aimed at one role: members see their plan and check-ins, trainers work the desk with a QR scanner, and the owner gets the business view. All three share one core package holding the theme, API client, auth and push handling, and each app refuses accounts of any other kind at sign-in rather than hiding UI after login.

Membership state is modelled as more than active/expired — frozen is a first-class status, because pausing a membership for travel or injury is routine and a system that only knows the two extremes gets worked around with spreadsheets.

Where integrations are not configured, the reminder queue parks the job with a reason the admin can read, instead of reporting a WhatsApp or push message as sent.

The problem

Gyms track memberships in a register and dues in the owner's head. Expiries are noticed after a member has already stopped coming, personal-training packs are counted manually, and there is no single place that answers what came in today against what is still owed.

Discovery

Sat with the questions an owner asks first — who is active, who lapses this week, what did we collect, who owes — and made those the dashboard rather than burying them under navigation.

The solution

Built a multi-tenant NestJS platform with a Next.js dashboard and three role-specific Flutter apps sharing one core package. Membership state, dues and attendance are one model, so the expiry list and the collections figure come from the same records.

Technical stack

Frontend

  • Next.js 16
  • React Query
  • Tailwind CSS
  • Flutter
  • TypeScript

Backend

  • NestJS 11

Data

  • MySQL 8

Also used

  • Prisma 6
  • shadcn/ui
  • BLoC
  • go_router
  • Zod

System architecture

  1. 1

    NestJS 11 + Prisma 6 over MySQL 8.4, with a shared TypeScript/Zod contract package

  2. 2

    Next.js 16 dashboard with React Query, Tailwind and shadcn/ui

  3. 3

    Three Flutter apps — member, trainer (with mobile_scanner), owner — over a shared apps/core package

  4. 4

    One theme, one API client and one login flow in core; each app rejects other roles at sign-in

  5. 5

    pnpm workspaces + Turborepo for TypeScript, Docker Compose for local MySQL

  6. 6

    Reminder queue behind a channel abstraction — unconfigured providers park rather than fake a send

Engineering challenges

Challenge

Modelling membership as active or expired forced staff into workarounds every time someone paused for travel or injury, and those pauses ended up tracked outside the system.

How I solved it

Made frozen a first-class membership state with its own expiry arithmetic, so a pause is recorded, resumes correctly and still shows in the expiry forecast.

Challenge

Three apps that each implemented their own theme, API client and auth drifted apart, and a contract change meant three separate fixes.

How I solved it

Extracted theme, API client, auth and push into a single Dart core package that all three apps depend on, leaving each app as a thin role-specific shell.

Challenge

A member app, a trainer app and an owner app all authenticating against the same endpoint let anyone sign into any of them and simply see less.

How I solved it

Each app declares the single role it serves and rejects accounts of any other kind at sign-in, rather than authenticating first and hiding UI afterwards.

Core features

  • Multi-tenant — one deployment serving many gyms with isolated data
  • Members with plans, expiry tracking, dues and frozen/expired states
  • Membership plans including personal-training session packs
  • Attendance with trainer-side QR scanning
  • Billing, invoices and a point-of-sale counter
  • Inventory with low-stock alerts, plus expense tracking
  • Owner dashboard — revenue, attendance trend, expiries and pending payments
  • Trainer assignment and per-trainer member lists
  • Three Flutter apps (member, trainer, owner) over one shared core package
  • Reminder queue that parks unconfigured channels with a readable reason

Results & impact

The owner opens one dashboard and sees active members, this week's expiries, today's collections, outstanding dues and low stock. Front-desk check-in is a QR scan, and members see their own plan without asking the desk.

Screenshots

Technaut Gym — Gym Management Platform screenshot 1
Technaut Gym — Gym Management Platform screenshot 2
Technaut Gym — Gym Management Platform screenshot 3
Technaut Gym — Gym Management Platform screenshot 4
Technaut Gym — Gym Management Platform screenshot 5
Technaut Gym — Gym Management Platform screenshot 6
Technaut Gym — Gym Management Platform screenshot 7
Technaut Gym — Gym Management Platform screenshot 8

What I learned

  • The states a business actually uses — like "frozen" — belong in the model, or staff will keep a spreadsheet beside the software.
  • Role-specific apps over one shared core give focused UX without three codebases to maintain.

Related work

All projects
Next step

Have a system like this to build?

I take projects from the first conversation through to production, and stay on for the support that follows.