A full-stack basketball training operations platform
Manage halls, trainers, teams, players, attendance, and payments — all from your phone.
Mobile-first | RTL-native (Arabic + Hebrew) | Stateless HMAC Auth | 106 Automated Tests
Basketball Manager is a production-grade operations platform built for head coaches to run their entire basketball training program from a single app. It replaces spreadsheets, WhatsApp groups, and paper attendance sheets with a unified system designed for phones.
Built for:
- Head coaches managing multiple halls, trainers, and teams
- Trainers tracking attendance and rosters for their assigned teams
- Basketball academies handling seasonal payments and player data
| Module | Description |
|---|---|
| Teams | Create teams, assign trainers and halls, manage player rosters with jersey numbers |
| Attendance | Tap-to-toggle per event (present / absent / late), bulk save, 30-day team history matrix |
| Schedules | Define weekly recurring slots — events auto-generate daily from schedules |
| Payments | Per-player payment tracking with transaction logs and class-wide status views |
| Halls | Manage venues with monthly event calendars and weekly schedule grids |
| Trainers | Profile management, availability scheduling, team reassignment |
| Search | Global debounced search across trainees and trainers (parallel queries, 300ms) |
| Import/Export | 4-step Excel wizard with smart column detection and multilingual header matching |
| Admin Panel | Head coach area — trainer CRUD, bulk import/export, system management |
Additional capabilities:
- Bilingual RTL interface (Arabic + Hebrew) with trilingual database fields
- Stateless HMAC-SHA256 phone authentication via Vonage or Twilio SMS
- Glassmorphic dark UI with animated mesh backgrounds and Framer Motion transitions
- Loading states with animated basketball spinner on every route
- iOS safe area support with proper bottom nav padding
| Layer | Technology | Why |
|---|---|---|
| Framework | Next.js 16 | App Router, Server Components, Server Actions, Turbopack |
| UI | React 19 | Latest RSC support, concurrent features |
| Language | TypeScript 5.9 | Strict mode, full type safety |
| Styling | Tailwind CSS 4 | Utility-first with RTL support, custom design tokens |
| Database | Supabase | PostgreSQL with RLS + 24 SECURITY DEFINER RPCs |
| Auth | Custom HMAC-SHA256 | Stateless OTP via Vonage / Twilio |
| Animations | Framer Motion | Page transitions, wizard steps, modal animations |
| Icons | Lucide React | Tree-shakable, consistent icon set |
| Excel | SheetJS | Dynamic import for .xlsx / .csv parsing and generation |
| Testing | Vitest + Playwright | 82 unit + 24 E2E tests |
| Deployment | Vercel | Edge-optimized with automatic deployments |
┌─────────────────────────────────────────┐
│ Client (Browser) │
│ React 19 · Tailwind CSS 4 · Framer Motion│
└──────────────────┬──────────────────────┘
│
Server Components
Server Actions
│
┌──────────────────┴──────────────────────┐
│ Next.js 16 App Router │
│ Middleware (Auth + Locale Routing) │
│ Cookie-based Sessions (HMAC-signed) │
└──────────────────┬──────────────────────┘
│
supabase.rpc()
│
┌──────────────────┴──────────────────────┐
│ Supabase (PostgreSQL) │
│ RLS Policies (block direct writes) │
│ SECURITY DEFINER Functions (RPCs) │
│ 8 Tables · 24 RPC Functions │
└─────────────────────────────────────────┘
Key architectural decisions:
| Decision | Rationale |
|---|---|
| Server-first components | Pages default to RSC. 'use client' only for hooks and event handlers |
| RPC-only mutations | Direct writes blocked by RLS. All mutations go through SECURITY DEFINER functions |
| Surgical caching | No force-dynamic. revalidatePath() after mutations for targeted invalidation |
| Parallel queries | Independent Supabase calls always wrapped in Promise.all() |
| Stateless auth | HMAC-SHA256 tokens in httpOnly cookies — no session table, no JWT library |
| Israel timezone | All date logic uses Asia/Jerusalem via Intl.DateTimeFormat for Vercel UTC compatibility |
8 tables with Row Level Security enabled on all of them:
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ trainers │ │ halls │ │ payment_logs │
│──────────────│ │──────────────│ │──────────────│
│ id │◄──┐ │ id │◄──┐ │ id │
│ name_ar/he/en│ │ │ name_ar/he/en│ │ │ trainee_id ──│──┐
│ phone │ │ │ description │ │ │ amount │ │
│ role │ │ │ created_at │ │ │ season │ │
│ gender │ │ └──────────────┘ │ │ note │ │
│ availability │ │ │ └──────────────┘ │
│ avail_sched │ │ ┌──────────────┐ │ │
└──────────────┘ │ │ events │ │ ┌──────────────┐ │
▲ ├───│──────────────│ │ │ attendance │ │
│ │ │ id │◄──│───│──────────────│ │
│ │ │ hall_id ──│───┘ │ id │ │
│ │ │ trainer_id ──│───┐ │ trainee_id ──│──┤
│ │ │ type │ │ │ event_id ──│──┘
│ │ │ title_ar/he │ │ │ status │
│ │ │ event_date │ │ │ marked_by ──│───┐
│ │ │ start/end │ │ │ marked_at │ │
│ │ │ schedule_id │ │ └──────────────┘ │
│ │ └──────────────┘ │ │
│ │ ▲ │ ┌──────────────┐ │
│ │ │ │ │ categories │ │
│ │ ┌──────┴───────┐ │ │──────────────│ │
│ └───│ classes │ │ │ id │ │
│ │ (= Teams) │ │ │ name_ar/he/en│ │
│ │──────────────│ │ └──────┬───────┘ │
│ │ id │◄──│──────────┘ │
│ │ name_ar/he/en│ │ │
└───────────────│ trainer_id │ │ │
│ category_id │ │ │
└──────┬───────┘ │ │
│ │ │
┌────────────────────┤ │ │
│ │ │ │
┌──────┴───────┐ ┌─────┴────────┐ │ │
│ trainees │ │class_schedules│ │ │
│ (= Players) │ │──────────────│ │ │
│──────────────│ │ id │ │ │
│ id │ │ class_id ──│──┘ │
│ name_ar/he/en│ │ hall_id ──│ │
│ phone │ │ day_of_week │ │
│ jersey_number│ │ start/end │ │
│ class_id ──│ └─────────────┘ │
│ is_paid │ │
│ amount_paid │───────────────────────────────────────────────┘
│ gender │
└──────────────┘
Key relationships:
- A trainer manages many teams and is assigned to many events
- A team has one trainer, one category, many players, and many weekly schedule slots
- Schedule slots auto-generate events daily via the
ensure_events_for_schedulesRPC - Attendance records link a player to an event with a status (present / absent / late)
- Payment logs track individual transactions per player per season
- All text fields are trilingual —
name_ar,name_he,name_enon every entity
See docs/DATABASE.md for the complete column reference and ER diagram.
Custom cookie-based auth flow — no Supabase Auth, no JWT library:
Phone → Normalize (Arabic/Persian digits) → SMS OTP (Vonage/Twilio) → HMAC Verify → Session Cookie
- User enters phone number (supports Arabic ٠-٩ and Persian ۰-۹ digit input)
- Phone normalized to Israeli format (
05X→9725X) - 4-digit OTP sent via Vonage or Twilio (whichever keys are configured)
- OTP verified stateless via HMAC-SHA256 — no database lookup needed
- Signed
admin_sessionhttpOnly cookie set with{ id, name, role } - New users complete a profile setup step (name, gender, availability)
- Middleware enforces auth on all routes except
/login
Roles:
| Role | Access |
|---|---|
headcoach |
Full admin — manage trainers, teams, halls, import/export, all data |
trainer |
View assigned teams, mark attendance, edit own profile |
106 automated tests — all passing:
| Suite | Tests | Runner | Time |
|---|---|---|---|
| Unit | 82 | Vitest | ~3s |
| E2E | 24 | Playwright (Desktop Chrome) | ~55s |
npm test # Unit tests (watch mode)
npm run test:run # Unit tests (single run)
npm run test:coverage # Unit tests with v8 coverage report
npm run test:e2e # E2E tests (starts dev server automatically)Unit tests cover: utility functions (38), HMAC session tokens (10), i18n config (13), StatusToggle component (11), BottomNav component (10).
E2E tests cover: full OTP login flow, page navigation, team/hall/payment drill-down, attendance links, RTL direction verification, accessibility audits via axe-core, mobile UX (sticky header, nav structure, touch targets).
See docs/TESTING.md for the full testing strategy and how to add new tests.
| Locale | Direction | Status |
|---|---|---|
Arabic (ar) |
RTL | Primary — full UI translations |
Hebrew (he) |
RTL | Full UI translations |
- URL-based locale routing:
/ar/teams,/he/teams - Middleware auto-detects locale from cookie, defaults to Arabic
- Dictionary-based UI translations in
src/dictionaries/ - All database fields store three values (
_ar,_he,_en) getLocalizedField(obj, field, locale)utility with Arabic fallback- English (
en) columns exist for data export and admin tooling
src/
├── app/
│ ├── actions.ts # 40+ server actions (all mutations)
│ └── [locale]/ # All routes under locale prefix
│ ├── page.tsx # Dashboard — stats + today's schedule
│ ├── login/ # 3-step OTP login flow
│ ├── teams/ # List → detail → attendance history → add player
│ ├── halls/ # List → detail with monthly calendar
│ ├── attendance/[eventId]/ # Per-event attendance sheet
│ ├── trainers/ # List → profile with availability
│ ├── payments/ # Hub → per-team payment tracking
│ ├── schedule/ # 7-day schedule view
│ ├── head-coach/ # Admin panel + Excel import wizard
│ └── profile/, more/, settings/, reports/
├── components/ # 50+ components across 12 domains
│ ├── layout/ # Header, Sidebar, BottomNav, AppShell
│ ├── attendance/ # AttendanceSheet, StatusToggle
│ ├── teams/ # TraineeList, ScheduleEditor, AttendanceHistoryView
│ ├── halls/ # HallSchedule, InteractiveEventModal
│ ├── payments/ # PaymentModal, ClassPaymentsClient
│ ├── import/ # 4-step ImportWizard, ExportButton
│ └── ui/ # Toast, ScrollTimePicker, JerseyNumber, etc.
├── lib/
│ ├── utils.ts # cn(), date/timezone helpers, phone normalization
│ ├── session.ts # HMAC sign/verify (Web Crypto API)
│ ├── i18n/ # Locale config + dictionary loader
│ ├── excel/ # Parser, mapper, transformer, analyzer, exporter
│ └── supabase/ # Client/server instances + generated types
├── middleware.ts # Locale detection + auth enforcement
└── dictionaries/ # ar.json, he.json UI strings
e2e/ # 9 Playwright spec files
src/**/__tests__/ # 5 Vitest test files
- Node.js 20+ and npm
- Supabase project with the schema applied
- Vonage or Twilio API credentials (for production SMS OTP)
git clone https://github.com/Mhemd139/BasketBall.git
cd BasketBall
npm installCreate .env.local in the project root:
# Supabase
NEXT_PUBLIC_SUPABASE_URL=https://your-project.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=your_anon_key
# Session signing
HMAC_SECRET=your_random_secret_key
# SMS Provider (at least one for production)
VONAGE_API_KEY=your_key
VONAGE_API_SECRET=your_secret
VONAGE_FROM_NUMBER=your_numbernpm run dev # Start with Turbopack on localhost:3000Open http://localhost:3000/ar (Arabic) or http://localhost:3000/he (Hebrew).
| Token | Hex | Usage |
|---|---|---|
| Navy | #254263 |
Primary backgrounds, headers |
| Navy Dark | #0f1f3d |
Deep backgrounds |
| Gold | #ffd700 |
Accent, highlights, active indicators |
| Orange | #d97639 |
Basketball-themed secondary |
| Success | #22c55e |
Present, paid, positive states |
| Error | #ef4444 |
Absent, errors, negative states |
| Warning | #f59e0b |
Late, caution states |
| Font | Usage |
|---|---|
| Cairo | Arabic text |
| Rubik | Hebrew text |
| Inter | Default UI text |
| Syncopate | Display headings |
| Outfit | Secondary headings |
| Element | Size | Required CSS |
|---|---|---|
| Header | 64px fixed | pt-20 on page content |
| Sidebar | 240px (desktop only) | md:ml-[240px] on page content |
| Bottom Nav | 72px (mobile only) | pb-24 mobile, md:pb-8 desktop |
- Designed for 375px viewport first — desktop is progressive enhancement
- Touch targets >= 48px on all interactive elements
- Bottom nav is the primary mobile navigation (4-5 thumb-reachable tabs)
- Primary actions positioned in the thumb zone (bottom half of screen)
- iOS safe area:
pb-[max(env(safe-area-inset-bottom),8px)]
| Measure | Implementation |
|---|---|
| Row Level Security | Enabled on all tables — direct writes blocked |
| SECURITY DEFINER RPCs | 24 Postgres functions for controlled mutations |
| HMAC-SHA256 sessions | Signed, tamper-proof session tokens |
| httpOnly cookies | Not accessible via JavaScript |
| Middleware enforcement | Unauthenticated users redirected to login |
| Role-based access | Head coach vs. trainer permissions |
| No service role key | All elevated operations go through RPCs |
| Rule | Implementation |
|---|---|
| Parallel queries | Promise.all() for independent Supabase calls |
| Bounded queries | Every .select() has .limit() or date filter |
| Surgical caching | revalidatePath() after mutations — no force-dynamic |
| Debounced search | 300ms debounce on keystroke-triggered queries |
| Server Components | Default RSC — 'use client' only when necessary |
| Selective columns | Fetch only needed fields, not select('*') |
| Image optimization | next/image with proper sizing and lazy loading |
| Dynamic imports | Excel library loaded on demand (avoids 200KB upfront) |
See docs/PERFORMANCE.md for detailed rules with code examples.
| Command | Description |
|---|---|
npm run dev |
Start dev server (Turbopack) |
npm run build |
Production build |
npm start |
Start production server |
npm run lint |
ESLint check |
npm test |
Unit tests (watch mode) |
npm run test:run |
Unit tests (single run) |
npm run test:coverage |
Unit tests with v8 coverage |
npm run test:e2e |
E2E tests (Playwright) |
| Document | Description |
|---|---|
| Architecture | System design, auth flow, data flow diagrams |
| Features | Deep dives into each major feature |
| Database | Complete schema, ER diagram, RPC reference |
| Testing | Testing strategy and how to add new tests |
| API Reference | All 40+ server actions documented |
| Setup Guide | Local development setup and troubleshooting |
| Components | Component library reference |
| Performance | Enforced performance rules with code examples |
Built with Next.js 16 + React 19 + Supabase + Tailwind CSS 4
