dramaling-vocab-learning/frontend/app/review-old/page.tsx

254 lines
7.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

'use client'
import { useEffect } from 'react'
import { useRouter } from 'next/navigation'
import { Navigation } from '@/components/shared/Navigation'
import LearningComplete from '@/components/flashcards/LearningComplete'
import { Modal } from '@/components/ui/Modal'
// 新架構組件
import { ProgressTracker } from '@/components/review/ui/ProgressTracker'
import { TaskListModal } from '@/components/review/modals/TaskListModal'
import { LoadingStates } from '@/components/review/ui/LoadingStates'
import { ReviewRunner } from '@/components/review/core/ReviewRunner'
// 狀態管理
import { useReviewSessionStore } from '@/store/review/useReviewSessionStore'
import { useTestQueueStore } from '@/store/review/useTestQueueStore'
import { useTestResultStore } from '@/store/review/useTestResultStore'
import { useReviewDataStore } from '@/store/review/useReviewDataStore'
import { useReviewUIStore } from '@/store/review/useReviewUIStore'
import { ReviewService } from '@/lib/services/review/reviewService'
export default function LearnPage() {
const router = useRouter()
// Zustand stores
const { mounted, currentCard, error, setMounted, resetSession: resetSessionState } = useReviewSessionStore()
const {
testItems,
completedTests,
totalTests,
initializeTestQueue,
resetQueue
} = useTestQueueStore()
const { score, resetScore } = useTestResultStore()
const {
dueCards,
showComplete,
showNoDueCards,
isLoadingCards,
loadDueCards,
resetData,
setShowComplete
} = useReviewDataStore()
const {
showTaskListModal,
showReportModal,
modalImage,
reportReason,
reportingCard,
setShowTaskListModal,
closeReportModal,
closeImageModal,
setReportReason
} = useReviewUIStore()
// 初始化
useEffect(() => {
setMounted(true)
initializeSession()
}, [])
// 初始化學習會話
const initializeSession = async () => {
try {
await loadDueCards()
} catch (error) {
console.error('初始化複習會話失敗:', error)
}
}
// 監聽dueCards變化初始化測試隊列
useEffect(() => {
if (dueCards.length > 0) {
const initQueue = async () => {
try {
const cardIds = dueCards.map(c => c.id)
const completedTests = await ReviewService.loadCompletedTests(cardIds)
initializeTestQueue(dueCards, completedTests)
} catch (error) {
console.error('初始化測試隊列失敗:', error)
}
}
initQueue()
}
}, [dueCards, initializeTestQueue])
// 監聽測試隊列變化,設置當前卡片
useEffect(() => {
if (testItems.length > 0 && dueCards.length > 0) {
const currentTestItem = testItems.find(item => item.isCurrent)
if (currentTestItem) {
const card = dueCards.find(c => c.id === currentTestItem.cardId)
if (card) {
const { setCurrentCard } = useReviewSessionStore.getState()
setCurrentCard(card)
}
}
}
}, [testItems, dueCards])
// 監聽測試完成狀態
useEffect(() => {
if (totalTests > 0 && completedTests >= totalTests) {
setShowComplete(true)
}
}, [completedTests, totalTests, setShowComplete])
// 重新開始
const handleRestart = async () => {
resetSessionState()
resetQueue()
resetScore()
resetData()
await initializeSession()
}
// 載入狀態
if (!mounted || isLoadingCards) {
return (
<LoadingStates
isLoadingCard={isLoadingCards}
isAutoSelecting={true}
/>
)
}
if (showNoDueCards) {
return (
<LoadingStates
showNoDueCards={true}
onRestart={handleRestart}
/>
)
}
if (!currentCard) {
return <LoadingStates isLoadingCard={true} />
}
return (
<div className="min-h-screen bg-gradient-to-br from-blue-50 to-indigo-100">
<Navigation />
<div className="max-w-4xl mx-auto px-4 py-8">
{/* 進度追蹤 */}
<ProgressTracker
completedTests={completedTests}
totalTests={totalTests}
onShowTaskList={() => setShowTaskListModal(true)}
/>
{/* 測驗執行器 */}
<ReviewRunner />
{/* 任務清單Modal */}
<TaskListModal
isOpen={showTaskListModal}
onClose={() => setShowTaskListModal(false)}
testItems={testItems}
completedTests={completedTests}
totalTests={totalTests}
/>
{/* 學習完成 */}
{showComplete && (
<LearningComplete
score={score}
mode={'flip-memory'} // 可以從store獲取
onRestart={handleRestart}
onBackToDashboard={() => router.push('/dashboard')}
/>
)}
{/* 圖片Modal */}
{modalImage && (
<div
className="fixed inset-0 bg-black bg-opacity-75 flex items-center justify-center z-50"
onClick={closeImageModal}
>
<div className="relative max-w-4xl max-h-[90vh] mx-4">
<img
src={modalImage}
alt="放大圖片"
className="max-w-full max-h-full rounded-lg"
/>
<button
onClick={closeImageModal}
className="absolute top-4 right-4 bg-black bg-opacity-50 text-white p-2 rounded-full hover:bg-opacity-75"
>
</button>
</div>
</div>
)}
{/* 錯誤回報Modal */}
<Modal
isOpen={showReportModal}
onClose={closeReportModal}
title="回報錯誤"
size="md"
>
<div className="p-6">
<div className="mb-4">
<p className="text-sm text-gray-600 mb-2">
{reportingCard?.word}
</p>
</div>
<div className="mb-4">
<label className="block text-sm font-medium text-gray-700 mb-2">
</label>
<select
value={reportReason}
onChange={(e) => setReportReason(e.target.value)}
className="w-full p-2 border border-gray-300 rounded-md focus:ring-blue-500 focus:border-blue-500"
>
<option value=""></option>
<option value="translation"></option>
<option value="definition"></option>
<option value="pronunciation"></option>
<option value="example"></option>
<option value="image"></option>
<option value="other"></option>
</select>
</div>
<div className="flex gap-2">
<button
onClick={closeReportModal}
className="flex-1 px-4 py-2 border border-gray-300 text-gray-700 rounded-md hover:bg-gray-50"
>
</button>
<button
onClick={() => {
console.log('Report submitted:', { card: reportingCard, reason: reportReason })
closeReportModal()
}}
disabled={!reportReason}
className="flex-1 px-4 py-2 bg-red-600 text-white rounded-md hover:bg-red-700 disabled:opacity-50 disabled:cursor-not-allowed"
>
</button>
</div>
</div>
</Modal>
</div>
</div>
)
}