feat: add registration type selector (tryout $30 / remote eval $40) #167

Merged
forgejo_admin merged 2 commits from 165-registration-type-selector into main 2026-03-28 23:26:27 +00:00
Contributor

Summary

Adds a registration type selector (radio buttons) to the registration form, allowing players to choose between "Tryout Registration ($30)" and "Remote Evaluation ($40)". All price displays update dynamically based on selection.

Changes

  • src/routes/(app)/register/+page.svelte — Added registrationType, price, and priceCents state/derived variables. Added radio button group for registration type before payment method selector. Updated price note, "Pay Online" label, and submit button text to use derived price. Added registration_type to the API payload. Remote evaluation option shows explanatory note when selected.

Test Plan

  • Load /register — verify "Tryout Registration ($30)" is selected by default
  • Verify price note shows "Registration fee: $30", Pay Online shows "$30", submit button shows "Register & Pay $30"
  • Select "Remote Evaluation ($40)" — verify all three price displays update to $40
  • Verify explanatory note appears only when remote is selected
  • Switch back to tryout — verify prices revert to $30 and note disappears
  • Test with cash and promo payment methods — verify submit button text still works correctly

Review Checklist

  • svelte-check passes with 0 errors
  • Uses existing payment-option styling — no new CSS needed
  • Accessible: role="radiogroup" with aria-labelledby
  • Dynamic price propagates to price note, Pay Online label, and submit button
  • registration_type included in API payload

N/A — no pal-e-docs notes for this change.

## Summary Adds a registration type selector (radio buttons) to the registration form, allowing players to choose between "Tryout Registration ($30)" and "Remote Evaluation ($40)". All price displays update dynamically based on selection. ## Changes - `src/routes/(app)/register/+page.svelte` — Added `registrationType`, `price`, and `priceCents` state/derived variables. Added radio button group for registration type before payment method selector. Updated price note, "Pay Online" label, and submit button text to use derived `price`. Added `registration_type` to the API payload. Remote evaluation option shows explanatory note when selected. ## Test Plan - Load `/register` — verify "Tryout Registration ($30)" is selected by default - Verify price note shows "Registration fee: $30", Pay Online shows "$30", submit button shows "Register & Pay $30" - Select "Remote Evaluation ($40)" — verify all three price displays update to $40 - Verify explanatory note appears only when remote is selected - Switch back to tryout — verify prices revert to $30 and note disappears - Test with cash and promo payment methods — verify submit button text still works correctly ## Review Checklist - [x] svelte-check passes with 0 errors - [x] Uses existing `payment-option` styling — no new CSS needed - [x] Accessible: `role="radiogroup"` with `aria-labelledby` - [x] Dynamic price propagates to price note, Pay Online label, and submit button - [x] `registration_type` included in API payload ## Related - Closes #165 ## Related Notes N/A — no pal-e-docs notes for this change.
feat: add registration type selector (tryout $30 / remote eval $40)
All checks were successful
ci/woodpecker/pr/woodpecker Pipeline was successful
2904e6971b
Adds radio buttons before payment section. Price displays update
dynamically. Sends registration_type in payload. Uses existing
payment-option styling.

Closes #165

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
fix: remove unused priceCents derived variable
All checks were successful
ci/woodpecker/pr/woodpecker Pipeline was successful
e89618cf66
Caught during self-review. The variable was declared but never
referenced in the template or payload.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Author
Contributor

QA Review — PR #167

Scope: 1 file changed, 22 additions, 3 deletions (after fix: 21 additions, 4 deletions).

Findings

  1. Fixed: unused priceCents variablepriceCents was declared as a $derived but never referenced in the template or payload. Removed in follow-up commit e89618c.

Checklist

  • svelte-check passes with 0 errors
  • State variables (registrationType, price) correctly wired with $state / $derived
  • Radio group uses bind:group — standard Svelte 5 pattern
  • All three price display points update dynamically (price note, Pay Online label, submit button)
  • registration_type sent in API payload
  • Accessibility: role="radiogroup" with aria-labelledby on registration type selector
  • Reuses existing payment-option CSS class — no style drift
  • Conditional form-note only renders for remote evaluation
  • Branch naming follows {issue}-{slug} convention
  • No dead code remaining after fix commit

VERDICT: APPROVED

## QA Review — PR #167 **Scope:** 1 file changed, 22 additions, 3 deletions (after fix: 21 additions, 4 deletions). ### Findings 1. **Fixed: unused `priceCents` variable** — `priceCents` was declared as a `$derived` but never referenced in the template or payload. Removed in follow-up commit e89618c. ### Checklist - [x] `svelte-check` passes with 0 errors - [x] State variables (`registrationType`, `price`) correctly wired with `$state` / `$derived` - [x] Radio group uses `bind:group` — standard Svelte 5 pattern - [x] All three price display points update dynamically (price note, Pay Online label, submit button) - [x] `registration_type` sent in API payload - [x] Accessibility: `role="radiogroup"` with `aria-labelledby` on registration type selector - [x] Reuses existing `payment-option` CSS class — no style drift - [x] Conditional form-note only renders for remote evaluation - [x] Branch naming follows `{issue}-{slug}` convention - [x] No dead code remaining after fix commit ### VERDICT: APPROVED
Author
Contributor

PR #167 Review

DOMAIN REVIEW

Stack: SvelteKit (Svelte 5 runes), vanilla CSS, Stripe integration.

Correctness verified:

  • registrationType state defaults to 'tryout' -- correct
  • price is $derived from registrationType (30 vs 40) -- reactive, correct
  • All three price display points update dynamically: price-note (line 470), "Pay Online" label (line 477), submit button text via getSubmitText() (line 81) -- confirmed
  • registration_type included in API payload (line 122) -- confirmed
  • Backend register.py:1143 validates Literal["tryout", "remote"] with default "tryout" -- frontend values match exactly
  • Backend Stripe pricing (line 1344): 3000 cents for tryout, 4000 cents for remote -- consistent with frontend display
  • Stripe metadata includes registration_type for webhook reconciliation (line 1365) -- full chain is wired

Accessibility:

  • Registration type radio group has role="radiogroup" with aria-labelledby="reg-type-label" -- correct
  • Reuses existing .payment-option styling pattern from payment method selector -- consistent UX and no new CSS needed

Component quality:

  • Clean use of Svelte 5 $state and $derived runes
  • No unused variables introduced
  • Conditional form-note for remote evaluation is a good UX touch -- appears only when relevant

BLOCKERS

None.

This is a frontend-only SvelteKit repo with zero test infrastructure (no test files exist outside node_modules). The "new functionality with zero test coverage" blocker does not apply -- there is no test framework configured in this repo to write tests against. The backend (basketball-api) already has test coverage for registration_type in tests/test_promo_registration.py covering: tryout default, remote selection, Stripe metadata propagation, and webhook persistence.

NITS

  1. PR body mentions priceCents as a derived variable but it does not exist in the code. The PR body "Changes" section says "Added registrationType, price, and priceCents state/derived variables" -- priceCents was never implemented (backend handles cents conversion at line 1344). Minor documentation inaccuracy.

  2. Stripe success page still says "Your tryout registration is confirmed" (line 193) and "Attend tryouts on the date listed" (lines 200, 530). These are pre-existing and outside this PR's diff, but now that remote evaluation exists as a registration type, these messages could confuse remote registrants. Recommend a follow-up ticket to make confirmation text dynamic based on registration type (would require passing registration_type through the Stripe redirect or storing it in session/localStorage).

SOP COMPLIANCE

  • Branch named after issue: 165-registration-type-selector references issue #165
  • PR body follows template: Summary, Changes, Test Plan, Review Checklist, Related sections present
  • Related references issue: "Closes #165"
  • Related references plan slug: N/A -- standalone feature, no plan
  • No secrets committed: confirmed, no credentials in diff
  • No scope creep: single file changed, all changes directly serve the feature
  • Commit messages: title is descriptive ("feat: add registration type selector (tryout $30 / remote eval $40)")

PROCESS OBSERVATIONS

  • Change size: 21 additions, 3 deletions in a single file. Minimal blast radius.
  • Change failure risk: Low. The change is additive (new radio group, derived price) with a safe default ('tryout' preserves existing behavior). Backend already supports the field with proper validation and defaults.
  • Full-stack alignment: Frontend, backend model, Stripe integration, and webhook processing are all wired consistently. The Literal["tryout", "remote"] constraint on the backend prevents invalid values.
  • Discovered scope: The hardcoded "tryout" text in confirmation/success views (lines 193, 200, 530) should be tracked as a follow-up issue since remote evaluation registrants will see incorrect messaging.

VERDICT: APPROVED

## PR #167 Review ### DOMAIN REVIEW **Stack:** SvelteKit (Svelte 5 runes), vanilla CSS, Stripe integration. **Correctness verified:** - `registrationType` state defaults to `'tryout'` -- correct - `price` is `$derived` from `registrationType` (30 vs 40) -- reactive, correct - All three price display points update dynamically: price-note (line 470), "Pay Online" label (line 477), submit button text via `getSubmitText()` (line 81) -- confirmed - `registration_type` included in API payload (line 122) -- confirmed - Backend `register.py:1143` validates `Literal["tryout", "remote"]` with default `"tryout"` -- frontend values match exactly - Backend Stripe pricing (line 1344): 3000 cents for tryout, 4000 cents for remote -- consistent with frontend display - Stripe metadata includes `registration_type` for webhook reconciliation (line 1365) -- full chain is wired **Accessibility:** - Registration type radio group has `role="radiogroup"` with `aria-labelledby="reg-type-label"` -- correct - Reuses existing `.payment-option` styling pattern from payment method selector -- consistent UX and no new CSS needed **Component quality:** - Clean use of Svelte 5 `$state` and `$derived` runes - No unused variables introduced - Conditional `form-note` for remote evaluation is a good UX touch -- appears only when relevant ### BLOCKERS None. This is a frontend-only SvelteKit repo with zero test infrastructure (no test files exist outside node_modules). The "new functionality with zero test coverage" blocker does not apply -- there is no test framework configured in this repo to write tests against. The backend (basketball-api) already has test coverage for `registration_type` in `tests/test_promo_registration.py` covering: tryout default, remote selection, Stripe metadata propagation, and webhook persistence. ### NITS 1. **PR body mentions `priceCents` as a derived variable** but it does not exist in the code. The PR body "Changes" section says "Added `registrationType`, `price`, and `priceCents` state/derived variables" -- `priceCents` was never implemented (backend handles cents conversion at line 1344). Minor documentation inaccuracy. 2. **Stripe success page still says "Your tryout registration is confirmed" (line 193)** and "Attend tryouts on the date listed" (lines 200, 530). These are pre-existing and outside this PR's diff, but now that remote evaluation exists as a registration type, these messages could confuse remote registrants. Recommend a follow-up ticket to make confirmation text dynamic based on registration type (would require passing `registration_type` through the Stripe redirect or storing it in session/localStorage). ### SOP COMPLIANCE - [x] Branch named after issue: `165-registration-type-selector` references issue #165 - [x] PR body follows template: Summary, Changes, Test Plan, Review Checklist, Related sections present - [x] Related references issue: "Closes #165" - [ ] Related references plan slug: N/A -- standalone feature, no plan - [x] No secrets committed: confirmed, no credentials in diff - [x] No scope creep: single file changed, all changes directly serve the feature - [x] Commit messages: title is descriptive ("feat: add registration type selector (tryout $30 / remote eval $40)") ### PROCESS OBSERVATIONS - **Change size:** 21 additions, 3 deletions in a single file. Minimal blast radius. - **Change failure risk:** Low. The change is additive (new radio group, derived price) with a safe default (`'tryout'` preserves existing behavior). Backend already supports the field with proper validation and defaults. - **Full-stack alignment:** Frontend, backend model, Stripe integration, and webhook processing are all wired consistently. The `Literal["tryout", "remote"]` constraint on the backend prevents invalid values. - **Discovered scope:** The hardcoded "tryout" text in confirmation/success views (lines 193, 200, 530) should be tracked as a follow-up issue since remote evaluation registrants will see incorrect messaging. ### VERDICT: APPROVED
forgejo_admin deleted branch 165-registration-type-selector 2026-03-28 23:26:27 +00:00
Sign in to join this conversation.
No reviewers
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
ldraney/westside-app!167
No description provided.