Tumaini GYM
gym.tumainifitness is both a website and a management system for Tumaini Fitness, providing users with information about the gym, programs, and services while helping manage memberships, classes, and

Tumaini Fitness — Gym Management Platform
Case Study · Full-Stack Web Application
| Client / Project | Tumaini Fitness Centre, Kasarani, Nairobi |
| Live Site | gym.tumainifitness.co.ke |
| Type | Custom gym management & public marketing platform |
| Timeline | [Add your project timeline, e.g. Jan 2025 – Jul 2025] |
| Role | [Add your role, e.g. Full-Stack Developer / Solo Developer] |
| Status | Production |
Overview
Tumaini Fitness is a full-stack gym management platform built for a real fitness centre in Nairobi, Kenya. The system combines a public-facing marketing website with a secure admin dashboard that handles member registration, membership plans, payment processing, and reporting — including native M-Pesa (Safaricom STK Push) integration for mobile money payments.
The platform replaces manual record-keeping and fragmented payment tracking with a single, modern web application that staff can use daily and members can discover online.
[Hero image: landing page hero section showing gym branding, headline, and CTA]
The Problem
Before this platform, Tumaini Fitness relied on manual processes to manage members and payments:
- Member records were scattered across paper forms and spreadsheets
- Payment tracking (cash, M-Pesa, bank transfer) had no central audit trail
- Membership expiry and renewals were hard to monitor at scale
- There was no professional online presence tied to live membership pricing
- Staff had no unified dashboard for day-to-day operations
The business needed a system that was practical for Kenyan operations — especially mobile money — while still presenting a polished public website to attract new members.
Project Goals
- Centralize operations — One dashboard for members, plans, and payments
- Support local payments — Integrate M-Pesa STK Push alongside cash, card, and bank transfer
- Automate membership lifecycle — Track subscriptions, expiry, and renewals automatically after payment
- Enable self-service registration — Let prospective members sign up online without admin intervention
- Build for discoverability — SEO, structured data, and a responsive marketing site
- Secure by default — Role-based access for admins vs. members
My Role & Responsibilities
[Customize this section. Example below.]
- End-to-end product design and full-stack development
- Database schema design and API architecture
- M-Pesa Daraja API integration (STK Push + callback handling)
- Admin dashboard UI with analytics and reporting
- Authentication, authorization, and route protection
- Deployment and production configuration
Tech Stack
Frontend
| Technology | Purpose |
|---|---|
| Next.js 15 (App Router) | Full-stack React framework, SSR/SSG, API routes |
| React 19 | UI rendering and component architecture |
| TypeScript | End-to-end type safety |
| Tailwind CSS 4 | Utility-first styling |
| Radix UI + shadcn/ui | Accessible, composable UI components |
| TanStack React Query | Server state, caching, and data fetching |
| TanStack React Table | Sortable, filterable data tables |
| Recharts | Dashboard analytics charts |
| Framer Motion | Page and component animations |
| React Hook Form + Zod | Form handling and validation |
Backend
| Technology | Purpose |
|---|---|
| Next.js API Routes | RESTful backend endpoints |
| Prisma ORM | Database access and migrations |
| PostgreSQL | Relational data storage |
| NextAuth.js | Authentication (JWT + credentials) |
| bcryptjs | Password hashing |
Integrations & Tools
| Technology | Purpose |
|---|---|
| Safaricom M-Pesa Daraja API | STK Push mobile payments |
| Resend | Transactional email (contact form) |
| React Email | Email template components |
| jsPDF | PDF receipt generation |
| date-fns | Date formatting and calculations |
| ESLint + Prettier | Code quality and formatting |
[Tech stack graphic: Next.js, React, TypeScript, PostgreSQL, Prisma, M-Pesa, Tailwind logos in a grid]
System Architecture
The application follows a monolithic Next.js architecture — a single codebase serving both the public website and the authenticated dashboard, with API routes acting as the backend layer and Prisma connecting to PostgreSQL.
┌─────────────────────────────────────────────────────────────┐ │ Client (Browser) │ ├──────────────────────────┬──────────────────────────────────┤ │ Public Website │ Admin / Member Dashboard │ │ / /about /contact │ /dashboard/* │ │ /register /nutrition │ (Protected routes) │ └────────────┬─────────────┴──────────────┬───────────────────┘ │ │ ▼ ▼ ┌─────────────────────────────────────────────────────────────┐ │ Next.js 15 App Router + API Routes │ │ ┌─────────────┐ ┌──────────────┐ ┌─────────────────────┐ │ │ │ Middleware │ │ NextAuth.js │ │ React Query Hooks │ │ │ │ (Auth/RBAC) │ │ (JWT Session)│ │ (Data Fetching) │ │ │ └─────────────┘ └──────────────┘ └─────────────────────┘ │ └────────────┬───────────────────────────────┬────────────────┘ │ │ ▼ ▼ ┌────────────────────────┐ ┌──────────────────────────────┐ │ PostgreSQL (Prisma) │ │ External Services │ │ Users, Members, │ │ • M-Pesa Daraja API │ │ Plans, Payments, │ │ • Resend (Email) │ │ Subscriptions │ │ • Cloudinary (Images) │ └────────────────────────┘ └──────────────────────────────┘ [Architecture diagram: the flow above rendered visually with icons for each layer]
Application Structure
src/ ├── app/ │ ├── (main)/ # Public pages — home, about, contact, nutrition │ ├── (auth)/ # Sign-in │ ├── dashboard/ # Protected admin & member dashboard │ ├── register/ # Public member registration │ └── api/ # REST API endpoints ├── components/ # UI, dashboard, payments, members, etc. ├── hooks/ # React Query data hooks ├── lib/ # Auth, Prisma, M-Pesa, validations, SEO └── types/ # Shared TypeScript types Database Design
The data model is built around six core business entities, with NextAuth tables for session management.
| Model | Description |
|---|---|
| User | Authentication identity with role (ADMIN or MEMBER) |
| Member | Extended profile — health info, emergency contact, membership status |
| MembershipPlan | Configurable plans with duration, price, and feature list |
| MembershipSubscription | Active/expired subscription linked to a payment |
| Payment | Transaction record with method, status, and M-Pesa reference |
| Account / Session | NextAuth OAuth and session storage |
Key Relationships
- Every Member belongs to one User (1:1)
- A Payment can activate a MembershipSubscription
- Subscriptions automatically extend if a member renews before expiry
- Payment methods: CASH, CARD, BANK_TRANSFER, MOBILE_MONEY
Membership Status Flow
INACTIVE → (payment completed) → ACTIVE → (subscription expires) → EXPIRED ↓ SUSPENDED (admin action) [ERD image: Users, Members, MembershipPlans, MembershipSubscriptions, and Payments with relationships]
Key Features
1. Public Marketing Website
A responsive, SEO-optimized landing experience for Tumaini Fitness:
- Hero section with gym imagery and primary CTA
- Services overview (strength training, cardio, nutrition, kids karate)
- Dynamic membership pricing pulled from the live database
- Member testimonials and gym statistics
- Dedicated pages: About, Contact, Nutrition
- JSON-LD structured data, sitemap, and robots.txt for search engines
[Landing page screenshot — hero and services section]
[Pricing section screenshot — membership plan cards]
2. Online Member Registration
Prospective members can register through a public form at /register without needing admin credentials. The flow:
- User fills in personal details, health info, and fitness goals
- Form is validated client-side (Zod) and server-side
- A User + Member record is created in a single transaction
- A success screen confirms registration and guides next steps
The registration endpoint is intentionally public (POST /api/members) while all other member APIs require admin authentication.
[Registration form screenshot — desktop or mobile]
3. Admin Dashboard
Role-aware dashboard at /dashboard with real-time business metrics:
Key metrics
- Total, active, expired, suspended, and inactive members
- Total and monthly revenue (KES)
- Pending and failed payment counts
Dashboard sections
- Overview with quick actions and upcoming renewals
- Recent activity feed (new members, completed payments)
- Analytics tab with revenue and membership charts
[Dashboard overview screenshot — metric cards]
[Analytics tab screenshot — revenue and membership charts]
4. Member Management
Full CRUD for gym members with:
- Searchable, filterable member table
- Individual member profiles with subscription history
- Suspend / reactivate membership status
- Member reports with summary statistics and export
- Days-remaining calculation for active subscriptions
[Members table screenshot — filters and status badges]
[Member profile page screenshot — subscription and payment history]
5. Membership Plans
Admins can create and manage flexible membership plans:
- Name, description, duration (days), and price (KES)
- Feature list (stored as a string array)
- Active/inactive toggle
- Plans appear dynamically on the public homepage pricing section
[Plan management screenshot]
6. Payment Processing
A comprehensive payment module supporting multiple methods:
| Method | Flow |
|---|---|
| Cash | Manual entry, instant completion |
| Card | Manual entry with reference |
| Bank Transfer | Manual entry with reference |
| M-Pesa (Mobile Money) | STK Push to member's phone, async callback confirmation |
Payment features
- New payment form with member lookup and plan selection
- Real-time M-Pesa status tracker in the UI
- Payment history table with filters, sorting, and pagination
- PDF receipt generation (branded with gym logo and colors)
- CSV export for accounting
- Payment reports page with date-range filtering
[New payment form screenshot]
[Payments table screenshot]
[PDF receipt preview]
7. M-Pesa Integration
Native integration with Safaricom's Daraja API using STK Push (Lipa na M-Pesa Online):
Admin initiates payment │ ▼ POST /api/payments/push │ ├── Create PENDING payment in database ├── Send STK Push to member's phone └── Store CheckoutRequestID as transactionRef │ ▼ Member enters M-Pesa PIN on phone │ ▼ POST /api/payments/callback (Safaricom webhook) │ ├── ResultCode 0 → COMPLETED │ ├── Update payment with M-Pesa receipt number │ └── Create/extend MembershipSubscription │ └── ResultCode ≠ 0 → FAILED Implementation highlights
- Dedicated
MpesaServiceclass with token caching and environment-aware config - Phone number validation and formatting for Kenyan numbers (07… / 254…)
- Prisma transactions ensure payment + subscription updates are atomic
- Smart subscription extension: renewals stack onto existing active subscriptions
- Graceful error handling with health-check endpoints
[M-Pesa STK Push modal screenshot — phone input and status tracker]
8. Authentication & Authorization
Authentication
- NextAuth.js with credentials provider (email + password)
- JWT session strategy (24-hour expiry)
- bcrypt password hashing
- Rate limiting on login attempts (5 attempts per 15 minutes per IP/email)
Authorization
- Two roles: ADMIN and MEMBER
- Next.js middleware protects
/dashboardand/api/*routes - Admin-only routes: members, plans, payments, dashboard stats
- Members see a simplified dashboard (extensible for future self-service features)
[Sign-in page screenshot]
9. Contact Form & Email
The contact page sends inquiries via Resend with:
- Server-side validation and XSS sanitization
- React Email template for branded HTML emails
- Rate limiting and error handling
[Contact page screenshot]
UI / UX Design
Design System
- Primary brand color: Yellow/Gold (#F0B100) — energy, fitness, optimism
- Dark mode: Full theme support via next-themes
- Component library: shadcn/ui built on Radix UI primitives
- Typography: Inter (Google Fonts)
- Layout: Collapsible sidebar navigation in the dashboard
- Responsive: Mobile-first public site; adaptive dashboard with sidebar trigger
UX Decisions
- Skeleton loaders on dashboard and data tables to reduce perceived wait time
- Toast notifications (Sonner) for action feedback
- Breadcrumb navigation in the dashboard for wayfinding
- Currency formatting in Kenyan Shillings (KES) throughout
- Duration formatting that reads naturally (e.g. "1 month 5 days" instead of "35 days")
[Mobile responsive view screenshot]
[Dark mode screenshot]
Development Approach
Phase 1 — Foundation
- Project scaffolding with Next.js 15 App Router and TypeScript
- Prisma schema design and PostgreSQL setup
- NextAuth authentication with role-based sessions
- Middleware for route protection
Phase 2 — Core Admin Features
- Member CRUD with validation schemas (Zod)
- Membership plan management
- Payment recording (manual methods)
- Dashboard statistics API with parallel Prisma queries
Phase 3 — M-Pesa & Automation
- M-Pesa Daraja API service layer
- STK Push initiation and callback webhook
- Automatic subscription creation/extension on successful payment
- Payment status polling in the UI
Phase 4 — Public Website & Polish
- Marketing homepage with dynamic pricing
- Public member registration flow
- SEO (metadata, JSON-LD, sitemap, robots.txt)
- Contact form with Resend email integration
- PDF receipt generation and CSV export
- UI refinements, dark mode, and responsive polish
Code Quality Practices
- TypeScript strict mode across frontend and API routes
- Zod schemas shared between client forms and server validation
- React Query hooks for consistent data fetching patterns
- Component co-location (feature folders for payments, members, plans)
- ESLint + Prettier with Tailwind class sorting
API Overview
| Endpoint | Method | Access | Description |
|---|---|---|---|
| /api/members | GET | Admin | List all members with subscription info |
| /api/members | POST | Public | Register a new member |
| /api/members/[id] | GET/PATCH/DELETE | Admin | Member CRUD |
| /api/members/[id]/suspend | POST | Admin | Suspend/reactivate member |
| /api/members/reports | GET | Admin | Member reports data |
| /api/membership-plans | GET/POST | Admin | List/create plans |
| /api/membership-plans/[id] | GET/PATCH/DELETE | Admin | Plan CRUD |
| /api/payments | GET/POST | Admin | List/create payments |
| /api/payments/push | POST | Admin | Initiate M-Pesa STK Push |
| /api/payments/callback | POST | Public (M-Pesa) | M-Pesa webhook |
| /api/payments/[id]/receipt | GET | Admin | Receipt data for PDF |
| /api/dashboard/stats | GET | Admin | Dashboard metrics |
| /api/contact | POST | Public | Contact form submission |
| /api/auth/[...nextauth] | * | Public | NextAuth handlers |
Challenges & Solutions
Challenge 1 — M-Pesa Callback Reliability
Problem: M-Pesa callbacks can arrive before the UI updates, or fail silently if the server returns an error.
Solution: Always respond with ResultCode: 0 to Safaricom (prevent retries), process callbacks inside Prisma transactions, and provide a client-side status polling endpoint (/api/payments/status) so admins see real-time payment confirmation.
Challenge 2 — Subscription Overlap on Renewal
Problem: When a member pays for a new plan while still active, naively starting a new subscription would waste remaining days.
Solution: Before creating a subscription, check for an active one. If remaining days exist, the new subscription starts from the current end date — effectively stacking renewals.
Challenge 3 — Role-Based Route Protection
Problem: Both UI routes and API routes need consistent authorization; exposing admin APIs would be a security risk.
Solution: Centralized Next.js middleware with an explicit allowlist for public routes (POST /api/members, M-Pesa callback, auth endpoints) and role checks for admin-only paths.
Challenge 4 — Receipt Generation Performance
Problem: Generating PDF receipts with embedded logos was slow and bloated file sizes.
Solution: Client-side PDF generation with jsPDF, logo compression/caching (window.__TUMAINI_LOGO_CACHE__), and optimized PDF options (compress: true, putOnlyUsedFonts: true).
Results & Impact
[Customize with real numbers where possible.]
| Metric | Before | After |
|---|---|---|
| Member record keeping | Manual / spreadsheets | Centralized database |
| Payment tracking | Fragmented | Full audit trail with receipts |
| M-Pesa payments | Manual confirmation | Automated STK Push + subscription activation |
| Online presence | None / basic | Full marketing site with live pricing |
| Membership renewals | Manual tracking | Automated expiry and renewal alerts |
Qualitative outcomes
- Staff can process a payment and activate membership in under a minute
- Every completed payment generates a downloadable branded PDF receipt
- Membership plans on the website stay in sync with admin-configured pricing
- The platform is built to scale with additional member self-service features
[Before/after comparison image — optional]
What I Learned
- Designing for local context matters — M-Pesa isn't optional in Kenya; building first-class mobile money support changed how usable the product is for the client
- Webhook-driven flows need defensive coding — Async payment confirmation requires idempotent handlers, transaction safety, and UI polling
- Monolithic Next.js is productive for SMB tools — One codebase for marketing site + admin dashboard + API reduced complexity for a solo/small-team build
- Schema design upfront saves refactoring — Modeling subscriptions separately from payments made renewal logic and reporting much cleaner
Future Roadmap
- Expand member dashboard (view own subscription, payment history, renewal)
- Automated email/SMS reminders for expiring memberships
- Attendance / check-in tracking
- Multi-branch support
- Admin notification system for pending payments and new registrations
Project Links
| Resource | URL |
|---|---|
| Live Application | gym.tumainifitness.co.ke |
| Source Code | [Add your GitHub repo URL if public] |
| Portfolio | [Add your portfolio URL] |