website refactor
This commit is contained in:
@@ -1,8 +1,15 @@
|
||||
'use client';
|
||||
|
||||
import React from 'react';
|
||||
import { Scale, Users, Clock, Bell, Shield, Vote, UserCheck, AlertTriangle } from 'lucide-react';
|
||||
import { Scale, Clock, Bell, Shield, Vote, AlertTriangle } from 'lucide-react';
|
||||
import type { LeagueConfigFormModel } from '@/lib/types/LeagueConfigFormModel';
|
||||
import { Box } from '@/ui/Box';
|
||||
import { Text } from '@/ui/Text';
|
||||
import { Stack } from '@/ui/Stack';
|
||||
import { Heading } from '@/ui/Heading';
|
||||
import { Icon } from '@/ui/Icon';
|
||||
import { Select } from '@/ui/Select';
|
||||
import { Checkbox } from '@/ui/Checkbox';
|
||||
|
||||
interface LeagueStewardingSectionProps {
|
||||
form: LeagueConfigFormModel;
|
||||
@@ -23,14 +30,14 @@ const decisionModeOptions: DecisionModeOption[] = [
|
||||
value: 'single_steward',
|
||||
label: 'Single Steward',
|
||||
description: 'A single steward/admin makes all penalty decisions',
|
||||
icon: <Shield className="w-5 h-5" />,
|
||||
icon: <Icon icon={Shield} size={5} />,
|
||||
requiresVotes: false,
|
||||
},
|
||||
{
|
||||
value: 'committee_vote',
|
||||
label: 'Committee Vote',
|
||||
description: 'A group votes to uphold/dismiss protests',
|
||||
icon: <Scale className="w-5 h-5" />,
|
||||
icon: <Icon icon={Scale} size={5} />,
|
||||
requiresVotes: true,
|
||||
},
|
||||
];
|
||||
@@ -66,305 +73,342 @@ export function LeagueStewardingSection({
|
||||
const selectedMode = decisionModeOptions.find((m) => m.value === stewarding.decisionMode);
|
||||
|
||||
return (
|
||||
<div className="space-y-8">
|
||||
<Stack gap={8}>
|
||||
{/* Decision Mode Selection */}
|
||||
<div>
|
||||
<h3 className="text-sm font-semibold text-white mb-1 flex items-center gap-2">
|
||||
<Scale className="w-4 h-4 text-primary-blue" />
|
||||
How are protest decisions made?
|
||||
</h3>
|
||||
<p className="text-xs text-gray-400 mb-4">
|
||||
<Box>
|
||||
<Heading level={3} fontSize="sm" weight="semibold" color="text-white" mb={1}>
|
||||
<Stack direction="row" align="center" gap={2}>
|
||||
<Icon icon={Scale} size={4} color="text-primary-blue" />
|
||||
How are protest decisions made?
|
||||
</Stack>
|
||||
</Heading>
|
||||
<Text size="xs" color="text-gray-400" mb={4} block>
|
||||
Choose who has the authority to issue penalties
|
||||
</p>
|
||||
</Text>
|
||||
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3">
|
||||
<Box display="grid" gridCols={{ base: 1, sm: 2, lg: 3 }} gap={3}>
|
||||
{decisionModeOptions.map((option) => (
|
||||
<button
|
||||
<Box
|
||||
key={option.value}
|
||||
as="button"
|
||||
type="button"
|
||||
disabled={readOnly}
|
||||
onClick={() => updateStewarding({ decisionMode: option.value })}
|
||||
className={`
|
||||
relative flex flex-col items-start gap-2 p-4 rounded-xl border-2 transition-all text-left
|
||||
${stewarding.decisionMode === option.value
|
||||
? 'border-primary-blue bg-primary-blue/5 shadow-[0_0_16px_rgba(25,140,255,0.15)]'
|
||||
: 'border-charcoal-outline bg-iron-gray/30 hover:border-gray-500'
|
||||
}
|
||||
${readOnly ? 'opacity-60 cursor-not-allowed' : 'cursor-pointer'}
|
||||
`}
|
||||
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'}
|
||||
>
|
||||
<div
|
||||
className={`p-2 rounded-lg ${
|
||||
stewarding.decisionMode === option.value
|
||||
? 'bg-primary-blue/20 text-primary-blue'
|
||||
: 'bg-charcoal-outline/50 text-gray-400'
|
||||
}`}
|
||||
<Box
|
||||
p={2}
|
||||
rounded="lg"
|
||||
bg={stewarding.decisionMode === option.value ? 'bg-primary-blue/20' : 'bg-charcoal-outline/50'}
|
||||
color={stewarding.decisionMode === option.value ? 'text-primary-blue' : 'text-gray-400'}
|
||||
>
|
||||
{option.icon}
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm font-medium text-white">{option.label}</p>
|
||||
<p className="text-xs text-gray-400 mt-0.5">{option.description}</p>
|
||||
</div>
|
||||
</Box>
|
||||
<Box>
|
||||
<Text size="sm" weight="medium" color="text-white" block>{option.label}</Text>
|
||||
<Text size="xs" color="text-gray-400" mt={0.5} block>{option.description}</Text>
|
||||
</Box>
|
||||
{stewarding.decisionMode === option.value && (
|
||||
<div className="absolute top-2 right-2 w-2 h-2 rounded-full bg-primary-blue" />
|
||||
<Box position="absolute" top="2" right="2" w="2" h="2" rounded="full" bg="bg-primary-blue" />
|
||||
)}
|
||||
</button>
|
||||
</Box>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
{/* Vote Requirements (conditional) */}
|
||||
{selectedMode?.requiresVotes && (
|
||||
<div className="p-4 rounded-xl bg-iron-gray/40 border border-charcoal-outline space-y-4">
|
||||
<h4 className="text-sm font-medium text-white flex items-center gap-2">
|
||||
<Vote className="w-4 h-4 text-primary-blue" />
|
||||
Voting Configuration
|
||||
</h4>
|
||||
<Box p={4} rounded="xl" bg="bg-iron-gray/40" border borderColor="border-charcoal-outline">
|
||||
<Stack gap={4}>
|
||||
<Heading level={4} fontSize="sm" weight="medium" color="text-white">
|
||||
<Stack direction="row" align="center" gap={2}>
|
||||
<Icon icon={Vote} size={4} color="text-primary-blue" />
|
||||
Voting Configuration
|
||||
</Stack>
|
||||
</Heading>
|
||||
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label className="block text-xs font-medium text-gray-400 mb-1.5">
|
||||
Required votes to uphold
|
||||
</label>
|
||||
<select
|
||||
value={stewarding.requiredVotes ?? 2}
|
||||
onChange={(e) => updateStewarding({ requiredVotes: parseInt(e.target.value, 10) })}
|
||||
disabled={readOnly}
|
||||
className="w-full px-3 py-2 bg-deep-graphite border border-charcoal-outline rounded-lg text-white text-sm focus:ring-1 focus:ring-primary-blue focus:border-primary-blue"
|
||||
>
|
||||
<option value={1}>1 vote</option>
|
||||
<option value={2}>2 votes</option>
|
||||
<option value={3}>3 votes (majority of 5)</option>
|
||||
<option value={4}>4 votes</option>
|
||||
<option value={5}>5 votes</option>
|
||||
</select>
|
||||
</div>
|
||||
<Box display="grid" gridCols={{ base: 1, sm: 2 }} gap={4}>
|
||||
<Box>
|
||||
<Text as="label" size="xs" weight="medium" color="text-gray-400" block mb={1.5}>
|
||||
Required votes to uphold
|
||||
</Text>
|
||||
<Select
|
||||
value={stewarding.requiredVotes?.toString() ?? '2'}
|
||||
onChange={(e) => updateStewarding({ requiredVotes: parseInt(e.target.value, 10) })}
|
||||
disabled={readOnly}
|
||||
options={[
|
||||
{ value: '1', label: '1 vote' },
|
||||
{ value: '2', label: '2 votes' },
|
||||
{ value: '3', label: '3 votes (majority of 5)' },
|
||||
{ value: '4', label: '4 votes' },
|
||||
{ value: '5', label: '5 votes' },
|
||||
]}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
<div>
|
||||
<label className="block text-xs font-medium text-gray-400 mb-1.5">
|
||||
Voting time limit
|
||||
</label>
|
||||
<select
|
||||
value={stewarding.voteTimeLimit}
|
||||
onChange={(e) => updateStewarding({ voteTimeLimit: parseInt(e.target.value, 10) })}
|
||||
disabled={readOnly}
|
||||
className="w-full px-3 py-2 bg-deep-graphite border border-charcoal-outline rounded-lg text-white text-sm focus:ring-1 focus:ring-primary-blue focus:border-primary-blue"
|
||||
>
|
||||
<option value={24}>24 hours</option>
|
||||
<option value={48}>48 hours</option>
|
||||
<option value={72}>72 hours (3 days)</option>
|
||||
<option value={96}>96 hours (4 days)</option>
|
||||
<option value={168}>168 hours (7 days)</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Box>
|
||||
<Text as="label" size="xs" weight="medium" color="text-gray-400" block mb={1.5}>
|
||||
Voting time limit
|
||||
</Text>
|
||||
<Select
|
||||
value={stewarding.voteTimeLimit?.toString()}
|
||||
onChange={(e) => 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)' },
|
||||
]}
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
{/* Defense Settings */}
|
||||
<div>
|
||||
<h3 className="text-sm font-semibold text-white mb-1 flex items-center gap-2">
|
||||
<Shield className="w-4 h-4 text-primary-blue" />
|
||||
Defense Requirements
|
||||
</h3>
|
||||
<p className="text-xs text-gray-400 mb-4">
|
||||
<Box>
|
||||
<Heading level={3} fontSize="sm" weight="semibold" color="text-white" mb={1}>
|
||||
<Stack direction="row" align="center" gap={2}>
|
||||
<Icon icon={Shield} size={4} color="text-primary-blue" />
|
||||
Defense Requirements
|
||||
</Stack>
|
||||
</Heading>
|
||||
<Text size="xs" color="text-gray-400" mb={4} block>
|
||||
Should accused drivers be required to submit a defense?
|
||||
</p>
|
||||
</Text>
|
||||
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3">
|
||||
<button
|
||||
<Box display="grid" gridCols={{ base: 1, sm: 2 }} gap={3}>
|
||||
<Box
|
||||
as="button"
|
||||
type="button"
|
||||
disabled={readOnly}
|
||||
onClick={() => updateStewarding({ requireDefense: false })}
|
||||
className={`
|
||||
flex items-center gap-3 p-4 rounded-xl border-2 transition-all text-left
|
||||
${!stewarding.requireDefense
|
||||
? 'border-primary-blue bg-primary-blue/5'
|
||||
: 'border-charcoal-outline bg-iron-gray/30 hover:border-gray-500'
|
||||
}
|
||||
${readOnly ? 'opacity-60 cursor-not-allowed' : 'cursor-pointer'}
|
||||
`}
|
||||
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'}
|
||||
>
|
||||
<div className={`w-4 h-4 rounded-full border-2 flex items-center justify-center ${
|
||||
!stewarding.requireDefense ? 'border-primary-blue' : 'border-gray-500'
|
||||
}`}>
|
||||
{!stewarding.requireDefense && <div className="w-2 h-2 rounded-full bg-primary-blue" />}
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm font-medium text-white">Defense optional</p>
|
||||
<p className="text-xs text-gray-400">Proceed without waiting for defense</p>
|
||||
</div>
|
||||
</button>
|
||||
<Box w="4" h="4" rounded="full" border borderWidth="2px" display="flex" alignItems="center" justifyContent="center" borderColor={!stewarding.requireDefense ? 'border-primary-blue' : 'border-gray-500'}>
|
||||
{!stewarding.requireDefense && <Box w="2" h="2" rounded="full" bg="bg-primary-blue" />}
|
||||
</Box>
|
||||
<Box>
|
||||
<Text size="sm" weight="medium" color="text-white" block>Defense optional</Text>
|
||||
<Text size="xs" color="text-gray-400" block>Proceed without waiting for defense</Text>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
<button
|
||||
<Box
|
||||
as="button"
|
||||
type="button"
|
||||
disabled={readOnly}
|
||||
onClick={() => updateStewarding({ requireDefense: true })}
|
||||
className={`
|
||||
flex items-center gap-3 p-4 rounded-xl border-2 transition-all text-left
|
||||
${stewarding.requireDefense
|
||||
? 'border-primary-blue bg-primary-blue/5'
|
||||
: 'border-charcoal-outline bg-iron-gray/30 hover:border-gray-500'
|
||||
}
|
||||
${readOnly ? 'opacity-60 cursor-not-allowed' : 'cursor-pointer'}
|
||||
`}
|
||||
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'}
|
||||
>
|
||||
<div className={`w-4 h-4 rounded-full border-2 flex items-center justify-center ${
|
||||
stewarding.requireDefense ? 'border-primary-blue' : 'border-gray-500'
|
||||
}`}>
|
||||
{stewarding.requireDefense && <div className="w-2 h-2 rounded-full bg-primary-blue" />}
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm font-medium text-white">Defense required</p>
|
||||
<p className="text-xs text-gray-400">Wait for defense before deciding</p>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
<Box w="4" h="4" rounded="full" border borderWidth="2px" display="flex" alignItems="center" justifyContent="center" borderColor={stewarding.requireDefense ? 'border-primary-blue' : 'border-gray-500'}>
|
||||
{stewarding.requireDefense && <Box w="2" h="2" rounded="full" bg="bg-primary-blue" />}
|
||||
</Box>
|
||||
<Box>
|
||||
<Text size="sm" weight="medium" color="text-white" block>Defense required</Text>
|
||||
<Text size="xs" color="text-gray-400" block>Wait for defense before deciding</Text>
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
{stewarding.requireDefense && (
|
||||
<div className="mt-4 p-4 rounded-xl bg-iron-gray/40 border border-charcoal-outline">
|
||||
<label className="block text-xs font-medium text-gray-400 mb-1.5">
|
||||
<Box mt={4} p={4} rounded="xl" bg="bg-iron-gray/40" border borderColor="border-charcoal-outline">
|
||||
<Text as="label" size="xs" weight="medium" color="text-gray-400" block mb={1.5}>
|
||||
Defense time limit
|
||||
</label>
|
||||
<select
|
||||
value={stewarding.defenseTimeLimit}
|
||||
</Text>
|
||||
<Select
|
||||
value={stewarding.defenseTimeLimit?.toString()}
|
||||
onChange={(e) => updateStewarding({ defenseTimeLimit: parseInt(e.target.value, 10) })}
|
||||
disabled={readOnly}
|
||||
className="w-full px-3 py-2 bg-deep-graphite border border-charcoal-outline rounded-lg text-white text-sm focus:ring-1 focus:ring-primary-blue focus:border-primary-blue"
|
||||
>
|
||||
<option value={24}>24 hours</option>
|
||||
<option value={48}>48 hours (2 days)</option>
|
||||
<option value={72}>72 hours (3 days)</option>
|
||||
<option value={96}>96 hours (4 days)</option>
|
||||
</select>
|
||||
<p className="text-xs text-gray-500 mt-2">
|
||||
options={[
|
||||
{ value: '24', label: '24 hours' },
|
||||
{ value: '48', label: '48 hours (2 days)' },
|
||||
{ value: '72', label: '72 hours (3 days)' },
|
||||
{ value: '96', label: '96 hours (4 days)' },
|
||||
]}
|
||||
/>
|
||||
<Text size="xs" color="text-gray-500" mt={2} block>
|
||||
After this time, the decision can proceed without a defense
|
||||
</p>
|
||||
</div>
|
||||
</Text>
|
||||
</Box>
|
||||
)}
|
||||
</div>
|
||||
</Box>
|
||||
|
||||
{/* Deadlines */}
|
||||
<div>
|
||||
<h3 className="text-sm font-semibold text-white mb-1 flex items-center gap-2">
|
||||
<Clock className="w-4 h-4 text-primary-blue" />
|
||||
Deadlines
|
||||
</h3>
|
||||
<p className="text-xs text-gray-400 mb-4">
|
||||
<Box>
|
||||
<Heading level={3} fontSize="sm" weight="semibold" color="text-white" mb={1}>
|
||||
<Stack direction="row" align="center" gap={2}>
|
||||
<Icon icon={Clock} size={4} color="text-primary-blue" />
|
||||
Deadlines
|
||||
</Stack>
|
||||
</Heading>
|
||||
<Text size="xs" color="text-gray-400" mb={4} block>
|
||||
Set time limits for filing protests and closing stewarding
|
||||
</p>
|
||||
</Text>
|
||||
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
|
||||
<div className="p-4 rounded-xl bg-iron-gray/40 border border-charcoal-outline">
|
||||
<label className="block text-xs font-medium text-gray-400 mb-1.5">
|
||||
<Box display="grid" gridCols={{ base: 1, sm: 2 }} gap={4}>
|
||||
<Box p={4} rounded="xl" bg="bg-iron-gray/40" border borderColor="border-charcoal-outline">
|
||||
<Text as="label" size="xs" weight="medium" color="text-gray-400" block mb={1.5}>
|
||||
Protest filing deadline (after race)
|
||||
</label>
|
||||
<select
|
||||
value={stewarding.protestDeadlineHours}
|
||||
</Text>
|
||||
<Select
|
||||
value={stewarding.protestDeadlineHours?.toString()}
|
||||
onChange={(e) => updateStewarding({ protestDeadlineHours: parseInt(e.target.value, 10) })}
|
||||
disabled={readOnly}
|
||||
className="w-full px-3 py-2 bg-deep-graphite border border-charcoal-outline rounded-lg text-white text-sm focus:ring-1 focus:ring-primary-blue focus:border-primary-blue"
|
||||
>
|
||||
<option value={12}>12 hours</option>
|
||||
<option value={24}>24 hours (1 day)</option>
|
||||
<option value={48}>48 hours (2 days)</option>
|
||||
<option value={72}>72 hours (3 days)</option>
|
||||
<option value={168}>168 hours (7 days)</option>
|
||||
</select>
|
||||
<p className="text-xs text-gray-500 mt-2">
|
||||
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)' },
|
||||
]}
|
||||
/>
|
||||
<Text size="xs" color="text-gray-500" mt={2} block>
|
||||
Drivers cannot file protests after this time
|
||||
</p>
|
||||
</div>
|
||||
</Text>
|
||||
</Box>
|
||||
|
||||
<div className="p-4 rounded-xl bg-iron-gray/40 border border-charcoal-outline">
|
||||
<label className="block text-xs font-medium text-gray-400 mb-1.5">
|
||||
<Box p={4} rounded="xl" bg="bg-iron-gray/40" border borderColor="border-charcoal-outline">
|
||||
<Text as="label" size="xs" weight="medium" color="text-gray-400" block mb={1.5}>
|
||||
Stewarding closes (after race)
|
||||
</label>
|
||||
<select
|
||||
value={stewarding.stewardingClosesHours}
|
||||
</Text>
|
||||
<Select
|
||||
value={stewarding.stewardingClosesHours?.toString()}
|
||||
onChange={(e) => updateStewarding({ stewardingClosesHours: parseInt(e.target.value, 10) })}
|
||||
disabled={readOnly}
|
||||
className="w-full px-3 py-2 bg-deep-graphite border border-charcoal-outline rounded-lg text-white text-sm focus:ring-1 focus:ring-primary-blue focus:border-primary-blue"
|
||||
>
|
||||
<option value={72}>72 hours (3 days)</option>
|
||||
<option value={96}>96 hours (4 days)</option>
|
||||
<option value={168}>168 hours (7 days)</option>
|
||||
<option value={336}>336 hours (14 days)</option>
|
||||
</select>
|
||||
<p className="text-xs text-gray-500 mt-2">
|
||||
options={[
|
||||
{ value: '72', label: '72 hours (3 days)' },
|
||||
{ value: '96', label: '96 hours (4 days)' },
|
||||
{ value: '168', label: '168 hours (7 days)' },
|
||||
{ value: '336', label: '336 hours (14 days)' },
|
||||
]}
|
||||
/>
|
||||
<Text size="xs" color="text-gray-500" mt={2} block>
|
||||
All stewarding must be concluded by this time
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Text>
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
{/* Notifications */}
|
||||
<div>
|
||||
<h3 className="text-sm font-semibold text-white mb-1 flex items-center gap-2">
|
||||
<Bell className="w-4 h-4 text-primary-blue" />
|
||||
Notifications
|
||||
</h3>
|
||||
<p className="text-xs text-gray-400 mb-4">
|
||||
<Box>
|
||||
<Heading level={3} fontSize="sm" weight="semibold" color="text-white" mb={1}>
|
||||
<Stack direction="row" align="center" gap={2}>
|
||||
<Icon icon={Bell} size={4} color="text-primary-blue" />
|
||||
Notifications
|
||||
</Stack>
|
||||
</Heading>
|
||||
<Text size="xs" color="text-gray-400" mb={4} block>
|
||||
Configure automatic notifications for involved parties
|
||||
</p>
|
||||
</Text>
|
||||
|
||||
<div className="space-y-3">
|
||||
<label
|
||||
className={`flex items-center gap-3 p-4 rounded-xl bg-iron-gray/40 border border-charcoal-outline cursor-pointer hover:bg-iron-gray/60 transition-colors ${
|
||||
readOnly ? 'opacity-60 cursor-not-allowed' : ''
|
||||
}`}
|
||||
<Stack gap={3}>
|
||||
<Box
|
||||
p={4}
|
||||
rounded="xl"
|
||||
bg="bg-iron-gray/40"
|
||||
border
|
||||
borderColor="border-charcoal-outline"
|
||||
transition
|
||||
hoverBg={!readOnly ? 'bg-iron-gray/60' : undefined}
|
||||
opacity={readOnly ? 0.6 : 1}
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
<Checkbox
|
||||
label="Notify accused driver"
|
||||
checked={stewarding.notifyAccusedOnProtest}
|
||||
onChange={(e) => updateStewarding({ notifyAccusedOnProtest: e.target.checked })}
|
||||
onChange={(checked) => updateStewarding({ notifyAccusedOnProtest: checked })}
|
||||
disabled={readOnly}
|
||||
className="w-4 h-4 rounded border-charcoal-outline bg-deep-graphite text-primary-blue focus:ring-primary-blue focus:ring-offset-0"
|
||||
/>
|
||||
<div>
|
||||
<p className="text-sm font-medium text-white">Notify accused driver</p>
|
||||
<p className="text-xs text-gray-400">
|
||||
<Box ml={7} mt={1}>
|
||||
<Text size="xs" color="text-gray-400" block>
|
||||
Send notification when a protest is filed against them
|
||||
</p>
|
||||
</div>
|
||||
</label>
|
||||
</Text>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
<label
|
||||
className={`flex items-center gap-3 p-4 rounded-xl bg-iron-gray/40 border border-charcoal-outline cursor-pointer hover:bg-iron-gray/60 transition-colors ${
|
||||
readOnly ? 'opacity-60 cursor-not-allowed' : ''
|
||||
}`}
|
||||
<Box
|
||||
p={4}
|
||||
rounded="xl"
|
||||
bg="bg-iron-gray/40"
|
||||
border
|
||||
borderColor="border-charcoal-outline"
|
||||
transition
|
||||
hoverBg={!readOnly ? 'bg-iron-gray/60' : undefined}
|
||||
opacity={readOnly ? 0.6 : 1}
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
<Checkbox
|
||||
label="Notify voters"
|
||||
checked={stewarding.notifyOnVoteRequired}
|
||||
onChange={(e) => updateStewarding({ notifyOnVoteRequired: e.target.checked })}
|
||||
onChange={(checked) => updateStewarding({ notifyOnVoteRequired: checked })}
|
||||
disabled={readOnly}
|
||||
className="w-4 h-4 rounded border-charcoal-outline bg-deep-graphite text-primary-blue focus:ring-primary-blue focus:ring-offset-0"
|
||||
/>
|
||||
<div>
|
||||
<p className="text-sm font-medium text-white">Notify voters</p>
|
||||
<p className="text-xs text-gray-400">
|
||||
<Box ml={7} mt={1}>
|
||||
<Text size="xs" color="text-gray-400" block>
|
||||
Send notification to stewards/members when their vote is needed
|
||||
</p>
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</Text>
|
||||
</Box>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Box>
|
||||
|
||||
{/* Warning about strict settings */}
|
||||
{stewarding.requireDefense && stewarding.decisionMode !== 'single_steward' && (
|
||||
<div className="flex items-start gap-3 p-4 rounded-xl bg-warning-amber/10 border border-warning-amber/20">
|
||||
<AlertTriangle className="w-5 h-5 text-warning-amber shrink-0 mt-0.5" />
|
||||
<div>
|
||||
<p className="text-sm font-medium text-warning-amber">Strict settings enabled</p>
|
||||
<p className="text-xs text-warning-amber/80 mt-1">
|
||||
<Box display="flex" alignItems="start" gap={3} p={4} rounded="xl" bg="bg-warning-amber/10" border borderColor="border-warning-amber/20">
|
||||
<Icon icon={AlertTriangle} size={5} color="text-warning-amber" mt={0.5} />
|
||||
<Box>
|
||||
<Text size="sm" weight="medium" color="text-warning-amber" block>Strict settings enabled</Text>
|
||||
<Text size="xs" color="text-warning-amber" opacity={0.8} mt={1} block>
|
||||
Requiring defense and voting may delay penalty decisions. Make sure your stewards/members
|
||||
are active enough to meet the deadlines.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</Text>
|
||||
</Box>
|
||||
</Box>
|
||||
)}
|
||||
</div>
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user