132 lines
4.1 KiB
TypeScript
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)
|
|
}
|
|
}))
|
|
) |