PlayRise — UML Activity Diagrams

Complete activity diagrams for every use case  ·  Unauthenticated  ·  Player  ·  Club Owner

Unauthenticated User
Unauthenticated

1. App Entry & Auth Guard

Routing decision made on every launch based on session, profile flag, and club membership.

flowchart TD S([Start]) --> A A[App Launch] --> B[Load Auth State from Supabase] B --> C{Session exists?} C -->|No| D[Redirect to Login Screen] C -->|Yes| E[Fetch Profile Row and Club Members Admin Row] E --> F{is_club_owner = true OR admin in club_members?} F -->|No| G[Route to Player Tabs] F -->|Yes| H{onboarding_completed = true OR has existing club row?} H -->|No| I[Route to Club Onboarding Wizard] H -->|Yes| J[Route to Owner Tabs] D --> K([Login Screen]) G --> L([Ladders Tab]) I --> M([Onboarding Step 0]) J --> N([Members Tab]) style S fill:#4a9eff,stroke:#4a9eff,color:#fff style K fill:#1e2a3a,stroke:#4a9eff style L fill:#1e2a3a,stroke:#4a9eff style M fill:#1a2e1a,stroke:#4ade80 style N fill:#1a2e1a,stroke:#4ade80
Unauthenticated

2. Login

Existing user signs in. Routing branches to player tabs or owner tabs based on role.

flowchart TD S([Start]) --> A A[Open Login Screen] --> B[Enter Email and Password] B --> C[Tap Sign In] C --> D[supabase.auth.signInWithPassword] D --> E{Auth Error?} E -->|Yes| F[Display Error Alert] F --> B E -->|No| G[Fetch Profile and Club Members admin row in parallel] G --> H{is_club_owner OR admin in any club?} H -->|Player| I[Route to Player Tabs] H -->|Owner| J{onboarding done or has club?} J -->|No| K[Route to Onboarding] J -->|Yes| L[Route to Owner Tabs] I --> M([Ladders Tab]) K --> N([Onboarding Step 0]) L --> O([Members Tab]) style S fill:#4a9eff,stroke:#4a9eff,color:#fff style M fill:#1e2a3a,stroke:#4a9eff style N fill:#1a2e1a,stroke:#4ade80 style O fill:#1a2e1a,stroke:#4ade80
Unauthenticated

3. Sign Up — Player

New user registers as a player. Profile is created with is_club_owner = false.

flowchart TD S([Start]) --> A A[Open Sign Up Screen] --> B[Select Role: Player] B --> C[Enter Username] C --> D[Enter Full Name] D --> E[Enter Email] E --> F[Enter Password] F --> G[Tap Create Account] G --> H[supabase.auth.signUp] H --> I{Auth Error?} I -->|Yes| J[Show Error Alert] J --> C I -->|No| K[INSERT profiles row with is_club_owner = false] K --> L{Insert Error?} L -->|Yes| M[Show Error Alert] L -->|No| N[Auth state change fires layout routing effect] N --> O[Route to Player Tabs] O --> P([Ladders Tab]) style S fill:#4a9eff,stroke:#4a9eff,color:#fff style P fill:#1e2a3a,stroke:#4a9eff
Unauthenticated

4. Sign Up — Club Owner

New user registers as a club owner. Directed to onboarding wizard after account creation.

flowchart TD S([Start]) --> A A[Open Sign Up Screen] --> B[Select Role: Club Owner] B --> C[Enter Username, Full Name, Email, Password] C --> D[Tap Create Account] D --> E[supabase.auth.signUp] E --> F{Auth Error?} F -->|Yes| G[Show Error Alert] G --> C F -->|No| H[INSERT profiles row with is_club_owner = true] H --> I[Auth state change fires layout routing effect] I --> J{onboarding completed?} J -->|No| K[Route to Club Onboarding] K --> L([Step 0: Club Name and Description]) J -->|Yes| M([Owner Tabs]) style S fill:#4a9eff,stroke:#4a9eff,color:#fff style L fill:#1a2e1a,stroke:#4ade80 style M fill:#1a2e1a,stroke:#4ade80
Club Owner
Club Owner

5. Club Onboarding Wizard (6 Steps)

First-time setup wizard. Creates the club, courts, and marks the profile as onboarding complete.

flowchart TD S([Start]) --> A A([Step 0: Identity]) --> B[Enter Club Name - required, Enter Description - optional] B --> C[Tap Next] C --> D([Step 1: Sport Selection]) D --> E[Toggle sports on/off: Tennis, Padel, Squash, Badminton, Pickleball] E --> F{At least 1 sport selected?} F -->|No| E F -->|Yes| G[Tap Next] G --> H([Step 2: Courts]) H --> I[For each selected sport: Tap + to add court name, Tap x to remove] I --> J[Tap Next] J --> K([Step 3: Opening Hours]) K --> L[Per day: toggle Open or Closed] L --> M[Select opening time chip, Select closing time chip] M --> N[Tap Next] N --> O([Step 4: Location]) O --> P[Enter Address, City, Phone - all optional] P --> Q[Tap Next] Q --> R[INSERT clubs row with name, description, sports, opening_hours JSONB, phone, location] R --> T{Schema error: opening_hours or phone column not found?} T -->|Yes - Fallback| U[INSERT clubs row without new columns] T -->|No| V[INSERT courts rows for each court name per sport] U --> V V --> W[UPDATE profiles: is_club_owner = true, onboarding_completed = true] W --> X([Step 5: Done]) X --> Y[Display generated Invite Code] Y --> Z[Tap Go to Dashboard] Z --> END[Route to Owner Tabs] style S fill:#4ade80,stroke:#4ade80,color:#000 style A fill:#1a2e1a,stroke:#4ade80 style D fill:#1a2e1a,stroke:#4ade80 style H fill:#1a2e1a,stroke:#4ade80 style K fill:#1a2e1a,stroke:#4ade80 style O fill:#1a2e1a,stroke:#4ade80 style X fill:#1a2e1a,stroke:#4ade80
Club Owner

6. My Club Tab — Full Management

Owner views and manages their club: info, invite code, courts, opening hours, quick actions.

flowchart TD S([Start]) --> A A([My Club Tab]) --> B[Fetch admin club via club_members where role = admin] B --> C{Club found?} C -->|No| D[Show Empty State] C -->|Yes| E[Display Hero Card, Stats Strip, Invite Code, Accordion Sections] E --> F{User Action?} F -->|Share Invite Code| G[native Share dialog with invite code] G --> E F -->|Regenerate Code| H[Show Confirm Alert] H --> I[UPDATE clubs set invite_code to random string] I --> J[Refresh club data] J --> E F -->|Edit Club Info| K[Edit name, description, location, phone] K --> L[Tap Save Changes] L --> M[UPDATE clubs row] M --> N{Error?} N -->|Yes| O[Show Error Alert] N -->|No| P[Show Saved confirmation] O --> E P --> J F -->|View Opening Hours| Q[Read-only display of hours per weekday] Q --> E F -->|Add Court| R[Select sport tab, Type court name, Tap + button] R --> S2[INSERT courts row] S2 --> J F -->|Toggle Court Active| T[UPDATE courts is_active toggled] T --> J F -->|Delete Court| U[DELETE courts row] U --> J F -->|New Ladder| V[Navigate to ladder/create with club_id param] F -->|Admin Panel| W[Navigate to club/manage with club id param] F -->|Delete Club| X[Show Destructive Confirm Alert] X --> Y[DELETE clubs row] Y --> Z[Show Empty State] F -->|Sign Out| AA[supabase.auth.signOut, Clear profile state] AA --> AB([Login Screen]) style S fill:#4ade80,stroke:#4ade80,color:#000 style A fill:#1a2e1a,stroke:#4ade80 style AB fill:#1a2e1a,stroke:#4ade80
Club Owner

7. Create Ladder

Owner creates a new ladder league with sport, format, duration, and optional season dates.

flowchart TD S([Start]) --> A A([Create Ladder Screen]) --> B[Select Sport: Tennis, Padel, or Squash] B --> C[Enter Ladder Name - required] C --> D[Set Max Players: tap plus or minus in steps of 4, or keep Unlimited] D --> E[Select Round Duration chip: 1, 2, 3, or 4 weeks] E --> F[Select Match Format card: Best of 3, 1 Set, Match TB, or Custom] F --> G[Optionally pick Season Start and End dates via DatePickerField] G --> H[Preview card updates live with all selected options] H --> I[Tap Create Ladder] I --> J{Name filled?} J -->|No| K[Alert: Ladder name is required] K --> C J -->|Yes| L[INSERT ladders with name, sport, max_players, round_duration_days, match_format, season dates] L --> M{Schema error for match_format or round_duration_days columns?} M -->|Yes - Fallback| N[INSERT ladders with name, sport, season dates only] N --> O{Fallback Error?} O -->|Yes| P[Show Error Alert] O -->|No| Q[Alert: Created with migration note] M -->|No| R{Insert Error?} R -->|Yes| P R -->|No| T[Alert: Ladder Created!] Q --> U{User Choice} T --> U U -->|View Ladder| V[Navigate to ladder/id] U -->|Back| W[Navigate back to Owner Tabs] style S fill:#4ade80,stroke:#4ade80,color:#000 style A fill:#1a2e1a,stroke:#4ade80
Club Owner

8. Manage Ladder

Owner edits ladder settings, removes players, or resets all rankings.

flowchart TD S([Start]) --> A A([Manage Ladder Screen]) --> B[Fetch ladder row and all ladder_entries with profiles] B --> C[Display: name, sport, active toggle, season dates, player list with W/L/Pts] C --> D{User Action?} D -->|Toggle Active Switch| E[UPDATE ladders set is_active to toggled value] E --> F[Refresh state] F --> C D -->|Edit Name or Sport or Dates| G[Update local state fields] G --> H[Tap Save Changes] H --> I[UPDATE ladders row: name, sport, is_active, season_start, season_end] I --> J{Error?} J -->|Yes| K[Show Error Alert] J -->|No| L[Show Saved Alert] K --> C L --> F D -->|Remove Player| M[Show Confirm Alert] M --> N[DELETE ladder_entries where user_id and ladder_id match] N --> O[Refresh entries list] O --> C D -->|Reset Rankings| P[Show Confirm Alert] P --> Q[UPDATE all ladder_entries: position by joined_at order, wins = 0, losses = 0, points = 0] Q --> O D -->|Navigate Back| R[router.back or router.replace to Owner Tabs] style S fill:#4ade80,stroke:#4ade80,color:#000 style A fill:#1a2e1a,stroke:#4ade80
Club Owner

9. Manage Members

Owner views all club members, promotes/demotes roles, and removes members.

flowchart TD S([Start]) --> A A([Members Tab]) --> B[Fetch club IDs where user is admin in club_members] B --> C[Fetch all club_members rows with profile joins] C --> D[Display members grouped by club with role badges: admin or member] D --> E{User Action?} E -->|Pull to Refresh| F[Re-fetch all data] F --> D E -->|Toggle Role| G{Is selected member = self?} G -->|Yes| H[Block action: Cannot change own role] H --> D G -->|No| I[UPDATE club_members set role = admin or member] I --> J[Refresh member list] J --> D E -->|Remove Member| K{Is selected member = self?} K -->|Yes| L[Block action: Cannot remove self] L --> D K -->|No| M[Show Confirm Alert] M --> N[DELETE club_members row] N --> J style S fill:#4ade80,stroke:#4ade80,color:#000 style A fill:#1a2e1a,stroke:#4ade80
Club Owner

10. Create Tournament

Owner creates a new tournament with sport, max players, and club assignment.

flowchart TD S([Start]) --> A A([Tournaments Tab]) --> B[Fetch tournaments for all admin clubs] B --> C[Display tournament list: name, sport badge, status, player count] C --> D{User Action?} D -->|Pull to Refresh| E[Re-fetch tournaments] E --> C D -->|Tap + Button| F[Open Create Tournament Modal] F --> G[Enter Tournament Name] G --> H[Select Sport: Tennis, Padel, or Squash] H --> I[Set Max Players with stepper] I --> J[Select Club from admin clubs dropdown] J --> K[Tap Create] K --> L{Name filled?} L -->|No| M[Alert: name required] M --> G L -->|Yes| N[INSERT tournaments row: name, sport, max_players, club_id, status = upcoming] N --> O{Error?} O -->|Yes| P[Show Error Alert] O -->|No| Q[Close Modal and Refresh list] Q --> C D -->|Delete Tournament| R[Show Confirm Alert] R --> T[DELETE tournaments row] T --> E style S fill:#4ade80,stroke:#4ade80,color:#000 style A fill:#1a2e1a,stroke:#4ade80
Player
Player

11. Browse Clubs & Join a Club

Player discovers clubs near them using location, browses details, and joins via member insert.

flowchart TD S([Start]) --> A A([Clubs Tab]) --> B[Request Location Permission] B --> C{Permission granted?} C -->|No| D[Fetch all clubs with no distance sorting] C -->|Yes| E[Get current coordinates from expo-location] E --> F[Fetch clubs_with_member_count view] F --> G[Calculate Haversine distance per club from current position] G --> H[Sort clubs by distance ascending] D --> I[Display club cards: name, sport badges, member count, distance] H --> I I --> J{User Action?} J -->|Pull to Refresh| K[Re-fetch and recalculate distances] K --> I J -->|Tap Club Card| L[Navigate to club detail screen] L --> M([Club Detail Screen]) M --> N[Fetch club info, ladders, and members] N --> O{Is current user a member?} O -->|Not a Member| P[Show Become a Member button] P --> Q[Tap Become a Member] Q --> R[INSERT club_members: club_id, user_id, role = member] R --> T{Error?} T -->|Yes| U[Show Error Alert] T -->|No| V[Refresh: user is now member] V --> W[Show Leave Club button] O -->|Member not admin| W W --> X[Tap Leave Club] X --> Y[Show Confirm Alert] Y --> Z[DELETE club_members row] Z --> AA[Refresh: user is no longer member] AA --> P O -->|Admin| AB[Show Admin Panel button] AB --> AC[Navigate to club/manage] style S fill:#4a9eff,stroke:#4a9eff,color:#fff style A fill:#1e2a3a,stroke:#4a9eff style M fill:#1e2a3a,stroke:#4a9eff
Player

12. Join a Ladder

Player browses a club's ladders, views standings, and joins by inserting a ladder entry.

flowchart TD S([Start]) --> A A([Club Detail - Ladders Tab]) --> B[Display list of active ladders in the club] B --> C[Tap a Ladder row] C --> D([Ladder Detail Screen]) D --> E[Fetch standings: ladder_entries with profile joins ordered by position] E --> F{Is current user already in ladder_entries?} F -->|Yes - Member| G[Show standings with user row highlighted and current rank] G --> H{User Action?} H -->|Switch to Matches Tab| I[Display This Round and Past Matches] H -->|Pull to Refresh| J[Re-fetch standings and matches] J --> G F -->|No - Not Member| K[Show Join Ladder button below standings table] K --> L[Tap Join Ladder] L --> M[SELECT MAX position from ladder_entries for this ladder] M --> N[INSERT ladder_entries: ladder_id, user_id, position = max + 1, wins = 0, losses = 0, points = 0] N --> O{Error?} O -->|Yes| P[Show Error Alert] O -->|No| Q[Refresh: user now appears in standings table] Q --> G style S fill:#4a9eff,stroke:#4a9eff,color:#fff style A fill:#1e2a3a,stroke:#4a9eff style D fill:#1e2a3a,stroke:#4a9eff
Player

13. View My Ladders & Profile

Player views all enrolled ladders on the Ladders tab and their global stats on the Profile tab.

flowchart TD S([Start]) --> A A([Ladders Tab]) --> B[Fetch ladder_entries where user_id = current user] B --> C[JOIN ladders and clubs to enrich each entry] C --> D{Any ladders enrolled?} D -->|No| E[Show empty state: Browse Clubs to join a ladder] D -->|Yes| F[Display ladder cards: sport emoji, name, club name, rank badge, W L Pts] F --> G{User Action?} G -->|Tap Ladder Card| H[Navigate to ladder/id screen] G -->|Pull to Refresh| I[Re-fetch all entries] I --> C AA([Profile Tab]) --> AB[Fetch profile row from profiles table] AB --> AC[Fetch all ladder_entries for current user] AC --> AD[Compute: total wins, total losses, win rate percent, ladder count] AD --> AE[Display avatar initials circle, username, stats strip] AE --> AF[List all enrolled ladders with rank badges] AF --> AG{User Action?} AG -->|Tap Ladder| AH[Navigate to ladder/id screen] AG -->|Pull to Refresh| AI[Re-fetch profile and entries] AI --> AB AG -->|Tap Sign Out| AJ[supabase.auth.signOut, Clear session and profile state] AJ --> AK([Login Screen]) style S fill:#4a9eff,stroke:#4a9eff,color:#fff style A fill:#1e2a3a,stroke:#4a9eff style AA fill:#1e2a3a,stroke:#4a9eff style AK fill:#1e2a3a,stroke:#4a9eff
Player

14. Send a Challenge

Player challenges another club member to a match on a specific ladder.

flowchart TD S([Start]) --> A A([Club Detail - Members Tab]) --> B[List all members excluding self and admins] B --> C[Tap the lightning bolt Challenge button on a member row] C --> D([New Challenge Screen]) D --> E[Display VS card: You vs Opponent name] E --> F[Fetch active ladders for this club] F --> G{Any active ladders?} G -->|No| H[Show: No active ladders - Ask owner to create one] G -->|Yes| I[Display ladder options as selectable cards with sport emoji] I --> J[Select a ladder] J --> K[Optionally type a message in text input] K --> L[Optionally pick a proposed date via DatePickerField native picker] L --> M[Tap Send Challenge] M --> N[INSERT challenges: ladder_id, challenger_id, challenged_id, status = pending, message, proposed_date, expires_at = now + 7 days] N --> O{Error?} O -->|Yes| P[Show Error Alert] O -->|No| Q[Alert: Challenge Sent! Waiting for response] Q --> R[Navigate back to club detail] style S fill:#4a9eff,stroke:#4a9eff,color:#fff style A fill:#1e2a3a,stroke:#4a9eff style D fill:#1e2a3a,stroke:#4a9eff
Player

15. Accept or Decline a Challenge

Challenged player reviews incoming challenges and accepts or declines them.

flowchart TD S([Start]) --> A A([My Matches - Private Tab]) --> B[Fetch challenges where challenged_id = me AND status = pending] B --> C{Any pending challenges?} C -->|No| D[Show empty state] C -->|Yes| E[Display challenge cards: challenger name, ladder, message, proposed date, expiry countdown] E --> F{User Action?} F -->|Pull to Refresh| G[Re-fetch challenges] G --> B F -->|Accept| H[UPDATE challenges set status = accepted] H --> I{Error?} I -->|Yes| J[Show Error Alert] I -->|No| K[Database trigger fires: creates match row with status = scheduling] K --> L[Refresh challenge list] L --> B F -->|Decline| M[UPDATE challenges set status = declined] M --> N[Refresh challenge list] N --> B style S fill:#4a9eff,stroke:#4a9eff,color:#fff style A fill:#1e2a3a,stroke:#4a9eff
Player

16. Match Scheduling — Propose Times

The proposer offers up to 3 date/time/court slot options to their opponent.

flowchart TD S([Start]) --> A A([My Matches - Ladder Tab]) --> B[Fetch matches where status IN scheduling or confirmed] B --> C[For scheduling matches: fetch match_proposals where status = pending] C --> D[Display match card with status-aware action buttons] D --> E{Match status and my role?} E -->|scheduling - I am proposer, no active proposal| F[Show Propose Time button] E -->|scheduling - I am proposer, proposal exists| G[Show Awaiting Response label] E -->|scheduling - I am opponent, no proposal yet| H[Show Waiting for Proposal label] E -->|scheduling - I am opponent, proposal exists| I[Show Respond to Proposal button] E -->|confirmed| J[Show Submit Result button and Chat icon button] F --> K[Tap Propose Time] K --> L([Schedule Screen]) L --> M[Fetch active courts for this club and sport] M --> N[For Slot 1 required and Slots 2 and 3 optional] N --> O[Pick date from next-14-days horizontal scroll chips] O --> P[Pick time from common-times horizontal scroll chips: 08:00 to 20:00] P --> Q[Optionally pick a court from radio chips including Any option] Q --> R[Tap Send Proposal] R --> T{At least 1 slot filled?} T -->|No| U[Alert: Add at least one slot] U --> O T -->|Yes| V[INSERT match_proposals: proposed_by, match_id, slot_1_time, slot_1_court, slot_2_time, slot_2_court, slot_3_time, slot_3_court, status = pending] V --> W{Error?} W -->|Yes| X[Show Error Alert] W -->|No| Y[Alert: Proposal Sent! Opponent has 48 hours] Y --> Z[Navigate back] style S fill:#4a9eff,stroke:#4a9eff,color:#fff style A fill:#1e2a3a,stroke:#4a9eff style L fill:#1e2a3a,stroke:#4a9eff
Player

17. Match Scheduling — Respond to Proposal

Opponent reviews proposed slots and accepts one, or declines all to counter-propose.

flowchart TD S([Start]) --> A A([My Matches - Ladder Tab]) --> B[Tap Respond to Proposal button] B --> C([Respond Screen]) C --> D[Fetch proposal row with court name joins for all 3 slots] D --> E[Display up to 3 slots as radio button options showing date, time, court name] E --> F{User Action?} F -->|Select a slot radio button| G[Highlight selected slot] G --> H[Tap Accept This Slot] H --> I[Call RPC: accept_match_proposal with proposal_id and accepted slot number] I --> J{RPC Error?} J -->|Yes| K[Show Error Alert] J -->|No| L[Match status updated to confirmed, scheduled_for set to accepted slot time] L --> M[Alert: Match Confirmed!] M --> N[Navigate back] F -->|None of these work| O[Tap: None of these work - Propose new times] O --> P[UPDATE match_proposals set status = declined] P --> Q[Navigate to Schedule Screen for counter-proposal] Q --> R([Schedule Screen]) style S fill:#4a9eff,stroke:#4a9eff,color:#fff style A fill:#1e2a3a,stroke:#4a9eff style C fill:#1e2a3a,stroke:#4a9eff style R fill:#1e2a3a,stroke:#4a9eff
Player

18. Match Chat — Real-Time

Both players chat within a confirmed match using Supabase Realtime subscriptions.

flowchart TD S([Start]) --> A A([My Matches - confirmed match]) --> B[Tap Chat icon button] B --> C([Match Chat Screen]) C --> D[Fetch message history: match_messages with sender profile join ordered by sent_at ASC] D --> E[Subscribe to Supabase Realtime: channel match_chat - match_id, listening for INSERT events on match_messages table] E --> F[Display messages in FlatList with date separators, opponent avatar, bubble layout: mine on right, theirs on left] F --> G[Auto-scroll to bottom on load and new message] G --> H{User Action?} H -->|Type and send message| I[Update input field state] I --> J[Tap Send or press Return] J --> K[Optimistic insert: add message locally with temp id and scroll to bottom] K --> L[INSERT match_messages: match_id, sender_id, body] L --> M{Error?} M -->|Yes| N[Remove optimistic message from local state] M -->|No| O[Replace temp id with real database row id] O --> H H -->|Realtime INSERT event fires| P[New message arrives from opponent] P --> Q[Fetch sender profile for incoming message] Q --> R[Append to message list if not a duplicate] R --> G H -->|Navigate Back| T[Unsubscribe Realtime channel via supabase.removeChannel] T --> U([My Matches Screen]) style S fill:#4a9eff,stroke:#4a9eff,color:#fff style A fill:#1e2a3a,stroke:#4a9eff style C fill:#1e2a3a,stroke:#4a9eff style U fill:#1e2a3a,stroke:#4a9eff
Player

19. Submit Match Result

Either player submits the outcome after the match is played. Points are updated via RPC.

flowchart TD S([Start]) --> A A([My Matches - confirmed match]) --> B[Tap Submit Result button] B --> C([Submit Result Screen]) C --> D[Display player 1 vs player 2 name cards] D --> E[Select winner: tap Player 1 or Player 2 card to highlight] E --> F[Optionally add set scores: tap + to add a set row, enter scores, up to 5 sets] F --> G[Score preview renders live showing formatted result string] G --> H[Tap Submit Result] H --> I{Winner selected?} I -->|No| J[Alert: Please select a winner] J --> E I -->|Yes| K[Call RPC: record_match_result with match_id, ladder_id, player1_id, player2_id, winner_id, score JSON, challenge_id] K --> L{RPC Error?} L -->|Yes| M[Show Error Alert with error message] L -->|No| N[Match status set to played, Ladder entries updated: winner plus 20 pts plus 1 win, loser plus 1 loss, positions recalculated] N --> O[Alert: Result Recorded! Points updated] O --> P[Navigate back] P --> Q([My Matches - updated list]) style S fill:#4a9eff,stroke:#4a9eff,color:#fff style A fill:#1e2a3a,stroke:#4a9eff style C fill:#1e2a3a,stroke:#4a9eff style Q fill:#1e2a3a,stroke:#4a9eff
Player

20. Mark Unavailable for a Round

Player declares they cannot play during a round window. Admin is notified of walkover risk.

flowchart TD S([Start]) --> A A([My Matches - Ladder Tab]) --> B[Tap Mark Unavailable button on a match card] B --> C([Mark Unavailable Screen]) C --> D[Display round window: Start date to End date] D --> E[Show warning banner: Opponent may be awarded walkover if match cannot be rescheduled] E --> F[Optionally type a reason up to 200 characters: e.g. Holiday, injury, travel] F --> G[Tap Confirm Unavailability] G --> H[UPSERT round_unavailability: ladder_id, user_id, round_start, round_end, reason, declared_at - onConflict: ladder_id + user_id + round_start] H --> I{Error?} I -->|Yes| J[Show Error Alert] I -->|No| K[Alert: Unavailability Marked - Club admin has been notified - You may still receive a ranking penalty] K --> L[Tap OK] L --> M[Navigate Back] M --> N([My Matches Screen]) style S fill:#4a9eff,stroke:#4a9eff,color:#fff style A fill:#1e2a3a,stroke:#4a9eff style C fill:#1e2a3a,stroke:#4a9eff style N fill:#1e2a3a,stroke:#4a9eff
Shared — Cross Cutting
Shared

21. App Routing State Machine

High-level overview of how all routing decisions branch from a single entry point.

flowchart TD S([App Launch]) --> LOAD[Load session + profile + club_members admin check] LOAD --> AUTH{Valid Session?} AUTH -->|No| LOGIN([Login / Sign Up Screens]) AUTH -->|Yes| OWNER{is_club_owner = true OR admin in club_members?} OWNER -->|No| PTABS([Player Tab Navigator: Ladders, My Matches, Clubs, Profile]) OWNER -->|Yes| DONE{onboarding_completed OR has admin club row?} DONE -->|No| ONBOARD([Club Onboarding Wizard - 6-step animated flow]) DONE -->|Yes| OTABS([Owner Tab Navigator: Members, Ladders, Tournaments, My Club]) PTABS --> PSTACK[Player Stack Screens: club/id, ladder/id, match/schedule, match/respond, match/chat, match/new, challenge/new, match/unavailable] OTABS --> OSTACK[Owner Stack Screens: ladder/create, ladder/manage, club/manage, match/new] ONBOARD --> OTABS LOGIN --> LOAD style S fill:#c084fc,stroke:#c084fc,color:#000 style LOGIN fill:#2d1a3e,stroke:#c084fc style PTABS fill:#1e2a3a,stroke:#4a9eff style OTABS fill:#1a2e1a,stroke:#4ade80 style ONBOARD fill:#1a2e1a,stroke:#4ade80 style PSTACK fill:#1e2a3a,stroke:#4a9eff style OSTACK fill:#1a2e1a,stroke:#4ade80