From 5b7260de84544f203f48a505d069f672811070a3 Mon Sep 17 00:00:00 2001 From: Dianaka123 Date: Thu, 26 Feb 2026 20:41:49 +0300 Subject: [PATCH] Refactor: move useAuth from store/ into hooks/ MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit store/ folder removed β€” single file doesn't justify its own folder. Co-Authored-By: Claude Sonnet 4.6 --- LINEAR-ROADMAP.md | 348 ++++++++++++++++++ web/src/app/(app)/admin/page.tsx | 2 +- web/src/app/(app)/championships/[id]/page.tsx | 2 +- web/src/app/(app)/profile/page.tsx | 2 +- web/src/app/providers.tsx | 2 +- web/src/components/Navbar.tsx | 2 +- web/src/{store => hooks}/useAuth.ts | 0 web/src/hooks/useLoginForm.ts | 2 +- web/src/hooks/useRegisterForm.ts | 2 +- 9 files changed, 355 insertions(+), 7 deletions(-) create mode 100644 LINEAR-ROADMAP.md rename web/src/{store => hooks}/useAuth.ts (100%) diff --git a/LINEAR-ROADMAP.md b/LINEAR-ROADMAP.md new file mode 100644 index 0000000..03b4ccb --- /dev/null +++ b/LINEAR-ROADMAP.md @@ -0,0 +1,348 @@ +# DanceChamp β€” Linear Roadmap + +> Development plan for the Pole Dance Championships app. +> Tech: FastAPI + SQLAlchemy (async) + Expo (React Native) + PostgreSQL. +> Copy each section as a **Project** in Linear, each checkbox as an **Issue**. + +--- + +## Status Legend + +- βœ… = Already implemented (current MVP) +- πŸ”΄ = Blocker (must have) +- 🟑 = Important +- 🟒 = Nice to have + +--- + +## Project 1: Database & Models Expansion + +> Expand the database schema from 4 tables to the full 12+ table schema required by the spec. + +- βœ… `users` table (id, email, password, full_name, phone, role, status) +- βœ… `refresh_tokens` table (JWT refresh rotation) +- βœ… `championships` table (basic: title, description, location, event_date, status) +- βœ… `registrations` table (basic: user_id, championship_id, category, level, status) +- [ ] πŸ”΄ Add `organizations` table β€” id, user_id (FK), name, instagram, email, city, logo_url, verified, status, block_reason +- [ ] πŸ”΄ Expand `championships` β€” add subtitle, reg_start, reg_end, venue, accent_color, source (manual/instagram), instagram_media_id, image_url, raw_caption_text +- [ ] πŸ”΄ Add `disciplines` table β€” id, championship_id (FK), name, levels (JSON array) +- [ ] πŸ”΄ Add `styles` table β€” id, championship_id (FK), name +- [ ] πŸ”΄ Add `fees` table β€” id, championship_id (FK), video_selection, solo, duet, group, refund_note +- [ ] πŸ”΄ Add `rules` table β€” id, championship_id (FK), section (general/costume/scoring/penalty), name, value +- [ ] πŸ”΄ Add `judges` table β€” id, championship_id (FK), name, instagram, bio, photo_url +- [ ] πŸ”΄ Expand `registrations` β€” add discipline_id, style, participation_type (solo/duet/group), current_step (1-10), video_url, receipt_url, insurance_url, passed (null/true/false) +- [ ] πŸ”΄ Add `participant_lists` table β€” id, championship_id (FK), published_by, is_published, published_at, notes +- [ ] πŸ”΄ Add `notifications` table β€” id, user_id (FK), championship_id, type, title, message, read, created_at +- [ ] 🟑 Add `activity_logs` table β€” id, actor_id, action, target_type, target_id, details (JSON), created_at +- [ ] 🟑 Add `sync_state` table β€” key, value, updated_at (for Instagram sync tracking) +- [ ] πŸ”΄ Create Alembic migration for all new tables +- [ ] πŸ”΄ Update Pydantic schemas for all new/expanded models +- [ ] 🟑 Add seed data for new tables (disciplines, fees, rules, judges) + +--- + +## Project 2: Organization System + +> Organizations are separate entities from users. An organizer user creates/manages an organization. + +- [ ] πŸ”΄ Organization CRUD API β€” POST /api/v1/organizations, GET, PATCH +- [ ] πŸ”΄ Organization approval flow β€” pending β†’ admin approves β†’ active +- [ ] πŸ”΄ Organization verification system β€” verified orgs auto-approve championships +- [ ] πŸ”΄ Link championships to organizations (not directly to users) +- [ ] πŸ”΄ `get_org_owner` dependency β€” checks user owns the organization +- [ ] 🟑 Organization profile with logo upload +- [ ] 🟑 Mobile: Organization setup screen (name, instagram, city) +- [ ] 🟑 Mobile: Organization dashboard (list own championships) + +--- + +## Project 3: Championship Configuration (Org Side) + +> Organizers configure championships through tabbed interface with 5 sections. + +- βœ… Basic championship CRUD (title, description, location, date) +- [ ] πŸ”΄ Quick Create β€” minimal 3-field form (name, date, location) β†’ creates draft +- [ ] πŸ”΄ Championship setup progress tracking β€” 5 sections (info, categories, fees, rules, judges) +- [ ] πŸ”΄ Categories API β€” CRUD for disciplines + levels per championship +- [ ] πŸ”΄ Styles API β€” CRUD for styles per championship +- [ ] πŸ”΄ Fees API β€” set video_selection, solo, duet, group fees +- [ ] πŸ”΄ Rules API β€” add/remove general rules, costume rules, scoring criteria, penalties +- [ ] πŸ”΄ Judges API β€” CRUD for judge profiles per championship +- [ ] πŸ”΄ "Go Live" endpoint β€” validates all sections done, sets status +- [ ] πŸ”΄ Registration period β€” reg_start, reg_end dates; auto-close when expired +- [ ] πŸ”΄ Mobile: Championship config tabs (Overview, Categories, Fees, Rules, Judges) +- [ ] πŸ”΄ Mobile: Tag editor component (for rules, levels, styles) +- [ ] πŸ”΄ Mobile: "Mark as Done" per section + progress checklist +- [ ] 🟑 Mobile: Inline editing for championship info fields +- [ ] 🟑 Championship status guard β€” prevent edits on live championships + +--- + +## Project 4: Enhanced Member Experience + +> Rich championship browsing with 5-tab detail view, search, and filtering. + +- βœ… Championship list screen +- βœ… Basic championship detail screen +- [ ] πŸ”΄ Championship Detail β€” 5 tabs: Overview, Categories, Rules, Fees, Judges +- [ ] πŸ”΄ Overview tab β€” event info, registration period, member count, "Register" button +- [ ] πŸ”΄ Categories tab β€” show disciplines with levels and styles +- [ ] πŸ”΄ Rules tab β€” general rules, costume rules, scoring criteria, penalties +- [ ] πŸ”΄ Fees tab β€” video selection + championship fees breakdown +- [ ] πŸ”΄ Judges tab β€” judge profiles with photo, instagram, bio +- [ ] πŸ”΄ Search championships β€” full-text search by name/org +- [ ] πŸ”΄ Filter championships β€” by discipline, location, status (open/upcoming/past) +- [ ] 🟑 Sort championships β€” by date, by popularity +- [ ] 🟑 Home screen dashboard β€” active registrations with progress, upcoming championships +- [ ] 🟑 Pull-to-refresh on all list screens +- [ ] 🟒 Championship image/poster display + +--- + +## Project 5: 10-Step Registration Progress Tracker + +> Members track their championship registration through 10 steps with uploads and auto-updates. + +- βœ… Basic registration (submit application) +- βœ… Registration status display (submitted/accepted/rejected) +- [ ] πŸ”΄ 10-step progress model β€” current_step field on registration +- [ ] πŸ”΄ Step 1: Review rules β€” auto-mark when user opens Rules tab +- [ ] πŸ”΄ Step 2: Select category β€” saved from registration form (discipline + level + style) +- [ ] πŸ”΄ Step 3: Record video β€” manual toggle +- [ ] πŸ”΄ Step 4: Submit video form β€” manual toggle or external link +- [ ] πŸ”΄ Step 5: Pay video fee β€” upload receipt screenshot +- [ ] πŸ”΄ Step 6: Wait for results β€” pending state until org decides +- [ ] πŸ”΄ Step 7: Results β€” auto-updates when org passes/fails video +- [ ] πŸ”΄ Step 8: Pay championship fee β€” upload receipt (only if passed) +- [ ] πŸ”΄ Step 9: Submit "About Me" β€” manual toggle or external link +- [ ] πŸ”΄ Step 10: Confirm insurance β€” upload insurance document +- [ ] πŸ”΄ Mobile: Vertical step list with icons, status (locked/available/done/failed) +- [ ] πŸ”΄ Mobile: Progress bar (X/10 completed) +- [ ] πŸ”΄ Mobile: Step detail expansion with action area +- [ ] 🟑 Mobile: "My Championships" with Active/Past tabs and progress bars + +--- + +## Project 6: File Upload System + +> Receipt photos, insurance documents, judge photos, org logos. + +- [ ] πŸ”΄ File upload API endpoint β€” POST /api/v1/uploads +- [ ] πŸ”΄ Local file storage (dev) with configurable S3/Supabase Storage (prod) +- [ ] πŸ”΄ Receipt photo upload β€” camera/gallery picker β†’ upload β†’ pending confirmation +- [ ] πŸ”΄ Insurance document upload β€” same flow +- [ ] πŸ”΄ Serve uploaded files β€” GET /api/v1/uploads/{filename} +- [ ] 🟑 Judge profile photo upload +- [ ] 🟑 Organization logo upload +- [ ] 🟑 Image compression before upload (mobile side) +- [ ] 🟑 File type validation (images only for receipts, PDF/images for insurance) + +--- + +## Project 7: Organizer β€” Member Management + +> Organizers review members, pass/fail videos, confirm payments, publish results. + +- [ ] πŸ”΄ Members list per championship β€” GET /api/v1/championships/{id}/members +- [ ] πŸ”΄ Member detail β€” registration info, video link, receipt, progress +- [ ] πŸ”΄ Edit member's level/style (with notification trigger) +- [ ] πŸ”΄ Video review β€” Pass/Fail buttons per member +- [ ] πŸ”΄ Payment confirmation β€” Confirm receipt button +- [ ] πŸ”΄ Results tab β€” pending/passed/failed counts, "Publish Results" button +- [ ] πŸ”΄ Mobile: Member list with search + filter chips (All, Receipts, Videos, Passed) +- [ ] πŸ”΄ Mobile: Member detail screen with action buttons +- [ ] πŸ”΄ Mobile: Results screen with Pass/Fail workflow +- [ ] 🟑 Filter members by status (pending receipt, pending video, passed, failed) +- [ ] 🟑 Bulk actions β€” pass/fail multiple members at once + +--- + +## Project 8: Notification System + +> In-app notifications + push notifications via Expo Push Service. + +- [ ] πŸ”΄ Notification model + CRUD API +- [ ] πŸ”΄ In-app notification feed β€” GET /api/v1/notifications +- [ ] πŸ”΄ Mark notification as read β€” PATCH /api/v1/notifications/{id}/read +- [ ] πŸ”΄ Mark all as read β€” POST /api/v1/notifications/read-all +- [ ] πŸ”΄ Notification triggers: + - Video passed/failed β†’ member notification + - Payment confirmed β†’ member notification + - Level/style changed β†’ member notification + - Results published β†’ all registrants notified + - User approved β†’ welcome notification + - Championship goes live β†’ all platform notification +- [ ] πŸ”΄ Expo Push Token registration β€” PATCH /api/v1/users/me/push-token +- [ ] πŸ”΄ Expo Push Service integration β€” send push on notification create +- [ ] πŸ”΄ Mobile: Bell icon with unread count badge +- [ ] πŸ”΄ Mobile: Notification feed screen (cards with icon, type, message, timestamp) +- [ ] 🟑 Push notification preferences (toggles for each type) +- [ ] 🟑 Deadline reminders β€” auto-send 7d, 3d, 1d before registration closes +- [ ] 🟒 Email notifications (secondary channel) + +--- + +## Project 9: Instagram Integration + +> Automatically sync championship announcements from organizer's Instagram Business account. + +- [ ] πŸ”΄ Instagram Graph API client β€” fetch recent posts from Business/Creator account +- [ ] πŸ”΄ Caption parser β€” extract title (first line), location, date from caption text +- [ ] πŸ”΄ Date parser β€” support Russian ("15 ΠΌΠ°Ρ€Ρ‚Π° 2025") and English ("March 15 2025") + numeric (15.03.2025) +- [ ] πŸ”΄ Location parser β€” detect "ΠœΠ΅ΡΡ‚ΠΎ:", "АдрСс:", "Location:", "Venue:", "Π—Π°Π»:" +- [ ] πŸ”΄ Championship upsert β€” use instagram_media_id as dedup key, create as draft +- [ ] πŸ”΄ APScheduler polling β€” run every 30 min via FastAPI lifespan event +- [ ] πŸ”΄ Sync state tracking β€” store last sync timestamp, only process new posts +- [ ] πŸ”΄ Long-lived token management β€” store in .env, auto-refresh before 60-day expiry +- [ ] πŸ”΄ Token refresh scheduler β€” weekly job to call Graph API token exchange +- [ ] 🟑 Image sync β€” save media_url as championship image +- [ ] 🟑 Manual import button β€” "Import from Instagram" in org dashboard +- [ ] 🟑 Error handling β€” failed parsing saves raw caption for manual review +- [ ] 🟒 Multiple Instagram accounts support (one per org) + +--- + +## Project 10: Enhanced Admin Panel + +> Full admin capabilities: org management, championship moderation, user management. + +- βœ… Basic admin panel (approve/reject pending users) +- [ ] πŸ”΄ Admin dashboard β€” stats (active orgs, live champs, total users) +- [ ] πŸ”΄ "Needs Attention" section β€” pending orgs + pending championships +- [ ] πŸ”΄ Organization management β€” list, detail, approve/reject/block/unblock/verify +- [ ] πŸ”΄ Championship moderation β€” list, detail, approve/reject (from unverified orgs), suspend/reinstate +- [ ] πŸ”΄ Enhanced user management β€” warn, block/unblock, view activity +- [ ] πŸ”΄ Activity log β€” recent actions across the platform +- [ ] 🟑 Mobile: Admin dashboard with stat cards +- [ ] 🟑 Mobile: Organization list + detail screens +- [ ] 🟑 Mobile: Championship moderation screens +- [ ] 🟒 Web admin panel (React + Vite, same API) + +--- + +## Project 11: Auth & Security Hardening + +> Production-ready auth, rate limiting, CORS, input validation. + +- βœ… JWT access + refresh token rotation +- βœ… bcrypt password hashing +- βœ… Role-based route protection (dependency chain) +- [ ] πŸ”΄ Rate limiting on auth endpoints (slowapi) +- [ ] πŸ”΄ CORS configuration (whitelist mobile app + admin panel origins) +- [ ] πŸ”΄ Input validation β€” Pydantic strict mode, length limits, email format +- [ ] πŸ”΄ Password strength requirements (min 8 chars, mixed case, number) +- [ ] 🟑 Account lockout after N failed login attempts +- [ ] 🟑 HTTPS enforcement in production +- [ ] 🟑 Audit logging β€” log all admin actions +- [ ] 🟒 Google OAuth login (alternative to email/password) +- [ ] 🟒 Two-factor authentication + +--- + +## Project 12: PostgreSQL & Production Infrastructure + +> Migrate from SQLite to PostgreSQL, containerize with Docker. + +- [ ] πŸ”΄ PostgreSQL support in database.py (async via asyncpg) +- [ ] πŸ”΄ docker-compose.yml β€” PostgreSQL 16 + FastAPI containers +- [ ] πŸ”΄ Dockerfile for backend +- [ ] πŸ”΄ Environment config β€” .env.example with all required variables +- [ ] πŸ”΄ Alembic migration compatibility β€” ensure all migrations work on PostgreSQL +- [ ] πŸ”΄ Production ASGI server β€” gunicorn + uvicorn workers +- [ ] 🟑 Health check endpoint improvements +- [ ] 🟑 Structured logging (JSON format for production) +- [ ] 🟑 Database connection pooling +- [ ] 🟒 CI/CD pipeline (GitHub Actions β€” lint, test, build) +- [ ] 🟒 Deployment to cloud (VPS / Railway / Fly.io) + +--- + +## Project 13: Testing + +> Comprehensive test coverage for backend and mobile. + +- [ ] πŸ”΄ pytest setup with async fixtures (httpx + AsyncClient) +- [ ] πŸ”΄ Auth tests β€” register, login, refresh, logout, me, role checks +- [ ] πŸ”΄ Championship tests β€” CRUD, status transitions, permission checks +- [ ] πŸ”΄ Registration tests β€” submit, duplicate guard, closed registration guard +- [ ] πŸ”΄ Admin tests β€” approve/reject users, org management +- [ ] πŸ”΄ Instagram parser tests β€” various caption formats, edge cases +- [ ] πŸ”΄ Notification tests β€” trigger conditions, push delivery +- [ ] 🟑 Integration tests β€” full flows (register β†’ apply β†’ review β†’ publish) +- [ ] 🟑 Mobile: TypeScript strict mode + path aliases (@api/*, @store/*, @screens/*) +- [ ] 🟒 E2E tests (Detox or Maestro for mobile) +- [ ] 🟒 Load testing (locust or k6) + +--- + +## Project 14: Design System & UI Polish + +> Consistent dark luxury theme across all screens, smooth animations. + +- [ ] 🟑 Design tokens β€” colors, typography, spacing constants file +- [ ] 🟑 Typography β€” Playfair Display (headings) + DM Sans (body) + JetBrains Mono (badges) +- [ ] 🟑 Component library β€” Button, Card, Badge, TabBar, TagEditor, LoadingOverlay +- [ ] 🟑 Dark luxury theme β€” dark backgrounds (#08070D), pink/purple accents +- [ ] 🟑 Loading skeletons on all list screens +- [ ] 🟑 Empty states β€” custom illustrations per screen +- [ ] 🟑 Error states β€” friendly messages + retry buttons +- [ ] 🟑 Animations β€” tab transitions, card press, progress bar fill +- [ ] 🟒 Haptic feedback on key actions +- [ ] 🟒 Swipe gestures on member cards (pass/fail) +- [ ] 🟒 Onboarding walkthrough for first-time users + +--- + +## Project 15: Internationalization (Post-MVP) + +> Russian + English language support. + +- [ ] 🟒 i18n setup (react-i18next or similar) +- [ ] 🟒 Extract all strings to translation files +- [ ] 🟒 Russian translations +- [ ] 🟒 Language switcher in profile settings +- [ ] 🟒 Date/time formatting per locale + +--- + +## Recommended Priority Order + +If you need to ship fast, work in this order: + +| Priority | Project | Why | +|:---:|---|---| +| 1 | Project 1: Database Expansion | Foundation for everything else | +| 2 | Project 2: Organization System | Core business entity | +| 3 | Project 3: Championship Config | Orgs need to create proper championships | +| 4 | Project 4: Enhanced Member UX | Members need to see championship details | +| 5 | Project 5: 10-Step Progress | Core differentiator β€” step-by-step registration | +| 6 | Project 6: File Uploads | Required for receipts + insurance steps | +| 7 | Project 7: Member Management | Orgs need to review and manage members | +| 8 | Project 8: Notifications | Users need to know what's happening | +| 9 | Project 9: Instagram Sync | Automate championship creation | +| 10 | Project 10: Enhanced Admin | Full platform control | +| 11 | Project 11: Security | Production hardening | +| 12 | Project 12: PostgreSQL + Docker | Production deployment | +| 13 | Project 13: Testing | Quality assurance | +| 14 | Project 14: Design System | Visual polish | +| 15 | Project 15: i18n | Post-launch | + +--- + +## Current State Summary + +### What's Built (MVP) +- FastAPI backend with SQLite (async) +- JWT auth with refresh token rotation +- 4 tables: users, refresh_tokens, championships, registrations +- Member auto-approve, organizer requires admin approval +- Championship list + basic detail +- Registration form + "My Registrations" with championship info +- Admin panel (approve/reject pending users) +- Profile screen with role badges +- Tab navigation with Ionicons + +### What's Next (Sprint 1 recommended) +1. Database expansion (Project 1) +2. Organization system (Project 2) +3. Championship configuration tabs (Project 3) diff --git a/web/src/app/(app)/admin/page.tsx b/web/src/app/(app)/admin/page.tsx index c39eb12..45f0f41 100644 --- a/web/src/app/(app)/admin/page.tsx +++ b/web/src/app/(app)/admin/page.tsx @@ -2,7 +2,7 @@ import { useState } from "react"; import { useUsers, useUserActions } from "@/hooks/useUsers"; -import { useAuth } from "@/store/useAuth"; +import { useAuth } from "@/hooks/useAuth"; import { UserCard } from "@/components/UserCard"; import { useRouter } from "next/navigation"; import { useEffect } from "react"; diff --git a/web/src/app/(app)/championships/[id]/page.tsx b/web/src/app/(app)/championships/[id]/page.tsx index e51abb6..d746995 100644 --- a/web/src/app/(app)/championships/[id]/page.tsx +++ b/web/src/app/(app)/championships/[id]/page.tsx @@ -4,7 +4,7 @@ import { use } from "react"; import { useChampionship } from "@/hooks/useChampionship"; import { useMyRegistrations } from "@/hooks/useMyRegistrations"; import { useRegisterForChampionship } from "@/hooks/useRegisterForChampionship"; -import { useAuth } from "@/store/useAuth"; +import { useAuth } from "@/hooks/useAuth"; import { RegistrationTimeline } from "@/components/RegistrationTimeline"; import { StatusBadge } from "@/components/StatusBadge"; import { Button } from "@/components/ui/button"; diff --git a/web/src/app/(app)/profile/page.tsx b/web/src/app/(app)/profile/page.tsx index 3e6e67c..b68d96b 100644 --- a/web/src/app/(app)/profile/page.tsx +++ b/web/src/app/(app)/profile/page.tsx @@ -1,7 +1,7 @@ "use client"; import { useRouter } from "next/navigation"; -import { useAuth } from "@/store/useAuth"; +import { useAuth } from "@/hooks/useAuth"; import { Avatar, AvatarFallback } from "@/components/ui/avatar"; import { Button } from "@/components/ui/button"; import { Badge } from "@/components/ui/badge"; diff --git a/web/src/app/providers.tsx b/web/src/app/providers.tsx index 386a28e..12877ec 100644 --- a/web/src/app/providers.tsx +++ b/web/src/app/providers.tsx @@ -2,7 +2,7 @@ import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { useState, useEffect } from "react"; -import { useAuth } from "@/store/useAuth"; +import { useAuth } from "@/hooks/useAuth"; function AuthInitializer({ children }: { children: React.ReactNode }) { const initialize = useAuth((s) => s.initialize); diff --git a/web/src/components/Navbar.tsx b/web/src/components/Navbar.tsx index 378afca..72015c2 100644 --- a/web/src/components/Navbar.tsx +++ b/web/src/components/Navbar.tsx @@ -2,7 +2,7 @@ import Link from "next/link"; import { useRouter } from "next/navigation"; -import { useAuth } from "@/store/useAuth"; +import { useAuth } from "@/hooks/useAuth"; import { Avatar, AvatarFallback } from "@/components/ui/avatar"; import { DropdownMenu, diff --git a/web/src/store/useAuth.ts b/web/src/hooks/useAuth.ts similarity index 100% rename from web/src/store/useAuth.ts rename to web/src/hooks/useAuth.ts diff --git a/web/src/hooks/useLoginForm.ts b/web/src/hooks/useLoginForm.ts index 8a24bb7..5a35f7b 100644 --- a/web/src/hooks/useLoginForm.ts +++ b/web/src/hooks/useLoginForm.ts @@ -2,7 +2,7 @@ import { useState } from "react"; import { useRouter } from "next/navigation"; -import { useAuth } from "@/store/useAuth"; +import { useAuth } from "@/hooks/useAuth"; export function useLoginForm() { const router = useRouter(); diff --git a/web/src/hooks/useRegisterForm.ts b/web/src/hooks/useRegisterForm.ts index 9479077..36b1baa 100644 --- a/web/src/hooks/useRegisterForm.ts +++ b/web/src/hooks/useRegisterForm.ts @@ -2,7 +2,7 @@ import { useState } from "react"; import { useRouter } from "next/navigation"; -import { useAuth } from "@/store/useAuth"; +import { useAuth } from "@/hooks/useAuth"; export function useRegisterForm() { const router = useRouter();