Backend (FastAPI + SQLAlchemy + SQLite): - JWT auth with access/refresh tokens, bcrypt password hashing - User model with member/organizer/admin roles, auto-approve members - Championship, Registration, ParticipantList, Notification models - Alembic async migrations, seed data with test users - Registration endpoint returns tokens for members, pending for organizers - /registrations/my returns championship title/date/location via eager loading - Admin endpoints: list users, approve/reject organizers Mobile (React Native + Expo + TypeScript): - Zustand auth store, Axios client with token refresh interceptor - Role-based registration (Member vs Organizer) with contextual form labels - Tab navigation with Ionicons, safe area headers, admin tab for admin role - Championships list with status badges, detail screen with registration progress - My Registrations with championship title, progress bar, and tap-to-navigate - Admin panel with pending/all filter, approve/reject with confirmation - Profile screen with role badge, Ionicons info rows, sign out - Password visibility toggle (Ionicons), keyboard flow hints (returnKeyType) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
6.9 KiB
CLAUDE.md — DanceChamp
What is this project?
DanceChamp is a mobile platform for pole dance championships. Three apps, one database:
- Member App (React Native / Expo) — Dancers discover championships, register, track their 10-step progress
- Org App (React Native / Expo) — Championship organizers create events, manage members, review videos, confirm payments
- Admin Panel (React + Vite, web) — Platform admin approves orgs, reviews championships from unverified orgs, manages users
Project Structure
/
├── CLAUDE.md ← You are here
├── apps/
│ ├── mobile/ ← Expo app (Member + Org views, switched by role)
│ │ ├── src/
│ │ │ ├── screens/
│ │ │ │ ├── member/ ← Home, MyChamps, Search, Profile, ChampDetail, Progress
│ │ │ │ ├── org/ ← Dashboard, ChampDetail (tabbed), MemberDetail, Settings
│ │ │ │ └── auth/ ← SignIn, SignUp, Onboarding
│ │ │ ├── components/ ← Shared UI components
│ │ │ ├── navigation/ ← Tab + Stack navigators
│ │ │ ├── store/ ← Zustand stores
│ │ │ ├── lib/ ← Supabase client, helpers
│ │ │ └── theme/ ← Colors, fonts, spacing
│ │ └── app.json
│ └── admin/ ← Vite React app
│ └── src/
│ ├── pages/ ← Dashboard, Orgs, Champs, Users
│ ├── components/
│ └── lib/
├── packages/
│ └── shared/ ← Shared types, constants, validation
│ ├── types.ts ← TypeScript interfaces (User, Championship, etc.)
│ └── constants.ts ← Status enums, role enums
├── supabase/
│ ├── migrations/ ← SQL migration files
│ └── seed.sql ← Demo data
└── docs/
├── SPEC.md ← Full technical specification
├── PLAN.md ← Phase-by-phase dev plan with checkboxes
├── DATABASE.md ← Complete database schema + RLS policies
├── DESIGN-SYSTEM.md ← Colors, fonts, components, patterns
└── SCREENS.md ← Screen-by-screen reference for all 3 apps
Tech Stack
| Layer | Choice | Notes |
|---|---|---|
| Mobile | React Native (Expo) | npx create-expo-app with TypeScript |
| Admin | React + Vite | Separate web app |
| Language | TypeScript | Everywhere |
| Navigation | React Navigation | Bottom tabs + stack |
| State | Zustand | Lightweight stores |
| Backend | Supabase | Auth, Postgres DB, Storage, Realtime, Edge Functions |
| Push | Expo Notifications | Via Supabase Edge Function triggers |
Key Architecture Decisions
1. One mobile app, two views
Member and Org use the same Expo app. After login, the app checks user.role and shows the appropriate navigation:
role === "member"→ Member tabs (Home, My Champs, Search, Profile)role === "organization"→ Org tabs (Dashboard, Settings)
2. Everything is scoped per-championship
Members, results, categories, rules, fees, judges — all belong to a specific championship. There is no "global members list" for an org. Each championship is self-contained.
3. Configurable tabs (Org)
Orgs don't fill a giant wizard. They quick-create a championship (name + date + location), then configure each section (Categories, Fees, Rules, Judges) at their own pace. Each section has a "✓ Mark as Done" button. Championship can only go live when all sections are done.
4. Approval flow
- Verified orgs → "Go Live" sets status to
liveimmediately (auto-approved) - Unverified orgs → "Go Live" sets status to
pending_approval→ admin must approve
5. Registration dates (not deadline)
Championships have: event_date, reg_start, reg_end. Registration close date must be before event date. No single "deadline" field.
6. Judges = People, Scoring = Rules
The "Judges" tab shows judge profiles (name, instagram, bio). Scoring criteria and penalties live in the "Rules" tab.
Conventions
Code Style
- Functional components only, no class components
- Use hooks:
useState,useEffect, custom hooks for data fetching - Zustand for global state (auth, current user, championships cache)
- Local state for UI-only state (modals, form inputs, filters)
- TypeScript strict mode
Naming
- Files:
kebab-case.ts/kebab-case.tsx - Components:
PascalCase - Hooks:
useCamelCase - Zustand stores:
use[Name]Store - DB tables:
snake_case - DB columns:
snake_case
Supabase Patterns
// Client init
import { createClient } from '@supabase/supabase-js'
const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY)
// Fetching
const { data, error } = await supabase
.from('championships')
.select('*, disciplines(*), fees(*)')
.eq('status', 'live')
// Realtime subscription
supabase.channel('registrations')
.on('postgres_changes', { event: '*', schema: 'public', table: 'registrations' }, handler)
.subscribe()
Navigation Pattern
// Member
const MemberTabs = () => (
<Tab.Navigator>
<Tab.Screen name="Home" component={HomeStack} />
<Tab.Screen name="MyChamps" component={MyChampsStack} />
<Tab.Screen name="Search" component={SearchStack} />
<Tab.Screen name="Profile" component={ProfileStack} />
</Tab.Navigator>
)
// Org
const OrgTabs = () => (
<Tab.Navigator>
<Tab.Screen name="Dashboard" component={DashboardStack} />
<Tab.Screen name="Settings" component={SettingsStack} />
</Tab.Navigator>
)
Important Docs
Before coding any feature, read the relevant doc:
| Doc | When to read |
|---|---|
docs/SPEC.md |
Full feature spec — read first for any new feature |
docs/PLAN.md |
Dev plan with phases — check what's next |
docs/DATABASE.md |
Schema — read before any DB work |
docs/DESIGN-SYSTEM.md |
UI — read before any screen work |
docs/SCREENS.md |
Screen details — read before building specific screens |
Quick Commands
# Start mobile app
cd apps/mobile && npx expo start
# Start admin panel
cd apps/admin && npm run dev
# Supabase local dev
npx supabase start
npx supabase db reset # Reset + re-seed
# Generate types from Supabase
npx supabase gen types typescript --local > packages/shared/database.types.ts
Current Status
Prototypes completed (JSX files in /prototypes):
dance-champ-mvp.jsx— Member app prototypedance-champ-org.jsx— Org app prototypedance-champ-admin.jsx— Admin panel prototype
These are reference implementations showing the exact UI, data structure, and flows. Use them as visual guides — don't copy the code directly (they're single-file React prototypes, not production React Native).