'use client'; import type { LeagueConfigFormModel } from '@/lib/types/LeagueConfigFormModel'; import { Checkbox } from '@/ui/Checkbox'; import { Heading } from '@/ui/Heading'; import { Icon } from '@/ui/Icon'; import { Stack } from '@/ui/Stack'; import { Select } from '@/ui/Select'; import { Text } from '@/ui/Text'; import { AlertTriangle, Bell, Clock, Scale, Shield, Vote } from 'lucide-react'; import React from 'react'; interface LeagueStewardingSectionProps { form: LeagueConfigFormModel; onChange: (form: LeagueConfigFormModel) => void; readOnly?: boolean; } type DecisionModeOption = { value: NonNullable['decisionMode']; label: string; description: string; icon: React.ReactNode; requiresVotes: boolean; }; const decisionModeOptions: DecisionModeOption[] = [ { value: 'single_steward', label: 'Single Steward', description: 'A single steward/admin makes all penalty decisions', icon: , requiresVotes: false, }, { value: 'committee_vote', label: 'Committee Vote', description: 'A group votes to uphold/dismiss protests', icon: , requiresVotes: true, }, ]; export function LeagueStewardingSection({ form, onChange, readOnly = false, }: LeagueStewardingSectionProps) { // Provide default stewarding settings if not present const stewarding = form.stewarding ?? { decisionMode: 'single_steward' as const, requiredVotes: 2, requireDefense: false, defenseTimeLimit: 48, voteTimeLimit: 72, protestDeadlineHours: 48, stewardingClosesHours: 168, notifyAccusedOnProtest: true, notifyOnVoteRequired: true, }; const updateStewarding = (updates: Partial>) => { onChange({ ...form, stewarding: { ...stewarding, ...updates, }, }); }; const selectedMode = decisionModeOptions.find((m) => m.value === stewarding.decisionMode); return ( {/* Decision Mode Selection */} How are protest decisions made? Choose who has the authority to issue penalties {decisionModeOptions.map((option) => ( updateStewarding({ decisionMode: option.value })} position="relative" display="flex" flexDirection="col" alignItems="start" gap={2} p={4} rounded="xl" border borderWidth="2px" transition textAlign="left" borderColor={stewarding.decisionMode === option.value ? 'border-primary-blue' : 'border-charcoal-outline'} bg={stewarding.decisionMode === option.value ? 'bg-primary-blue/5' : 'bg-iron-gray/30'} shadow={stewarding.decisionMode === option.value ? '0_0_16px_rgba(25,140,255,0.15)' : undefined} hoverBorderColor={!readOnly && stewarding.decisionMode !== option.value ? 'border-gray-500' : undefined} opacity={readOnly ? 0.6 : 1} cursor={readOnly ? 'not-allowed' : 'pointer'} > {option.icon} {option.label} {option.description} {stewarding.decisionMode === option.value && ( )} ))} {/* Vote Requirements (conditional) */} {selectedMode?.requiresVotes && ( Voting Configuration Required votes to uphold updateStewarding({ voteTimeLimit: parseInt(e.target.value, 10) })} disabled={readOnly} options={[ { value: '24', label: '24 hours' }, { value: '48', label: '48 hours' }, { value: '72', label: '72 hours (3 days)' }, { value: '96', label: '96 hours (4 days)' }, { value: '168', label: '168 hours (7 days)' }, ]} /> )} {/* Defense Settings */} Defense Requirements Should accused drivers be required to submit a defense? updateStewarding({ requireDefense: false })} display="flex" alignItems="center" gap={3} p={4} rounded="xl" border borderWidth="2px" transition textAlign="left" borderColor={!stewarding.requireDefense ? 'border-primary-blue' : 'border-charcoal-outline'} bg={!stewarding.requireDefense ? 'bg-primary-blue/5' : 'bg-iron-gray/30'} hoverBorderColor={!readOnly && stewarding.requireDefense ? 'border-gray-500' : undefined} opacity={readOnly ? 0.6 : 1} cursor={readOnly ? 'not-allowed' : 'pointer'} > {!stewarding.requireDefense && } Defense optional Proceed without waiting for defense updateStewarding({ requireDefense: true })} display="flex" alignItems="center" gap={3} p={4} rounded="xl" border borderWidth="2px" transition textAlign="left" borderColor={stewarding.requireDefense ? 'border-primary-blue' : 'border-charcoal-outline'} bg={stewarding.requireDefense ? 'bg-primary-blue/5' : 'bg-iron-gray/30'} hoverBorderColor={!readOnly && !stewarding.requireDefense ? 'border-gray-500' : undefined} opacity={readOnly ? 0.6 : 1} cursor={readOnly ? 'not-allowed' : 'pointer'} > {stewarding.requireDefense && } Defense required Wait for defense before deciding {stewarding.requireDefense && ( Defense time limit updateStewarding({ protestDeadlineHours: parseInt(e.target.value, 10) })} disabled={readOnly} options={[ { value: '12', label: '12 hours' }, { value: '24', label: '24 hours (1 day)' }, { value: '48', label: '48 hours (2 days)' }, { value: '72', label: '72 hours (3 days)' }, { value: '168', label: '168 hours (7 days)' }, ]} /> Drivers cannot file protests after this time Stewarding closes (after race)