This commit is contained in:
2025-12-09 22:45:03 +01:00
parent 3adf2e5e94
commit 3659d25e52
20 changed files with 2537 additions and 85 deletions

View File

@@ -15,6 +15,7 @@ import {
AlertCircle,
Sparkles,
Check,
Scale,
} from 'lucide-react';
import Card from '@/components/ui/Card';
import Button from '@/components/ui/Button';
@@ -42,6 +43,7 @@ import {
} from './LeagueScoringSection';
import { LeagueDropSection } from './LeagueDropSection';
import { LeagueTimingsSection } from './LeagueTimingsSection';
import { LeagueStewardingSection } from './LeagueStewardingSection';
// ============================================================================
// LOCAL STORAGE PERSISTENCE
@@ -99,9 +101,9 @@ function getHighestStep(): number {
}
}
type Step = 1 | 2 | 3 | 4 | 5 | 6;
type Step = 1 | 2 | 3 | 4 | 5 | 6 | 7;
type StepName = 'basics' | 'visibility' | 'structure' | 'schedule' | 'scoring' | 'review';
type StepName = 'basics' | 'visibility' | 'structure' | 'schedule' | 'scoring' | 'stewarding' | 'review';
interface CreateLeagueWizardProps {
stepName: StepName;
@@ -120,8 +122,10 @@ function stepNameToStep(stepName: StepName): Step {
return 4;
case 'scoring':
return 5;
case 'review':
case 'stewarding':
return 6;
case 'review':
return 7;
}
}
@@ -138,6 +142,8 @@ function stepToStepName(step: Step): StepName {
case 5:
return 'scoring';
case 6:
return 'stewarding';
case 7:
return 'review';
}
}
@@ -198,6 +204,17 @@ function createDefaultForm(): LeagueConfigFormModel {
timezoneId: 'UTC',
seasonStartDate: getDefaultSeasonStartDate(),
},
stewarding: {
decisionMode: 'admin_only',
requiredVotes: 2,
requireDefense: false,
defenseTimeLimit: 48,
voteTimeLimit: 72,
protestDeadlineHours: 48,
stewardingClosesHours: 168,
notifyAccusedOnProtest: true,
notifyOnVoteRequired: true,
},
};
}
@@ -287,7 +304,7 @@ export default function CreateLeagueWizard({ stepName, onStepChange }: CreateLea
if (!validateStep(step)) {
return;
}
const nextStep = (step < 6 ? ((step + 1) as Step) : step);
const nextStep = (step < 7 ? ((step + 1) as Step) : step);
saveHighestStep(nextStep);
setHighestCompletedStep((prev) => Math.max(prev, nextStep));
onStepChange(stepToStepName(nextStep));
@@ -353,7 +370,8 @@ export default function CreateLeagueWizard({ stepName, onStepChange }: CreateLea
{ id: 3 as Step, label: 'Structure', icon: Users, shortLabel: 'Mode' },
{ id: 4 as Step, label: 'Schedule', icon: Calendar, shortLabel: 'Time' },
{ id: 5 as Step, label: 'Scoring', icon: Trophy, shortLabel: 'Points' },
{ id: 6 as Step, label: 'Review', icon: CheckCircle2, shortLabel: 'Done' },
{ id: 6 as Step, label: 'Stewarding', icon: Scale, shortLabel: 'Rules' },
{ id: 7 as Step, label: 'Review', icon: CheckCircle2, shortLabel: 'Done' },
];
const getStepTitle = (currentStep: Step): string => {
@@ -369,6 +387,8 @@ export default function CreateLeagueWizard({ stepName, onStepChange }: CreateLea
case 5:
return 'Scoring & championships';
case 6:
return 'Stewarding & protests';
case 7:
return 'Review & create';
default:
return '';
@@ -388,6 +408,8 @@ export default function CreateLeagueWizard({ stepName, onStepChange }: CreateLea
case 5:
return 'Select a scoring preset, enable championships, and set drop rules.';
case 6:
return 'Configure how protests are handled and penalties decided.';
case 7:
return 'Everything looks good? Launch your new league!';
default:
return '';
@@ -629,6 +651,16 @@ export default function CreateLeagueWizard({ stepName, onStepChange }: CreateLea
)}
{step === 6 && (
<div className="animate-fade-in">
<LeagueStewardingSection
form={form}
onChange={setForm}
readOnly={false}
/>
</div>
)}
{step === 7 && (
<div className="animate-fade-in space-y-6">
<LeagueReviewSummary form={form} presets={presets} />
{errors.submit && (
@@ -669,7 +701,7 @@ export default function CreateLeagueWizard({ stepName, onStepChange }: CreateLea
))}
</div>
{step < 6 ? (
{step < 7 ? (
<Button
type="button"
variant="primary"