'use client' import { useState, useMemo, useCallback } from 'react' import { useParams } from 'next/navigation' import Link from 'next/link' import type { Route } from 'next' import { trpc } from '@/lib/trpc/client' import { toast } from 'sonner' import { cn } from '@/lib/utils' import { Button } from '@/components/ui/button' import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card' import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs' import { Skeleton } from '@/components/ui/skeleton' import { Badge } from '@/components/ui/badge' import { Input } from '@/components/ui/input' import { Switch } from '@/components/ui/switch' import { Label } from '@/components/ui/label' import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger, } from '@/components/ui/dropdown-menu' import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger, } from '@/components/ui/alert-dialog' import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from '@/components/ui/dialog' import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select' import { ArrowLeft, Save, Loader2, ChevronDown, Play, Square, Archive, Layers, Users, CalendarDays, BarChart3, ClipboardList, Settings, Zap, Shield, UserPlus, CheckCircle2, AlertTriangle, FileText, Trophy, Clock, Send, Download, Plus, Trash2, ArrowRight, X, } from 'lucide-react' import { RoundConfigForm } from '@/components/admin/competition/round-config-form' import { ProjectStatesTable } from '@/components/admin/round/project-states-table' import { SubmissionWindowManager } from '@/components/admin/round/submission-window-manager' import { FileRequirementsEditor } from '@/components/admin/round/file-requirements-editor' import { FilteringDashboard } from '@/components/admin/round/filtering-dashboard' import { CoverageReport } from '@/components/admin/assignment/coverage-report' import { AssignmentPreviewSheet } from '@/components/admin/assignment/assignment-preview-sheet' import { CsvExportDialog } from '@/components/shared/csv-export-dialog' // ── Status & type config maps ────────────────────────────────────────────── const roundStatusConfig = { ROUND_DRAFT: { label: 'Draft', bgClass: 'bg-gray-100 text-gray-700', dotClass: 'bg-gray-500', description: 'Not yet active. Configure before launching.', }, ROUND_ACTIVE: { label: 'Active', bgClass: 'bg-emerald-100 text-emerald-700', dotClass: 'bg-emerald-500 animate-pulse', description: 'Round is live. Projects can be processed.', }, ROUND_CLOSED: { label: 'Closed', bgClass: 'bg-blue-100 text-blue-700', dotClass: 'bg-blue-500', description: 'No longer accepting changes. Results are final.', }, ROUND_ARCHIVED: { label: 'Archived', bgClass: 'bg-muted text-muted-foreground', dotClass: 'bg-muted-foreground', description: 'Historical record only.', }, } as const const roundTypeConfig: Record = { INTAKE: { label: 'Intake', color: 'bg-gray-100 text-gray-700', description: 'Collecting applications' }, FILTERING: { label: 'Filtering', color: 'bg-amber-100 text-amber-700', description: 'AI + manual screening' }, EVALUATION: { label: 'Evaluation', color: 'bg-blue-100 text-blue-700', description: 'Jury evaluation & scoring' }, SUBMISSION: { label: 'Submission', color: 'bg-purple-100 text-purple-700', description: 'Document submission' }, MENTORING: { label: 'Mentoring', color: 'bg-teal-100 text-teal-700', description: 'Mentor-guided development' }, LIVE_FINAL: { label: 'Live Final', color: 'bg-red-100 text-red-700', description: 'Live presentations & voting' }, DELIBERATION: { label: 'Deliberation', color: 'bg-indigo-100 text-indigo-700', description: 'Final jury deliberation' }, } const stateColors: Record = { PENDING: 'bg-gray-400', IN_PROGRESS: 'bg-blue-500', PASSED: 'bg-green-500', REJECTED: 'bg-red-500', COMPLETED: 'bg-emerald-500', WITHDRAWN: 'bg-orange-400', } // ═══════════════════════════════════════════════════════════════════════════ // Main Page Component // ═══════════════════════════════════════════════════════════════════════════ export default function RoundDetailPage() { const params = useParams() const roundId = params.roundId as string const [config, setConfig] = useState>({}) const [hasChanges, setHasChanges] = useState(false) const [activeTab, setActiveTab] = useState('overview') const [previewSheetOpen, setPreviewSheetOpen] = useState(false) const [exportOpen, setExportOpen] = useState(false) const [advanceDialogOpen, setAdvanceDialogOpen] = useState(false) const utils = trpc.useUtils() // ── Core data queries ────────────────────────────────────────────────── const { data: round, isLoading } = trpc.round.getById.useQuery({ id: roundId }) const { data: projectStates } = trpc.roundEngine.getProjectStates.useQuery({ roundId }) const competitionId = round?.competitionId ?? '' const { data: juryGroups } = trpc.juryGroup.list.useQuery( { competitionId }, { enabled: !!competitionId }, ) const { data: fileRequirements } = trpc.file.listRequirements.useQuery({ roundId }) // Fetch awards linked to this round const { data: competition } = trpc.competition.getById.useQuery( { id: competitionId }, { enabled: !!competitionId }, ) const programId = competition?.programId const { data: awards } = trpc.specialAward.list.useQuery( { programId: programId! }, { enabled: !!programId }, ) const roundAwards = awards?.filter((a) => a.evaluationRoundId === roundId) ?? [] // Sync config from server when not dirty if (round && !hasChanges) { const roundConfig = (round.configJson as Record) ?? {} if (JSON.stringify(roundConfig) !== JSON.stringify(config)) { setConfig(roundConfig) } } // ── Mutations ────────────────────────────────────────────────────────── const updateMutation = trpc.round.update.useMutation({ onSuccess: () => { utils.round.getById.invalidate({ id: roundId }) toast.success('Round configuration saved') setHasChanges(false) }, onError: (err) => toast.error(err.message), }) const activateMutation = trpc.roundEngine.activate.useMutation({ onSuccess: () => { utils.round.getById.invalidate({ id: roundId }) toast.success('Round activated') }, onError: (err) => toast.error(err.message), }) const closeMutation = trpc.roundEngine.close.useMutation({ onSuccess: () => { utils.round.getById.invalidate({ id: roundId }) toast.success('Round closed') }, onError: (err) => toast.error(err.message), }) const archiveMutation = trpc.roundEngine.archive.useMutation({ onSuccess: () => { utils.round.getById.invalidate({ id: roundId }) toast.success('Round archived') }, onError: (err) => toast.error(err.message), }) const assignJuryMutation = trpc.round.update.useMutation({ onSuccess: () => { utils.round.getById.invalidate({ id: roundId }) toast.success('Jury group updated') }, onError: (err) => toast.error(err.message), }) const advanceMutation = trpc.round.advanceProjects.useMutation({ onSuccess: (data) => { utils.round.getById.invalidate({ id: roundId }) utils.roundEngine.getProjectStates.invalidate({ roundId }) toast.success(`Advanced ${data.advancedCount} project(s) to ${data.targetRoundName}`) setAdvanceDialogOpen(false) }, onError: (err) => toast.error(err.message), }) const isTransitioning = activateMutation.isPending || closeMutation.isPending || archiveMutation.isPending const handleConfigChange = useCallback((newConfig: Record) => { setConfig(newConfig) setHasChanges(true) }, []) const handleSave = useCallback(() => { updateMutation.mutate({ id: roundId, configJson: config }) }, [roundId, config, updateMutation]) // ── Computed values ──────────────────────────────────────────────────── const projectCount = round?._count?.projectRoundStates ?? 0 const stateCounts = useMemo(() => projectStates?.reduce((acc: Record, ps: any) => { acc[ps.state] = (acc[ps.state] || 0) + 1 return acc }, {} as Record) ?? {}, [projectStates]) const passedCount = stateCounts['PASSED'] ?? 0 const juryGroup = round?.juryGroup const juryMemberCount = juryGroup?.members?.length ?? 0 const isFiltering = round?.roundType === 'FILTERING' const isEvaluation = round?.roundType === 'EVALUATION' const poolLink = `/admin/projects/pool?roundId=${roundId}&competitionId=${competitionId}` as Route // ── Loading state ────────────────────────────────────────────────────── if (isLoading) { return (
{[1, 2, 3, 4].map((i) => )}
) } if (!round) { return (

Round Not Found

This round does not exist.

) } const status = round.status as keyof typeof roundStatusConfig const statusCfg = roundStatusConfig[status] || roundStatusConfig.ROUND_DRAFT const typeCfg = roundTypeConfig[round.roundType] || roundTypeConfig.INTAKE // ── Readiness checklist ──────────────────────────────────────────────── const readinessItems = [ { label: 'Projects assigned', ready: projectCount > 0, detail: projectCount > 0 ? `${projectCount} projects` : 'No projects yet', action: projectCount === 0 ? poolLink : undefined, actionLabel: 'Assign Projects', }, ...((isEvaluation || isFiltering) ? [{ label: 'Jury group set', ready: !!juryGroup, detail: juryGroup ? `${juryGroup.name} (${juryMemberCount} members)` : 'No jury group assigned', action: undefined as Route | undefined, actionLabel: undefined as string | undefined, }] : []), { label: 'Dates configured', ready: !!round.windowOpenAt && !!round.windowCloseAt, detail: round.windowOpenAt && round.windowCloseAt ? `${new Date(round.windowOpenAt).toLocaleDateString()} \u2014 ${new Date(round.windowCloseAt).toLocaleDateString()}` : 'No dates set \u2014 configure in Config tab', action: undefined as Route | undefined, actionLabel: undefined as string | undefined, }, { label: 'File requirements set', ready: (fileRequirements?.length ?? 0) > 0, detail: (fileRequirements?.length ?? 0) > 0 ? `${fileRequirements?.length} requirement(s)` : 'No file requirements \u2014 configure in Config tab', action: undefined as Route | undefined, actionLabel: undefined as string | undefined, }, ] const readyCount = readinessItems.filter((i) => i.ready).length // ═════════════════════════════════════════════════════════════════════════ // Render // ═════════════════════════════════════════════════════════════════════════ return (
{/* ===== HEADER ===== */}

{round.name}

{typeCfg.label} {/* Status dropdown */} {status === 'ROUND_DRAFT' && ( activateMutation.mutate({ roundId })} disabled={isTransitioning} > Activate Round )} {status === 'ROUND_ACTIVE' && ( closeMutation.mutate({ roundId })} disabled={isTransitioning} > Close Round )} {status === 'ROUND_CLOSED' && ( <> activateMutation.mutate({ roundId })} disabled={isTransitioning} > Reactivate Round archiveMutation.mutate({ roundId })} disabled={isTransitioning} > Archive Round )} {isTransitioning && (
Updating...
)}

{typeCfg.description}

{/* Action buttons */}
{hasChanges && ( )}
{/* ===== STATS BAR ===== */}
{/* Projects */}
Projects

{projectCount}

{Object.entries(stateCounts).map(([state, count]) => ( {String(count)} {state.toLowerCase().replace('_', ' ')} ))}
{/* Jury (with inline group selector) */}
Jury
{juryGroups && juryGroups.length > 0 ? ( ) : juryGroup ? ( <>

{juryMemberCount}

{juryGroup.name}

) : ( <>

No jury groups yet

)}
{/* Window */}
Window
{round.windowOpenAt || round.windowCloseAt ? ( <>

{round.windowOpenAt ? new Date(round.windowOpenAt).toLocaleDateString() : 'No start'}

{round.windowCloseAt ? `Closes ${new Date(round.windowCloseAt).toLocaleDateString()}` : 'No deadline'}

) : ( <>

No dates set

)}
{/* Advancement */}
Advancement
{round.advancementRules && round.advancementRules.length > 0 ? ( <>

{round.advancementRules.length}

{round.advancementRules.map((r: any) => r.ruleType.replace('_', ' ').toLowerCase()).join(', ')}

) : ( <>

Admin selection

)}
{/* ===== TABS ===== */} Overview Projects {isFiltering && ( Filtering )} {isEvaluation && ( Assignments )} Config Submissions Awards {roundAwards.length > 0 && ( {roundAwards.length} )} {/* ═══════════ OVERVIEW TAB ═══════════ */} {/* Readiness Checklist */}
Readiness Checklist {readyCount}/{readinessItems.length} items ready
{readyCount === readinessItems.length ? 'Ready' : 'Incomplete'}
{readinessItems.map((item) => (
{item.ready ? ( ) : ( )}

{item.label}

{item.detail}

{item.action && ( )}
))}
{/* Quick Actions */} Quick Actions Common operations for this round
{/* Status transitions */} {status === 'ROUND_DRAFT' && ( Activate this round? The round will go live. Projects can be processed and jury members will be able to see their assignments. Cancel activateMutation.mutate({ roundId })}> Activate )} {status === 'ROUND_ACTIVE' && ( Close this round? No further changes will be accepted. You can reactivate later if needed. {projectCount > 0 && ( {projectCount} projects are currently in this round. )} Cancel closeMutation.mutate({ roundId })}> Close Round )} {/* Assign projects */} {/* Filtering specific */} {isFiltering && ( )} {/* Jury assignment for evaluation/filtering */} {(isEvaluation || isFiltering) && !juryGroup && ( )} {/* Evaluation: manage assignments */} {isEvaluation && ( )} {/* View projects */} {/* Advance projects (shown when PASSED > 0) */} {passedCount > 0 && ( )}
{/* Advance Projects Confirmation Dialog */} Advance {passedCount} project(s)? All projects with PASSED status in this round will be moved to the next round. This action creates new entries in the next round and marks current entries as completed. Cancel advanceMutation.mutate({ roundId })} disabled={advanceMutation.isPending} > {advanceMutation.isPending && } Advance Projects {/* Round Info + Project Breakdown */}
Round Details
Type {typeCfg.label}
Status {statusCfg.label}
Sort Order {round.sortOrder}
{round.purposeKey && (
Purpose {round.purposeKey}
)}
Jury Group {juryGroup ? juryGroup.name : '\u2014'}
Opens {round.windowOpenAt ? new Date(round.windowOpenAt).toLocaleString() : '\u2014'}
Closes {round.windowCloseAt ? new Date(round.windowCloseAt).toLocaleString() : '\u2014'}
Project Breakdown {projectCount === 0 ? (

No projects assigned yet

) : (
{['PENDING', 'IN_PROGRESS', 'PASSED', 'REJECTED', 'COMPLETED', 'WITHDRAWN'].map((state) => { const count = stateCounts[state] || 0 if (count === 0) return null const pct = ((count / projectCount) * 100).toFixed(0) return (
{state.toLowerCase().replace('_', ' ')} {count} ({pct}%)
) })}
)}
{/* ═══════════ PROJECTS TAB ═══════════ */} {/* ═══════════ FILTERING TAB ═══════════ */} {isFiltering && ( )} {/* ═══════════ ASSIGNMENTS TAB (Evaluation rounds) ═══════════ */} {isEvaluation && ( {/* Coverage Report */} {/* Generate Assignments */}
Assignment Generation AI-suggested jury-to-project assignments based on expertise and workload
{!juryGroup && (
Assign a jury group first before generating assignments.
)} {projectCount === 0 && (
Add projects to this round first.
)} {juryGroup && projectCount > 0 && (

Click "Generate Assignments" to preview AI-suggested assignments. You can review and execute them from the preview sheet.

)}
{/* Jury Progress + Score Distribution */}
{/* Actions: Send Reminders + Export */}
{/* Individual Assignments Table */} {/* Unassigned Queue */} {/* Assignment Preview Sheet */} {/* CSV Export Dialog */}
)} {/* ═══════════ CONFIG TAB ═══════════ */} {/* General Round Settings */} General Settings Settings that apply to this round regardless of type

Send an automated email to project applicants when their project enters this round

{ handleConfigChange({ ...config, notifyOnEntry: checked }) }} />
{/* Round-type-specific config */} ({ id: jg.id, name: jg.name }))} /> {/* Evaluation Criteria Editor (EVALUATION rounds only) */} {isEvaluation && } {/* Document Requirements */} Document Requirements Files applicants must submit for this round {round.windowCloseAt && ( <> — due by {new Date(round.windowCloseAt).toLocaleDateString()} )}
{/* ═══════════ SUBMISSION WINDOWS TAB ═══════════ */} {/* ═══════════ AWARDS TAB ═══════════ */} {roundAwards.length === 0 ? (

No awards linked to this round

Create an award and set this round as its evaluation round to see it here

) : (
{roundAwards.map((award) => { const eligibleCount = award._count?.eligibilities || 0 const autoTagRules = award.autoTagRulesJson as { rules?: unknown[] } | null const ruleCount = autoTagRules?.rules?.length || 0 return (

{award.name}

{award.eligibilityMode === 'SEPARATE_POOL' ? 'Separate Pool' : 'Stay in Main'}
{award.description && (

{award.description}

)}
{ruleCount}
{ruleCount === 1 ? 'rule' : 'rules'}
{eligibleCount}
eligible
) })}
)}
) } // ═══════════════════════════════════════════════════════════════════════════ // Sub-components // ═══════════════════════════════════════════════════════════════════════════ // ── Unassigned projects queue ──────────────────────────────────────────── function RoundUnassignedQueue({ roundId }: { roundId: string }) { const { data: unassigned, isLoading } = trpc.roundAssignment.unassignedQueue.useQuery( { roundId, requiredReviews: 3 }, ) return ( Unassigned Projects Projects with fewer than 3 jury assignments {isLoading ? (
{[1, 2, 3].map((i) => )}
) : unassigned && unassigned.length > 0 ? (
{unassigned.map((project: any) => (

{project.title}

{project.competitionCategory || 'No category'} {project.teamName && ` \u00b7 ${project.teamName}`}

{project.assignmentCount || 0} / 3
))}
) : (

All projects have sufficient assignments

)}
) } // ── Jury Progress Table ────────────────────────────────────────────────── function JuryProgressTable({ roundId }: { roundId: string }) { const { data: workload, isLoading } = trpc.analytics.getJurorWorkload.useQuery({ roundId }) return ( Jury Progress Evaluation completion per juror {isLoading ? (
{[1, 2, 3].map((i) => )}
) : !workload || workload.length === 0 ? (

No assignments yet

) : (
{workload.map((juror) => { const pct = juror.completionRate const barColor = pct === 100 ? 'bg-emerald-500' : pct >= 50 ? 'bg-blue-500' : pct > 0 ? 'bg-amber-500' : 'bg-gray-300' return (
{juror.name} {juror.completed}/{juror.assigned} ({pct}%)
) })}
)} ) } // ── Score Distribution ─────────────────────────────────────────────────── function ScoreDistribution({ roundId }: { roundId: string }) { const { data: dist, isLoading } = trpc.analytics.getRoundScoreDistribution.useQuery({ roundId }) const maxCount = useMemo(() => dist ? Math.max(...dist.globalDistribution.map((b) => b.count), 1) : 1, [dist]) return ( Score Distribution {dist ? `${dist.totalEvaluations} evaluations \u2014 avg ${dist.averageGlobalScore.toFixed(1)}` : 'Loading...'} {isLoading ? (
{Array.from({ length: 10 }).map((_, i) => )}
) : !dist || dist.totalEvaluations === 0 ? (

No evaluations submitted yet

) : (
{dist.globalDistribution.map((bucket) => { const heightPct = (bucket.count / maxCount) * 100 return (
{bucket.count || ''}
{bucket.score}
) })}
)} ) } // ── Send Reminders Button ──────────────────────────────────────────────── function SendRemindersButton({ roundId }: { roundId: string }) { const [open, setOpen] = useState(false) const mutation = trpc.evaluation.triggerReminders.useMutation({ onSuccess: (data) => { toast.success(`Sent ${data.sent} reminder(s)`) setOpen(false) }, onError: (err) => toast.error(err.message), }) return ( <> Send evaluation reminders? This will send reminder emails to all jurors who have incomplete evaluations for this round. Cancel mutation.mutate({ roundId })} disabled={mutation.isPending} > {mutation.isPending && } Send Reminders ) } // ── Export Evaluations Dialog ───────────────────────────────────────────── function ExportEvaluationsDialog({ roundId, open, onOpenChange, }: { roundId: string open: boolean onOpenChange: (open: boolean) => void }) { const [exportData, setExportData] = useState(undefined) const [isLoadingExport, setIsLoadingExport] = useState(false) const utils = trpc.useUtils() const handleRequestData = async () => { setIsLoadingExport(true) try { const data = await utils.export.evaluations.fetch({ roundId, includeDetails: true }) setExportData(data) return data } finally { setIsLoadingExport(false) } } return ( ) } // ── Individual Assignments Table ───────────────────────────────────────── function IndividualAssignmentsTable({ roundId }: { roundId: string }) { const [addDialogOpen, setAddDialogOpen] = useState(false) const [newUserId, setNewUserId] = useState('') const [newProjectId, setNewProjectId] = useState('') const utils = trpc.useUtils() const { data: assignments, isLoading } = trpc.assignment.listByStage.useQuery({ roundId }) const deleteMutation = trpc.assignment.delete.useMutation({ onSuccess: () => { utils.assignment.listByStage.invalidate({ roundId }) toast.success('Assignment removed') }, onError: (err) => toast.error(err.message), }) const createMutation = trpc.assignment.create.useMutation({ onSuccess: () => { utils.assignment.listByStage.invalidate({ roundId }) toast.success('Assignment created') setAddDialogOpen(false) setNewUserId('') setNewProjectId('') }, onError: (err) => toast.error(err.message), }) return (
All Assignments {assignments?.length ?? 0} individual jury-project assignments
{isLoading ? (
{[1, 2, 3, 4, 5].map((i) => )}
) : !assignments || assignments.length === 0 ? (

No assignments yet. Generate assignments or add one manually.

) : (
Juror Project Status
{assignments.map((a: any) => (
{a.user?.name || a.user?.email || 'Unknown'} {a.project?.title || 'Unknown'} {a.evaluation?.status || 'PENDING'}
))}
)}
{/* Add Assignment Dialog */} Add Assignment Manually assign a juror to evaluate a project
setNewUserId(e.target.value)} />
setNewProjectId(e.target.value)} />
) } // ── Evaluation Criteria Editor ─────────────────────────────────────────── function EvaluationCriteriaEditor({ roundId }: { roundId: string }) { const [editing, setEditing] = useState(false) const [criteria, setCriteria] = useState>([]) const utils = trpc.useUtils() const { data: form, isLoading } = trpc.evaluation.getForm.useQuery({ roundId }) const upsertMutation = trpc.evaluation.upsertForm.useMutation({ onSuccess: () => { utils.evaluation.getForm.invalidate({ roundId }) toast.success('Evaluation criteria saved') setEditing(false) }, onError: (err) => toast.error(err.message), }) // Sync from server if (form && !editing) { const serverCriteria = form.criteriaJson ?? [] if (JSON.stringify(serverCriteria) !== JSON.stringify(criteria)) { setCriteria(serverCriteria) } } const handleAdd = () => { setCriteria([...criteria, { id: `c-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`, label: '', description: '', weight: 1, minScore: 0, maxScore: 10, }]) setEditing(true) } const handleRemove = (id: string) => { setCriteria(criteria.filter((c) => c.id !== id)) } const handleChange = (id: string, field: string, value: string | number) => { setCriteria(criteria.map((c) => c.id === id ? { ...c, [field]: value } : c, )) setEditing(true) } const handleSave = () => { const validCriteria = criteria.filter((c) => c.label.trim()) if (validCriteria.length === 0) { toast.error('Add at least one criterion') return } upsertMutation.mutate({ roundId, criteria: validCriteria }) } return (
Evaluation Criteria {form ? `Version ${form.version} \u2014 ${form.criteriaJson.length} criteria` : 'No criteria defined yet'}
{editing && ( )} {editing ? ( ) : ( )}
{isLoading ? (
{[1, 2, 3].map((i) => )}
) : criteria.length === 0 ? (

No evaluation criteria defined

Add criteria that jurors will use to score projects

) : (
{criteria.map((c, idx) => (
{idx + 1}
handleChange(c.id, 'label', e.target.value)} className="h-8 text-sm" /> handleChange(c.id, 'description', e.target.value)} className="h-7 text-xs" />
handleChange(c.id, 'weight', Number(e.target.value))} className="h-7 text-xs" />
handleChange(c.id, 'minScore', Number(e.target.value))} className="h-7 text-xs" />
handleChange(c.id, 'maxScore', Number(e.target.value))} className="h-7 text-xs" />
))} {!editing && ( )}
)}
) }