"use client" import { useEffect, useState } from "react" import { useRouter } from "next/navigation" import { useTranslations } from "next-intl" import { toast } from "sonner" import { Save, ChevronLeft, ChevronRight, User, Clock } from "lucide-react" import { Button } from "@/shared/components/ui/button" import { Card, CardHeader } from "@/shared/components/ui/card" import { Input } from "@/shared/components/ui/input" import { Label } from "@/shared/components/ui/label" import { Textarea } from "@/shared/components/ui/textarea" import { Badge } from "@/shared/components/ui/badge" import { ScrollArea } from "@/shared/components/ui/scroll-area" import { ResizablePanel } from "@/shared/components/ui/resizable-panel" import { formatDate } from "@/shared/lib/utils" import { gradeHomeworkSubmissionAction, getScansAction } from "../actions" import { QuestionRenderer } from "./question-renderer" import { ScanImageViewer } from "./scan-image-viewer" import type { HomeworkSubmissionAnswerDetails, ScanAttachment } from "../types" type HomeworkScanGradingViewProps = { submissionId: string studentName: string assignmentTitle: string submittedAt: string | null status: string totalScore: number | null answers: HomeworkSubmissionAnswerDetails[] prevSubmissionId?: string | null nextSubmissionId?: string | null } interface GradingState { score: string feedback: string } export function HomeworkScanGradingView({ submissionId, studentName, assignmentTitle, submittedAt, status, totalScore, answers, prevSubmissionId, nextSubmissionId, }: HomeworkScanGradingViewProps) { const router = useRouter() const t = useTranslations("examHomework") const [scans, setScans] = useState([]) const [loadingScans, setLoadingScans] = useState(true) const [isSaving, setIsSaving] = useState(false) const [grading, setGrading] = useState>(() => { const obj: Record = {} for (const ans of answers) { obj[ans.id] = { score: ans.score?.toString() ?? "", feedback: ans.feedback ?? "", } } return obj }) useEffect(() => { void (async () => { const result = await getScansAction(submissionId) if (result.success && result.data) { setScans(result.data) } setLoadingScans(false) })() }, [submissionId]) const handleSave = async () => { setIsSaving(true) try { const answersPayload = answers.map((ans) => { const g = grading[ans.id] return { id: ans.id, score: g ? Number(g.score) || 0 : 0, feedback: g?.feedback ?? null, } }) const fd = new FormData() fd.set("submissionId", submissionId) fd.set("answersJson", JSON.stringify(answersPayload)) const result = await gradeHomeworkSubmissionAction(null, fd) if (result.success) { toast.success(t("homework.grade.gradesSaved")) } else { toast.error(result.message || t("homework.grade.gradesSaveFailed")) } } catch { toast.error(t("homework.grade.saveFailed")) } finally { setIsSaving(false) } } const handleNavigate = (targetId: string | null | undefined) => { if (!targetId) return router.push(`/teacher/homework/submissions/${targetId}/scan-grading`) } return (
{/* 顶部信息栏 */}
{studentName}
{assignmentTitle} {submittedAt && ( <>
{formatDate(submittedAt)}
)} {status} {totalScore !== null && ( {t("homework.grade.totalScore")}: {totalScore} )}
{/* 主体:左题目右扫描图 */}
{t("homework.grade.questionsCount", { count: answers.length })}
{answers.map((ans, idx) => (
setGrading((prev) => ({ ...prev, [ans.id]: { score: e.target.value, feedback: prev[ans.id]?.feedback ?? "", }, })) } className="h-8 w-20" /> {t("homework.grade.scoreOutOf", { max: ans.maxScore })}