'use client' import { useState } from 'react' import { trpc } from '@/lib/trpc/client' import { Badge } from '@/components/ui/badge' import { Button } from '@/components/ui/button' import { Textarea } from '@/components/ui/textarea' import { Sheet, SheetContent, SheetDescription, SheetHeader, SheetTitle, } from '@/components/ui/sheet' import { UserAvatar } from '@/components/shared/user-avatar' import { toast } from 'sonner' import { formatDate } from '@/lib/utils' import { BarChart3, ThumbsUp, ThumbsDown, MessageSquare, Pencil, Loader2, Check, X, } from 'lucide-react' type EvaluationEditSheetProps = { /** The assignment object with user, evaluation, and roundId */ // eslint-disable-next-line @typescript-eslint/no-explicit-any assignment: any open: boolean onOpenChange: (open: boolean) => void /** Called after a successful feedback edit */ onSaved?: () => void } export function EvaluationEditSheet({ assignment, open, onOpenChange, onSaved, }: EvaluationEditSheetProps) { const [isEditing, setIsEditing] = useState(false) const [editedFeedback, setEditedFeedback] = useState('') const editMutation = trpc.evaluation.adminEditEvaluation.useMutation({ onSuccess: () => { toast.success('Feedback updated') setIsEditing(false) onSaved?.() }, onError: (err) => toast.error(err.message), }) if (!assignment?.evaluation) return null const ev = assignment.evaluation const criterionScores = (ev.criterionScoresJson || {}) as Record const hasScores = Object.keys(criterionScores).length > 0 const roundId = assignment.roundId as string | undefined return ( { if (!v) setIsEditing(false) onOpenChange(v) }}> {assignment.user && ( )} {assignment.user?.name || assignment.user?.email || 'Juror'} {ev.submittedAt ? `Submitted ${formatDate(ev.submittedAt)}` : 'Evaluation details'}
{/* Global stats */}

Score

{ev.globalScore !== null && ev.globalScore !== undefined ? `${ev.globalScore}/10` : '-'}

Decision

{ev.binaryDecision !== null && ev.binaryDecision !== undefined ? ( ev.binaryDecision ? (
Yes
) : (
No
) ) : ( - )}
{/* Criterion Scores */} {hasScores && ( )} {/* Feedback Text — editable */} { setEditedFeedback(ev.feedbackText || '') setIsEditing(true) }} onCancelEdit={() => setIsEditing(false)} onSave={() => { editMutation.mutate({ evaluationId: ev.id, feedbackText: editedFeedback, }) }} onChangeFeedback={setEditedFeedback} isSaving={editMutation.isPending} />
) } function CriterionScoresSection({ criterionScores, roundId, }: { criterionScores: Record roundId?: string }) { const { data: activeForm } = trpc.evaluation.getStageForm.useQuery( { roundId: roundId ?? '' }, { enabled: !!roundId } ) const criteriaMap = new Map() if (activeForm?.criteriaJson) { for (const c of activeForm.criteriaJson as Array<{ id: string; label: string; type?: string; trueLabel?: string; falseLabel?: string }>) { criteriaMap.set(c.id, { label: c.label, type: c.type || 'numeric', trueLabel: c.trueLabel, falseLabel: c.falseLabel, }) } } return (

Criterion Scores

{Object.entries(criterionScores).map(([key, value]) => { const meta = criteriaMap.get(key) const label = meta?.label || key const type = meta?.type || (typeof value === 'boolean' ? 'boolean' : typeof value === 'string' ? 'text' : 'numeric') if (type === 'section_header') return null if (type === 'boolean' || type === 'advance') { return (
{label} {value === true ? ( {meta?.trueLabel || 'Yes'} ) : ( {meta?.falseLabel || 'No'} )}
) } if (type === 'text') { return (
{label}
{typeof value === 'string' ? value : String(value)}
) } // Numeric return (
{label}
{typeof value === 'number' ? value : '-'}
) })}
) } function FeedbackSection({ evaluationId, feedbackText, isEditing, editedFeedback, onStartEdit, onCancelEdit, onSave, onChangeFeedback, isSaving, }: { evaluationId: string feedbackText: string | null isEditing: boolean editedFeedback: string onStartEdit: () => void onCancelEdit: () => void onSave: () => void onChangeFeedback: (v: string) => void isSaving: boolean }) { return (

Feedback

{!isEditing && evaluationId && ( )}
{isEditing ? (