import React, { useState, useEffect, useRef } from 'react'; import { Brain, Send, Loader2, CheckCircle, AlertCircle, ChevronRight, History, ClipboardCheck, RefreshCcw, FileText, Star, Award, Trophy } from 'lucide-react'; import { motion, AnimatePresence } from 'framer-motion'; import { useLanguage } from '../../contexts/LanguageContext'; import { assessmentService, AssessmentSession, AssessmentState } from '../../services/assessmentService'; import { knowledgeGroupService } from '../../services/knowledgeGroupService'; import { KnowledgeGroup } from '../../types'; import { cn } from '../../src/utils/cn'; interface AssessmentViewProps { onLogout: () => void; onNavigate: (path: string) => void; isAdmin: boolean; } export const AssessmentView: React.FC = ({ onLogout, onNavigate, isAdmin }) => { const { language, t } = useLanguage(); const [groups, setGroups] = useState([]); const [selectedGroup, setSelectedGroup] = useState(null); const [session, setSession] = useState(null); const [state, setState] = useState(null); const [inputValue, setInputValue] = useState(''); const [isLoading, setIsLoading] = useState(false); const [processStep, setProcessStep] = useState(''); const [error, setError] = useState(null); const [history, setHistory] = useState([]); const [loadingHistoryId, setLoadingHistoryId] = useState(null); const messagesEndRef = useRef(null); useEffect(() => { const fetchGroups = async () => { try { const data = await knowledgeGroupService.getGroups(); setGroups(data); } catch (err) { console.error('Failed to fetch groups:', err); } }; fetchGroups(); }, []); useEffect(() => { messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }); }, [state?.messages, isLoading]); const fetchHistory = async () => { try { const data = await assessmentService.getHistory(); setHistory(data); } catch (err) { console.error('Failed to fetch history:', err); } }; useEffect(() => { fetchHistory(); }, []); const isZh = language === 'zh'; const isJa = language === 'ja'; const getStatusText = (node: string) => { const mapping: Record = { generator: isZh ? '正在生成测评问题...' : isJa ? '問題を生成中...' : 'Generating questions...', grader: isZh ? '正在评估您的回答...' : isJa ? '回答を評価中...' : 'Evaluating your answer...', interviewer: isZh ? '正在准备下一个问题...' : isJa ? '次の質問を準備中...' : 'Preparing next question...', analyzer: isZh ? '正在生成最终报告...' : isJa ? 'レポートを生成中...' : 'Generating final report...', }; return mapping[node] || (isZh ? '正在处理...' : isJa ? '処理中...' : 'Processing...'); }; const handleSelectHistory = async (histSession: AssessmentSession) => { if (isLoading) return; setLoadingHistoryId(histSession.id); setIsLoading(true); setError(null); try { const histState = await assessmentService.getSessionState(histSession.id); setState(histState); setSession(histSession); } catch (err: any) { setError(err.message || 'Failed to load historical assessment'); } finally { setIsLoading(false); setLoadingHistoryId(null); } }; const handleStartAssessment = async () => { if (!selectedGroup) return; setIsLoading(true); setError(null); setProcessStep(isZh ? '正在初始化...' : isJa ? '初期化中...' : 'Initializing...'); try { const newSession = await assessmentService.startSession(selectedGroup, language); setSession(newSession); for await (const event of assessmentService.startSessionStream(newSession.id)) { if (event.type === 'node') { setProcessStep(getStatusText(event.node)); if (event.data) { setState(prev => { if (!prev) return event.data; const prevMessages = prev.messages || []; return { ...prev, ...event.data, messages: event.data.messages ? [...prevMessages, ...event.data.messages.filter((m: any) => !prevMessages.some((pm: any) => pm.content === m.content && pm.role === m.role))] : prevMessages, feedbackHistory: event.data.feedbackHistory ? [...(prev.feedbackHistory || []), ...event.data.feedbackHistory.filter((fh: any) => !(prev.feedbackHistory || []).some((pfh: any) => pfh.content === fh.content))] : (prev.feedbackHistory || []), scores: { ...(prev.scores || {}), ...(event.data.scores || {}) } } as any; }); } } else if (event.type === 'final') { setState(event.data); } } } catch (err: any) { setError(err.message || 'Failed to start assessment'); } finally { setIsLoading(false); setProcessStep(''); } }; const handleRetry = async () => { if (!session) return; setIsLoading(true); setError(null); setProcessStep(isZh ? '正在重新尝试生成...' : isJa ? '再生成中...' : 'Retrying generation...'); try { for await (const event of assessmentService.startSessionStream(session.id)) { if (event.type === 'node') { setProcessStep(getStatusText(event.node)); } else if (event.type === 'final') { setState(event.data); } } } catch (err: any) { setError(err.message || 'Retry failed'); } finally { setIsLoading(false); setProcessStep(''); } }; const handleSubmitAnswer = async () => { if (!session || !inputValue.trim() || isLoading) return; const answer = inputValue.trim(); setInputValue(''); setIsLoading(true); setError(null); setProcessStep(isZh ? '正在准备发送...' : isJa ? '送信準備中...' : 'Preparing to send...'); try { setState(prev => ({ ...prev!, messages: [ ...(prev?.messages || []), { role: 'user' as const, content: answer, timestamp: Date.now() } ] })); for await (const event of assessmentService.submitAnswerStream(session.id, answer, language)) { if (event.type === 'node') { setProcessStep(getStatusText(event.node)); if (event.data) { setState(prev => { if (!prev) return event.data; const prevMessages = prev.messages || []; const mergedMessages = event.data.messages ? [...prevMessages, ...event.data.messages.filter((m: any) => !prevMessages.some((pm: any) => pm.content === m.content && pm.role === m.role))] : prevMessages; return { ...prev, ...event.data, messages: mergedMessages, feedbackHistory: event.data.feedbackHistory ? [...(prev.feedbackHistory || []), ...event.data.feedbackHistory.filter((fh: any) => !(prev.feedbackHistory || []).some((pfh: any) => pfh.content === fh.content))] : (prev.feedbackHistory || []), scores: { ...(prev.scores || {}), ...(event.data.scores || {}) } } as any; }); } } else if (event.type === 'final') { setState(event.data); if (event.data.status === 'COMPLETED') { setSession(prev => prev ? { ...prev, status: 'COMPLETED' } : null); fetchHistory(); } } } } catch (err: any) { setError(err.message || 'Failed to submit answer'); } finally { setIsLoading(false); setProcessStep(''); } }; const renderHeader = () => (

{t('assessmentTitle')}

{t('assessmentDesc')}

{session && (
{session.status === 'IN_PROGRESS' ? t('inProgress') : t('statusReadyFragment')}
)}
); const renderSetup = () => (
{/* Main Setup Content */}

{t('readyForAssessment')}

{t('readyForAssessmentDesc')}

{groups.map(group => ( ))} {groups.length === 0 && (

No knowledge groups found.

)}
{error && (

{error}

)}
{t('aiPoweredAnalysis')}
{t('masteryScoring')}
{/* Assessment History Sidebar */} {history.length > 0 && (

{t('recentAssessments')}

{history.map(hist => ( ))}
)}
); const renderAssessment = () => { const currentQuestionNo = (state?.currentQuestionIndex || 0) + 1; const totalQuestions = state?.questions?.length || 0; const progressLabel = totalQuestions > 0 ? t('questionProgress', currentQuestionNo, totalQuestions) : t('initializingQuestion', currentQuestionNo); const messages = state?.messages || []; const filteredMessages = messages.filter(m => m.role !== 'system' && !(m.role === 'assistant' && (m.content?.toString().startsWith('Score:') || m.content?.toString().startsWith('得分:'))) ); const feedbackHistory = state?.feedbackHistory || []; const lastFeedbackMessage = feedbackHistory[feedbackHistory.length - 1]; const feedbackMatch = lastFeedbackMessage?.content?.toString().match(/(?:Score|得分): (\d+)\/10\n\n(?:Feedback|反馈): ([\s\S]*)/i); const latestScore = feedbackMatch ? feedbackMatch[1] : null; const latestFeedback = feedbackMatch ? feedbackMatch[2] : (lastFeedbackMessage?.content || null); return (
{/* Left: Chat Area */}
{progressLabel} {isLoading && (
{processStep || t('aiIsProcessing')} )}
{filteredMessages.map((msg, idx) => (
{msg.content}
{new Date(msg.timestamp).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}
))} {isLoading && (
)}