chih.yikuan
email-done
5ee5085
import { useState, useEffect } from "react";
import { ChatKit, useChatKit } from "@openai/chatkit-react";
import { CHATKIT_API_DOMAIN_KEY, CHATKIT_API_URL } from "../lib/config";
import { ReasoningPanel } from "./ReasoningPanel";
interface ExamAnalyzerProps {
teacherEmail: string;
onBack: () => void;
}
export function ExamAnalyzer({ teacherEmail, onBack }: ExamAnalyzerProps) {
const [showCopiedToast, setShowCopiedToast] = useState(false);
const [selectedModel, setSelectedModel] = useState("gpt-4.1-mini");
const [availableModels, setAvailableModels] = useState<Record<string, {name: string; description: string; cost: string}>>({});
// Load available models
useEffect(() => {
fetch("/api/models")
.then(res => res.json())
.then(data => {
setAvailableModels(data.models || {});
setSelectedModel(data.default || "gpt-4.1-mini");
})
.catch(err => console.error("Failed to load models:", err));
}, []);
// Force Traditional Chinese locale for ChatKit
useEffect(() => {
// Set document language to zh-TW
document.documentElement.lang = 'zh-TW';
// Try to override browser language detection
if (navigator.language) {
Object.defineProperty(navigator, 'language', {
get: () => 'zh-TW',
configurable: true,
});
}
// Set Accept-Language header hint (may not work for iframe)
const meta = document.createElement('meta');
meta.httpEquiv = 'Content-Language';
meta.content = 'zh-TW';
document.head.appendChild(meta);
}, []);
// Store selected model in sessionStorage for backend access
useEffect(() => {
sessionStorage.setItem("selected_model", selectedModel);
}, [selectedModel]);
const chatkit = useChatKit({
api: {
url: CHATKIT_API_URL,
domainKey: CHATKIT_API_DOMAIN_KEY,
},
composer: {
attachments: { enabled: false },
},
});
const handleCopyPrompt = (prompt: string) => {
navigator.clipboard.writeText(prompt);
setShowCopiedToast(true);
setTimeout(() => setShowCopiedToast(false), 2000);
};
const examplePrompts = [
{
title: "📊 分析 Google 試算表",
prompt: `請分析這個 Google 試算表中的考試答案:
https://docs.google.com/spreadsheets/d/YOUR_SHEET_ID/edit
標準答案是:
- Q1: 4
- Q2: 加速度是速度的變化率
我的電子郵件是 ${teacherEmail}。請評分、解釋錯誤答案、建議同儕學習小組,並將報告發送到我的電子郵件。`,
},
{
title: "📝 分析 CSV 資料",
prompt: `請分析這些考試資料:
Timestamp,Email,Student Name,Q1 (2+2),Q2 (Explain acceleration)
2026-01-26 08:00:00,alice@student.edu,Alice,4,Acceleration is the rate of change of velocity
2026-01-26 08:01:00,bob@student.edu,Bob,3,I dont know
2026-01-26 08:02:00,carol@student.edu,Carol,4,velocity change over time
2026-01-26 08:03:00,david@student.edu,David,5,speed
標準答案是:
- Q1: 4
- Q2: Acceleration is the rate of change of velocity over time
我的電子郵件是 ${teacherEmail}。請評分並建立完整報告。`,
},
{
title: "⚡ 快速摘要",
prompt: `請給我班級表現的快速摘要。我的電子郵件是 ${teacherEmail}。`,
},
];
return (
<div className="min-h-screen pt-24 pb-8 px-4">
{/* Copied toast */}
{showCopiedToast && (
<div className="fixed top-24 left-1/2 -translate-x-1/2 z-50 px-4 py-2 bg-green-500 text-white rounded-lg shadow-lg">
✓ 提示已複製!請在聊天中貼上。
</div>
)}
<div className="max-w-7xl mx-auto">
{/* Top bar */}
<div className="flex items-center justify-between mb-6">
<button
onClick={onBack}
className="flex items-center gap-2 text-gray-500 hover:text-gray-700 transition-colors"
>
<svg className="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M10 19l-7-7m0 0l7-7m-7 7h18" />
</svg>
返回首頁
</button>
<div className="flex items-center gap-3">
<span className="text-sm text-gray-500">已連接為</span>
<span className="px-3 py-1 bg-green-100 text-green-700 rounded-full text-sm font-medium">
{teacherEmail}
</span>
<div className="flex items-center gap-2">
<span className="text-sm text-gray-500">模型:</span>
<select
value={selectedModel}
onChange={(e) => setSelectedModel(e.target.value)}
className="px-3 py-1 bg-white border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500"
>
{Object.entries(availableModels).map(([key, model]) => (
<option key={key} value={key}>
{model.name} {key === "gpt-4.1-mini" ? "(默認)" : ""} - {model.description}
</option>
))}
</select>
</div>
</div>
</div>
<div className="grid lg:grid-cols-4 gap-6">
{/* Left Sidebar - AI Reasoning */}
<div className="lg:col-span-1 space-y-4">
{/* AI Reasoning Panel - Shows LLM thinking process */}
<ReasoningPanel sessionId="default" />
{/* Example Prompts */}
<div className="bg-white rounded-xl shadow-sm border p-4">
<h3 className="text-sm font-semibold text-gray-800 mb-3">
快速開始提示
</h3>
<div className="space-y-2">
{examplePrompts.map((example, i) => (
<button
key={i}
onClick={() => handleCopyPrompt(example.prompt)}
className="w-full text-left p-3 rounded-lg bg-gray-50 hover:bg-blue-50 transition-colors group border border-transparent hover:border-blue-200"
>
<div className="text-sm font-medium text-gray-700 group-hover:text-blue-600">
{example.title}
</div>
<div className="text-xs text-gray-500">
點擊複製
</div>
</button>
))}
</div>
</div>
{/* Tips */}
<div className="bg-gradient-to-br from-blue-50 to-indigo-50 rounded-xl p-4 border border-blue-100">
<h3 className="text-sm font-semibold text-gray-800 mb-2 flex items-center gap-2">
💡 專業提示
</h3>
<ul className="space-y-1.5 text-xs text-gray-600">
<li className="flex items-start gap-1.5">
<span className="text-green-500"></span>
包含標準答案以提高準確度
</li>
<li className="flex items-start gap-1.5">
<span className="text-green-500"></span>
將 Google 試算表設為公開,或使用 CSV
</li>
<li className="flex items-start gap-1.5">
<span className="text-green-500"></span>
要求以電子郵件發送報告
</li>
</ul>
</div>
</div>
{/* Main Chat Area - Use same structure as SimpleChatPanel */}
<div className="lg:col-span-3">
<div className="bg-white rounded-xl shadow-sm border overflow-hidden">
{/* Chat Header */}
<div className="bg-gradient-to-r from-blue-600 to-indigo-600 px-6 py-4">
<div className="flex items-center justify-between">
<div className="flex items-center gap-3">
<div className="w-10 h-10 rounded-xl bg-white/20 flex items-center justify-center">
<svg className="w-5 h-5 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4" />
</svg>
</div>
<div>
<h2 className="text-lg font-semibold text-white">
ClassLens 助手
</h2>
<p className="text-sm text-white/70">
請在下方貼上您的考試資料或 Google 試算表網址
</p>
</div>
</div>
<span className="px-2 py-1 rounded-full bg-white/20 text-white text-xs">
{availableModels[selectedModel]?.name || selectedModel}
</span>
</div>
</div>
{/* ChatKit - Using the EXACT same structure as SimpleChatPanel that works */}
<div className="h-[calc(100vh-320px)]">
<ChatKit
control={chatkit.control}
className="block h-full w-full"
/>
</div>
</div>
</div>
</div>
</div>
</div>
);
}