dramaling-vocab-learning/frontend/store/useTestQueueStore.ts

195 lines
5.8 KiB
TypeScript

import { create } from 'zustand'
import { subscribeWithSelector } from 'zustand/middleware'
import { getReviewTypesByCEFR } from '@/lib/utils/cefrUtils'
// 複習模式類型
export type ReviewMode = 'flip-memory' | 'vocab-choice' | 'vocab-listening' | 'sentence-listening' | 'sentence-fill' | 'sentence-reorder' | 'sentence-speaking'
// 測驗項目接口
export interface TestItem {
id: string
cardId: string
word: string
testType: ReviewMode
testName: string
isCompleted: boolean
isCurrent: boolean
order: number
}
// 測驗隊列狀態接口
interface TestQueueState {
// 測驗隊列狀態
testItems: TestItem[]
currentTestIndex: number
completedTests: number
totalTests: number
currentMode: ReviewMode
// Actions
setTestItems: (items: TestItem[]) => void
setCurrentTestIndex: (index: number) => void
setCompletedTests: (completed: number) => void
setTotalTests: (total: number) => void
setCurrentMode: (mode: ReviewMode) => void
initializeTestQueue: (dueCards: any[], completedTests: any[]) => void
goToNextTest: () => void
skipCurrentTest: () => void
markTestCompleted: (testIndex: number) => void
resetQueue: () => void
}
// 工具函數
function getTestTypeName(testType: string): string {
const names = {
'flip-memory': '翻卡記憶',
'vocab-choice': '詞彙選擇',
'sentence-fill': '例句填空',
'sentence-reorder': '例句重組',
'vocab-listening': '詞彙聽力',
'sentence-listening': '例句聽力',
'sentence-speaking': '例句口說'
}
return names[testType as keyof typeof names] || testType
}
export const useTestQueueStore = create<TestQueueState>()(
subscribeWithSelector((set, get) => ({
// 初始狀態
testItems: [],
currentTestIndex: 0,
completedTests: 0,
totalTests: 0,
currentMode: 'flip-memory',
// Actions
setTestItems: (items) => set({ testItems: items }),
setCurrentTestIndex: (index) => set({ currentTestIndex: index }),
setCompletedTests: (completed) => set({ completedTests: completed }),
setTotalTests: (total) => set({ totalTests: total }),
setCurrentMode: (mode) => set({ currentMode: mode }),
initializeTestQueue: (dueCards = [], completedTests = []) => {
const userCEFRLevel = localStorage.getItem('userEnglishLevel') || 'A2'
let remainingTestItems: TestItem[] = []
let order = 1
dueCards.forEach(card => {
const wordCEFRLevel = card.difficultyLevel || 'A2'
const allTestTypes = getReviewTypesByCEFR(userCEFRLevel, wordCEFRLevel)
const completedTestTypes = completedTests
.filter(ct => ct.flashcardId === card.id)
.map(ct => ct.testType)
const remainingTestTypes = allTestTypes.filter(testType =>
!completedTestTypes.includes(testType)
)
console.log(`🎯 詞卡 ${card.word}: 總共${allTestTypes.length}個測驗, 已完成${completedTestTypes.length}個, 剩餘${remainingTestTypes.length}`)
remainingTestTypes.forEach(testType => {
remainingTestItems.push({
id: `${card.id}-${testType}`,
cardId: card.id,
word: card.word,
testType: testType as ReviewMode,
testName: getTestTypeName(testType),
isCompleted: false,
isCurrent: false,
order
})
order++
})
})
if (remainingTestItems.length === 0) {
console.log('🎉 所有測驗都已完成!')
return
}
// 標記第一個測驗為當前
remainingTestItems[0].isCurrent = true
set({
testItems: remainingTestItems,
totalTests: remainingTestItems.length,
currentTestIndex: 0,
completedTests: 0,
currentMode: remainingTestItems[0].testType
})
console.log('📝 剩餘測驗項目:', remainingTestItems.length, '個')
},
goToNextTest: () => {
const { testItems, currentTestIndex } = get()
if (currentTestIndex + 1 < testItems.length) {
const nextIndex = currentTestIndex + 1
const updatedTestItems = testItems.map((item, index) => ({
...item,
isCurrent: index === nextIndex
}))
const nextTestItem = updatedTestItems[nextIndex]
set({
testItems: updatedTestItems,
currentTestIndex: nextIndex,
currentMode: nextTestItem.testType
})
console.log(`🔄 載入下一個測驗: ${nextTestItem.word} - ${nextTestItem.testType}`)
} else {
console.log('🎉 所有測驗完成!')
}
},
skipCurrentTest: () => {
const { testItems, currentTestIndex } = get()
const currentTest = testItems[currentTestIndex]
if (!currentTest) return
// 將當前測驗移到隊列最後
const newItems = [...testItems]
newItems.splice(currentTestIndex, 1)
newItems.push({ ...currentTest, isCurrent: false })
// 標記新的當前項目
if (newItems[currentTestIndex]) {
newItems[currentTestIndex].isCurrent = true
}
set({ testItems: newItems })
console.log(`⏭️ 跳過測驗: ${currentTest.word} - ${currentTest.testType}`)
},
markTestCompleted: (testIndex) => {
const { testItems } = get()
const updatedTestItems = testItems.map((item, index) =>
index === testIndex
? { ...item, isCompleted: true, isCurrent: false }
: item
)
set({
testItems: updatedTestItems,
completedTests: get().completedTests + 1
})
},
resetQueue: () => set({
testItems: [],
currentTestIndex: 0,
completedTests: 0,
totalTests: 0,
currentMode: 'flip-memory'
})
}))
)