import { useState, useRef } from "react"; import { apiUpload } from "../../lib/api"; import { ModelSelector } from "../ModelSelector"; interface FileUploadPanelProps { sessionId: number; parsedData: Record; onParsedDataUpdate: (dataType: string, data: unknown) => void; onGoToStep2: () => void; } interface UploadZone { type: "questions" | "student_answers" | "teacher_answers"; label: string; description: string; placeholder: string; icon: string; } const zones: UploadZone[] = [ { type: "questions", label: "考試題目", description: "上傳考試試卷(任意格式)", placeholder: "例如:國中三年級數學期中考,共 20 題選擇題和 5 題計算題", icon: "📝", }, { type: "student_answers", label: "學生答案", description: "上傳學生作答資料(任意格式)", placeholder: "例如:35 位學生的答案卷,手寫掃描", icon: "👨‍🎓", }, { type: "teacher_answers", label: "標準答案", description: "上傳教師答案/解答(任意格式)", placeholder: "例如:教師提供的標準答案與解題步驟", icon: "✅", }, ]; export function FileUploadPanel({ sessionId, parsedData, onParsedDataUpdate, onGoToStep2, }: FileUploadPanelProps) { const [files, setFiles] = useState>({ questions: [], student_answers: [], teacher_answers: [], }); const [descriptions, setDescriptions] = useState>({ questions: "", student_answers: "", teacher_answers: "", }); const [analyzing, setAnalyzing] = useState>({}); const [errors, setErrors] = useState>({}); const [model, setModel] = useState("gpt-5.4"); const fileInputRefs = useRef>({}); const handleFileSelect = (type: string, selected: FileList | null) => { if (!selected) return; setFiles((prev) => ({ ...prev, [type]: Array.from(selected) })); setErrors((prev) => ({ ...prev, [type]: "" })); }; const handleDrop = (type: string, e: React.DragEvent) => { e.preventDefault(); handleFileSelect(type, e.dataTransfer.files); }; const handleAnalyze = async (type: string) => { const typeFiles = files[type]; if (!typeFiles.length) return; setAnalyzing((prev) => ({ ...prev, [type]: true })); setErrors((prev) => ({ ...prev, [type]: "" })); try { const formData = new FormData(); formData.append("data_type", type); formData.append("description", descriptions[type] || ""); formData.append("model", model); for (const f of typeFiles) { formData.append("files", f); } const res = await apiUpload<{ data: unknown }>(`/api/sessions/${sessionId}/upload`, formData); onParsedDataUpdate(type, res.data); } catch (err: unknown) { setErrors((prev) => ({ ...prev, [type]: err instanceof Error ? err.message : "Analysis failed", })); } finally { setAnalyzing((prev) => ({ ...prev, [type]: false })); } }; const hasAnyParsedData = Object.keys(parsedData).length > 0; const isAnyAnalyzing = Object.values(analyzing).some(Boolean); return (
{/* Model selector */}
{/* Info note */}

支援任意格式檔案(PDF、圖片、文字檔等)。若檔案中包含圖片、圖表或示意圖,AI 會自動將其轉換為文字描述,以確保後續分析完整準確。

{/* Upload zones */}
{zones.map((zone) => { const zoneData = parsedData[zone.type] as Record | undefined; const isAnalyzing = analyzing[zone.type]; const hasFiles = files[zone.type].length > 0; const hasParsed = !!zoneData; return (
e.preventDefault()} onDrop={(e) => handleDrop(zone.type, e)} >
{zone.icon}

{zone.label}

{zone.description}

{/* Per-zone description */}