dramaling-vocab-learning/frontend/lib/data/reviewSimpleData.ts

157 lines
4.8 KiB
TypeScript

// 模擬真實API數據結構
import apiSeeds from './api_seeds.json'
// API響應接口 (匹配真實API結構 + 同義詞擴展)
export interface ApiFlashcard {
id: string
word: string
translation: string
definition: string
partOfSpeech: string
pronunciation: string
example: string
exampleTranslation: string
isFavorite: boolean
difficultyLevelNumeric: number
cefr: string
createdAt: string
updatedAt: string
hasExampleImage: boolean
primaryImageUrl: string | null
// 添加同義詞支持
synonyms?: string[]
}
// 前端狀態擴展接口 (延遲計數系統)
export interface CardState extends ApiFlashcard {
// 延遲計數欄位
skipCount: number // 跳過次數
wrongCount: number // 答錯次數
isCompleted: boolean // 是否已完成
originalOrder: number // 原始順序
}
// 測驗項目接口 (線性流程核心)
export interface QuizItem {
id: string // 測驗項目ID
cardId: string // 所屬詞卡ID
quizType: 'flip-card' | 'vocab-choice' // 測驗類型
isCompleted: boolean // 個別測驗完成狀態
skipCount: number // 跳過次數
wrongCount: number // 答錯次數
order: number // 序列順序
cardData: CardState // 詞卡數據引用
}
export interface ApiResponse {
success: boolean
data: {
flashcards: ApiFlashcard[]
count: number
}
message: string | null
timestamp: string
}
// 模擬API響應數據 (直接使用真實API格式)
export const MOCK_API_RESPONSE: ApiResponse = apiSeeds as ApiResponse
// 為詞卡添加延遲計數狀態
const addStateFields = (flashcard: ApiFlashcard, index: number): CardState => ({
...flashcard,
skipCount: 0,
wrongCount: 0,
isCompleted: false,
originalOrder: index
})
// 提取詞卡數據 (方便組件使用)
export const SIMPLE_CARDS = MOCK_API_RESPONSE.data.flashcards.map(addStateFields)
// 生成線性測驗項目序列
export const generateQuizItems = (cards: CardState[]): QuizItem[] => {
const quizItems: QuizItem[] = []
let order = 0
cards.forEach((card) => {
// 為每張詞卡生成兩個測驗項目:先翻卡記憶,再詞彙選擇
const flipCardQuiz: QuizItem = {
id: `${card.id}-flip-card`,
cardId: card.id,
quizType: 'flip-card',
isCompleted: false,
skipCount: 0,
wrongCount: 0,
order: order++,
cardData: card
}
const vocabChoiceQuiz: QuizItem = {
id: `${card.id}-vocab-choice`,
cardId: card.id,
quizType: 'vocab-choice',
isCompleted: false,
skipCount: 0,
wrongCount: 0,
order: order++,
cardData: card
}
quizItems.push(flipCardQuiz, vocabChoiceQuiz)
})
return quizItems
}
// 測驗項目優先級排序 (修正後的延遲計數系統)
export const sortQuizItemsByPriority = (quizItems: QuizItem[]): QuizItem[] => {
return quizItems.sort((a, b) => {
// 1. 已完成的測驗項目排到最後
if (a.isCompleted && !b.isCompleted) return 1
if (!a.isCompleted && b.isCompleted) return -1
// 2. 未完成項目按延遲分數排序 (修正:延遲分數低的排前面)
const aDelayScore = a.skipCount + a.wrongCount
const bDelayScore = b.skipCount + b.wrongCount
if (aDelayScore !== bDelayScore) {
return aDelayScore - bDelayScore // 修正:延遲分數低的排前面,保持線性順序
}
// 3. 延遲分數相同時按原始順序
return a.order - b.order
})
}
// 舊版排序函數 (保留向後兼容)
export const sortCardsByPriority = (cards: CardState[]): CardState[] => {
return cards.sort((a, b) => {
// 1. 已完成的卡片排到最後
if (a.isCompleted && !b.isCompleted) return 1
if (!a.isCompleted && b.isCompleted) return -1
// 2. 未完成卡片按延遲分數排序 (越少越前面)
const aDelayScore = a.skipCount + a.wrongCount
const bDelayScore = b.skipCount + b.wrongCount
if (aDelayScore !== bDelayScore) {
return aDelayScore - bDelayScore
}
// 3. 延遲分數相同時按原始順序
return a.originalOrder - b.originalOrder
})
}
// 生成詞彙選擇測驗選項
export const generateVocabOptions = (correctWord: string, allCards: CardState[]): string[] => {
const allWords = allCards.map(card => card.word).filter(word => word !== correctWord)
const shuffledWords = allWords.sort(() => Math.random() - 0.5)
const distractors = shuffledWords.slice(0, 3)
const options = [correctWord, ...distractors]
return options.sort(() => Math.random() - 0.5)
}
// 初始化測驗項目列表
export const INITIAL_TEST_ITEMS = generateQuizItems(SIMPLE_CARDS)