brackt/app/components/league/WizardStepper.tsx
Chris Parsons e201ecd28a
Extract reusable league wizard components; wire settings page (#350)
* Extract reusable league wizard components; wire settings page (#103)

Extracts 9 domain components from new.tsx and $leagueId.settings.tsx into
app/components/league/ (each with a Storybook story), consolidates wizard
form-building into wizard-state.ts, and updates the settings page to use
the shared components instead of hand-rolled duplicates. new.tsx shrinks
from ~2000 → ~1280 lines.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Fix sports-season test: add participants to mock data

findDraftableSportsSeasons now returns participantCount, which requires
participants in the mock db response.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Fix sports-season test: loosen makeMockDb type to Record<string, unknown>[]

typeof mockSeasons became too strict after adding participants to the mock
data, breaking inline arrays in other tests that don't include participants.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-28 22:24:13 -07:00

63 lines
2.2 KiB
TypeScript

import { Fragment } from "react";
import { Check } from "lucide-react";
import { cn } from "~/lib/utils";
export interface WizardStepperProps {
currentStep: number;
steps: string[];
onStepClick: (n: number) => void;
}
export function WizardStepper({ currentStep, steps, onStepClick }: WizardStepperProps) {
return (
<div className="mb-6">
<div className="flex items-center">
{steps.map((label, i) => {
const n = i + 1;
const done = n < currentStep;
const active = n === currentStep;
return (
<Fragment key={label}>
{i > 0 && (
<div className={cn("flex-1 h-0.5 transition-colors", done ? "bg-primary" : "bg-border")} />
)}
<button
type="button"
onClick={() => done && onStepClick(n)}
className={cn(
"shrink-0 w-9 h-9 rounded-full border-2 flex items-center justify-center text-sm font-semibold bg-background transition-colors",
done && "bg-primary border-primary text-primary-foreground",
active && !done && "border-primary text-primary",
!done && !active && "border-border text-muted-foreground",
done ? "cursor-pointer" : "cursor-default"
)}
>
{done ? <Check className="h-4 w-4" /> : n}
</button>
</Fragment>
);
})}
</div>
<div className="flex mt-1.5">
{steps.map((label, i) => {
const n = i + 1;
const done = n < currentStep;
const active = n === currentStep;
return (
<Fragment key={label}>
{i > 0 && <div className="flex-1" />}
<div className="shrink-0 w-9 flex justify-center">
<span className={cn(
"text-xs hidden sm:block whitespace-nowrap",
active ? "text-primary font-medium" : done ? "text-foreground" : "text-muted-foreground"
)}>
{label}
</span>
</div>
</Fragment>
);
})}
</div>
</div>
);
}