import { useEffect, useState } from "react"; import type { MouseEvent } from "react"; import { apiPost } from "../../lib/api"; import { spawnSparkles } from "../../lib/sparkle"; interface DataPreviewModalProps { onGenerate: () => void; onClose: () => void; isGenerating: boolean; sessionId: number; selectedIndices: number[]; students: { index: number; name: string }[]; } interface PreviewResponse { student_index: number; student_name: string; total_questions: number; wrong_count: number; markdown_prompt: string; } export function DataPreviewModal({ onGenerate, onClose, isGenerating, sessionId, selectedIndices, students, }: DataPreviewModalProps) { const [previewIdx, setPreviewIdx] = useState( selectedIndices.length > 0 ? selectedIndices[0] : 0 ); const [loading, setLoading] = useState(false); const [previews, setPreviews] = useState>({}); const [error, setError] = useState(""); const [triggering, setTriggering] = useState(false); // Let the sparkle burst actually play before the modal closes and the step 3 // loading screen takes over — calling onGenerate() immediately unmounted // this button (and its in-progress particles) before the animation finished. const handleGenerateClick = (e: MouseEvent) => { spawnSparkles(e); setTriggering(true); setTimeout(onGenerate, 450); }; // Fetch every selected student's prompt in one request up front — switching // the dropdown below then just reads from local state, no re-fetch per switch. useEffect(() => { let active = true; setLoading(true); setError(""); setPreviews({}); apiPost<{ previews: PreviewResponse[] }>( `/api/sessions/${sessionId}/preview-student-prompts`, { student_indices: selectedIndices } ) .then((res) => { if (!active) return; const map: Record = {}; for (const p of res.previews) map[p.student_index] = p; setPreviews(map); }) .catch((e) => active && setError(e instanceof Error ? e.message : "Failed to load preview") ) .finally(() => active && setLoading(false)); return () => { active = false; }; }, [sessionId, selectedIndices]); const preview = previews[previewIdx] ?? null; return (

最終提示詞預覽

{preview && ( <> {preview.student_name} 總題數 {preview.total_questions} 錯 {preview.wrong_count} )}

以下是實際送給 LLM 的完整提示詞(僅含該學生答錯的題目)

{loading ? (
) : error ? (
{error}
) : (