"use client"; import { useCallback, useEffect, useRef, useState } from "react"; import { useRouter } from "next/navigation"; import { Button } from "@/components/ui/button"; import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"; import { Label } from "@/components/ui/label"; import { Alert, AlertTitle, AlertDescription } from "@/components/ui/alert"; import { Progress, ProgressLabel, ProgressValue } from "@/components/ui/progress"; import { Card, CardContent, CardHeader, CardTitle, } from "@/components/ui/card"; import { CheckCircle2, XCircle } from "lucide-react"; const CATEGORIES = [ "Board Governance", "Management Role", "Risk Management Process", "Third-Party Risk", "Incident Disclosure", "Strategy Integration", "None/Other", ] as const; const SPECIFICITY_LABELS = [ "Generic Boilerplate", "Sector-Adapted", "Firm-Specific", "Quantified-Verifiable", ] as const; interface WarmupParagraph { id: string; text: string; index: number; total: number; } interface FeedbackData { categoryCorrect: boolean; specificityCorrect: boolean; goldCategory: string; goldSpecificity: number; explanation: string; warmupCompleted: number; done: boolean; } export default function WarmupPage() { const router = useRouter(); const [paragraph, setParagraph] = useState(null); const [warmupCompleted, setWarmupCompleted] = useState(0); const [category, setCategory] = useState(""); const [specificity, setSpecificity] = useState(0); const [feedback, setFeedback] = useState(null); const [loading, setLoading] = useState(true); const [submitting, setSubmitting] = useState(false); const [error, setError] = useState(null); const formRef = useRef(null); const fetchNext = useCallback(async () => { setLoading(true); setError(null); try { const res = await fetch("/api/warmup"); const data = await res.json(); if (!res.ok) { setError(data.error ?? "Failed to load warmup"); return; } if (data.done) { router.push("/label"); return; } setParagraph(data.paragraph); setWarmupCompleted(data.warmupCompleted); setCategory(""); setSpecificity(0); setFeedback(null); } catch { setError("Network error"); } finally { setLoading(false); } }, [router]); useEffect(() => { fetchNext(); }, [fetchNext]); const handleSubmit = useCallback(async () => { if (!category || !specificity || !paragraph || feedback || submitting) return; setSubmitting(true); try { const res = await fetch("/api/warmup", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ category, specificity, warmupIndex: paragraph.index, }), }); const data = await res.json(); if (!res.ok) { setError(data.error ?? "Failed to submit"); return; } setFeedback(data); setWarmupCompleted(data.warmupCompleted); } catch { setError("Network error"); } finally { setSubmitting(false); } }, [category, specificity, paragraph, feedback, submitting]); // Keyboard shortcuts useEffect(() => { function handleKeyDown(e: KeyboardEvent) { if (e.target instanceof HTMLTextAreaElement) return; if (feedback) { // After feedback, Enter or Space advances to next if (e.key === "Enter" || e.key === " ") { e.preventDefault(); if (feedback.done) { router.push("/label"); } else { fetchNext(); } } return; } if (e.shiftKey && e.key >= "1" && e.key <= "4") { e.preventDefault(); setSpecificity(parseInt(e.key)); } else if (!e.shiftKey && e.key >= "1" && e.key <= "7") { e.preventDefault(); setCategory(CATEGORIES[parseInt(e.key) - 1]); } else if (e.key === "Enter" && category && specificity) { e.preventDefault(); handleSubmit(); } } window.addEventListener("keydown", handleKeyDown); return () => window.removeEventListener("keydown", handleKeyDown); }, [category, specificity, feedback, handleSubmit, fetchNext, router]); if (loading) { return (

Loading warmup...

); } if (error) { return (
Error {error}
); } if (!paragraph) return null; const allCorrect = feedback?.categoryCorrect && feedback?.specificityCorrect; return (
{/* Header */}

Warm-up Practice

Progress {() => `${warmupCompleted} / ${paragraph.total}`}
{/* Main content */}
{/* Paragraph display */} Warm-up {warmupCompleted + (feedback ? 0 : 1)} of{" "} {paragraph.total}
{paragraph.text}
{/* Labeling form */}
{/* Content Category */} Content Category {CATEGORIES.map((cat, i) => (
))}
{/* Specificity Level */} Specificity Level setSpecificity(parseInt(v)) } > {SPECIFICITY_LABELS.map((label, i) => (
))}
{/* Submit or feedback */} {!feedback ? ( ) : (
{allCorrect ? ( ) : ( )} {allCorrect ? "Correct!" : feedback.categoryCorrect ? "Specificity incorrect" : feedback.specificityCorrect ? "Category incorrect" : "Both incorrect"}

Gold category:{" "} {feedback.goldCategory} {!feedback.categoryCorrect && ( (you chose: {category}) )}

Gold specificity:{" "} {feedback.goldSpecificity} -{" "} {SPECIFICITY_LABELS[feedback.goldSpecificity - 1]} {!feedback.specificityCorrect && ( (you chose: {specificity} -{" "} {SPECIFICITY_LABELS[specificity - 1]}) )}

{feedback.explanation}

)}
); }