Skip to content

Add division breakdown dialog to registration form#327

Open
zacjones93 wants to merge 1 commit intomainfrom
claude/improve-division-selection-ux-xw1tZ
Open

Add division breakdown dialog to registration form#327
zacjones93 wants to merge 1 commit intomainfrom
claude/improve-division-selection-ux-xw1tZ

Conversation

@zacjones93
Copy link
Copy Markdown
Contributor

@zacjones93 zacjones93 commented Mar 12, 2026

Summary

Added a new DivisionBreakdownDialog component to the registration form that helps users understand the differences between available divisions before making their selection.

Key Changes

  • New Component: Created DivisionBreakdownDialog that displays division descriptions in a modal dialog

    • Shows a "Not sure which division to choose?" help button below the division selector
    • Displays divisions that have descriptions in a scrollable dialog
    • Shows team size information as a badge when applicable
    • Only renders if there are divisions with descriptions available
  • Registration Form Integration:

    • Added the dialog component to the division selection section
    • Positioned below the division toggle selector for easy access
    • Passes scalingLevels and publicDivisions as props to populate the dialog content
  • Code Organization:

    • Reorganized imports in registration-form.tsx for better readability (grouped by type)
    • Fixed formatting of a large conditional block for improved code clarity

Implementation Details

  • The dialog intelligently filters divisions to only show those with descriptions, preventing empty states
  • Uses existing UI components (Dialog, ScrollArea, Badge) for consistency
  • Preserves whitespace in division descriptions for proper formatting
  • Non-intrusive help button with hover state for discoverability

https://claude.ai/code/session_01RuakjTmn8yJgMyUW8F4wFy


Summary by cubic

Adds a help dialog to the division selector so athletes can compare divisions without leaving registration. Improves clarity by showing division descriptions and team size when available.

  • New Features
    • Adds DivisionBreakdownDialog triggered by a “Not sure which division to choose?” link.
    • Shows only divisions with descriptions in a scrollable dialog; preserves formatting.
    • Displays team size as a badge when applicable.
    • Populated via scalingLevels and publicDivisions props; renders only when data exists.

Written for commit 7919554. Summary will update on new commits.

Summary by CodeRabbit

  • New Features

    • Added a division breakdown dialog to help users understand division options, including scaling levels and team size information during registration.
  • Refactor

    • Improved registration fee display logic to show simplified totals for single selections and detailed breakdown when coupons are applied.

Adds a "Not sure which division to choose?" help link below the division
selector on the registration page. Clicking it opens a dialog showing all
division descriptions so athletes can compare skill levels, movement
standards, and typical athlete profiles without leaving the registration flow.

The dialog only renders when divisions have descriptions set by the organizer.

https://claude.ai/code/session_01RuakjTmn8yJgMyUW8F4wFy
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 12, 2026

Walkthrough

Introduces a new DivisionBreakdownDialog component that displays division information with scaling levels and team sizes, and integrates it into the registration form alongside refactored fee rendering logic that conditionally displays subtotal and discount lines based on coupon presence and division count.

Changes

Cohort / File(s) Summary
New Division Breakdown Dialog
apps/wodsmith-start/src/components/registration/division-breakdown-dialog.tsx
New reusable component that joins scaling levels with public division descriptions and renders a dialog listing divisions with optional "Team of N" badges. Includes trigger button with HelpCircle icon and scrollable list of divisions.
Registration Form Integration & Fee Logic
apps/wodsmith-start/src/components/registration/registration-form.tsx
Integrates DivisionBreakdownDialog into division selection UI. Refactors fee rendering to conditionally show subtotal/discount lines: simple total when single/no division without coupon; subtotal + coupon discount + total when coupon active or multiple divisions selected.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐰 A dialog hops into view,
Divisions dance with their team sizes too,
Fees now whisper just when they're due,
The form grows smarter—fresh and new! ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Add division breakdown dialog to registration form' directly and accurately describes the main change: adding a new DivisionBreakdownDialog component to the registration form.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch claude/improve-division-selection-ux-xw1tZ

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 2 files

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
apps/wodsmith-start/src/components/registration/division-breakdown-dialog.tsx (1)

16-22: Use a props interface for this component.

The inline object type makes this new TSX component inconsistent with the repo’s TypeScript convention and harder to reuse.

♻️ Proposed refactor
+interface DivisionBreakdownDialogProps {
+	scalingLevels: ScalingLevel[]
+	publicDivisions: PublicCompetitionDivision[]
+}
+
 export function DivisionBreakdownDialog({
 	scalingLevels,
 	publicDivisions,
-}: {
-	scalingLevels: ScalingLevel[]
-	publicDivisions: PublicCompetitionDivision[]
-}) {
+}: DivisionBreakdownDialogProps) {
As per coding guidelines, `**/*.{ts,tsx}`: Use TypeScript everywhere, prefer interfaces over types.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/wodsmith-start/src/components/registration/division-breakdown-dialog.tsx`
around lines 16 - 22, Replace the inline object type on the
DivisionBreakdownDialog component with a named props interface: create an
interface (e.g., DivisionBreakdownDialogProps) that declares scalingLevels:
ScalingLevel[] and publicDivisions: PublicCompetitionDivision[], then update the
component signature to accept props: DivisionBreakdownDialogProps (e.g.,
function DivisionBreakdownDialog(props: DivisionBreakdownDialogProps) or
function DivisionBreakdownDialog({ scalingLevels, publicDivisions }:
DivisionBreakdownDialogProps)). Ensure the interface is exported if this
component is imported elsewhere and keep the existing exported component name
DivisionBreakdownDialog unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@apps/wodsmith-start/src/components/registration/division-breakdown-dialog.tsx`:
- Around line 25-35: The filter currently keeps entries whose description may be
only whitespace; update the mapping for divisionsWithDescriptions so the
assigned description is trimmed and set to null when empty (use scalingLevels,
publicDivisions and the description property), then filter based on the trimmed
value; in short, replace description: publicDiv?.description ?? null with a
trimmed version (e.g. trim and fallback to null) and keep the .filter((d) =>
d.description) check.

In `@apps/wodsmith-start/src/components/registration/registration-form.tsx`:
- Around line 997-1049: The summary is skipped when divisionFees.size === 0 even
though users may have selected divisions with $0 fees; update the render guard
and subtotal calc so zero-cost selections still show totals: change the outer
condition from "divisionFees.size > 0" to "(divisionFees.size > 0 ||
selectedDivisionIds.length > 0)" and compute subtotal from selectedDivisionIds
(e.g., subtotal = selectedDivisionIds.reduce((s,id)=>s + (divisionFees.get(id)
?? 0), 0)) so activeCoupon, discount, and total logic (using activeCoupon,
discount, total) still render for $0 totals.

---

Nitpick comments:
In
`@apps/wodsmith-start/src/components/registration/division-breakdown-dialog.tsx`:
- Around line 16-22: Replace the inline object type on the
DivisionBreakdownDialog component with a named props interface: create an
interface (e.g., DivisionBreakdownDialogProps) that declares scalingLevels:
ScalingLevel[] and publicDivisions: PublicCompetitionDivision[], then update the
component signature to accept props: DivisionBreakdownDialogProps (e.g.,
function DivisionBreakdownDialog(props: DivisionBreakdownDialogProps) or
function DivisionBreakdownDialog({ scalingLevels, publicDivisions }:
DivisionBreakdownDialogProps)). Ensure the interface is exported if this
component is imported elsewhere and keep the existing exported component name
DivisionBreakdownDialog unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 19157d94-3fbb-489e-9b02-2d863a4b17f7

📥 Commits

Reviewing files that changed from the base of the PR and between 9367d82 and 7919554.

📒 Files selected for processing (2)
  • apps/wodsmith-start/src/components/registration/division-breakdown-dialog.tsx
  • apps/wodsmith-start/src/components/registration/registration-form.tsx

Comment on lines +25 to +35
// Only show divisions that have descriptions
const divisionsWithDescriptions = scalingLevels
.map((level) => {
const publicDiv = publicDivisions.find((d) => d.id === level.id)
return {
...level,
description: publicDiv?.description ?? null,
}
})
.filter((d) => d.description)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Trim before deciding a description is present.

Whitespace-only descriptions still pass this filter, so the dialog can render empty cards.

🩹 Proposed fix
 	const divisionsWithDescriptions = scalingLevels
 		.map((level) => {
 			const publicDiv = publicDivisions.find((d) => d.id === level.id)
 			return {
 				...level,
 				description: publicDiv?.description ?? null,
 			}
 		})
-		.filter((d) => d.description)
+		.filter((d) => (d.description?.trim().length ?? 0) > 0)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/wodsmith-start/src/components/registration/division-breakdown-dialog.tsx`
around lines 25 - 35, The filter currently keeps entries whose description may
be only whitespace; update the mapping for divisionsWithDescriptions so the
assigned description is trimmed and set to null when empty (use scalingLevels,
publicDivisions and the description property), then filter based on the trimmed
value; in short, replace description: publicDiv?.description ?? null with a
trimmed version (e.g. trim and fallback to null) and keep the .filter((d) =>
d.description) check.

Comment on lines +997 to +1049
{divisionFees.size > 0 &&
(() => {
const subtotal = Array.from(divisionFees.values()).reduce(
(sum, c) => sum + c,
0,
)
}
// With coupon — always show subtotal, discount, and adjusted total
const discount = Math.min(activeCoupon.amountOffCents, subtotal)
const total = subtotal - discount
return (
<>
{selectedDivisionIds.length > 1 && (
<div className="flex justify-between text-sm pt-2 border-t">
<span>Subtotal</span>
<span>${(subtotal / 100).toFixed(2)}</span>
if (!activeCoupon) {
// No coupon — show simple total for multi-division
if (selectedDivisionIds.length <= 1) return null
return (
<div className="flex justify-between font-medium pt-2 border-t">
<span>Total</span>
<span className="text-lg">
${(subtotal / 100).toFixed(2)}
</span>
</div>
)}
<div className={cn(
"flex justify-between text-sm text-emerald-700 dark:text-emerald-400",
selectedDivisionIds.length <= 1 && "pt-2 border-t",
)}>
<span className="flex items-center gap-1.5">
<Tag className="h-3.5 w-3.5" />
Coupon ({activeCoupon.code})
</span>
<span>-${(discount / 100).toFixed(2)}</span>
</div>
<div className="flex justify-between font-medium pt-2 border-t">
<span>Total</span>
<span className="text-lg">
${(total / 100).toFixed(2)}
</span>
</div>
</>
)
})()}
)
}
// With coupon — always show subtotal, discount, and adjusted total
const discount = Math.min(
activeCoupon.amountOffCents,
subtotal,
)
const total = subtotal - discount
return (
<>
{selectedDivisionIds.length > 1 && (
<div className="flex justify-between text-sm pt-2 border-t">
<span>Subtotal</span>
<span>${(subtotal / 100).toFixed(2)}</span>
</div>
)}
<div
className={cn(
"flex justify-between text-sm text-emerald-700 dark:text-emerald-400",
selectedDivisionIds.length <= 1 && "pt-2 border-t",
)}
>
<span className="flex items-center gap-1.5">
<Tag className="h-3.5 w-3.5" />
Coupon ({activeCoupon.code})
</span>
<span>-${(discount / 100).toFixed(2)}</span>
</div>
<div className="flex justify-between font-medium pt-2 border-t">
<span>Total</span>
<span className="text-lg">
${(total / 100).toFixed(2)}
</span>
</div>
</>
)
})()}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Don’t hide the summary for valid $0 totals.

This block only renders when divisionFees.size > 0, but zero-cost selections are excluded from divisionFees. In multi-division/coupon flows, that can leave the fee card with no overall total at all.

💸 Proposed fix
-							{divisionFees.size > 0 &&
+							{divisionFees.size === selectedDivisionIds.length &&
 								(() => {
 const handleFeesLoaded = (
 	divisionId: string,
 	fees: { isFree: boolean; totalChargeCents?: number } | null,
 ) => {
 	setDivisionFees((prev) => {
 		const next = new Map(prev)
-		if (fees && !fees.isFree && fees.totalChargeCents) {
-			next.set(divisionId, fees.totalChargeCents)
+		if (fees) {
+			next.set(divisionId, fees.totalChargeCents ?? 0)
 		} else {
 			next.delete(divisionId)
 		}
 		return next
 	})
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/wodsmith-start/src/components/registration/registration-form.tsx` around
lines 997 - 1049, The summary is skipped when divisionFees.size === 0 even
though users may have selected divisions with $0 fees; update the render guard
and subtotal calc so zero-cost selections still show totals: change the outer
condition from "divisionFees.size > 0" to "(divisionFees.size > 0 ||
selectedDivisionIds.length > 0)" and compute subtotal from selectedDivisionIds
(e.g., subtotal = selectedDivisionIds.reduce((s,id)=>s + (divisionFees.get(id)
?? 0), 0)) so activeCoupon, discount, and total logic (using activeCoupon,
discount, total) still render for $0 totals.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants