Files
PoleDanceApp/dance-champ-app.md
Dianaka123 9eb68695e9 Clear project — starting fresh from spec
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-24 14:36:47 +03:00

311 lines
10 KiB
Markdown

# Pole Dance Championships App — Technical Document
## 1. Project Overview
### Problem
Pole dance championships lack dedicated tools for organizers and participants. Organizers rely on Instagram posts to announce events, and registrations are managed via Google Forms — leading to:
- **No visibility** — Participants can't track their registration status in one place.
- **Scattered info** — Dates, rules, and results are buried across Instagram posts.
- **No access control** — Anyone can register; organizers need a way to accept or reject participants.
### Solution
A **members-only mobile app** for pole dance championship participants and organizers.
- New users register and wait for admin approval before accessing the app.
- Championship announcements are auto-imported from Instagram (Graph API).
- Members browse championships, submit registrations, and track their status.
- Organizers accept, reject, or waitlist participants and publish the final participant list.
- Push notifications alert members when results are published.
---
## 2. User Roles
| Role | Access |
|---|---|
| **Member** | Browse championships, register, track own registration status |
| **Organizer** | All of the above + manage registrations, publish participant lists |
| **Admin** | All of the above + approve/reject new member accounts |
New accounts start as `pending` and must be approved by an admin before they can use the app.
---
## 3. Tech Stack
| Layer | Technology |
|---|---|
| **Mobile** | React Native + TypeScript (Expo managed workflow) |
| **Navigation** | React Navigation v6 |
| **Server state** | TanStack React Query v5 |
| **Client state** | Zustand |
| **Backend** | FastAPI (Python, async) |
| **ORM** | SQLAlchemy 2.0 async |
| **Database** | SQLite (local dev) / PostgreSQL (production) |
| **Migrations** | Alembic |
| **Auth** | JWT — access tokens (15 min) + refresh tokens (7 days, rotation-based) |
| **Instagram sync** | Instagram Graph API, polled every 30 min via APScheduler |
| **Push notifications** | Expo Push API (routes to FCM + APNs) |
| **Token storage** | expo-secure-store (with in-memory cache for reliability) |
---
## 4. Architecture
```
┌─────────────────────┐ ┌──────────────────────────────┐
│ React Native App │◄──────►│ FastAPI Backend │
│ (Expo Go / APK) │ HTTPS │ /api/v1/... │
└─────────────────────┘ │ │
│ ┌──────────────────────┐ │
│ │ APScheduler │ │
│ │ - Instagram poll/30m│ │
│ │ - Token refresh/7d │ │
│ └──────────────────────┘ │
│ │
│ ┌──────────────────────┐ │
│ │ SQLite / PostgreSQL │ │
│ └──────────────────────┘ │
└──────────────────────────────┘
```
---
## 5. Data Model
### User
```
id UUID (PK)
email String (unique)
full_name String
phone String (nullable)
hashed_password String
role Enum: member | organizer | admin
status Enum: pending | approved | rejected
expo_push_token String (nullable)
created_at DateTime
updated_at DateTime
```
### Championship
```
id UUID (PK)
title String
description String (nullable)
location String (nullable)
event_date DateTime (nullable)
registration_open_at DateTime (nullable)
registration_close_at DateTime (nullable)
status Enum: draft | open | closed | completed
source Enum: manual | instagram
instagram_media_id String (nullable, unique)
image_url String (nullable)
raw_caption_text Text (nullable)
created_at DateTime
updated_at DateTime
```
### Registration
```
id UUID (PK)
championship_id UUID (FK → Championship)
user_id UUID (FK → User)
category String (nullable)
level String (nullable)
notes Text (nullable)
status Enum: submitted | accepted | rejected | waitlisted
submitted_at DateTime
decided_at DateTime (nullable)
```
### ParticipantList
```
id UUID (PK)
championship_id UUID (FK → Championship, unique)
published_by UUID (FK → User)
is_published Boolean
published_at DateTime (nullable)
notes Text (nullable)
```
### RefreshToken
```
id UUID (PK)
user_id UUID (FK → User)
token_hash String (SHA-256, unique)
expires_at DateTime
revoked Boolean
created_at DateTime
```
### NotificationLog
```
id UUID (PK)
user_id UUID (FK → User)
championship_id UUID (FK → Championship, nullable)
title String
body String
sent_at DateTime
status String
```
---
## 6. API Endpoints
### Auth
| Method | Path | Description |
|---|---|---|
| POST | `/api/v1/auth/register` | Register new account (status=pending) |
| POST | `/api/v1/auth/login` | Login, get access + refresh tokens |
| POST | `/api/v1/auth/refresh` | Refresh access token |
| POST | `/api/v1/auth/logout` | Revoke refresh token |
| GET | `/api/v1/auth/me` | Get current user |
### Championships
| Method | Path | Access |
|---|---|---|
| GET | `/api/v1/championships` | Approved members |
| GET | `/api/v1/championships/{id}` | Approved members |
| POST | `/api/v1/championships` | Organizer+ |
| PATCH | `/api/v1/championships/{id}` | Organizer+ |
| DELETE | `/api/v1/championships/{id}` | Admin |
### Registrations
| Method | Path | Access |
|---|---|---|
| POST | `/api/v1/championships/{id}/register` | Approved member |
| GET | `/api/v1/championships/{id}/registrations` | Organizer+ |
| PATCH | `/api/v1/registrations/{id}/status` | Organizer+ |
| GET | `/api/v1/registrations/mine` | Member (own only) |
| DELETE | `/api/v1/registrations/{id}` | Member (own, before decision) |
### Participant Lists
| Method | Path | Access |
|---|---|---|
| GET | `/api/v1/championships/{id}/participant-list` | Approved member |
| PUT | `/api/v1/championships/{id}/participant-list` | Organizer+ |
| POST | `/api/v1/championships/{id}/participant-list/publish` | Organizer+ |
| POST | `/api/v1/championships/{id}/participant-list/unpublish` | Organizer+ |
### Users (Admin)
| Method | Path | Description |
|---|---|---|
| GET | `/api/v1/users` | List all users |
| PATCH | `/api/v1/users/{id}/status` | Approve/reject member |
| PATCH | `/api/v1/users/me/push-token` | Register push token |
---
## 7. Mobile Screens
### Auth Flow
- **LoginScreen** — Email + password login
- **RegisterScreen** — Full name, phone, email, password
- **PendingApprovalScreen** — Shown after register until admin approves
### App (approved members)
- **ChampionshipListScreen** — All championships with status badges
- **ChampionshipDetailScreen** — Full info + registration button
- **RegistrationFormScreen** — Category, level, notes
- **ProfileScreen** — User info, logout
### Navigation
```
No user ──► AuthStack (Login / Register)
Pending ──► PendingApprovalScreen
Approved ──► AppStack (Championships / Profile)
```
---
## 8. Instagram Integration
### How It Works
1. Admin configures `INSTAGRAM_USER_ID` and `INSTAGRAM_ACCESS_TOKEN` in `.env`.
2. APScheduler polls the Instagram Graph API every 30 minutes.
3. New media posts are parsed: title (first line of caption), location (`Место:` / `Location:` prefix), dates (regex for RU + EN date formats).
4. New championships are created with `status=draft` for admin review.
5. Long-lived tokens are refreshed weekly automatically.
### Parsing Logic
- **Title** — First non-empty line of the caption.
- **Location** — Line starting with `Место:` or `Location:`.
- **Date** — Regex matches formats like `15 апреля 2026`, `April 15, 2026`, `15.04.2026`.
- **Deduplication** — Championships are matched by `instagram_media_id`.
---
## 9. Local Development Setup
### Prerequisites
- Python 3.11+
- Node.js 18+
- Expo Go app on phone
### Backend
```bat
cd backend
python -m venv .venv
.venv\Scripts\pip install -r requirements.txt
alembic upgrade head
python scripts/create_admin.py # creates admin@poledance.app / Admin1234
uvicorn app.main:app --host 0.0.0.0 --port 8000 --reload
```
Or use: `start-backend.bat`
### Mobile
```bat
cd mobile
npm install
npx expo start --lan --clear
```
Or use: `start-mobile.bat`
### Environment (backend/.env)
```
DATABASE_URL=sqlite+aiosqlite:///./poledance.db
SECRET_KEY=dev-secret-key-change-before-production-32x
INSTAGRAM_USER_ID=
INSTAGRAM_ACCESS_TOKEN=
INSTAGRAM_POLL_INTERVAL=1800
```
### Environment (mobile/.env)
```
EXPO_PUBLIC_API_URL=http://<your-local-ip>:8000/api/v1
```
### Test Accounts
| Email | Password | Role | Status |
|---|---|---|---|
| admin@poledance.app | Admin1234 | admin | approved |
| anna@example.com | Member1234 | member | approved |
| maria@example.com | Member1234 | member | approved |
| elena@example.com | Member1234 | member | pending |
---
## 10. Known Limitations (Local Dev)
- **SQLite** is used instead of PostgreSQL — change `DATABASE_URL` for production.
- **Push notifications** don't work in Expo Go SDK 53 — requires a development build.
- **Instagram polling** requires a valid Business/Creator account token.
- Windows Firewall must allow inbound TCP on port 8000 for phone to reach the backend.
---
## 11. Future Features
- **Web admin panel** — Browser-based dashboard for organizers to manage registrations and championships.
- **In-app notifications feed** — History of received push notifications.
- **Calendar sync** — Export championship dates to phone calendar.
- **Production deployment** — Docker + PostgreSQL + nginx + SSL.
- **OCR / LLM parsing** — Better extraction of championship details from Instagram images and captions.
- **Multi-organizer** — Support multiple Instagram accounts for different championships.