Brand alignment + registration tokens (Phase 3a) #19

Closed
opened 2026-03-10 00:02:18 +00:00 by forgejo_admin · 0 comments

Lineage

plan-2026-03-08-tryout-prep → Phase 3a

Repo

forgejo_admin/basketball-api

User Story

As a parent who paid via Stripe,
I want to click a personalized link and see a branded Westside registration form pre-filled with my child's info,
So that I can complete their tryout profile without guessing emails or seeing a generic-looking page.

Context

The registration form, coach onboarding, and roster pages are live at https://basketball-api.tail5b443a.ts.net but use a blue/gold color scheme (#0a1628 background, #f5b731 accents) that doesn't match the Westside marketing site (~/west-side-basketball/), which uses red & black (#d42026, #0a0a0a). Parents coming from the marketing site would think they're on a different website.

Additionally, 34 families have paid $30 via Stripe but we can't send them profile-completion emails yet because the form has no token-based pre-fill. The current flow requires parents to remember which email they used for Stripe — if they don't, dedup breaks and they might get asked to pay again.

Registration tokens solve this: each family gets a unique link (/register?token=abc123) that maps directly to their Stripe payment record. Same pattern as the existing coach invite_token.

File Targets

Files to modify:

  • src/basketball_api/routes/register.py — replace inline blue/gold CSS with red/black Westside brand tokens; add ?token= query param support; pre-fill form from Parent/Player record when token is valid
  • src/basketball_api/routes/coach.py — replace inline CSS with Westside brand
  • src/basketball_api/routes/roster.py — replace inline CSS with Westside brand
  • src/basketball_api/models.py — add registration_token column to Parent model (String(100), unique, nullable)
  • alembic/versions/ — new migration 004 for registration_token column
  • src/basketball_api/routes/admin.py — new file, admin endpoint to generate tokens

CSS source of truth:

  • ~/west-side-basketball/css/style.css — design tokens to extract (lines 7-50: colors, typography, spacing, border radius)

Files NOT to touch:

  • src/basketball_api/routes/webhooks.py — Stripe webhook handler is correct as-is
  • src/basketball_api/services/registration.py — registration service logic unchanged
  • src/basketball_api/routes/health.py — no UI

Acceptance Criteria

  • All server-rendered pages use Westside red/black brand: --color-red: #d42026, --color-black: #0a0a0a, --color-dark: #141414, system font stack. No blue/gold remnants.
  • /register title reads "Westside Kings & Queens — Player Registration"
  • /coach/onboard uses same red/black brand
  • Roster view uses same red/black brand
  • Parent.registration_token column exists (String(100), unique, nullable)
  • GET /register?token=abc123 with valid token → form pre-fills parent name, player name, height, graduating class from existing Stripe data. Shows "Already paid" indicator.
  • GET /register?token=abc123 with invalid token → clear error message with instructions to contact admin
  • GET /register?token=abc123 with no matching parent → clear error
  • GET /register (no token) still works — blank form for walk-ups
  • GET /register (no token) + submit with email matching existing paid parent → pre-fills on re-render, shows "Already paid", no Stripe redirect
  • POST /admin/generate-tokens — generates registration_token for all paid parents who don't have one. Returns count of tokens generated. Auth-protected (admin role).
  • Confirmation page after form submit shows payment status + profile summary
  • All pages mobile-responsive on phone screens

Test Expectations

  • Unit test: token lookup returns correct Parent/Player, pre-fills form values
  • Unit test: invalid token returns error response
  • Unit test: no token → blank form (walk-up flow unchanged)
  • Unit test: email dedup still works for QR code flow (no token, paid parent enters email)
  • Unit test: generate-tokens endpoint creates tokens for paid parents only
  • Unit test: generate-tokens is idempotent (doesn't overwrite existing tokens)
  • Run command: pytest tests/ -v

Constraints

  • Extract CSS design tokens from ~/west-side-basketball/css/style.css — do NOT invent new colors. Use the exact values from the marketing site.
  • Token format: use secrets.token_urlsafe(32) — same approach as coach invite_token
  • Registration token does NOT expire (unlike coach invite_token which has 7-day TTL). Parents should be able to click their link anytime.
  • The /register form must handle all three entry paths in a single route: (1) token pre-fill, (2) email dedup for QR walk-ups, (3) blank form for new walk-ups
  • Admin endpoint must be auth-protected — only admin role can generate tokens
  • Alembic migration must be safe to run on existing data (nullable column, no data loss)

Checklist

  • PR opened
  • Tests pass
  • No unrelated changes
  • Ruff format + lint clean
  • project-westside-basketball
  • plan-2026-03-08-tryout-prep → Phase 3a
  • Unblocks Phase 3b (email blast — needs tokens + branded pages)
### Lineage `plan-2026-03-08-tryout-prep` → Phase 3a ### Repo `forgejo_admin/basketball-api` ### User Story As a parent who paid via Stripe, I want to click a personalized link and see a branded Westside registration form pre-filled with my child's info, So that I can complete their tryout profile without guessing emails or seeing a generic-looking page. ### Context The registration form, coach onboarding, and roster pages are live at `https://basketball-api.tail5b443a.ts.net` but use a blue/gold color scheme (`#0a1628` background, `#f5b731` accents) that doesn't match the Westside marketing site (`~/west-side-basketball/`), which uses red & black (`#d42026`, `#0a0a0a`). Parents coming from the marketing site would think they're on a different website. Additionally, 34 families have paid $30 via Stripe but we can't send them profile-completion emails yet because the form has no token-based pre-fill. The current flow requires parents to remember which email they used for Stripe — if they don't, dedup breaks and they might get asked to pay again. Registration tokens solve this: each family gets a unique link (`/register?token=abc123`) that maps directly to their Stripe payment record. Same pattern as the existing coach `invite_token`. ### File Targets Files to modify: - `src/basketball_api/routes/register.py` — replace inline blue/gold CSS with red/black Westside brand tokens; add `?token=` query param support; pre-fill form from Parent/Player record when token is valid - `src/basketball_api/routes/coach.py` — replace inline CSS with Westside brand - `src/basketball_api/routes/roster.py` — replace inline CSS with Westside brand - `src/basketball_api/models.py` — add `registration_token` column to Parent model (String(100), unique, nullable) - `alembic/versions/` — new migration 004 for registration_token column - `src/basketball_api/routes/admin.py` — new file, admin endpoint to generate tokens CSS source of truth: - `~/west-side-basketball/css/style.css` — design tokens to extract (lines 7-50: colors, typography, spacing, border radius) Files NOT to touch: - `src/basketball_api/routes/webhooks.py` — Stripe webhook handler is correct as-is - `src/basketball_api/services/registration.py` — registration service logic unchanged - `src/basketball_api/routes/health.py` — no UI ### Acceptance Criteria - [ ] All server-rendered pages use Westside red/black brand: `--color-red: #d42026`, `--color-black: #0a0a0a`, `--color-dark: #141414`, system font stack. No blue/gold remnants. - [ ] `/register` title reads "Westside Kings & Queens — Player Registration" - [ ] `/coach/onboard` uses same red/black brand - [ ] Roster view uses same red/black brand - [ ] `Parent.registration_token` column exists (String(100), unique, nullable) - [ ] `GET /register?token=abc123` with valid token → form pre-fills parent name, player name, height, graduating class from existing Stripe data. Shows "Already paid" indicator. - [ ] `GET /register?token=abc123` with invalid token → clear error message with instructions to contact admin - [ ] `GET /register?token=abc123` with no matching parent → clear error - [ ] `GET /register` (no token) still works — blank form for walk-ups - [ ] `GET /register` (no token) + submit with email matching existing paid parent → pre-fills on re-render, shows "Already paid", no Stripe redirect - [ ] `POST /admin/generate-tokens` — generates `registration_token` for all paid parents who don't have one. Returns count of tokens generated. Auth-protected (admin role). - [ ] Confirmation page after form submit shows payment status + profile summary - [ ] All pages mobile-responsive on phone screens ### Test Expectations - [ ] Unit test: token lookup returns correct Parent/Player, pre-fills form values - [ ] Unit test: invalid token returns error response - [ ] Unit test: no token → blank form (walk-up flow unchanged) - [ ] Unit test: email dedup still works for QR code flow (no token, paid parent enters email) - [ ] Unit test: generate-tokens endpoint creates tokens for paid parents only - [ ] Unit test: generate-tokens is idempotent (doesn't overwrite existing tokens) - Run command: `pytest tests/ -v` ### Constraints - Extract CSS design tokens from `~/west-side-basketball/css/style.css` — do NOT invent new colors. Use the exact values from the marketing site. - Token format: use `secrets.token_urlsafe(32)` — same approach as coach invite_token - Registration token does NOT expire (unlike coach invite_token which has 7-day TTL). Parents should be able to click their link anytime. - The `/register` form must handle all three entry paths in a single route: (1) token pre-fill, (2) email dedup for QR walk-ups, (3) blank form for new walk-ups - Admin endpoint must be auth-protected — only admin role can generate tokens - Alembic migration must be safe to run on existing data (nullable column, no data loss) ### Checklist - [ ] PR opened - [ ] Tests pass - [ ] No unrelated changes - [ ] Ruff format + lint clean ### Related - `project-westside-basketball` - `plan-2026-03-08-tryout-prep` → Phase 3a - Unblocks Phase 3b (email blast — needs tokens + branded pages)
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#19
No description provided.