MOPC-App/src/components/admin/rounds/config/deliberation-config.tsx

177 lines
7.0 KiB
TypeScript

'use client'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
import { Input } from '@/components/ui/input'
import { Label } from '@/components/ui/label'
import { Switch } from '@/components/ui/switch'
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'
type DeliberationConfigProps = {
config: Record<string, unknown>
onChange: (config: Record<string, unknown>) => void
juryGroups?: Array<{ id: string; name: string }>
}
export function DeliberationConfig({ config, onChange, juryGroups }: DeliberationConfigProps) {
const update = (key: string, value: unknown) => {
onChange({ ...config, [key]: value })
}
return (
<div className="space-y-6">
{/* Jury Group Selection */}
<Card>
<CardHeader>
<CardTitle className="text-base">Deliberation Jury</CardTitle>
<CardDescription>Which jury group participates in this deliberation round</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div className="space-y-2">
<Label htmlFor="juryGroupId">Jury Group</Label>
<p className="text-xs text-muted-foreground">
The jury group that will cast votes during deliberation
</p>
{juryGroups && juryGroups.length > 0 ? (
<Select
value={(config.juryGroupId as string) ?? ''}
onValueChange={(v) => update('juryGroupId', v)}
>
<SelectTrigger id="juryGroupId" className="w-72">
<SelectValue placeholder="Select a jury group" />
</SelectTrigger>
<SelectContent>
{juryGroups.map((g) => (
<SelectItem key={g.id} value={g.id}>{g.name}</SelectItem>
))}
</SelectContent>
</Select>
) : (
<Input
id="juryGroupId"
placeholder="Jury group ID"
value={(config.juryGroupId as string) ?? ''}
onChange={(e) => update('juryGroupId', e.target.value)}
/>
)}
</div>
</CardContent>
</Card>
{/* Voting Settings */}
<Card>
<CardHeader>
<CardTitle className="text-base">Voting Settings</CardTitle>
<CardDescription>How deliberation votes are structured</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div className="space-y-2">
<Label htmlFor="mode">Deliberation Mode</Label>
<p className="text-xs text-muted-foreground">How the final decision is made</p>
<Select
value={(config.mode as string) ?? 'SINGLE_WINNER_VOTE'}
onValueChange={(v) => update('mode', v)}
>
<SelectTrigger id="mode" className="w-64">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="SINGLE_WINNER_VOTE">Single Winner Vote</SelectItem>
<SelectItem value="FULL_RANKING">Full Ranking</SelectItem>
</SelectContent>
</Select>
</div>
<div className="grid gap-4 sm:grid-cols-2">
<div className="space-y-2">
<Label htmlFor="votingDuration">Voting Duration (min)</Label>
<p className="text-xs text-muted-foreground">Time limit for voting round</p>
<Input
id="votingDuration"
type="number"
min={1}
className="w-32"
value={(config.votingDuration as number) ?? 60}
onChange={(e) => update('votingDuration', parseInt(e.target.value, 10) || 60)}
/>
</div>
<div className="space-y-2">
<Label htmlFor="topN">Top N Projects</Label>
<p className="text-xs text-muted-foreground">Number of finalists to select</p>
<Input
id="topN"
type="number"
min={1}
className="w-32"
value={(config.topN as number) ?? 3}
onChange={(e) => update('topN', parseInt(e.target.value, 10) || 3)}
/>
</div>
</div>
<div className="space-y-2">
<Label htmlFor="tieBreakMethod">Tie Break Method</Label>
<Select
value={(config.tieBreakMethod as string) ?? 'ADMIN_DECIDES'}
onValueChange={(v) => update('tieBreakMethod', v)}
>
<SelectTrigger id="tieBreakMethod" className="w-64">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="ADMIN_DECIDES">Admin Decides</SelectItem>
<SelectItem value="RUNOFF">Runoff Vote</SelectItem>
<SelectItem value="SCORE_FALLBACK">Score Fallback (use prior scores)</SelectItem>
</SelectContent>
</Select>
</div>
</CardContent>
</Card>
{/* Visibility & Overrides */}
<Card>
<CardHeader>
<CardTitle className="text-base">Visibility & Overrides</CardTitle>
<CardDescription>What information jurors can see during deliberation</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div className="flex items-center justify-between">
<div>
<Label htmlFor="showCollectiveRankings">Show Collective Rankings</Label>
<p className="text-xs text-muted-foreground">Display aggregate rankings to jurors during voting</p>
</div>
<Switch
id="showCollectiveRankings"
checked={(config.showCollectiveRankings as boolean) ?? false}
onCheckedChange={(v) => update('showCollectiveRankings', v)}
/>
</div>
<div className="flex items-center justify-between">
<div>
<Label htmlFor="showPriorJuryData">Show Prior Jury Data</Label>
<p className="text-xs text-muted-foreground">Display evaluation scores from previous rounds</p>
</div>
<Switch
id="showPriorJuryData"
checked={(config.showPriorJuryData as boolean) ?? false}
onCheckedChange={(v) => update('showPriorJuryData', v)}
/>
</div>
<div className="flex items-center justify-between">
<div>
<Label htmlFor="allowAdminOverride">Allow Admin Override</Label>
<p className="text-xs text-muted-foreground">Admin can override deliberation results</p>
</div>
<Switch
id="allowAdminOverride"
checked={(config.allowAdminOverride as boolean) ?? true}
onCheckedChange={(v) => update('allowAdminOverride', v)}
/>
</div>
</CardContent>
</Card>
</div>
)
}