Full app rebuild: FastAPI backend + React Native mobile with auth, championships, admin
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>
This commit is contained in:
357
dancechamp-claude-code/docs/DATABASE.md
Normal file
357
dancechamp-claude-code/docs/DATABASE.md
Normal file
@@ -0,0 +1,357 @@
|
||||
# DanceChamp — Database Schema
|
||||
|
||||
## Overview
|
||||
|
||||
Backend: **Supabase** (PostgreSQL + Auth + Storage + Realtime)
|
||||
|
||||
All tables use `uuid` primary keys generated by `gen_random_uuid()`.
|
||||
All tables have `created_at` and `updated_at` timestamps.
|
||||
|
||||
---
|
||||
|
||||
## Tables
|
||||
|
||||
### users
|
||||
Extended from Supabase Auth. This is a `public.users` table that mirrors `auth.users` via trigger.
|
||||
|
||||
```sql
|
||||
create table public.users (
|
||||
id uuid primary key references auth.users(id) on delete cascade,
|
||||
email text not null,
|
||||
name text not null,
|
||||
role text not null check (role in ('admin', 'organization', 'member')),
|
||||
city text,
|
||||
instagram_handle text,
|
||||
experience_years integer,
|
||||
disciplines text[] default '{}', -- ['Pole Exotic', 'Pole Art']
|
||||
auth_provider text default 'email', -- 'email' | 'google' | 'instagram'
|
||||
avatar_url text,
|
||||
status text not null default 'active' check (status in ('active', 'warned', 'blocked')),
|
||||
warn_reason text,
|
||||
block_reason text,
|
||||
created_at timestamptz default now(),
|
||||
updated_at timestamptz default now()
|
||||
);
|
||||
```
|
||||
|
||||
### organizations
|
||||
One-to-one with a user (role = 'organization').
|
||||
|
||||
```sql
|
||||
create table public.organizations (
|
||||
id uuid primary key default gen_random_uuid(),
|
||||
user_id uuid not null references public.users(id) on delete cascade,
|
||||
name text not null,
|
||||
instagram_handle text,
|
||||
email text,
|
||||
city text,
|
||||
logo_url text,
|
||||
verified boolean not null default false,
|
||||
status text not null default 'pending' check (status in ('active', 'pending', 'blocked')),
|
||||
block_reason text,
|
||||
created_at timestamptz default now(),
|
||||
updated_at timestamptz default now()
|
||||
);
|
||||
```
|
||||
|
||||
### championships
|
||||
Belongs to an organization. Core entity.
|
||||
|
||||
```sql
|
||||
create table public.championships (
|
||||
id uuid primary key default gen_random_uuid(),
|
||||
org_id uuid not null references public.organizations(id) on delete cascade,
|
||||
name text not null,
|
||||
subtitle text,
|
||||
event_date text, -- "May 30, 2026" or ISO date
|
||||
reg_start text, -- registration opens
|
||||
reg_end text, -- registration closes (must be before event_date)
|
||||
location text, -- "Minsk, Belarus"
|
||||
venue text, -- "Prime Hall"
|
||||
accent_color text default '#D4145A',
|
||||
image_emoji text default '💃',
|
||||
status text not null default 'draft' check (status in ('draft', 'pending_approval', 'live', 'completed', 'blocked')),
|
||||
-- configurable sections progress
|
||||
config_info boolean not null default false,
|
||||
config_categories boolean not null default false,
|
||||
config_fees boolean not null default false,
|
||||
config_rules boolean not null default false,
|
||||
config_judges boolean not null default false,
|
||||
-- links
|
||||
form_url text, -- Google Forms URL
|
||||
rules_doc_url text, -- Rules document URL
|
||||
created_at timestamptz default now(),
|
||||
updated_at timestamptz default now()
|
||||
);
|
||||
```
|
||||
|
||||
### disciplines
|
||||
Championship has many disciplines. Each discipline has levels.
|
||||
|
||||
```sql
|
||||
create table public.disciplines (
|
||||
id uuid primary key default gen_random_uuid(),
|
||||
championship_id uuid not null references public.championships(id) on delete cascade,
|
||||
name text not null, -- "Exotic Pole Dance"
|
||||
levels text[] default '{}', -- ['Beginners', 'Amateur', 'Semi-Pro', 'Profi', 'Elite']
|
||||
sort_order integer default 0,
|
||||
created_at timestamptz default now()
|
||||
);
|
||||
```
|
||||
|
||||
### styles
|
||||
Championship-level styles (not per-discipline).
|
||||
|
||||
```sql
|
||||
create table public.styles (
|
||||
id uuid primary key default gen_random_uuid(),
|
||||
championship_id uuid not null references public.championships(id) on delete cascade,
|
||||
name text not null, -- "Classic", "Flow", "Theater"
|
||||
sort_order integer default 0,
|
||||
created_at timestamptz default now()
|
||||
);
|
||||
```
|
||||
|
||||
### fees
|
||||
One-to-one with championship.
|
||||
|
||||
```sql
|
||||
create table public.fees (
|
||||
id uuid primary key default gen_random_uuid(),
|
||||
championship_id uuid not null unique references public.championships(id) on delete cascade,
|
||||
video_selection text, -- "50 BYN / 1,500 RUB"
|
||||
solo text, -- "280 BYN / 7,500 RUB"
|
||||
duet text, -- "210 BYN / 5,800 RUB pp"
|
||||
"group" text, -- "190 BYN / 4,500 RUB pp"
|
||||
refund_note text,
|
||||
created_at timestamptz default now(),
|
||||
updated_at timestamptz default now()
|
||||
);
|
||||
```
|
||||
|
||||
### rules
|
||||
Championship has many rules across sections.
|
||||
|
||||
```sql
|
||||
create table public.rules (
|
||||
id uuid primary key default gen_random_uuid(),
|
||||
championship_id uuid not null references public.championships(id) on delete cascade,
|
||||
section text not null check (section in ('general', 'costume', 'scoring', 'penalty')),
|
||||
name text not null, -- rule text or criterion name
|
||||
value text, -- for scoring: "10" (max), for penalty: "-2" or "DQ"
|
||||
sort_order integer default 0,
|
||||
created_at timestamptz default now()
|
||||
);
|
||||
```
|
||||
|
||||
### judges
|
||||
Championship has many judges.
|
||||
|
||||
```sql
|
||||
create table public.judges (
|
||||
id uuid primary key default gen_random_uuid(),
|
||||
championship_id uuid not null references public.championships(id) on delete cascade,
|
||||
name text not null,
|
||||
instagram text,
|
||||
bio text,
|
||||
photo_url text,
|
||||
sort_order integer default 0,
|
||||
created_at timestamptz default now()
|
||||
);
|
||||
```
|
||||
|
||||
### registrations
|
||||
Links a member to a championship. Tracks the 10-step progress.
|
||||
|
||||
```sql
|
||||
create table public.registrations (
|
||||
id uuid primary key default gen_random_uuid(),
|
||||
user_id uuid not null references public.users(id) on delete cascade,
|
||||
championship_id uuid not null references public.championships(id) on delete cascade,
|
||||
discipline_id uuid references public.disciplines(id),
|
||||
level text, -- "Semi-Pro"
|
||||
style text, -- "Classic"
|
||||
participation_type text default 'solo' check (participation_type in ('solo', 'duet', 'group')),
|
||||
-- Progress steps (step 1–10)
|
||||
step_rules_reviewed boolean default false,
|
||||
step_category_selected boolean default false,
|
||||
step_video_recorded boolean default false,
|
||||
step_form_submitted boolean default false,
|
||||
step_video_fee_paid boolean default false, -- confirmed by org
|
||||
step_video_fee_receipt_url text, -- uploaded receipt
|
||||
step_results text check (step_results in ('pending', 'passed', 'failed')),
|
||||
step_champ_fee_paid boolean default false,
|
||||
step_champ_fee_receipt_url text,
|
||||
step_about_me_submitted boolean default false,
|
||||
step_insurance_confirmed boolean default false,
|
||||
step_insurance_doc_url text,
|
||||
-- Video
|
||||
video_url text,
|
||||
-- Meta
|
||||
current_step integer default 1,
|
||||
created_at timestamptz default now(),
|
||||
updated_at timestamptz default now(),
|
||||
unique(user_id, championship_id)
|
||||
);
|
||||
```
|
||||
|
||||
### notifications
|
||||
Push to member's in-app feed.
|
||||
|
||||
```sql
|
||||
create table public.notifications (
|
||||
id uuid primary key default gen_random_uuid(),
|
||||
user_id uuid not null references public.users(id) on delete cascade,
|
||||
championship_id uuid references public.championships(id) on delete set null,
|
||||
type text not null check (type in (
|
||||
'category_changed', 'payment_confirmed', 'results',
|
||||
'deadline_reminder', 'registration_confirmed', 'announcement',
|
||||
'champ_approved', 'champ_rejected', 'org_approved', 'org_rejected'
|
||||
)),
|
||||
title text not null,
|
||||
message text not null,
|
||||
read boolean not null default false,
|
||||
created_at timestamptz default now()
|
||||
);
|
||||
```
|
||||
|
||||
### activity_logs
|
||||
Admin audit trail.
|
||||
|
||||
```sql
|
||||
create table public.activity_logs (
|
||||
id uuid primary key default gen_random_uuid(),
|
||||
actor_id uuid references public.users(id) on delete set null,
|
||||
action text not null, -- "org_approved", "user_blocked", "champ_auto_approved"
|
||||
target_type text not null, -- "organization", "championship", "user"
|
||||
target_id uuid,
|
||||
target_name text, -- denormalized for display
|
||||
details jsonb, -- extra context
|
||||
created_at timestamptz default now()
|
||||
);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Relationships Diagram
|
||||
|
||||
```
|
||||
users (1) ──── (1) organizations
|
||||
│
|
||||
│ has many
|
||||
▼
|
||||
championships
|
||||
┌────┼────┬────┬────┐
|
||||
│ │ │ │ │
|
||||
disciplines styles fees rules judges
|
||||
│
|
||||
registrations ─┘
|
||||
(user + championship)
|
||||
│
|
||||
notifications
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Row Level Security (RLS)
|
||||
|
||||
Enable RLS on all tables.
|
||||
|
||||
### users
|
||||
```sql
|
||||
-- Members can read/update their own row
|
||||
create policy "Users can read own" on users for select using (auth.uid() = id);
|
||||
create policy "Users can update own" on users for update using (auth.uid() = id);
|
||||
|
||||
-- Org admins can read members registered to their championships
|
||||
create policy "Orgs can read their members" on users for select using (
|
||||
id in (
|
||||
select r.user_id from registrations r
|
||||
join championships c on r.championship_id = c.id
|
||||
join organizations o on c.org_id = o.id
|
||||
where o.user_id = auth.uid()
|
||||
)
|
||||
);
|
||||
|
||||
-- Admin can read/update all
|
||||
create policy "Admin full access" on users for all using (
|
||||
exists (select 1 from users where id = auth.uid() and role = 'admin')
|
||||
);
|
||||
```
|
||||
|
||||
### championships
|
||||
```sql
|
||||
-- Anyone can read live championships
|
||||
create policy "Public read live" on championships for select using (status = 'live');
|
||||
|
||||
-- Org can CRUD their own
|
||||
create policy "Org manages own" on championships for all using (
|
||||
org_id in (select id from organizations where user_id = auth.uid())
|
||||
);
|
||||
|
||||
-- Admin full access
|
||||
create policy "Admin full access" on championships for all using (
|
||||
exists (select 1 from users where id = auth.uid() and role = 'admin')
|
||||
);
|
||||
```
|
||||
|
||||
### registrations
|
||||
```sql
|
||||
-- Members can read/create their own
|
||||
create policy "Member own registrations" on registrations for select using (user_id = auth.uid());
|
||||
create policy "Member can register" on registrations for insert with check (user_id = auth.uid());
|
||||
|
||||
-- Org can read/update registrations for their championships
|
||||
create policy "Org manages registrations" on registrations for all using (
|
||||
championship_id in (
|
||||
select c.id from championships c
|
||||
join organizations o on c.org_id = o.id
|
||||
where o.user_id = auth.uid()
|
||||
)
|
||||
);
|
||||
|
||||
-- Admin full access
|
||||
create policy "Admin full access" on registrations for all using (
|
||||
exists (select 1 from users where id = auth.uid() and role = 'admin')
|
||||
);
|
||||
```
|
||||
|
||||
### notifications
|
||||
```sql
|
||||
-- Users can read their own notifications
|
||||
create policy "Read own" on notifications for select using (user_id = auth.uid());
|
||||
-- Users can mark their own as read
|
||||
create policy "Update own" on notifications for update using (user_id = auth.uid());
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Storage Buckets
|
||||
|
||||
```
|
||||
receipts/ -- Payment receipt screenshots
|
||||
{user_id}/{registration_id}/receipt.jpg
|
||||
|
||||
insurance/ -- Insurance documents
|
||||
{user_id}/{registration_id}/insurance.pdf
|
||||
|
||||
judge-photos/ -- Judge profile photos
|
||||
{championship_id}/{judge_id}.jpg
|
||||
|
||||
org-logos/ -- Organization logos
|
||||
{org_id}/logo.jpg
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Seed Data
|
||||
|
||||
For development, seed with:
|
||||
- 1 admin user
|
||||
- 2 organizations (1 verified, 1 unverified/pending)
|
||||
- 2 championships for verified org (1 live, 1 draft)
|
||||
- 1 championship for unverified org (pending_approval)
|
||||
- 7 member users with registrations at various progress stages
|
||||
- Sample notifications, activity logs
|
||||
|
||||
This matches the prototype demo data.
|
||||
258
dancechamp-claude-code/docs/DESIGN-SYSTEM.md
Normal file
258
dancechamp-claude-code/docs/DESIGN-SYSTEM.md
Normal file
@@ -0,0 +1,258 @@
|
||||
# DanceChamp — Design System
|
||||
|
||||
## Theme: Dark Luxury
|
||||
|
||||
The app has a premium dark aesthetic. Think high-end dance competition branding — elegant, minimal, confident.
|
||||
|
||||
---
|
||||
|
||||
## Colors
|
||||
|
||||
### Core Palette
|
||||
```
|
||||
Background: #08070D (near-black with slight purple)
|
||||
Card: #12111A (elevated surface)
|
||||
Card Hover: #1A1926 (pressed/active state)
|
||||
Border: #1F1E2E (subtle separator)
|
||||
Text Primary: #F2F0FA (off-white)
|
||||
Text Dim: #5E5C72 (labels, placeholders)
|
||||
Text Mid: #8F8DA6 (secondary info)
|
||||
```
|
||||
|
||||
### Accent Colors
|
||||
```
|
||||
Pink (Primary): #D4145A ← Member app + Org app default
|
||||
Purple: #7C3AED ← Secondary accent (styles, alt champ branding)
|
||||
Indigo: #6366F1 ← Admin panel accent
|
||||
```
|
||||
|
||||
### Semantic Colors
|
||||
```
|
||||
Green: #10B981 (success, passed, active, confirmed)
|
||||
Yellow: #F59E0B (warning, pending, draft)
|
||||
Red: #EF4444 (error, failed, blocked, danger)
|
||||
Blue: #60A5FA (info, links, video)
|
||||
Orange: #F97316 (warned status, awaiting review)
|
||||
```
|
||||
|
||||
### Transparent Variants
|
||||
Each semantic color has a 10% opacity background:
|
||||
```
|
||||
Green Soft: rgba(16,185,129,0.10)
|
||||
Yellow Soft: rgba(245,158,11,0.10)
|
||||
Red Soft: rgba(239,68,68,0.10)
|
||||
Blue Soft: rgba(96,165,250,0.10)
|
||||
Purple Soft: rgba(139,92,246,0.10)
|
||||
```
|
||||
|
||||
For accent overlays use 15% opacity: `${color}15`
|
||||
For accent borders use 30% opacity: `${color}30`
|
||||
|
||||
### Per-Championship Branding
|
||||
Each championship can have its own accent color:
|
||||
- Zero Gravity: `#D4145A` (pink)
|
||||
- Pole Star: `#7C3AED` (purple)
|
||||
|
||||
This color is used for the championship's tab highlights, buttons, and member tags.
|
||||
|
||||
---
|
||||
|
||||
## Typography
|
||||
|
||||
### Font Stack
|
||||
```
|
||||
Display: 'Playfair Display', Georgia, serif ← Headings, numbers, titles
|
||||
Body: 'DM Sans', 'Segoe UI', sans-serif ← Body text, labels, buttons
|
||||
Mono: 'JetBrains Mono', monospace ← Badges, timestamps, codes, small labels
|
||||
```
|
||||
|
||||
### Sizes & Usage
|
||||
```
|
||||
Screen title: Playfair Display, 20px, 700 weight
|
||||
Section title: Playfair Display, 14px, 700 weight, Text Mid color
|
||||
Card title: DM Sans, 14-16px, 600 weight
|
||||
Body text: DM Sans, 12-13px, 400 weight
|
||||
Small label: JetBrains Mono, 9-10px, 500 weight, uppercase, letter-spacing 0.3-0.5
|
||||
Badge: JetBrains Mono, 8px, 700 weight, uppercase, letter-spacing 0.8
|
||||
Stat number: Playfair Display, 16-20px, 700 weight
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Components
|
||||
|
||||
### Card
|
||||
```
|
||||
Background: #12111A
|
||||
Border: 1px solid #1F1E2E
|
||||
Border Radius: 14px
|
||||
Padding: 16px
|
||||
```
|
||||
|
||||
### Status Badge
|
||||
Small pill with semantic color + soft background.
|
||||
```
|
||||
Font: JetBrains Mono, 8px, 700 weight, uppercase
|
||||
Padding: 3px 8px
|
||||
Border Radius: 4px
|
||||
```
|
||||
|
||||
Status mappings:
|
||||
| Status | Label | Color | Background |
|
||||
|---|---|---|---|
|
||||
| active / live | ACTIVE / LIVE | Green | Green Soft |
|
||||
| pending | PENDING | Yellow | Yellow Soft |
|
||||
| pending_approval | AWAITING REVIEW | Orange | Orange Soft |
|
||||
| draft | DRAFT | Dim | Dim 15% |
|
||||
| blocked | BLOCKED | Red | Red Soft |
|
||||
| warned | WARNED | Orange | Orange Soft |
|
||||
| passed | PASSED | Green | Green Soft |
|
||||
| failed | FAILED | Red | Red Soft |
|
||||
|
||||
### Tab Bar (in-screen tabs, not bottom nav)
|
||||
```
|
||||
Container: horizontal scroll, no scrollbar, gap 3px
|
||||
Tab: JetBrains Mono, 9px, 600 weight
|
||||
Active: accent color text, accent 15% bg, accent 30% border
|
||||
Inactive: Dim color text, transparent bg
|
||||
Border Radius: 16px (pill shape)
|
||||
Padding: 5px 10px
|
||||
```
|
||||
|
||||
Configurable tabs have a status dot (6px circle):
|
||||
- Green dot = section configured ✓
|
||||
- Yellow dot = section pending
|
||||
|
||||
### Input Field
|
||||
```
|
||||
Background: #08070D (same as page bg)
|
||||
Border: 1px solid #1F1E2E
|
||||
Border Radius: 10px
|
||||
Padding: 10px 12px
|
||||
Font: DM Sans, 13px
|
||||
Label: JetBrains Mono, 9px, uppercase, Dim color, 6px margin bottom
|
||||
```
|
||||
|
||||
### Action Button
|
||||
Two variants:
|
||||
- **Filled**: solid background, white text (for primary actions)
|
||||
- **Outline**: transparent bg, colored border 30%, colored text (for secondary/danger)
|
||||
|
||||
```
|
||||
Padding: 8px 14px
|
||||
Border Radius: 8px
|
||||
Font: DM Sans, 11px, 700 weight
|
||||
```
|
||||
|
||||
### Tag Editor
|
||||
For lists of editable items (rules, levels, styles):
|
||||
```
|
||||
Tag: DM Sans 11px, colored bg 10%, colored border 25%, 4px 10px padding, 16px radius
|
||||
Remove (×): 10px, Dim color
|
||||
Add input: same as Input Field but smaller (8px 12px, 12px font)
|
||||
Add button: colored bg, white "+" text, 8px 14px
|
||||
```
|
||||
|
||||
### Header
|
||||
```
|
||||
Padding: 14px 20px 6px
|
||||
Title: Playfair Display, 20px, 700
|
||||
Subtitle: DM Sans, 11px, Dim color
|
||||
Back button: 32×32px, Card bg, Border, 8px radius, "←" centered
|
||||
```
|
||||
|
||||
### Bottom Navigation
|
||||
```
|
||||
Border top: 1px solid #1F1E2E
|
||||
Padding: 10px 0 8px
|
||||
Items: flex, space-around
|
||||
Icon: 18px emoji
|
||||
Label: JetBrains Mono, 8px, letter-spacing 0.3
|
||||
Active: opacity 1
|
||||
Inactive: opacity 0.35
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Patterns
|
||||
|
||||
### Progress/Setup Checklist
|
||||
For configurable tabs on org side:
|
||||
```
|
||||
Each row:
|
||||
[Checkbox 22×22] [Label capitalize] [Configure › or ✓]
|
||||
|
||||
Checkbox: 6px radius, 2px border
|
||||
Done: Green border, Green Soft bg, "✓" inside
|
||||
Pending: Yellow border, transparent bg
|
||||
|
||||
Label done: Dim color, line-through
|
||||
Label pending: Text Primary, clickable → navigates to tab
|
||||
```
|
||||
|
||||
### Readiness Bar (dashboard cards)
|
||||
```
|
||||
Track: 4px height, Border color bg, 2px radius
|
||||
Fill: accent color, width = (done/total * 100)%
|
||||
Below: list of section names with ✓ (green) or ○ (yellow)
|
||||
```
|
||||
|
||||
### Member Card
|
||||
```
|
||||
Container: Card style, 12px padding
|
||||
Name: DM Sans 13px, 600 weight
|
||||
Instagram: JetBrains Mono 10px, accent color
|
||||
Tags: DM Sans 9px, Mid color, Mid 10% bg, 2px 7px padding, 10px radius
|
||||
Status badge: top-right corner
|
||||
```
|
||||
|
||||
### Stat Box
|
||||
```
|
||||
Container: Card style, 10px 6px padding, centered
|
||||
Number: Playfair Display, 16-20px, 700 weight, semantic color
|
||||
Label: JetBrains Mono, 7px, uppercase, Dim color
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phone Frame (for prototypes)
|
||||
|
||||
```
|
||||
Width: 375px
|
||||
Height: 740px
|
||||
Border Radius: 36px
|
||||
Border: 1.5px solid #1F1E2E
|
||||
Shadow: 0 0 80px rgba(accent, 0.06), 0 20px 40px rgba(0,0,0,0.5)
|
||||
|
||||
Status bar: 8px 24px padding
|
||||
Time: JetBrains Mono 11px, Dim
|
||||
Notch: 100×28px black, 14px radius
|
||||
Indicators: "●●●" JetBrains Mono 11px, Dim
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## React Native Adaptation
|
||||
|
||||
The prototypes use inline styles. For React Native:
|
||||
|
||||
| Prototype | React Native |
|
||||
|---|---|
|
||||
| `div` | `View` |
|
||||
| `span`, `p`, `h1` | `Text` |
|
||||
| `input` | `TextInput` |
|
||||
| `onClick` | `onPress` (via `Pressable` or `TouchableOpacity`) |
|
||||
| `overflow: auto` | `ScrollView` or `FlatList` |
|
||||
| `cursor: pointer` | Not needed |
|
||||
| `border: 1px solid` | `borderWidth: 1, borderColor:` |
|
||||
| `fontFamily: 'DM Sans'` | Loaded via `expo-font` |
|
||||
| `gap` | Use `marginBottom` on children (gap not fully supported) |
|
||||
| `overflowX: auto` with scrollbar hidden | `ScrollView horizontal showsHorizontalScrollIndicator={false}` |
|
||||
|
||||
### Fonts Loading (Expo)
|
||||
```typescript
|
||||
import { useFonts } from 'expo-font';
|
||||
import { PlayfairDisplay_700Bold } from '@expo-google-fonts/playfair-display';
|
||||
import { DMSans_400Regular, DMSans_500Medium, DMSans_600SemiBold } from '@expo-google-fonts/dm-sans';
|
||||
import { JetBrainsMono_400Regular, JetBrainsMono_500Medium, JetBrainsMono_700Bold } from '@expo-google-fonts/jetbrains-mono';
|
||||
```
|
||||
316
dancechamp-claude-code/docs/PLAN.md
Normal file
316
dancechamp-claude-code/docs/PLAN.md
Normal file
@@ -0,0 +1,316 @@
|
||||
# DanceChamp — Vibe Coding Plan
|
||||
|
||||
## How to use this plan
|
||||
- Work phase by phase, top to bottom
|
||||
- Check off tasks as you go: `[ ]` → `[x]`
|
||||
- Each phase has a **"Done when"** — don't move on until it's met
|
||||
- 🔴 = blocker (must do), 🟡 = important, 🟢 = nice to have for MVP
|
||||
- Estimated time is for vibe coding with AI (Claude Code / Cursor)
|
||||
|
||||
---
|
||||
|
||||
## Phase 0: Project Setup
|
||||
**Time: ~1 hour**
|
||||
|
||||
- [ ] 🔴 Init Expo project (React Native): `npx create-expo-app DanceChamp --template blank-typescript`
|
||||
- [ ] 🔴 Init Web admin panel: `npm create vite@latest admin-panel -- --template react-ts`
|
||||
- [ ] 🔴 Setup Supabase project (or Firebase): create account, new project
|
||||
- [ ] 🔴 Setup database tables (see Phase 1)
|
||||
- [ ] 🔴 Install core deps: `react-navigation`, `zustand`, `supabase-js`
|
||||
- [ ] 🟡 Setup Git repo + `.gitignore`
|
||||
- [ ] 🟡 Create `/apps/mobile`, `/apps/admin`, `/packages/shared` monorepo structure
|
||||
- [ ] 🟢 Add ESLint + Prettier
|
||||
|
||||
**Done when:** Both apps run locally, Supabase dashboard is accessible
|
||||
|
||||
---
|
||||
|
||||
## Phase 1: Database & Auth
|
||||
**Time: ~2-3 hours**
|
||||
|
||||
### 1.1 Database Tables
|
||||
- [ ] 🔴 `users` — id, email, name, role (admin | organization | member), city, instagram_handle, experience_years, disciplines[], auth_provider, status, created_at
|
||||
- [ ] 🔴 `organizations` — id, user_id (FK), name, instagram_handle, email, city, logo_url, verified (bool), status (active | pending | blocked), block_reason, created_at
|
||||
- [ ] 🔴 `championships` — id, org_id (FK), name, subtitle, event_date, reg_start, reg_end, location, venue, status (draft | pending_approval | live | completed | blocked), accent_color, created_at
|
||||
- [ ] 🔴 `disciplines` — id, championship_id (FK), name, levels[], styles[]
|
||||
- [ ] 🔴 `fees` — id, championship_id (FK), video_selection, solo, duet, group, refund_note
|
||||
- [ ] 🔴 `rules` — id, championship_id (FK), section (general | costume | scoring | penalty), text, value (for penalties)
|
||||
- [ ] 🔴 `judges` — id, championship_id (FK), name, instagram, bio, photo_url
|
||||
- [ ] 🔴 `registrations` — id, user_id (FK), championship_id (FK), discipline_id, level, style, type (solo | duet | group), current_step, video_url, fee_paid, receipt_uploaded, insurance_uploaded, passed (null | true | false), created_at
|
||||
- [ ] 🔴 `notifications` — id, user_id (FK), championship_id, type, title, message, read (bool), created_at
|
||||
- [ ] 🟡 `activity_logs` — id, actor_id, action, target_type, target_id, details, created_at
|
||||
|
||||
### 1.2 Auth
|
||||
- [ ] 🔴 Supabase Auth: enable Email + Google OAuth
|
||||
- [ ] 🔴 Role-based access: Row Level Security (RLS) policies
|
||||
- Members see only their own registrations
|
||||
- Orgs see only their own championships & members
|
||||
- Admin sees everything
|
||||
- [ ] 🔴 Sign up / Sign in screens (mobile)
|
||||
- [ ] 🔴 Admin login (web panel)
|
||||
- [ ] 🟡 Instagram OAuth (for member profiles)
|
||||
- [ ] 🟡 Onboarding flow: name → city → discipline → experience → done
|
||||
|
||||
**Done when:** Can sign up as member, org, and admin. RLS blocks cross-access.
|
||||
|
||||
---
|
||||
|
||||
## Phase 2: Member App — Core Screens
|
||||
**Time: ~4-5 hours**
|
||||
|
||||
### 2.1 Navigation
|
||||
- [ ] 🔴 Bottom tab nav: Home, My Champs, Search, Profile
|
||||
- [ ] 🔴 Stack navigation: screens → detail → sub-screens
|
||||
|
||||
### 2.2 Home Screen
|
||||
- [ ] 🔴 "Upcoming championships" feed — cards with name, date, location, status badge
|
||||
- [ ] 🔴 "My active registrations" section with progress bars
|
||||
- [ ] 🟡 Bell icon → notifications feed
|
||||
- [ ] 🟡 Deadline urgency banners ("Registration closes in 3 days!")
|
||||
|
||||
### 2.3 Championship Detail
|
||||
- [ ] 🔴 Header: name, dates, location, venue, registration period
|
||||
- [ ] 🔴 Tab: Overview (info + registration funnel)
|
||||
- [ ] 🔴 Tab: Categories (disciplines, levels, styles + eligibility)
|
||||
- [ ] 🔴 Tab: Rules (general, costume, scoring criteria, penalties)
|
||||
- [ ] 🔴 Tab: Fees (video selection + championship fees)
|
||||
- [ ] 🔴 Tab: Judges (judge profiles with photo, instagram, bio)
|
||||
- [ ] 🔴 "Register" button → starts onboarding
|
||||
|
||||
### 2.4 Search & Discover
|
||||
- [ ] 🔴 Search by championship name
|
||||
- [ ] 🔴 Filter by: discipline, location, status (open/upcoming/past)
|
||||
- [ ] 🟡 Sort by: date, popularity
|
||||
|
||||
### 2.5 Profile
|
||||
- [ ] 🔴 View/edit: name, city, instagram, disciplines, experience
|
||||
- [ ] 🔴 "My Championships" list (past + active)
|
||||
- [ ] 🟢 Competition history
|
||||
|
||||
**Done when:** Can browse championships, view full details across all tabs, search/filter, see profile.
|
||||
|
||||
---
|
||||
|
||||
## Phase 3: Member App — Registration & Progress Tracker
|
||||
**Time: ~4-5 hours**
|
||||
|
||||
### 3.1 Registration Flow
|
||||
- [ ] 🔴 Choose discipline → level → style → solo/duet/group
|
||||
- [ ] 🔴 Create `registration` record in DB
|
||||
- [ ] 🔴 Show 10-step progress checklist
|
||||
|
||||
### 3.2 Progress Steps (per championship)
|
||||
- [ ] 🔴 Step 1: Review rules — mark done when user opens Rules tab
|
||||
- [ ] 🔴 Step 2: Select category — saved from registration
|
||||
- [ ] 🔴 Step 3: Record video — manual toggle ("I've recorded my video")
|
||||
- [ ] 🔴 Step 4: Submit video form — manual toggle or link to Google Form
|
||||
- [ ] 🔴 Step 5: Pay video fee — upload receipt screenshot
|
||||
- [ ] 🔴 Step 6: Wait for results — shows "pending" until org decides
|
||||
- [ ] 🔴 Step 7: Results — auto-updates when org passes/fails
|
||||
- [ ] 🔴 Step 8: Pay championship fee — upload receipt (only if passed)
|
||||
- [ ] 🔴 Step 9: Submit "About Me" — manual toggle or link
|
||||
- [ ] 🔴 Step 10: Confirm insurance — upload document
|
||||
|
||||
### 3.3 Receipt & Document Upload
|
||||
- [ ] 🔴 Camera / gallery picker for receipt photos
|
||||
- [ ] 🔴 Upload to Supabase Storage
|
||||
- [ ] 🔴 Show upload status (pending org confirmation)
|
||||
|
||||
### 3.4 Notifications
|
||||
- [ ] 🔴 In-app notification feed (bell icon + unread count)
|
||||
- [ ] 🔴 Notification types: category changed, payment confirmed, results, deadline reminder, announcement
|
||||
- [ ] 🟡 Push notifications via Expo Notifications
|
||||
- [ ] 🟢 Notification preferences (toggle on/off)
|
||||
|
||||
**Done when:** Can register for a championship, track all 10 steps, upload receipts, receive notifications.
|
||||
|
||||
---
|
||||
|
||||
## Phase 4: Org App — Dashboard & Championship Management
|
||||
**Time: ~5-6 hours**
|
||||
|
||||
### 4.1 Org Dashboard
|
||||
- [ ] 🔴 Championship cards: name, dates, status badge, member count, progress bar (if draft)
|
||||
- [ ] 🔴 "+" button → Quick Create (name, date, location → creates draft)
|
||||
- [ ] 🔴 Tap card → championship detail
|
||||
|
||||
### 4.2 Championship Detail (tabbed, configurable)
|
||||
- [ ] 🔴 Overview tab: setup progress checklist, event info (editable), stats (if live)
|
||||
- [ ] 🔴 Categories tab: add/remove levels, add/remove styles → "Mark as Done"
|
||||
- [ ] 🔴 Fees tab: video selection + solo/duet/group fees → "Mark as Done"
|
||||
- [ ] 🔴 Rules tab: general rules + costume rules + scoring criteria + penalties → "Mark as Done"
|
||||
- [ ] 🔴 Judges tab: add judge profiles (name, instagram, bio) → "Mark as Done"
|
||||
- [ ] 🔴 "Go Live" button — appears when all sections are done
|
||||
- [ ] 🔴 If org is verified → status = `live` (auto-approved)
|
||||
- [ ] 🔴 If org is unverified → status = `pending_approval` (needs admin)
|
||||
|
||||
### 4.3 Members Tab (only for live championships)
|
||||
- [ ] 🔴 Member list with search + filters (All, Receipts, Videos, Passed)
|
||||
- [ ] 🔴 Member card: name, instagram, level, style, status badge, progress
|
||||
- [ ] 🔴 Tap member → member detail
|
||||
|
||||
### 4.4 Member Detail
|
||||
- [ ] 🔴 Profile info, registration details
|
||||
- [ ] 🔴 Edit level (picker + "member will be notified" warning)
|
||||
- [ ] 🔴 Edit style (picker + notification)
|
||||
- [ ] 🔴 Video section: view link + Pass/Fail buttons
|
||||
- [ ] 🔴 Payment section: view receipt + Confirm button
|
||||
- [ ] 🔴 "Send Notification" button
|
||||
|
||||
### 4.5 Results Tab
|
||||
- [ ] 🔴 Pending review list with Pass/Fail buttons per member
|
||||
- [ ] 🔴 Decided list (passed/failed)
|
||||
- [ ] 🔴 "Publish Results" button → notifies all members
|
||||
|
||||
### 4.6 Org Settings
|
||||
- [ ] 🔴 Edit org profile (name, instagram)
|
||||
- [ ] 🔴 Notification preferences (toggles)
|
||||
- [ ] 🟡 Connected accounts (Instagram, Gmail, Telegram)
|
||||
|
||||
**Done when:** Org can create championship, configure all tabs, go live, manage members, pass/fail videos, publish results.
|
||||
|
||||
---
|
||||
|
||||
## Phase 5: Admin Panel (Web)
|
||||
**Time: ~3-4 hours**
|
||||
|
||||
### 5.1 Dashboard
|
||||
- [ ] 🔴 Platform stats: orgs count, live champs, total users
|
||||
- [ ] 🔴 "Needs Attention" section: pending orgs, pending champs (from unverified orgs)
|
||||
- [ ] 🔴 Platform health: revenue, blocked users
|
||||
- [ ] 🔴 Recent activity log
|
||||
|
||||
### 5.2 Organizations Management
|
||||
- [ ] 🔴 List with search + filters (Active, Pending, Blocked)
|
||||
- [ ] 🔴 Org detail: profile, championships list, approval policy
|
||||
- [ ] 🔴 Actions: Approve / Reject, Block / Unblock, Verify, Delete
|
||||
|
||||
### 5.3 Championships Management
|
||||
- [ ] 🔴 List with search + filters (Live, Awaiting Review, Draft, Blocked)
|
||||
- [ ] 🔴 Champ detail: stats, approval policy indicator
|
||||
- [ ] 🔴 Actions: Approve / Reject (for unverified orgs), Suspend, Reinstate, Delete
|
||||
|
||||
### 5.4 Users Management
|
||||
- [ ] 🔴 List with search + filters (Active, Warned, Blocked, Org Admins)
|
||||
- [ ] 🔴 User detail: profile, role, championships joined
|
||||
- [ ] 🔴 Actions: Warn, Block / Unblock, Delete
|
||||
|
||||
**Done when:** Admin can approve/reject orgs, review championships from unverified orgs, manage users.
|
||||
|
||||
---
|
||||
|
||||
## Phase 6: Connecting It All
|
||||
**Time: ~3-4 hours**
|
||||
|
||||
### 6.1 Real-Time Sync
|
||||
- [ ] 🔴 Supabase Realtime: members see status updates instantly (pass/fail, payment confirmed)
|
||||
- [ ] 🔴 Org dashboard updates when new member registers
|
||||
- [ ] 🟡 Admin panel live counters
|
||||
|
||||
### 6.2 Notification Triggers
|
||||
- [ ] 🔴 Org passes/fails video → member gets notification
|
||||
- [ ] 🔴 Org confirms receipt → member gets notification
|
||||
- [ ] 🔴 Org changes member's level/style → member gets notification
|
||||
- [ ] 🔴 Org publishes results → all members get notification
|
||||
- [ ] 🟡 Auto deadline reminders (cron job: 7 days, 3 days, 1 day before)
|
||||
|
||||
### 6.3 Approval Flow
|
||||
- [ ] 🔴 Unverified org clicks "Go Live" → status = pending_approval
|
||||
- [ ] 🔴 Admin sees it in "Needs Attention"
|
||||
- [ ] 🔴 Admin approves → status = live, org gets notification
|
||||
- [ ] 🔴 Admin rejects → status = blocked, org gets notification with reason
|
||||
|
||||
### 6.4 File Uploads
|
||||
- [ ] 🔴 Receipt photo upload → Supabase Storage → org sees thumbnail in member detail
|
||||
- [ ] 🔴 Insurance doc upload → same flow
|
||||
- [ ] 🟢 Judge profile photos
|
||||
|
||||
**Done when:** All three apps talk to the same DB. Actions in one app reflect in others in real time.
|
||||
|
||||
---
|
||||
|
||||
## Phase 7: Polish & UX
|
||||
**Time: ~2-3 hours**
|
||||
|
||||
- [ ] 🟡 Loading states (skeletons, spinners)
|
||||
- [ ] 🟡 Empty states ("No championships yet", "No members match")
|
||||
- [ ] 🟡 Error handling (network errors, failed uploads, toast messages)
|
||||
- [ ] 🟡 Pull-to-refresh on lists
|
||||
- [ ] 🟡 Haptic feedback on key actions (pass/fail, payment confirm)
|
||||
- [ ] 🟡 Dark theme consistency across all screens
|
||||
- [ ] 🟡 Animations: tab transitions, card press, progress bar fill
|
||||
- [ ] 🟢 Onboarding walkthrough (first-time user tips)
|
||||
- [ ] 🟢 Swipe gestures on member cards (swipe right = pass, left = fail)
|
||||
|
||||
**Done when:** App feels smooth and professional. No raw errors shown to users.
|
||||
|
||||
---
|
||||
|
||||
## Phase 8: Integrations (Post-MVP)
|
||||
**Time: varies**
|
||||
|
||||
### 8.1 Instagram Parsing
|
||||
- [ ] 🟢 Apify Instagram scraper setup
|
||||
- [ ] 🟢 Claude API: extract structured data from post captions
|
||||
- [ ] 🟢 OCR (Google Vision): parse results from photo posts
|
||||
- [ ] 🟢 "Import from Instagram" button in org's championship creation
|
||||
|
||||
### 8.2 Gmail Integration
|
||||
- [ ] 🟢 Google OAuth for members
|
||||
- [ ] 🟢 Detect Google Forms confirmation emails → auto-mark steps
|
||||
|
||||
### 8.3 Payments
|
||||
- [ ] 🟢 In-app payment tracking
|
||||
- [ ] 🟢 Replace receipt uploads with direct payment
|
||||
|
||||
### 8.4 Multi-Language
|
||||
- [ ] 🟢 i18n setup (Russian + English)
|
||||
|
||||
---
|
||||
|
||||
## Phase 9: Testing & Launch
|
||||
**Time: ~2-3 hours**
|
||||
|
||||
- [ ] 🔴 Test full flow: member registers → org reviews → admin monitors
|
||||
- [ ] 🔴 Test approval flow: unverified org → pending → admin approves → live
|
||||
- [ ] 🔴 Test notifications: level change, payment confirm, results
|
||||
- [ ] 🔴 Test on real device (iOS + Android via Expo Go)
|
||||
- [ ] 🟡 Test edge cases: empty championships, blocked orgs, duplicate registrations
|
||||
- [ ] 🟡 Performance check: list with 50+ members, 10+ championships
|
||||
- [ ] 🟡 Expo build: `eas build` for iOS/Android
|
||||
- [ ] 🟢 TestFlight / Google Play internal testing
|
||||
- [ ] 🟢 Admin panel deploy (Vercel / Netlify)
|
||||
|
||||
**Done when:** All three roles can complete their full flow without bugs.
|
||||
|
||||
---
|
||||
|
||||
## Quick Reference: What Goes Where
|
||||
|
||||
| Feature | Member App | Org App | Admin Panel |
|
||||
|---|:---:|:---:|:---:|
|
||||
| Browse championships | ✅ | — | ✅ (view all) |
|
||||
| Register for championship | ✅ | — | — |
|
||||
| Progress tracker | ✅ | — | — |
|
||||
| Create/edit championship | — | ✅ | ✅ (edit/delete) |
|
||||
| Review members | — | ✅ | ✅ (view) |
|
||||
| Pass/Fail videos | — | ✅ | — |
|
||||
| Confirm payments | — | ✅ | — |
|
||||
| Approve orgs & champs | — | — | ✅ |
|
||||
| Block/warn users | — | — | ✅ |
|
||||
| Notifications | ✅ (receive) | ✅ (send) | — |
|
||||
|
||||
---
|
||||
|
||||
## Priority Order (if short on time)
|
||||
|
||||
If you need to ship fast, do these phases in order and stop when you have enough:
|
||||
|
||||
1. **Phase 0 + 1** — Foundation (can't skip)
|
||||
2. **Phase 2** — Member app core (users need to see something)
|
||||
3. **Phase 4** — Org app (orgs need to create championships)
|
||||
4. **Phase 3** — Registration flow (connects member ↔ org)
|
||||
5. **Phase 6** — Wire it together
|
||||
6. **Phase 5** — Admin panel (can manage via Supabase dashboard temporarily)
|
||||
7. **Phase 7** — Polish
|
||||
8. **Phase 8** — Integrations (post-launch)
|
||||
371
dancechamp-claude-code/docs/SCREENS.md
Normal file
371
dancechamp-claude-code/docs/SCREENS.md
Normal file
@@ -0,0 +1,371 @@
|
||||
# DanceChamp — Screen Reference
|
||||
|
||||
## Member App Screens
|
||||
|
||||
### Navigation: Bottom Tabs
|
||||
`Home` | `My Champs` | `Search` | `Profile`
|
||||
|
||||
---
|
||||
|
||||
### M1: Home
|
||||
**Purpose:** Dashboard for the dancer
|
||||
|
||||
**Elements:**
|
||||
- Header: "DanceChamp" title + bell icon (🔔) with unread count badge
|
||||
- "Your Active" section: cards for championships they're registered in, showing progress bar (e.g. "Step 5/10")
|
||||
- "Upcoming Championships" section: browseable cards for live championships
|
||||
- Each card: championship name, org name, dates, location, status badge, accent color bar
|
||||
|
||||
**Data:** `championships` (status = 'live') + `registrations` (for current user)
|
||||
|
||||
**Navigation:**
|
||||
- Tap card → M5 (Championship Detail)
|
||||
- Tap bell → M7 (Notifications)
|
||||
|
||||
---
|
||||
|
||||
### M2: My Champs
|
||||
**Purpose:** All championships user is registered for
|
||||
|
||||
**Elements:**
|
||||
- Tabs: Active | Past
|
||||
- Championship cards with progress indicator
|
||||
- Status per card: "Step 3/10 — Pay video fee" or "✅ Registered" or "❌ Failed"
|
||||
|
||||
**Data:** `registrations` joined with `championships`
|
||||
|
||||
**Navigation:**
|
||||
- Tap card → M6 (Progress Tracker)
|
||||
|
||||
---
|
||||
|
||||
### M3: Search
|
||||
**Purpose:** Discover championships
|
||||
|
||||
**Elements:**
|
||||
- Search bar (text input)
|
||||
- Filter chips: All, Pole Exotic, Pole Art, Registration Open, Upcoming
|
||||
- Championship result cards
|
||||
|
||||
**Data:** `championships` + `organizations` (for org name)
|
||||
|
||||
**Navigation:**
|
||||
- Tap card → M5 (Championship Detail)
|
||||
|
||||
---
|
||||
|
||||
### M4: Profile
|
||||
**Purpose:** User profile and settings
|
||||
|
||||
**Elements:**
|
||||
- Avatar, name, instagram handle, city
|
||||
- Dance profile: disciplines, experience years
|
||||
- "My Championships" summary (X active, Y completed)
|
||||
- Settings list: Edit Profile, Notification Preferences, Connected Accounts, Help, Log Out
|
||||
|
||||
**Data:** `users` (current user)
|
||||
|
||||
---
|
||||
|
||||
### M5: Championship Detail
|
||||
**Purpose:** Full championship info — 5 tabs
|
||||
|
||||
**Header:** Championship name, org name, back button
|
||||
**Tabs:** Overview | Categories | Rules | Fees | Judges
|
||||
|
||||
#### Tab: Overview
|
||||
- Event info: date, location, venue, registration period (start → end)
|
||||
- Stats: members registered, spots left (if limited)
|
||||
- "Register" button (if registration open and user not registered)
|
||||
- If already registered: shows current progress step
|
||||
|
||||
#### Tab: Categories
|
||||
- Disciplines list, each with levels
|
||||
- Styles list
|
||||
- If registered: user's selected level/style highlighted
|
||||
|
||||
#### Tab: Rules
|
||||
- General rules (expandable list)
|
||||
- Costume rules
|
||||
- Scoring criteria: name + max score (0–10)
|
||||
- Penalties: name + deduction / DQ
|
||||
|
||||
#### Tab: Fees
|
||||
- Video selection fee
|
||||
- Championship fees: solo, duet, group
|
||||
- Refund note
|
||||
|
||||
#### Tab: Judges
|
||||
- Judge profile cards: photo/emoji, name, instagram link, bio
|
||||
|
||||
**Data:** Full championship with all related tables
|
||||
|
||||
**Navigation:**
|
||||
- "Register" → M6 (starts registration flow, then shows Progress Tracker)
|
||||
|
||||
---
|
||||
|
||||
### M6: Progress Tracker
|
||||
**Purpose:** 10-step registration checklist for a specific championship
|
||||
|
||||
**Header:** Championship name, back button
|
||||
|
||||
**Elements:**
|
||||
- Vertical step list (1–10)
|
||||
- Each step: number, icon, title, status (locked/available/done/failed)
|
||||
- Current step expanded with action area:
|
||||
- Step 3 "Record Video": toggle "I've recorded my video"
|
||||
- Step 5 "Pay Video Fee": upload receipt button, status after upload
|
||||
- Step 7 "Results": shows PASS ✅ / FAIL ❌ / ⏳ Pending
|
||||
- Step 10 "Insurance": upload document button
|
||||
- Progress bar at top: X/10 completed
|
||||
|
||||
**Data:** `registrations` (single record for this user + championship)
|
||||
|
||||
**Actions:** Update step fields in `registrations` table
|
||||
|
||||
---
|
||||
|
||||
### M7: Notifications
|
||||
**Purpose:** In-app notification feed
|
||||
|
||||
**Header:** "Notifications" + "Read all" button
|
||||
|
||||
**Elements:**
|
||||
- Notification cards: icon, type badge, championship name, message, time ago
|
||||
- Unread: colored left border + accent background tint + dot indicator
|
||||
- Tap: marks as read
|
||||
|
||||
**Types:** 🔄 Category Changed, ✅ Payment Confirmed, 🏆 Results, ⏰ Deadline, 📋 Registered, 📢 Announcement
|
||||
|
||||
**Data:** `notifications` (for current user, ordered by created_at desc)
|
||||
|
||||
---
|
||||
|
||||
## Org App Screens
|
||||
|
||||
### Navigation: Bottom Tabs
|
||||
`Dashboard` | `Settings`
|
||||
|
||||
---
|
||||
|
||||
### O1: Dashboard
|
||||
**Purpose:** Overview of all org's championships
|
||||
|
||||
**Elements:**
|
||||
- Header: "Dashboard" + org name + org logo
|
||||
- "New Championship" card (accent gradient, "+" icon)
|
||||
- Championship cards: name, dates, location, status badge (LIVE / SETUP 3/5 / AWAITING REVIEW)
|
||||
- For drafts: readiness bar + section checklist (info ✓, categories ○, fees ○, etc.)
|
||||
- For live: mini stats (Members, Passed, Pending)
|
||||
|
||||
**Data:** `championships` (where org_id = current org) + `registrations` (counts)
|
||||
|
||||
**Navigation:**
|
||||
- Tap "New Championship" → O2 (Quick Create)
|
||||
- Tap championship card → O3 (Championship Detail)
|
||||
|
||||
---
|
||||
|
||||
### O2: Quick Create
|
||||
**Purpose:** Minimal form to create a draft championship
|
||||
|
||||
**Elements:**
|
||||
- Header: "New Championship" + back button
|
||||
- 3 inputs: Championship Name, Event Date, Location
|
||||
- Info card: "Your championship will be created as a draft. Configure details later."
|
||||
- "✨ Create Draft" button (disabled until name filled)
|
||||
|
||||
**Action:** Creates championship with status = 'draft', navigates to O3
|
||||
|
||||
---
|
||||
|
||||
### O3: Championship Detail (Tabbed)
|
||||
**Purpose:** Configure and manage a championship
|
||||
|
||||
**Header:** Championship name, subtitle, back button, "🚀 Go Live" (if all config done)
|
||||
|
||||
**Tabs (with config status dots):**
|
||||
`Overview` | `Categories` (🟢/🟡) | `Fees` (🟢/🟡) | `Rules` (🟢/🟡) | `Judges` (🟢/🟡)
|
||||
|
||||
For live championships, add: | `Members (N)` | `Results`
|
||||
|
||||
#### Tab: Overview
|
||||
- **If draft:** Setup Progress checklist (5 items with checkmarks, tap incomplete → jumps to tab)
|
||||
- **If all done:** "🚀 Open Registration" button (or "Go Live")
|
||||
- Event Info card: inline edit (✎ Edit / ✕ Close toggle)
|
||||
- Editable fields: name, subtitle, event date, location, venue
|
||||
- Registration period: Opens (date) + Closes (date), side by side
|
||||
- Warning: "⚠️ Registration close date must be before event date"
|
||||
- **If live:** Stats boxes (Members, Passed, Failed, Pending) + "⚡ Needs Action" (receipts to review, videos to review)
|
||||
|
||||
#### Tab: Categories
|
||||
- Levels: tag editor (add/remove levels)
|
||||
- Styles: tag editor (add/remove styles)
|
||||
- "✓ Mark Categories as Done" button (shown when at least 1 level + 1 style)
|
||||
|
||||
#### Tab: Fees
|
||||
- Video Selection Fee input
|
||||
- Championship Fees: Solo, Duet (pp), Group (pp) inputs
|
||||
- "✓ Mark Fees as Done" button (shown when video fee filled)
|
||||
|
||||
#### Tab: Rules
|
||||
- General Rules: tag editor
|
||||
- Costume Rules: tag editor
|
||||
- Scoring Criteria (0–10): list + tag editor to add new
|
||||
- Penalties: list with colored values (-2 yellow, DQ red) + tag editor
|
||||
- "✓ Mark Rules as Done" button (shown when at least 1 rule)
|
||||
|
||||
#### Tab: Judges
|
||||
- Judge profile cards: emoji avatar, name, instagram, bio, × to remove
|
||||
- "Add Judge" form: name, instagram, bio inputs + "+ Add Judge" button
|
||||
- "✓ Mark Judges as Done" button (shown when at least 1 judge)
|
||||
|
||||
#### Tab: Members (live only)
|
||||
- Search bar
|
||||
- Filter chips: All (N), 📸 Receipts (N), 🎬 Videos (N), ✅ Passed (N)
|
||||
- Member cards: name, instagram, level, style, city tags, status badge
|
||||
- Tap member → O4 (Member Detail)
|
||||
|
||||
#### Tab: Results (live only)
|
||||
- Stat boxes: Pending, Passed, Failed
|
||||
- Pending review cards: member name/level + "🎥 View" + Pass/Fail buttons
|
||||
- Decided list: member name + badge
|
||||
- "📢 Publish Results" button
|
||||
|
||||
---
|
||||
|
||||
### O4: Member Detail
|
||||
**Purpose:** View/edit a single member's registration
|
||||
|
||||
**Header:** Member name, championship + instagram, back button
|
||||
|
||||
**Elements:**
|
||||
- Profile card: avatar, name, instagram, city, status badge
|
||||
- Registration section:
|
||||
- Discipline (read-only)
|
||||
- Type: solo/duet/group (read-only)
|
||||
- **Level:** value + "✎ Edit" button → expands picker with ⚠️ "Member will be notified"
|
||||
- **Style:** value + "✎ Edit" button → expands picker with ⚠️ warning
|
||||
- Video section: link display + Pass/Fail buttons (if pending) or status badge
|
||||
- Payment section: fee amount, receipt status, "📸 Confirm" button (if receipt uploaded)
|
||||
- "🔔 Send Notification" button
|
||||
|
||||
**Actions:** Update `registrations` fields + create `notifications` record
|
||||
|
||||
---
|
||||
|
||||
### O5: Org Settings
|
||||
**Purpose:** Organization profile and preferences
|
||||
|
||||
**Elements:**
|
||||
- Org profile: logo, name, instagram (editable inline when "Edit Organization Profile" tapped)
|
||||
- Menu items:
|
||||
- ✎ Edit Organization Profile → inline form (name + instagram) replaces menu
|
||||
- 🔔 Notification Preferences → sub-screen with toggle switches
|
||||
- 🔗 Connected Accounts → sub-screen (Instagram ✅, Gmail ✅, Telegram ❌)
|
||||
- ❓ Help & Support
|
||||
- 🚪 Log Out (red text)
|
||||
|
||||
---
|
||||
|
||||
## Admin Panel Screens (Web)
|
||||
|
||||
### Navigation: Bottom Tabs (mobile-style for prototype, sidebar for production)
|
||||
`Overview` | `Orgs` | `Champs` | `Users`
|
||||
|
||||
---
|
||||
|
||||
### A1: Overview (Dashboard)
|
||||
**Purpose:** Platform health at a glance
|
||||
|
||||
**Elements:**
|
||||
- Header: "Admin Panel" + platform name + version
|
||||
- Stat boxes: Active Orgs, Live Champs, Total Users
|
||||
- "⚡ Needs Attention" card (yellow tint):
|
||||
- 🏢 Organizations awaiting approval (count) → tap goes to A2
|
||||
- 🏆 Champs awaiting approval from unverified orgs (count) → tap goes to A4
|
||||
- Platform Health table: total revenue, active/total orgs, blocked users, avg members/champ
|
||||
- Recent Activity log: action + target (colored by type) + date + actor
|
||||
|
||||
---
|
||||
|
||||
### A2: Organizations List
|
||||
**Purpose:** All organizations on the platform
|
||||
|
||||
**Elements:**
|
||||
- Search bar
|
||||
- Filter chips: All (N), ✅ Active (N), ⏳ Pending (N), 🚫 Blocked (N)
|
||||
- Org cards: logo, name, instagram, status badge, city, champs count, members count
|
||||
- Tap → A3 (Org Detail)
|
||||
|
||||
---
|
||||
|
||||
### A3: Organization Detail
|
||||
**Purpose:** Review and manage a single org
|
||||
|
||||
**Elements:**
|
||||
- Profile card: logo, name, instagram, email, city, status badge
|
||||
- Details table: Joined, Championships, Total members, Verified (✅/❌)
|
||||
- Approval Policy card:
|
||||
- Verified: "🛡️ Verified — Auto-approve events" (green tint)
|
||||
- Unverified: "⏳ Unverified — Events need manual approval" (yellow tint)
|
||||
- Championships list (belonging to this org)
|
||||
- Block reason card (if blocked, red tint)
|
||||
- Actions:
|
||||
- Pending: [Approve ✅] [Reject ❌]
|
||||
- Active: [Block 🚫] + [Verify 🛡️] (if not verified)
|
||||
- Blocked: [Unblock ✅]
|
||||
- Always: [Delete 🗑️]
|
||||
|
||||
---
|
||||
|
||||
### A4: Championships List
|
||||
**Purpose:** All championships across all orgs
|
||||
|
||||
**Elements:**
|
||||
- Search bar
|
||||
- Filter chips: All (N), 🟢 Live (N), ⏳ Awaiting (N), 📝 Draft (N), 🚫 Blocked (N)
|
||||
- Champ cards: name, "by Org Name 🛡️" (shield if verified), status badge, dates, location, members count
|
||||
- Tap → A5 (Champ Detail)
|
||||
|
||||
---
|
||||
|
||||
### A5: Championship Detail
|
||||
**Purpose:** Review and manage a single championship
|
||||
|
||||
**Elements:**
|
||||
- Profile card: trophy emoji, name, org name, dates, location, status badge
|
||||
- Stats table: Members, Passed, Pending, Revenue
|
||||
- Approval Policy card: explains why auto-approved or needs review
|
||||
- Actions:
|
||||
- Pending approval: [Approve ✅] [Reject ❌]
|
||||
- Live: [Suspend ⏸️]
|
||||
- Blocked: [Reinstate ✅]
|
||||
- Always: [Delete 🗑️]
|
||||
|
||||
---
|
||||
|
||||
### A6: Users List
|
||||
**Purpose:** All platform users
|
||||
|
||||
**Elements:**
|
||||
- Search bar (name, @handle, email)
|
||||
- Filter chips: All (N), ✅ Active (N), ⚠️ Warned (N), 🚫 Blocked (N), 🏢 Org Admins (N)
|
||||
- User cards: avatar (👤 or 🏢 for org admins), name, instagram, city, status badge
|
||||
- Tap → A7 (User Detail)
|
||||
|
||||
---
|
||||
|
||||
### A7: User Detail
|
||||
**Purpose:** Review and manage a single user
|
||||
|
||||
**Elements:**
|
||||
- Profile card: avatar, name, instagram, email, status badge
|
||||
- Info table: City, Joined, Championships joined, Role (Member or Org Admin + org name)
|
||||
- Warning/Block reason card (if applicable, orange or red tint)
|
||||
- Actions:
|
||||
- Active: [Warn ⚠️] [Block 🚫]
|
||||
- Warned: [Remove Warning ✅] [Block 🚫]
|
||||
- Blocked: [Unblock ✅]
|
||||
- Always: [Delete 🗑️]
|
||||
267
dancechamp-claude-code/docs/SPEC.md
Normal file
267
dancechamp-claude-code/docs/SPEC.md
Normal file
@@ -0,0 +1,267 @@
|
||||
# DanceChamp — Technical Specification
|
||||
|
||||
## 1. Overview
|
||||
|
||||
A mobile platform for pole dance championships. Dancers discover events and track registration. Organizers create and manage championships. Platform admin oversees everything.
|
||||
|
||||
### The Problem
|
||||
Championship info is scattered across Instagram posts, Telegram chats, and Google Docs. Dancers miss deadlines, lose track of multi-step registration, and can't verify submissions went through. Organizers manually manage everything via spreadsheets and DMs.
|
||||
|
||||
### The Solution
|
||||
One app with three roles:
|
||||
- **Members** browse championships, register, track 10-step progress
|
||||
- **Organizations** create championships, configure rules/fees/categories, review videos, confirm payments
|
||||
- **Admin** approves organizations, reviews championships from unverified orgs, manages users
|
||||
|
||||
### Real-World Reference: "Zero Gravity"
|
||||
International Pole Exotic Championship, Minsk, Belarus. Two disciplines (Exotic Pole Dance + Pole Art), 6+ levels per discipline, two-stage payment (video selection fee + championship fee), video selection with pass/fail, detailed judging criteria (6 categories, 0–10), strict costume/equipment rules, insurance required.
|
||||
|
||||
---
|
||||
|
||||
## 2. Roles & Permissions
|
||||
|
||||
### Member (Dancer)
|
||||
**Access:** Mobile app (member view)
|
||||
- Browse & search championships
|
||||
- View full details (rules, fees, categories, judges)
|
||||
- Register for championships
|
||||
- Track 10-step progress per championship
|
||||
- Upload receipts & documents
|
||||
- Receive notifications (results, deadline reminders, announcements)
|
||||
- View past participation history
|
||||
|
||||
**Cannot:** Create/edit championships, see other members' data, access org/admin features
|
||||
|
||||
### Organization
|
||||
**Access:** Mobile app (org view)
|
||||
- Create championships (quick create → configure tabs)
|
||||
- Manage disciplines, levels, styles, fees, rules, scoring, judges
|
||||
- View & manage registered members per championship
|
||||
- Review videos (pass/fail)
|
||||
- Confirm receipt payments
|
||||
- Edit member's level/style (triggers notification)
|
||||
- Publish results
|
||||
- Send announcements
|
||||
|
||||
**Cannot:** See other orgs' data, access admin features
|
||||
|
||||
### Admin
|
||||
**Access:** Web admin panel
|
||||
- View all orgs, championships, users
|
||||
- Approve/reject pending organizations
|
||||
- Approve/reject championships from unverified orgs
|
||||
- Block/unblock orgs and users
|
||||
- Warn users
|
||||
- Verify organizations (changes approval policy)
|
||||
- Delete any entity
|
||||
- View platform stats and activity logs
|
||||
|
||||
---
|
||||
|
||||
## 3. Championship Lifecycle
|
||||
|
||||
```
|
||||
[Org: Quick Create]
|
||||
name + date + location → status: DRAFT
|
||||
│
|
||||
▼
|
||||
[Org: Configure Tabs]
|
||||
Categories ✓ → Fees ✓ → Rules ✓ → Judges ✓
|
||||
(any order, mark each as done)
|
||||
│
|
||||
▼
|
||||
[Org: "Go Live"]
|
||||
│
|
||||
├── Org is VERIFIED (🛡️)
|
||||
│ → status: LIVE (auto-approved)
|
||||
│ → visible to members immediately
|
||||
│
|
||||
└── Org is UNVERIFIED
|
||||
→ status: PENDING_APPROVAL
|
||||
→ admin sees in "Needs Attention"
|
||||
→ admin approves → LIVE
|
||||
→ admin rejects → BLOCKED
|
||||
│
|
||||
▼
|
||||
[LIVE — Registration Open]
|
||||
reg_start ≤ today ≤ reg_end
|
||||
Members can register
|
||||
│
|
||||
▼
|
||||
[Registration Closed]
|
||||
today > reg_end
|
||||
No new registrations
|
||||
│
|
||||
▼
|
||||
[Event Day]
|
||||
event_date
|
||||
│
|
||||
▼
|
||||
[COMPLETED]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. Championship Data Structure
|
||||
|
||||
Each championship contains:
|
||||
|
||||
### Event Info
|
||||
- Name, subtitle
|
||||
- Event date (when it happens)
|
||||
- Registration period: start date → end date (must be before event date)
|
||||
- Location (city, country)
|
||||
- Venue name
|
||||
- Accent color (for branding)
|
||||
|
||||
### Categories (configurable tab)
|
||||
- Disciplines: e.g. "Exotic Pole Dance", "Pole Art"
|
||||
- Levels per discipline: e.g. "Beginners", "Amateur", "Semi-Pro", "Profi", "Elite", "Duets & Groups"
|
||||
- Styles: e.g. "Classic", "Flow", "Theater"
|
||||
|
||||
### Fees (configurable tab)
|
||||
- Video selection fee (e.g. "50 BYN / 1,500 RUB")
|
||||
- Championship fees by type:
|
||||
- Solo (e.g. "280 BYN / 7,500 RUB")
|
||||
- Duet per person (e.g. "210 BYN / 5,800 RUB pp")
|
||||
- Group per person (e.g. "190 BYN / 4,500 RUB pp")
|
||||
- Refund note (typically non-refundable)
|
||||
|
||||
### Rules (configurable tab)
|
||||
- General rules (list of text items)
|
||||
- Costume rules (list of text items)
|
||||
- Scoring criteria: name + max score (e.g. "Artistry: 0–10", "Technique: 0–10")
|
||||
- Penalties: name + value (e.g. "Fall: -2", "Exposure: DQ")
|
||||
|
||||
### Judges (configurable tab)
|
||||
- Judge profiles: name, Instagram handle, bio/description
|
||||
- These are people, not scoring criteria
|
||||
|
||||
### Members (only when LIVE)
|
||||
- Registered members scoped to this championship
|
||||
- Each with: discipline, level, style, type (solo/duet/group), progress steps, video URL, payment status, pass/fail result
|
||||
|
||||
---
|
||||
|
||||
## 5. Member Registration Flow (10 Steps)
|
||||
|
||||
```
|
||||
Step 1: 📋 Review Rules → Auto (tracked when user opens Rules tab)
|
||||
Step 2: 🎯 Select Category → Auto (saved from registration picker)
|
||||
Step 3: 🎬 Record Video → Manual toggle ("I've recorded my video")
|
||||
Step 4: 📝 Submit Video Form → Manual / link to Google Form
|
||||
Step 5: 💳 Pay Video Fee → Upload receipt screenshot → Org confirms
|
||||
Step 6: ⏳ Wait for Results → Pending until org decides
|
||||
Step 7: 🏆 Results → Auto-updates on pass/fail
|
||||
├── FAIL → Flow ends
|
||||
└── PASS → Continue ▼
|
||||
Step 8: 💰 Pay Championship Fee → Upload receipt → Org confirms
|
||||
Step 9: 📄 Submit "About Me" → Manual / link to form
|
||||
Step 10: 🛡️ Confirm Insurance → Upload document → Org confirms
|
||||
└── ✅ REGISTERED!
|
||||
```
|
||||
|
||||
### Detection Methods
|
||||
| Step | Method |
|
||||
|---|---|
|
||||
| Review rules | Auto — tracked on tab open |
|
||||
| Select category | Auto — saved from picker |
|
||||
| Record video | Manual toggle |
|
||||
| Submit video form | Manual or Gmail auto-detect (future) |
|
||||
| Pay video fee | Receipt upload → org confirms |
|
||||
| Results | Auto — org pass/fail updates member |
|
||||
| Pay championship fee | Receipt upload → org confirms |
|
||||
| About Me form | Manual or Gmail auto-detect (future) |
|
||||
| Insurance | Document upload → org confirms |
|
||||
|
||||
---
|
||||
|
||||
## 6. Notifications
|
||||
|
||||
### Types
|
||||
| Type | Trigger | Direction |
|
||||
|---|---|---|
|
||||
| 🔄 Category Changed | Org changes member's level/style | Org → Member |
|
||||
| ✅ Payment Confirmed | Org confirms uploaded receipt | Org → Member |
|
||||
| 🏆 Results | Org passes/fails video selection | Org → Member |
|
||||
| ⏰ Deadline Reminder | Auto (7d, 3d, 1d before reg_end) | System → Member |
|
||||
| 📋 Registration Confirmed | All 10 steps complete | System → Member |
|
||||
| 📢 Announcement | Org sends broadcast | Org → All Members |
|
||||
|
||||
### Delivery
|
||||
- In-app: Bell icon with unread count, notification feed
|
||||
- Push: Expo Notifications for critical updates
|
||||
- Email: Future enhancement
|
||||
|
||||
---
|
||||
|
||||
## 7. Org App — Configurable Tabs
|
||||
|
||||
When org creates a championship, it starts as DRAFT with 5 configurable sections:
|
||||
|
||||
| Section | Tab | What to configure | Mark as Done when |
|
||||
|---|---|---|---|
|
||||
| Info | Overview | Name, dates, location, venue, reg period | Has date + location |
|
||||
| Categories | Categories | Levels + styles | At least 1 level + 1 style |
|
||||
| Fees | Fees | Video fee + championship fees | Video fee filled |
|
||||
| Rules | Rules | General rules, costume rules, scoring criteria, penalties | At least 1 rule |
|
||||
| Judges | Judges | Judge profiles (name, instagram, bio) | At least 1 judge |
|
||||
|
||||
Setup progress shown on Overview tab as checklist. Each section shows green dot (done) or yellow dot (pending) in tab bar. "Go Live" button appears when all 5 sections are ✓.
|
||||
|
||||
---
|
||||
|
||||
## 8. Admin — Approval Flow
|
||||
|
||||
### Organization Approval
|
||||
- New org registers → status: `pending`
|
||||
- Admin reviews → Approve (status: `active`) or Reject (status: `blocked`)
|
||||
- Admin can also **verify** an active org (🛡️ badge)
|
||||
|
||||
### Championship Approval
|
||||
- Depends on org's verification status:
|
||||
- **Verified org** → Go Live = instant `live`
|
||||
- **Unverified org** → Go Live = `pending_approval` → admin reviews
|
||||
|
||||
### Admin Actions
|
||||
| Entity | Actions |
|
||||
|---|---|
|
||||
| Organization | Approve, Reject, Block, Unblock, Verify, Delete |
|
||||
| Championship | Approve, Reject, Suspend, Reinstate, Delete |
|
||||
| User | Warn, Block, Unblock, Delete |
|
||||
|
||||
---
|
||||
|
||||
## 9. Org Settings
|
||||
|
||||
- **Edit Organization Profile**: name, instagram (inline edit form)
|
||||
- **Notification Preferences**: toggles for push, email, registration alerts, payment alerts, deadline reminders
|
||||
- **Connected Accounts**: Instagram (connected/not), Gmail, Telegram
|
||||
- Help & Support
|
||||
- Log Out
|
||||
|
||||
---
|
||||
|
||||
## 10. Search & Discovery (Member)
|
||||
|
||||
Members can find championships by:
|
||||
- Text search (name, org name)
|
||||
- Filters: discipline, location, status (Registration Open / Upcoming / Past)
|
||||
- Sort: by date, by popularity
|
||||
|
||||
Championship cards show: name, org, dates, location, status badge, member count.
|
||||
|
||||
---
|
||||
|
||||
## 11. Future Features (Out of MVP Scope)
|
||||
|
||||
- Instagram parsing: auto-import championship data from org's posts
|
||||
- Gmail integration: auto-detect Google Forms confirmations
|
||||
- OCR results detection: parse results from Instagram photo posts
|
||||
- In-app payments: replace receipt uploads
|
||||
- In-app forms: replace Google Forms links
|
||||
- Telegram monitoring: detect results from Telegram chats
|
||||
- Category recommendation engine
|
||||
- Calendar sync (export to phone calendar)
|
||||
- Social features (see which friends are registered)
|
||||
- Multi-language (Russian + English)
|
||||
Reference in New Issue
Block a user