"use client"; import { useEffect, useRef, useState } from "react"; import { ApiError, gradeAnswer } from "@/lib/client-api"; import { useI18n } from "@/lib/i18n"; import { modelErrorCopy } from "@/lib/model-errors"; import type { Activity } from "@/lib/schemas"; import { cn } from "@/lib/utils"; import { Button, Spinner } from "@/components/ui"; import { ModelErrorActions } from "@/components/ModelErrorActions"; import { SpeakButton } from "@/components/SpeakButton"; import { ActivityHeader, FeedbackPanel, Hint, PromptCard, QuestionText, useNoHints, type ActivityComponentProps, } from "./shared"; type ShortAnswerActivity = Extract; type GradeState = | { status: "idle" } | { status: "grading" } | { status: "graded"; correct: boolean; feedback: string; missing: string[] } | { status: "error"; message: string; kind?: string } | { status: "self-check" }; export function ShortAnswer({ activity, active, language, onAnswered, onNext, isLast, nextLabel }: ActivityComponentProps) { const { t } = useI18n(); const noHints = useNoHints(); const [value, setValue] = useState(""); const [state, setState] = useState({ status: "idle" }); const textareaRef = useRef(null); useEffect(() => { if (active && state.status === "idle") { const timer = window.setTimeout(() => textareaRef.current?.focus(), 350); return () => window.clearTimeout(timer); } }, [active, state.status]); const grade = async () => { if (!value.trim() || state.status === "grading") return; setState({ status: "grading" }); try { const result = await gradeAnswer({ question: activity.question, referenceAnswer: activity.referenceAnswer, keyPoints: activity.keyPoints, userAnswer: value.trim(), language, }); setState({ status: "graded", correct: result.correct, feedback: result.feedback, missing: result.missing, }); onAnswered(result.correct, { userAnswer: value.trim() }); } catch (error) { setState({ status: "error", message: error instanceof ApiError ? error.message : t("short.gradeFailed"), kind: error instanceof ApiError ? error.kind : undefined, }); } }; const selfCheck = (correct: boolean) => { setState({ status: "graded", correct, feedback: t("short.selfChecked"), missing: [] }); onAnswered(correct, { userAnswer: value.trim() }); }; return ( <>
{activity.question}