import { useState, useRef, useEffect } from "react"; import type { StudentReport } from "../../App"; interface ReportViewerProps { reports: StudentReport[]; isGenerating: boolean; onBack: () => void; onExportAllHtml: () => void; onSaveToDb?: () => Promise<{ students_saved: number }>; } function CoffeeCup({ size = 32 }: { size?: number }) { const w = size; const h = Math.round(size * (380 / 300)); return ( ); } function estimateMinutes(total: number): string { const secs = total * 45; if (secs < 90) return "about a minute"; const mins = Math.ceil(secs / 60); return `about ${mins} minutes`; } export function ReportViewer({ reports, isGenerating, onBack, onExportAllHtml, onSaveToDb, }: ReportViewerProps) { const [activeIndex, setActiveIndex] = useState(0); const [saveState, setSaveState] = useState<"idle" | "saving" | "saved" | "error">("idle"); const [saveMsg, setSaveMsg] = useState(""); const iframeRef = useRef(null); const topRef = useRef(null); const handleSaveToDb = async () => { if (!onSaveToDb) return; setSaveState("saving"); setSaveMsg(""); try { const res = await onSaveToDb(); setSaveState("saved"); setSaveMsg(`已儲存 ${res.students_saved} 位學生成績`); } catch (e) { setSaveState("error"); setSaveMsg(e instanceof Error ? e.message : "儲存失敗"); } }; // Auto-switch to the report currently being generated useEffect(() => { const generatingIdx = reports.findIndex((r) => r.status === "generating"); if (generatingIdx >= 0) { setActiveIndex(generatingIdx); } else { let lastDone = -1; for (let j = reports.length - 1; j >= 0; j--) { if (reports[j].status === "done") { lastDone = j; break; } } if (lastDone >= 0) setActiveIndex(lastDone); } }, [reports]); const scrollToTop = () => { iframeRef.current?.contentWindow?.scrollTo({ top: 0, behavior: "smooth" }); }; const activeReport = reports[activeIndex]; const doneCount = reports.filter((r) => r.status === "done").length; const errorCount = reports.filter((r) => r.status === "error").length; const totalCount = reports.length; const handleDownloadAll = () => { const missing = totalCount - doneCount; if (missing > 0) { const ok = window.confirm( `尚有 ${missing} 位學生的報告尚未生成完成,這份下載檔將不完整(只包含目前已完成的 ${doneCount} 份報告,缺少的學生不會出現在檔案中)。\n\n確定要現在下載嗎?` ); if (!ok) return; } onExportAllHtml(); }; return (
{/* Progress bar */} {isGenerating && (
生成進度 {doneCount + errorCount} / {totalCount}

Hang tight — this will take {estimateMinutes(totalCount)}. Grab a coffee!

)} {/* Action bar */}
{activeReport?.status === "done" && ( )} {doneCount > 1 && ( )} {onSaveToDb && !isGenerating && doneCount > 0 && ( )}
{saveMsg && (

{saveMsg}

)} {/* Student tabs */}
{reports.map((r, i) => ( ))}
{/* Report content */}
{activeReport?.status === "done" && ( <>