Coordinating and organizing operations for a gym to make life easier for staff and members.
An app that serves as a center for membership management, class scheduling, ordering from the cafe and analytics for membership growth and usage.
![]() Joe Aguado |
Joe Gallego |
![]() Danny Rivas |
Diego Espino |
|
Raja Krishna (Tech Lead) |
Belle Duran (Tech Lead) |
- Smart Gym
- Technologies Used
- πͺ Smart Gym Documentation
- π Table of Contents
- π Introduction
- ποΈ Architecture Overview
- π οΈ Technology Stack
- πΎ Database Schema
- β¨ Key Features
- π API Details
- π§© Component Structure
- π Deployment
- π Commitlint Configuration
- π» Jira/Git Workflow
- π¨ Design System
- π Code Organization & Best Practices
Smart Gym is a modern operations hub for gyms and fitness studios. It streamlines daily workflows for staff and members, centralizing membership management, class scheduling and enrollment, cafe purchasing, messaging, and analytics. The app also includes an AI member assistant powered by Vercel AI SDK with Gemini, plus optional voice responses via ElevenLabs.
The application is built with a modular Next.js (App Router) architecture. UI is rendered with React, shadcn/ui, and Tailwind CSS. Server-side functionality is implemented via Next.js Route Handlers under app/api, integrating with Supabase for data and auth, Stripe for payments, and AI services for chat and optional voice.
| Component | Description |
|---|---|
| Frontend | Next.js 15 + React with shadcn/ui and Tailwind CSS; component-driven UI, client/server components as appropriate. |
| Backend API | Next.js Route Handlers in app/api/* for REST-style endpoints; TypeScript-first; integrates Supabase, Stripe, and AI providers. |
| Database | Supabase (PostgreSQL). Core tables include users, classes, enrollments, cafe products, messages, invoices, and check-ins. |
| Authentication | Supabase Auth for user management and sessions. |
| AI Assistant | Vercel AI SDK using Google Gemini models exposed via /api/gemini for member Q&A and guidance. |
| Voice AI | ElevenLabs for text-to-speech responses in the client where voice is enabled. |
| Payments | Stripe for cafe purchases and invoicing flows. |
| Technology | Version/Description |
|---|---|
| Framework | Next.js 15 |
| UI Library | shadcn/ui |
| AI | Vercel AI SDK (Gemini) |
| Voice AI | ElevenLabs |
| Icons | Lucide, Radix Icons |
| Styling | Tailwind CSS v4 |
| Technology | Version/Description |
|---|---|
| API Framework | Next.js API Routes |
| Database Client | Supabase JS SDK |
| Database | Supabase |
| Authentication | Supabase Auth |
| Tool | Purpose |
|---|---|
| TypeScript | Type checking and static analysis |
| Prettier | Code formatting and style consistency |
| ESLint | Code linting and enforcing best practices |
| Husky | Git hooks for pre-commit checks |
| Commitlint | Enforces commit message conventions |
The application uses a PostgreSQL(Supabase) database. Below is the ERD (Entity Relationship Diagram) illustrating the key models and their relationships:
- Browse classes by date and category
- View details (category, time, capacity, coach)
- Enroll with confirmation modal flow
- Create, update, cancel classes
- View enrolled and waitlisted members
- Sort and filter schedules by date/time
- Create, update, delete cafe products
- View top purchases analytics
- Fetch product detail and full catalog
- Create messages tied to a user ID
- Fetch all messages or by message ID
- Retrieve messages by user ID
- Member Q&A via
/api/gemini - Class guidance, FAQs, and tips
- Powered by Vercel AI SDK using Gemini models
- ElevenLabs text-to-speech playback
- Optional voice responses in client UI
- Per-session/user toggling
- Membership growth and attendance charts
- Revenue target progress tracking
- Cafe insights (e.g., top purchases)
- Create invoices and add purchases
- Update invoice totals on purchase
- Retrieve invoices by user
The application uses Next.js API Routes organized by feature:
| API Endpoint | Purpose |
|---|---|
/api/cafe/create |
create a cafe item |
/api/cafe/delete |
delete a cafe item |
/api/cafe/edit |
edit a cafe item |
/api/cafe/getAll |
get all cafe items |
/api/cafe/[productId] |
get a single cafe item |
/api/cafe/items/topPurchases |
get top 5 cafe purchases |
/api/classes/[classId] |
get a single class |
/api/classes/create |
creates class |
/api/classes/delete |
deletes class |
/api/classes/enrolled/[classId] |
get all members enrolled in a class |
/api/classes/getAll |
get classes |
/api/classes/update |
updates class |
/api/classes/memberdash |
get daily classes |
/api/classes/waitlisted/[classId] |
get all members waitlisted in a class |
/api/coaches/getAll |
get all coaches |
/api/gemini/ |
member AI assistant |
/api/gemini/aiCheckin |
checks to see last ai checkin time |
/api/gemini/mood |
checksi or updates user mood |
/api/messages/create |
creates msg associated w user ID |
/api/messages/delete |
deletes msg associated w msg ID |
/api/messages/getAll |
get all msgs |
/api/messages/getByMessageId |
gets msg by msg ID |
/api/messages/getByUserId |
gets msgs by user ID |
/api/users/[userId] |
gets user by user ID |
/api/users/create |
creates user |
/api/users/getAll |
get all users |
/api/users/update |
updates user |
/api/users/enrolled/[userId]. |
gets user classes or cancel |
/api/checkins |
gets all checkins by user ID |
/api/invoices/add |
adds invoice to user_invoice table |
/api/invoices/[userId] |
gets all invoices by user ID |
/api/invoices/purchase |
+ invoice, updates total, supports +1 |
/api/profile/[userId] |
gets all profile data by user ID |
/api/adminMemberProfile/[userId] |
gets profile data by user ID admin |
-
API Endpoint:
/api/[endpoint1] -
Purpose: [Purpose description]
-
Triggered by: [Triggered by description]
-
Steps:
- [Step 1]
- [Step 2]
- [Step 3]
- [Step 4]
-
API Endpoint:
/api/classes/memberdash -
Purpose: Get Gym classes by date and categories
-
Triggered by: Gym Member
-
Steps:
- Gym Member logs in
- System fetches all gym classes to display or mock fallback
-
API Endpoint:
/api/messages/getAll -
Purpose: Get all messages
-
Triggered by: Admin user
-
Steps:
- Admin user logs in
- Admin user navigates to messages page
- System fetches all messages from database
-
API Endpoint:
/api/messages/getById -
Purpose: Get messages relating to a specific user by user ID
-
Triggered by: Admin user
-
Steps:
- Admin user selects a specific message
- System queries database for messages associated with user ID
- User-specific messages are returned and displayed in the admin UI
-
API Endpoint:
/api/messages/create -
Purpose: Create message associated with user ID
-
Triggered by: Admin user
-
Steps:
- Admin user logs in
- Admin user navigates to messages page
- Admin user hits create new message button
- Admin user fills out the form
- System creates message record in database
- Message is associated with specified user ID
-
API Endpoint:
/api/cafe/getAll -
Purpose: Get all cafe items
-
Triggered by: User/Admin
-
Steps:
- User/Admin accesses cafe page
- System fetches all product records from database
- Cafe items are displayed with prices and availability + other data (e.g. description, image, etc.)
-
API Endpoint:
/api/cafe/[productId] -
Purpose: Get a single cafe item
-
Triggered by: User/Admin
-
Steps:
- User/Admin selects specific product
- System queries database using product ID
- Individual product details are returned and displayed
-
API Endpoint:
/api/cafe/create -
Purpose: Create a cafe item
-
Triggered by: Admin user
-
Steps:
- Admin user logs in
- Admin user navigates to cafe page
- Admin user navigates to product creation form
- Admin user fills in product details and pricing + other data (e.g. description, image, etc.)
- Admin user hits create new product button
- System validates and saves new product to database
- Product becomes available for purchase in the cafe
-
API Endpoint:
/api/cafe/update -
Purpose: Update a cafe item
-
Triggered by: Admin user
-
Steps:
- Admin user logs in
- Admin user navigates to cafe page
- Admin user selects product to modify
- Admin user updates product information
- System validates changes and updates database record
- Updated product information is reflected in cafe
-
API Endpoint:
/api/cafe/delete -
Purpose: Delete a cafe item
-
Triggered by: Admin user
-
Steps:
- Admin user logs in
- Admin user navigates to cafe page
- Admin user selects product to remove
- System confirms deletion request
- Product record is removed from database
- Product is no longer available for purchase
-
API Endpoint:
/api/cafe/items/topPurchases -
Purpose: Get top 5 cafe purchases
-
Triggered by: Admin user
-
Steps:
- Admin user logs in
- Admin user navigates to dashboard page
- System fetches top 5 cafe purchases from database
- Top 5 cafe purchases are displayed in the dashboard
-
API Endpoint:
/api/gemini/ -
Purpose: Member AI assistant
-
Triggered by: Member
-
Steps:
- Member logs in
- Member clicks on the AI assistant button in the navbar
- Modal opens with AI assistant
- Member interacts with AI assistant
The application uses a component-based architecture with the following key components:
Admin Sidebar: Sidebar for the admin dashboard with links to the different pagesPre Auth Navbar: Navbar for the pre-auth landing page with links to different spots on the landing page and a login buttonPost Auth Navbar: Navbar for the post-auth dashboard with links to the user's barcode, profile, and notification.Mobile Bottom Navbar: Bottom navbar for the mobile app with links to the user's barcode, profile, and notification.
[Component]: [Description]Admin Dashboard Page: Page for the admin to view the dashboard with top 5 cafe purchases and other metricsAdmin Messages Page: Page for the admin to view all messages from the users + initiate a new message to the userAdmin View Message Page: Page for the admin to view a specific message and its detailsAdmin View Class Page: Page for the admin to view a specific class and its detailsAdmin View Analytics Page: Page for the admin to view the analytics of the gymAdmin View Member Page: Page for the admin to view a specific member and their informationAdmin Members Page: Page for the admin to view all members and their information + sign up new membersAdmin View Member Page: Page for the admin to view a specific member and their informationAdmin Cafe Page: Page for the admin to view all products in the cafe + add new products + view all transactions
Send Admin Message Modal: Modal for the admin to send a message to a userUpdate Cafe Product Modal: Modal for the admin to update a product in the cafeCreate Cafe Product Modal: Modal for the admin to create a new product in the cafeAdmin Class Calendar: Calendar for the admin to view all classes and their detailsAdmin Class Card: Card for the admin to view a specific class and its details and holds cancel and view enrolled members buttonsCreate Class Modal: Modal for the admin to create a new classUpdate Class Modal: Modal for the admin to update a classCancel and Update Class Modal: Modal for the admin to cancel a class or update a class
ActiveGymHours: pie chart with darkest/most active hour slices popping outAnalyticsStatCard: generic card component for statistics at top of admin analytics pageTargetRevenueCard: shows progress to goal revenueMembershipGrowthCard: bar graph showing growth or decline in gym membershipsClassAttendanceCard: horizontal bar chart showing how many users attending each class[Component]: [Description]
The application is designed to be deployed using Vercel and Supabase:
| Component | Deployment Solution |
|---|---|
| Frontend and API | Vercel |
| Database | Supabase (PostgreSQL) |
| Authentication | Supabase Auth |
| File Storage | Supabase Storage |
| Environment Variables | Vercel Environment Manager |
This project uses commitlint to enforce consistent commit message conventions. The configuration extends the conventional commit standards with custom rules.
The commitlint configuration is defined in .commitlintrc.json:
{
"extends": ["@commitlint/config-conventional"],
"rules": {
"type-enum": [2, "always", ["feat", "fix", "chore", "docs", "style", "refactor", "revert", "build", "ci"]],
"header-max-length": [2, "always", 120],
"body-max-line-length": [2, "always", 200],
"footer-max-line-length": [2, "always", 200],
"subject-case": [0, "never", "sentence-case"]
}
}| Type | Description |
|---|---|
feat |
New feature |
fix |
Bug fix |
chore |
Changes to build process or auxiliary tools |
docs |
Documentation only changes |
style |
Changes that do not affect the meaning of code |
refactor |
A code change that neither fixes a bug nor adds a feature |
revert |
Reverts a previous commit |
build |
Changes that affect the build system or external dependencies |
ci |
Changes to CI configuration files and scripts |
feat: add user authentication system (PROJ-123)
fix: resolve navigation menu display issue (PROJ-456)
docs: update API documentation (PROJ-789)
chore: update dependencies to latest versions (PROJ-101)
style: format code according to prettier rules (PROJ-202)
refactor: simplify user profile component (PROJ-303)- In Jira, find a ticket to work on.
- Assign it to yourself and mark it as 'In Progress'.
- Use the JIRA interface to create a branch directly from the ticket. This will generate a branch name based on the ticket number and description.
- Follow the prompts to select the repository and branch source (usually
master). - Once the branch is created, copy the branch name.
-
Fetch the branch you created in Jira from the remote repository:
git fetch origin [BRANCH-NAME]
-
Check out the new branch:
git checkout -b [BRANCH-NAME] origin/[BRANCH-NAME]
-
Make your changes and then stage them. All commit messages must follow the project commitlint rules (see Commitlint Configuration section above):
- Start with one of the allowed commit types:
feat,fix,chore,docs,style,refactor,revert,buildorci - Keep the header under 120 characters
- The Jira ticket number should be at the end in parentheses
Example commit commands:
git commit -m "feat: add new button to dashboard (PROJ-123)"git commit -m "fix: resolve user fetch bug (PROJ-123)"git commit -m "chore: update dependencies (PROJ-123)" - Start with one of the allowed commit types:
-
Sync the remote repo with your local repo and your new branch:
git push origin [TICKET-NUMBER]
-
Under [GITHUB_REPO_URL/branches] you should find the branch you just pushed. Click on it.
-
Click "Compare & pull request".
-
Make sure base is set to master at the top.
-
Adjust the title as needed and add a description.
-
Add reviewers (2) by clicking the gear.
-
Click "Create pull request".
-
Once the PR is approved, the assignee (you) should complete the pull request by merging to master and delete the branch.
-
Go back to Jira and change ticket status to done.
In order to use the in-line color schema within any front-end component, under className, use one of the following color configuration names:
primary-[color]secondary-[color]accent-[color]neutral-[color][additional-color-names]
These can be applied to background colors, text colors and border colors using [CSS framework]'s utility classes.
<div className='bg-primary-[color] p-4'>This div has a primary [color] background.</div>| Directory / File | Description |
|---|---|
app/ |
Main application directory containing pages, API routes and entry points. |
app/api/ |
API route handlers (e.g., REST endpoints for client-server comms). |
components/ |
Reusable React components and UI building blocks. |
constants/ |
Constants. |
constants/labels.ts |
All hardcoded strings, labels and configuration values. |
constants/icons.ts |
Icon imports and icon component references. |
context/ |
React createContext, useContext hooks. |
hooks/ |
Custom React hooks. |
types/ |
TypeScript type definitions and interfaces. |
types/shared.ts |
Shared types/interfaces used across client and server. |
utils/ |
Utility functions. |
public/ |
Static assets (images, fonts, etc.) served directly by the app. |
README.md |
Project documentation and guidelines. |
Tip: Keep files and directories focused and organized according to their purpose for maintainability.



