Phase 4: Tryout day — roster, check-in, walk-ups #46

Closed
opened 2026-03-13 05:51:58 +00:00 by forgejo_admin · 0 comments

Lineage

plan-2026-03-08-tryout-prep → Phase 4

Repo

forgejo_admin/basketball-api

User Story

As a coach, I want to view the player roster on my phone during tryouts so I can match faces to names and see tryout numbers.

As Marcus (admin), I want to check players in at the door, see who paid and who completed registration, and handle cash walk-ups so tryout day runs smoothly.

As a coach, I want a printed roster on my clipboard with player numbers, names, and key info so I can take notes during evaluations.

Context

Tryouts are March 13 (tomorrow). Phases 1–3c are complete. Registration is live, 41 families paid, 38 emailed, players are actively registering with photos, division, and waivers.

Key decisions made in planning:

  • Tryout numbers: one sequence, assigned by registration order (#1 = first registered). Boys and girls share the same sequence.
  • Roster is public but unlisted — hard-to-guess URL path, <meta name="robots" content="noindex">. No auth required. Coaches access via link Marcus texts them.
  • Admin check-in view is auth-gated (pal-e-auth admin role).
  • Walk-up payment: GET /pay redirects to the Stripe checkout URL (https://buy.stripe.com/aFa8wRbky5KwgL0bI60VO01). QR code points here. Memorable URL for Marcus to say out loud.
  • Cash walk-ups use CASHPAID Stripe promo code (100% off, 50 max). Already created. Marcus collects cash, gives code, parent does $0 Stripe flow → auto email → registration.
  • Printed clipboard: Number | Name | Division | Age | School | Position | Height.

File Targets

Files to create:

  • src/basketball_api/routes/tryouts.py — new file: all tryout-day views (coach roster, print view, admin check-in, /pay redirect, bulk number assignment)

Files to modify:

  • src/basketball_api/main.py — register the new tryouts router
  • src/basketball_api/brand.py — may need new CSS constants for card layout

Files the agent should NOT touch:

  • src/basketball_api/routes/register.py — registration flow is stable, don't break it
  • src/basketball_api/models.pytryout_number and checked_in fields already exist, no migration needed

Acceptance Criteria

Sub-phase 4a: Setup

  • GET /pay returns 302 redirect to https://buy.stripe.com/aFa8wRbky5KwgL0bI60VO01
  • POST /tryouts/admin/{tenant_slug}/assign-numbers (auth-gated admin) bulk-assigns tryout numbers by registration order (earliest created_at = #1). Idempotent — re-running doesn't change existing assignments.

Sub-phase 4b: Coach roster (mobile)

  • GET /tryouts/roster/{tenant_slug} serves HTML with player cards (no auth)
  • Page has <meta name="robots" content="noindex">
  • Each card shows: large tryout number, photo thumbnail (from photo_url), player name, division (Boys/Girls), school, position, height, graduating class
  • If player has no photo, show a placeholder silhouette
  • Cards show a "checked in" badge/indicator if checked_in=True
  • Sticky search bar at top — client-side JS filters cards by name or number as user types
  • Division filter tabs: All | Boys | Girls
  • Cards sorted by tryout number (players without numbers at the bottom)
  • Mobile-first responsive — works on phone screens (single-column card layout)
  • Westside branded: red (#d42026) and black (#0a0a0a) color scheme, white text

Sub-phase 4c: Printable roster

  • GET /tryouts/roster/{tenant_slug}/print serves a print-optimized HTML table
  • Columns: Number, Name, Division, Age (calculated from date_of_birth), School, Position, Height
  • Sorted by tryout number
  • Print-friendly CSS: no dark backgrounds, compact rows, readable font, page-break-friendly
  • @media print styles applied automatically
  • Players without tryout numbers listed at the bottom with "—" for number

Sub-phase 4d: Admin check-in

  • GET /tryouts/admin/{tenant_slug} serves HTML (auth-gated: require_role("admin"))
  • Same card layout as coach roster but each card also shows:
    • Payment status: green "PAID" or red "UNPAID"
    • Registration status: "Form Complete" (waiver signed + photo uploaded) vs "Needs Form" (just paid, hasn't filled form)
    • "Check In" button (POST action)
  • POST /tryouts/admin/{tenant_slug}/checkin/{player_id} marks checked_in=True on the player. Returns redirect back to admin page.
  • Checked-in players get green highlight/border and sort to bottom of the list
  • Unchecked players with no payment are visually distinct (red "NEEDS PAYMENT" label)
  • Search bar works same as coach view — filter by name or number
  • Mobile-responsive — Marcus uses this on his phone at the front door

Test Expectations

  • Test: GET /pay returns 302 with correct Location header
  • Test: assign-numbers endpoint assigns sequential numbers by registration order
  • Test: assign-numbers is idempotent (re-running preserves existing numbers)
  • Test: coach roster returns HTML with player data, noindex meta tag
  • Test: admin check-in requires auth (returns 401/403 without valid token)
  • Test: checkin POST sets checked_in=True on the player
  • Test: print view returns HTML table with correct columns
  • Run: pytest tests/ -v

Constraints

  • All views are server-rendered HTML — no JavaScript frameworks. Only vanilla JS for search/filter.
  • Follow the existing brand pattern in src/basketball_api/brand.py for CSS colors and styles.
  • Use the existing ROSTER_CSS as a starting reference but the new views need card-based layout, not just a table.
  • Photos are served at /uploads/photos/{filename} via StaticFiles mount (already configured).
  • The STRIPE_TRYOUT_LINK constant already exists in routes/register.py — import or duplicate it, don't hardcode the URL in a third place. Actually, move it to config.py or brand.py if it makes sense.
  • Age calculation: use date_of_birth field. If null, show "—".
  • Keep the existing /{tenant_slug}/roster and /{tenant_slug}/roster/view routes — don't remove them.

Checklist

  • PR opened
  • Tests pass
  • No unrelated changes
  • project-westside-basketball
  • Existing roster: src/basketball_api/routes/roster.py
  • Brand constants: src/basketball_api/brand.py
  • Photo upload: src/basketball_api/routes/upload.py
### Lineage `plan-2026-03-08-tryout-prep` → Phase 4 ### Repo `forgejo_admin/basketball-api` ### User Story As a **coach**, I want to view the player roster on my phone during tryouts so I can match faces to names and see tryout numbers. As **Marcus (admin)**, I want to check players in at the door, see who paid and who completed registration, and handle cash walk-ups so tryout day runs smoothly. As a **coach**, I want a printed roster on my clipboard with player numbers, names, and key info so I can take notes during evaluations. ### Context Tryouts are **March 13** (tomorrow). Phases 1–3c are complete. Registration is live, 41 families paid, 38 emailed, players are actively registering with photos, division, and waivers. **Key decisions made in planning:** - Tryout numbers: one sequence, assigned by registration order (#1 = first registered). Boys and girls share the same sequence. - Roster is **public but unlisted** — hard-to-guess URL path, `<meta name="robots" content="noindex">`. No auth required. Coaches access via link Marcus texts them. - Admin check-in view is **auth-gated** (pal-e-auth admin role). - Walk-up payment: `GET /pay` redirects to the Stripe checkout URL (`https://buy.stripe.com/aFa8wRbky5KwgL0bI60VO01`). QR code points here. Memorable URL for Marcus to say out loud. - Cash walk-ups use `CASHPAID` Stripe promo code (100% off, 50 max). Already created. Marcus collects cash, gives code, parent does $0 Stripe flow → auto email → registration. - Printed clipboard: Number | Name | Division | Age | School | Position | Height. ### File Targets Files to create: - `src/basketball_api/routes/tryouts.py` — new file: all tryout-day views (coach roster, print view, admin check-in, `/pay` redirect, bulk number assignment) Files to modify: - `src/basketball_api/main.py` — register the new tryouts router - `src/basketball_api/brand.py` — may need new CSS constants for card layout Files the agent should NOT touch: - `src/basketball_api/routes/register.py` — registration flow is stable, don't break it - `src/basketball_api/models.py` — `tryout_number` and `checked_in` fields already exist, no migration needed ### Acceptance Criteria **Sub-phase 4a: Setup** - [ ] `GET /pay` returns 302 redirect to `https://buy.stripe.com/aFa8wRbky5KwgL0bI60VO01` - [ ] `POST /tryouts/admin/{tenant_slug}/assign-numbers` (auth-gated admin) bulk-assigns tryout numbers by registration order (earliest `created_at` = #1). Idempotent — re-running doesn't change existing assignments. **Sub-phase 4b: Coach roster (mobile)** - [ ] `GET /tryouts/roster/{tenant_slug}` serves HTML with player cards (no auth) - [ ] Page has `<meta name="robots" content="noindex">` - [ ] Each card shows: large tryout number, photo thumbnail (from `photo_url`), player name, division (Boys/Girls), school, position, height, graduating class - [ ] If player has no photo, show a placeholder silhouette - [ ] Cards show a "checked in" badge/indicator if `checked_in=True` - [ ] Sticky search bar at top — client-side JS filters cards by name or number as user types - [ ] Division filter tabs: All | Boys | Girls - [ ] Cards sorted by tryout number (players without numbers at the bottom) - [ ] Mobile-first responsive — works on phone screens (single-column card layout) - [ ] Westside branded: red (#d42026) and black (#0a0a0a) color scheme, white text **Sub-phase 4c: Printable roster** - [ ] `GET /tryouts/roster/{tenant_slug}/print` serves a print-optimized HTML table - [ ] Columns: Number, Name, Division, Age (calculated from `date_of_birth`), School, Position, Height - [ ] Sorted by tryout number - [ ] Print-friendly CSS: no dark backgrounds, compact rows, readable font, page-break-friendly - [ ] `@media print` styles applied automatically - [ ] Players without tryout numbers listed at the bottom with "—" for number **Sub-phase 4d: Admin check-in** - [ ] `GET /tryouts/admin/{tenant_slug}` serves HTML (auth-gated: `require_role("admin")`) - [ ] Same card layout as coach roster but each card also shows: - Payment status: green "PAID" or red "UNPAID" - Registration status: "Form Complete" (waiver signed + photo uploaded) vs "Needs Form" (just paid, hasn't filled form) - "Check In" button (POST action) - [ ] `POST /tryouts/admin/{tenant_slug}/checkin/{player_id}` marks `checked_in=True` on the player. Returns redirect back to admin page. - [ ] Checked-in players get green highlight/border and sort to bottom of the list - [ ] Unchecked players with no payment are visually distinct (red "NEEDS PAYMENT" label) - [ ] Search bar works same as coach view — filter by name or number - [ ] Mobile-responsive — Marcus uses this on his phone at the front door ### Test Expectations - [ ] Test: `GET /pay` returns 302 with correct Location header - [ ] Test: assign-numbers endpoint assigns sequential numbers by registration order - [ ] Test: assign-numbers is idempotent (re-running preserves existing numbers) - [ ] Test: coach roster returns HTML with player data, noindex meta tag - [ ] Test: admin check-in requires auth (returns 401/403 without valid token) - [ ] Test: checkin POST sets `checked_in=True` on the player - [ ] Test: print view returns HTML table with correct columns - Run: `pytest tests/ -v` ### Constraints - All views are **server-rendered HTML** — no JavaScript frameworks. Only vanilla JS for search/filter. - Follow the existing brand pattern in `src/basketball_api/brand.py` for CSS colors and styles. - Use the existing `ROSTER_CSS` as a starting reference but the new views need card-based layout, not just a table. - Photos are served at `/uploads/photos/{filename}` via StaticFiles mount (already configured). - The `STRIPE_TRYOUT_LINK` constant already exists in `routes/register.py` — import or duplicate it, don't hardcode the URL in a third place. Actually, move it to `config.py` or `brand.py` if it makes sense. - Age calculation: use `date_of_birth` field. If null, show "—". - Keep the existing `/{tenant_slug}/roster` and `/{tenant_slug}/roster/view` routes — don't remove them. ### Checklist - [ ] PR opened - [ ] Tests pass - [ ] No unrelated changes ### Related - `project-westside-basketball` - Existing roster: `src/basketball_api/routes/roster.py` - Brand constants: `src/basketball_api/brand.py` - Photo upload: `src/basketball_api/routes/upload.py`
forgejo_admin 2026-03-13 06:11:31 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
forgejo_admin/basketball-api#46
No description provided.