'use client' import { useEffect, useState } from 'react' import { Button } from '@/components/ui/button' import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card' import { Label } from '@/components/ui/label' import { Input } from '@/components/ui/input' import { Slider } from '@/components/ui/slider' import { Textarea } from '@/components/ui/textarea' import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, } from '@/components/ui/alert-dialog' import { CheckCircle2, Pencil } from 'lucide-react' interface LiveVotingCriterion { id: string label: string description?: string scale: number weight: number } interface LiveVotingFormProps { projectId: string votingMode?: 'simple' | 'criteria' criteria?: LiveVotingCriterion[] onVoteSubmit: (vote: { score: number criterionScores?: Record comment?: string }) => void disabled?: boolean existingVote?: { score: number criterionScoresJson?: Record comment?: string | null } | null /** Visual emphasis when the admin opens the scoring phase */ highlighted?: boolean } export function LiveVotingForm({ projectId, votingMode = 'simple', criteria, onVoteSubmit, disabled = false, existingVote, highlighted = false, }: LiveVotingFormProps) { const [score, setScore] = useState(existingVote?.score ?? 5) const [criterionScores, setCriterionScores] = useState>( existingVote?.criterionScoresJson ?? {} ) const [comment, setComment] = useState(existingVote?.comment ?? '') const [confirmDialogOpen, setConfirmDialogOpen] = useState(false) const [editing, setEditing] = useState(!existingVote) // When the ceremony cursor moves to a new project, reset the form state useEffect(() => { setScore(existingVote?.score ?? 5) setCriterionScores(existingVote?.criterionScoresJson ?? {}) setComment(existingVote?.comment ?? '') setEditing(!existingVote) // eslint-disable-next-line react-hooks/exhaustive-deps }, [projectId]) const handleConfirm = () => { if (votingMode === 'criteria' && criteria) { // The server recomputes the weighted score from criterionScores; this // 1-10 value is only a fallback and MUST stay within the API's range. let weightedSum = 0 for (const c of criteria) { const normalizedScore = ((criterionScores[c.id] ?? 0) / c.scale) * 10 weightedSum += normalizedScore * c.weight } const computedScore = Math.round(Math.min(10, Math.max(1, weightedSum))) onVoteSubmit({ score: computedScore, criterionScores, comment: comment.trim() || undefined, }) } else { onVoteSubmit({ score, comment: comment.trim() || undefined }) } setEditing(false) setConfirmDialogOpen(false) } if (!editing) { return (

Vote Submitted

{votingMode === 'criteria' && criteria ? (
{criteria.map((c) => (
{c.label}: {criterionScores[c.id] ?? 0}/{c.scale}
))}
) : (

Score: {score}/10

)} {comment.trim() && (

“{comment.trim()}”

)}

You can revise your vote until the session closes

) } const commentField = (