dramaling-vocab-learning/frontend/store/review/useReviewDataStore.ts

132 lines
4.1 KiB
TypeScript

import { create } from 'zustand'
import { subscribeWithSelector } from 'zustand/middleware'
import { flashcardsService } from '@/lib/services/flashcards'
import { ExtendedFlashcard } from '@/lib/types/review'
import { isTestMode, getMockDueCards } from '@/lib/mock/reviewMockData'
import { ReviewService } from '@/lib/services/review/reviewService'
// 數據狀態接口
interface ReviewDataState {
// 詞卡數據
dueCards: ExtendedFlashcard[]
// UI 顯示狀態
showComplete: boolean
showNoDueCards: boolean
// 數據載入狀態
isLoadingCards: boolean
loadingError: string | null
// Actions
setDueCards: (cards: ExtendedFlashcard[]) => void
setShowComplete: (show: boolean) => void
setShowNoDueCards: (show: boolean) => void
setLoadingCards: (loading: boolean) => void
setLoadingError: (error: string | null) => void
loadDueCards: () => Promise<void>
resetData: () => void
// 輔助方法
getDueCardsCount: () => number
findCardById: (cardId: string) => ExtendedFlashcard | undefined
}
export const useReviewDataStore = create<ReviewDataState>()(
subscribeWithSelector((set, get) => ({
// 初始狀態
dueCards: [],
showComplete: false,
showNoDueCards: false,
isLoadingCards: false,
loadingError: null,
// Actions
setDueCards: (cards) => set({ dueCards: cards }),
setShowComplete: (show) => set({ showComplete: show }),
setShowNoDueCards: (show) => set({ showNoDueCards: show }),
setLoadingCards: (loading) => set({ isLoadingCards: loading }),
setLoadingError: (error) => set({ loadingError: error }),
loadDueCards: async () => {
const { setLoadingCards, setLoadingError, setDueCards, setShowNoDueCards, setShowComplete } = get()
try {
setLoadingCards(true)
setLoadingError(null)
console.log('🔍 開始載入到期詞卡...')
// 🧪 測試模式:使用 Mock 數據
if (isTestMode()) {
console.log('🧪 [測試模式] 使用 Mock 數據')
const mockCards = getMockDueCards() as ExtendedFlashcard[]
// 模擬載入延遲
await new Promise(resolve => setTimeout(resolve, 500))
if (mockCards.length > 0) {
console.log('✅ [測試模式] 載入Mock數據成功:', mockCards.length, '張詞卡')
setDueCards(mockCards)
setShowNoDueCards(false)
setShowComplete(false)
} else {
console.log('❌ [測試模式] Mock數據為空')
setDueCards([])
setShowNoDueCards(true)
setShowComplete(false)
}
return
}
// 🌐 正常模式:使用後端 API
const apiResult = await flashcardsService.getDueFlashcards(50)
console.log('📡 API回應結果:', apiResult)
if (apiResult.success && apiResult.data && apiResult.data.length > 0) {
// 使用 ReviewService 轉換數據
const cards = apiResult.data.map(ReviewService.transformToExtendedFlashcard)
console.log('✅ 載入後端API數據成功:', cards.length, '張詞卡')
setDueCards(cards)
setShowNoDueCards(false)
setShowComplete(false)
} else {
console.log('❌ 沒有到期詞卡')
setDueCards([])
setShowNoDueCards(true)
setShowComplete(false)
}
} catch (error) {
console.error('💥 載入到期詞卡失敗:', error)
setLoadingError('載入詞卡失敗')
setDueCards([])
setShowNoDueCards(true)
} finally {
setLoadingCards(false)
}
},
resetData: () => set({
dueCards: [],
showComplete: false,
showNoDueCards: false,
isLoadingCards: false,
loadingError: null
}),
// 輔助方法
getDueCardsCount: () => {
const { dueCards } = get()
return dueCards.length
},
findCardById: (cardId) => {
const { dueCards } = get()
return dueCards.find(card => card.id === cardId)
}
}))
)