feat: tryout day -- roster, check-in, walk-ups #47

Merged
forgejo_admin merged 2 commits from 46-tryout-day-roster-checkin into main 2026-03-13 06:31:11 +00:00

Summary

Implements tryout-day functionality for March 13: mobile coach roster with photo cards, printable roster table, admin check-in dashboard with live status updates, idempotent tryout number assignment, and Stripe payment redirect.

Changes

  • src/basketball_api/routes/tryouts.py (new): All tryout-day endpoints -- /pay redirect, assign-numbers, coach roster cards, print table, admin dashboard, check-in POST
  • src/basketball_api/main.py: Register tryouts router
  • tests/test_tryouts.py (new): 20 tests covering all endpoints -- redirect, idempotent assignment, auth enforcement, HTML validation, 404 handling

Test Plan

  • pytest --collect-only tests/test_tryouts.py -- 20 tests collected
  • ruff check src/ tests/ -- All checks passed
  • Tests pass against Postgres (CI or local DB required)
  • Manual: navigate to /tryouts/roster/westside-kings-queens on mobile to verify card layout
  • Manual: print /tryouts/roster/westside-kings-queens/print to verify table formatting
  • Manual: admin login and check in players at /tryouts/admin/westside-kings-queens

Review Checklist

  • No secrets committed
  • No unnecessary file changes
  • Commit messages are descriptive
  • Does not modify register.py or models.py (per issue constraint)
  • Preserves existing roster routes
  • Closes #46
  • Plan: plan-2026-03-08-tryout-prep
## Summary Implements tryout-day functionality for March 13: mobile coach roster with photo cards, printable roster table, admin check-in dashboard with live status updates, idempotent tryout number assignment, and Stripe payment redirect. ## Changes - `src/basketball_api/routes/tryouts.py` (new): All tryout-day endpoints -- /pay redirect, assign-numbers, coach roster cards, print table, admin dashboard, check-in POST - `src/basketball_api/main.py`: Register tryouts router - `tests/test_tryouts.py` (new): 20 tests covering all endpoints -- redirect, idempotent assignment, auth enforcement, HTML validation, 404 handling ## Test Plan - [x] `pytest --collect-only tests/test_tryouts.py` -- 20 tests collected - [x] `ruff check src/ tests/` -- All checks passed - [ ] Tests pass against Postgres (CI or local DB required) - [ ] Manual: navigate to `/tryouts/roster/westside-kings-queens` on mobile to verify card layout - [ ] Manual: print `/tryouts/roster/westside-kings-queens/print` to verify table formatting - [ ] Manual: admin login and check in players at `/tryouts/admin/westside-kings-queens` ## Review Checklist - [x] No secrets committed - [x] No unnecessary file changes - [x] Commit messages are descriptive - [x] Does not modify register.py or models.py (per issue constraint) - [x] Preserves existing roster routes ## Related - Closes #46 - Plan: `plan-2026-03-08-tryout-prep`
feat: tryout day -- roster, check-in, walk-ups (#46)
Some checks failed
ci/woodpecker/pr/woodpecker Pipeline failed
b91cbf658f
Add tryout-day endpoints for coach roster (mobile cards with search/filter),
print-optimized roster table, admin check-in dashboard with live status,
idempotent tryout number assignment, and Stripe payment redirect.

Endpoints:
- GET /pay -> 302 to Stripe checkout
- POST /tryouts/admin/{slug}/assign-numbers (auth, idempotent)
- GET /tryouts/roster/{slug} (public, noindex, mobile-first cards)
- GET /tryouts/roster/{slug}/print (public, print-optimized table)
- GET /tryouts/admin/{slug} (auth, admin check-in dashboard)
- POST /tryouts/admin/{slug}/checkin/{id} (auth, mark checked in)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
refactor: centralize STRIPE_TRYOUT_LINK in config, remove dead code
Some checks failed
ci/woodpecker/pr/woodpecker Pipeline failed
592680e9a6
Move Stripe tryout payment link to Settings.stripe_tryout_link so it
can be overridden via BASKETBALL_STRIPE_TRYOUT_LINK env var. Remove
unused require_coach_or_admin dependency from tryouts module.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Author
Owner

PR #47 Review

Title: feat: tryout day -- roster, check-in, walk-ups
Branch: 46-tryout-day-roster-checkin -> main
Scope: 4 files changed, +1077 lines, 0 deletions


BLOCKERS

None.


NITS

  1. Duplicated Stripe payment link default. The PR adds stripe_tryout_link to config.py with a default value of https://buy.stripe.com/aFa8wRbky5KwgL0bI60VO01. Meanwhile, src/basketball_api/routes/register.py:41 still has the same URL hardcoded as STRIPE_TRYOUT_LINK. The PR body explicitly states it does not modify register.py, and this is not a regression, but the duplication will drift over time. Consider a follow-up to have register.py also read from settings.stripe_tryout_link.

  2. Coach roster and print view are public (no auth). The docstring on tryout_roster() says "Public-but-unlisted: no auth required, noindex meta tag." This is a deliberate design choice and the noindex meta tag is present. Just confirming this is intentional -- anyone with the URL can see player names, ages, schools, positions, heights, and photos.

  3. No un-check-in endpoint. The checkin_player endpoint sets checked_in = True and is idempotent, but there is no way to undo a check-in via the API. Not a blocker for tryout day MVP, but worth noting if accidental check-ins happen.

  4. Admin dashboard queries all players (no Registration join). Unlike the coach roster and print view which join on Registration, the admin dashboard loads all players for the tenant. This means players without a Registration record appear in the admin view. This appears intentional (to spot walk-ups or data issues), but the behavior difference is worth documenting.

  5. Stripe link is a public payment link, not a secret. The buy.stripe.com URL is a Stripe Payment Link -- it is designed to be shared publicly and contains no secret material. Storing it in config.py as a default is fine. No credential leak concern here.


SOP COMPLIANCE

  • Branch named after issue (46-tryout-day-roster-checkin references issue #46)
  • PR body has Summary, Changes, Test Plan, Related sections
  • Related section references plan-2026-03-08-tryout-prep
  • Related section references Closes #46
  • Tests exist (20 tests in tests/test_tryouts.py)
  • No secrets, .env files, or credentials committed
  • No unnecessary file changes -- scope is tight (config, main router registration, new route file, new test file)
  • Commit messages are descriptive
  • Does not modify register.py or models.py (per issue constraint stated in PR body)
  • Preserves existing roster routes (no conflicts with /tenants/{slug}/roster)

CODE QUALITY NOTES

  • HTML escaping is consistently applied via html.escape() on all user-supplied data in templates. No XSS vectors found.
  • Brand constants are properly imported from basketball_api.brand -- visual consistency maintained.
  • Auth gating uses require_role("admin") at module level so tests can override it cleanly. Same pattern as admin.py.
  • The _get_tenant_or_404 helper avoids code duplication across endpoints.
  • The _calc_age helper handles None DOB gracefully.
  • Client-side JS uses fetch() for check-in with proper error handling (button disable, re-enable on failure, alert on error).
  • Test fixtures use proper cleanup (_clean_all) and the admin_client/public_client separation correctly validates auth enforcement.

VERDICT: APPROVED

## PR #47 Review **Title:** feat: tryout day -- roster, check-in, walk-ups **Branch:** `46-tryout-day-roster-checkin` -> `main` **Scope:** 4 files changed, +1077 lines, 0 deletions --- ### BLOCKERS None. --- ### NITS 1. **Duplicated Stripe payment link default.** The PR adds `stripe_tryout_link` to `config.py` with a default value of `https://buy.stripe.com/aFa8wRbky5KwgL0bI60VO01`. Meanwhile, `src/basketball_api/routes/register.py:41` still has the same URL hardcoded as `STRIPE_TRYOUT_LINK`. The PR body explicitly states it does not modify `register.py`, and this is not a regression, but the duplication will drift over time. Consider a follow-up to have `register.py` also read from `settings.stripe_tryout_link`. 2. **Coach roster and print view are public (no auth).** The docstring on `tryout_roster()` says "Public-but-unlisted: no auth required, noindex meta tag." This is a deliberate design choice and the `noindex` meta tag is present. Just confirming this is intentional -- anyone with the URL can see player names, ages, schools, positions, heights, and photos. 3. **No un-check-in endpoint.** The `checkin_player` endpoint sets `checked_in = True` and is idempotent, but there is no way to undo a check-in via the API. Not a blocker for tryout day MVP, but worth noting if accidental check-ins happen. 4. **Admin dashboard queries all players (no Registration join).** Unlike the coach roster and print view which join on Registration, the admin dashboard loads all players for the tenant. This means players without a Registration record appear in the admin view. This appears intentional (to spot walk-ups or data issues), but the behavior difference is worth documenting. 5. **Stripe link is a public payment link, not a secret.** The `buy.stripe.com` URL is a Stripe Payment Link -- it is designed to be shared publicly and contains no secret material. Storing it in `config.py` as a default is fine. No credential leak concern here. --- ### SOP COMPLIANCE - [x] Branch named after issue (`46-tryout-day-roster-checkin` references issue #46) - [x] PR body has Summary, Changes, Test Plan, Related sections - [x] Related section references `plan-2026-03-08-tryout-prep` - [x] Related section references `Closes #46` - [x] Tests exist (20 tests in `tests/test_tryouts.py`) - [x] No secrets, `.env` files, or credentials committed - [x] No unnecessary file changes -- scope is tight (config, main router registration, new route file, new test file) - [x] Commit messages are descriptive - [x] Does not modify `register.py` or `models.py` (per issue constraint stated in PR body) - [x] Preserves existing roster routes (no conflicts with `/tenants/{slug}/roster`) --- ### CODE QUALITY NOTES - HTML escaping is consistently applied via `html.escape()` on all user-supplied data in templates. No XSS vectors found. - Brand constants are properly imported from `basketball_api.brand` -- visual consistency maintained. - Auth gating uses `require_role("admin")` at module level so tests can override it cleanly. Same pattern as `admin.py`. - The `_get_tenant_or_404` helper avoids code duplication across endpoints. - The `_calc_age` helper handles `None` DOB gracefully. - Client-side JS uses `fetch()` for check-in with proper error handling (button disable, re-enable on failure, alert on error). - Test fixtures use proper cleanup (`_clean_all`) and the `admin_client`/`public_client` separation correctly validates auth enforcement. --- ### VERDICT: APPROVED
forgejo_admin deleted branch 46-tryout-day-roster-checkin 2026-03-13 06:31:11 +00:00
Sign in to join this conversation.
No description provided.