20 KiB
20 KiB
複習系統設計工具重構規格
📋 現況問題分析
當前 review-design 頁面的問題
檔案: /frontend/app/review-design/page.tsx
❌ 問題 1: 靜態組件展示
// 當前實作:只是靜態展示不同測驗組件
const [activeTab, setActiveTab] = useState('FlipMemoryTest')
// 問題:無法模擬真實的複習流程
return <FlipMemoryTest cardData={staticData} onAnswer={logOnly} />
後果:
- 無法測試真實的 Store 協作
- 無法驗證答題→進度更新→下一題的完整流程
- 不能測試錯誤處理和邊界情況
❌ 問題 2: 測試資料寫死
// 當前實作:直接 import 靜態 JSON
import exampleData from './example-data.json'
// 問題:無法動態切換或重置測試場景
const cardData = exampleData[currentCardIndex]
後果:
- 無法測試空資料狀態
- 無法測試資料載入失敗情況
- 無法快速切換不同測試場景
❌ 問題 3: 缺乏真實性
// 當前實作:簡化的回調函數
const handleAnswer = (answer: string) => {
addLog(`答案: ${answer}`) // 只是記錄,不做真實處理
}
// 問題:無法測試真實的 Store 狀態更新
後果:
- 進度條不會真實更新
- Store 狀態不會改變
- 無法發現狀態同步問題
🎯 重構設計規格
目標:打造專業的複習系統開發工具
核心需求:
- 真實模擬: 完整模擬生產環境的複習流程
- 動態資料: 可動態匯入、重置、切換測試資料
- 狀態監控: 即時顯示所有 Store 狀態
- 調試功能: 提供開發者需要的調試工具
🏗️ 新架構設計
整體頁面架構
複習系統設計工具
├── 控制面板 (ControlPanel)
│ ├── 資料管理區
│ │ ├── 匯入測試資料按鈕
│ │ ├── 重置 Store 狀態按鈕
│ │ ├── 切換測試資料集下拉選單
│ │ └── Store 狀態重置按鈕
│ ├── 模擬控制區
│ │ ├── 開始複習模擬按鈕
│ │ ├── 暫停/繼續按鈕
│ │ └── 結束模擬按鈕
│ └── 快速測試區
│ ├── 單一組件測試模式
│ └── 完整流程測試模式
├── 複習模擬器 (ReviewSimulator)
│ ├── 真實的 ReviewRunner 組件
│ ├── 真實的進度條和導航
│ └── 真實的狀態管理
└── 調試面板 (DebugPanel)
├── Store 狀態監控 (即時)
├── 答題歷史記錄
├── 性能指標顯示
└── 錯誤日誌
1. 資料管理系統設計
動態資料匯入機制
interface TestDataManager {
// 測試資料集管理
availableDatasets: TestDataset[]
currentDataset: TestDataset | null
// 動態操作
importDataset(dataset: TestDataset): void
resetStores(): void
switchDataset(datasetId: string): void
// 預定義場景
loadScenario(scenario: TestScenario): void
}
// 測試場景定義
type TestScenario =
| 'empty' // 空資料測試
| 'single-card' // 單詞卡測試
| 'full-session' // 完整會話測試
| 'error-cases' // 錯誤情況測試
| 'performance' // 性能測試
測試資料結構
interface TestDataset {
id: string
name: string
description: string
flashcards: MockFlashcard[]
scenarios: {
completedTests?: CompletedTest[]
userProfile?: UserProfile
errorConditions?: ErrorCondition[]
}
}
2. 真實複習模擬器設計
完整Store整合
const ReviewSimulator = () => {
// 使用真實的 Store,不是模擬
const reviewSession = useReviewSessionStore()
const testQueue = useTestQueueStore()
const testResult = useTestResultStore()
const reviewData = useReviewDataStore()
const reviewUI = useReviewUIStore()
// 真實的初始化流程
const initializeSimulation = async (dataset: TestDataset) => {
// 1. 重置所有 Store
reviewSession.resetSession()
testQueue.resetQueue()
testResult.resetScore()
reviewData.resetData()
// 2. 載入測試資料到 ReviewDataStore
reviewData.setDueCards(dataset.flashcards)
// 3. 觸發真實的佇列初始化
testQueue.initializeTestQueue(dataset.flashcards, dataset.scenarios.completedTests || [])
// 4. 設置第一張卡片
if (dataset.flashcards.length > 0) {
reviewSession.setCurrentCard(dataset.flashcards[0])
}
}
return (
<div className="review-simulator">
{/* 使用真實的 ReviewRunner,不是模擬組件 */}
<ReviewRunner />
</div>
)
}
真實進度追蹤
// 真實的進度條,連接到真實 Store
const RealProgressTracker = () => {
const { testItems, currentTestIndex, completedTests } = useTestQueueStore()
const { score } = useTestResultStore()
// 真實計算,不是模擬
const progress = testItems.length > 0 ? (completedTests / testItems.length) * 100 : 0
const accuracy = score.total > 0 ? (score.correct / score.total) * 100 : 0
return (
<div className="real-progress-tracker">
<div className="progress-stats">
<span>進度: {completedTests}/{testItems.length} ({progress.toFixed(1)}%)</span>
<span>正確率: {score.correct}/{score.total} ({accuracy.toFixed(1)}%)</span>
</div>
<div className="progress-bar">
<div
className="progress-fill bg-blue-500 h-2 rounded transition-all duration-300"
style={{ width: `${progress}%` }}
/>
</div>
</div>
)
}
3. 調試面板設計
Store 狀態即時監控
const StoreMonitor = () => {
// 即時監控所有 Store 狀態
const sessionState = useReviewSessionStore()
const queueState = useTestQueueStore()
const resultState = useTestResultStore()
const dataState = useReviewDataStore()
const uiState = useReviewUIStore()
return (
<div className="store-monitor">
<div className="store-section">
<h4>Session Store</h4>
<pre className="store-state">
{JSON.stringify({
mounted: sessionState.mounted,
currentCard: sessionState.currentCard?.id,
mode: sessionState.mode,
isLoading: sessionState.isLoading,
error: sessionState.error
}, null, 2)}
</pre>
</div>
<div className="store-section">
<h4>Queue Store</h4>
<pre className="store-state">
{JSON.stringify({
totalTests: queueState.testItems.length,
currentIndex: queueState.currentTestIndex,
currentMode: queueState.currentMode,
completedCount: queueState.testItems.filter(t => t.isCompleted).length,
skippedCount: queueState.testItems.filter(t => t.isSkipped).length
}, null, 2)}
</pre>
</div>
<div className="store-section">
<h4>Result Store</h4>
<pre className="store-state">
{JSON.stringify(resultState.score, null, 2)}
</pre>
</div>
</div>
)
}
4. 操作控制面板設計
資料管理控制
const DataControlPanel = () => {
const [selectedDataset, setSelectedDataset] = useState<string>('')
const predefinedDatasets = [
{
id: 'basic',
name: '基礎詞彙 (5張卡)',
description: 'A1-A2 等級詞彙,適合基礎測試'
},
{
id: 'advanced',
name: '進階詞彙 (10張卡)',
description: 'B2-C1 等級詞彙,測試複雜邏輯'
},
{
id: 'mixed',
name: '混合難度 (15張卡)',
description: '各等級混合,測試自適應算法'
},
{
id: 'error-test',
name: '錯誤情況測試',
description: '包含異常資料,測試錯誤處理'
}
]
return (
<div className="data-control-panel">
<div className="control-section">
<h3>測試資料管理</h3>
<select
value={selectedDataset}
onChange={(e) => setSelectedDataset(e.target.value)}
>
<option value="">選擇測試資料集...</option>
{predefinedDatasets.map(dataset => (
<option key={dataset.id} value={dataset.id}>
{dataset.name}
</option>
))}
</select>
<div className="action-buttons">
<button onClick={() => importDataset(selectedDataset)}>
匯入資料
</button>
<button onClick={resetAllStores}>
重置 Store
</button>
<button onClick={clearAllData}>
清空資料
</button>
</div>
</div>
<div className="current-dataset-info">
{currentDataset && (
<div className="dataset-info">
<h4>當前資料集: {currentDataset.name}</h4>
<p>{currentDataset.description}</p>
<p>詞卡數量: {currentDataset.flashcards.length}</p>
</div>
)}
</div>
</div>
)
}
5. 模擬控制器設計
複習流程控制
const SimulationController = () => {
const [isSimulating, setIsSimulating] = useState(false)
const [simulationSpeed, setSimulationSpeed] = useState(1)
const simulationControls = {
// 開始完整複習模擬
startFullSimulation: async () => {
setIsSimulating(true)
await initializeRealReviewSession()
},
// 暫停模擬
pauseSimulation: () => {
setIsSimulating(false)
},
// 單步執行 (逐題測試)
stepThrough: async () => {
await processNextTest()
updateDebugInfo()
},
// 快速完成 (自動答題)
autoComplete: async () => {
while (!isAllTestsCompleted()) {
await autoAnswerCurrentTest()
await waitFor(1000 / simulationSpeed)
}
}
}
return (
<div className="simulation-controller">
<div className="simulation-status">
<span className={`status-indicator ${isSimulating ? 'active' : 'inactive'}`}>
{isSimulating ? '模擬進行中' : '模擬暫停'}
</span>
</div>
<div className="control-buttons">
<button onClick={simulationControls.startFullSimulation}>
開始完整模擬
</button>
<button onClick={simulationControls.stepThrough}>
單步執行
</button>
<button onClick={simulationControls.autoComplete}>
自動完成
</button>
<button onClick={simulationControls.pauseSimulation}>
暫停模擬
</button>
</div>
<div className="simulation-settings">
<label>
模擬速度:
<input
type="range"
min="0.1"
max="3"
step="0.1"
value={simulationSpeed}
onChange={(e) => setSimulationSpeed(Number(e.target.value))}
/>
{simulationSpeed}x
</label>
</div>
</div>
)
}
6. 調試工具增強
測驗佇列視覺化
const QueueVisualizer = () => {
const { testItems, currentTestIndex } = useTestQueueStore()
return (
<div className="queue-visualizer">
<h4>測驗佇列狀態</h4>
<div className="queue-timeline">
{testItems.map((test, index) => (
<div
key={test.id}
className={`queue-item ${
index === currentTestIndex ? 'current' :
test.isCompleted ? 'completed' :
test.isSkipped ? 'skipped' :
test.isIncorrect ? 'incorrect' : 'pending'
}`}
>
<div className="test-info">
<span className="test-type">{test.testType}</span>
<span className="word">{test.word}</span>
<span className="priority">P{test.priority}</span>
</div>
</div>
))}
</div>
</div>
)
}
答題歷史追蹤
const AnswerHistoryTracker = () => {
const [answerHistory, setAnswerHistory] = useState<AnswerRecord[]>([])
const trackAnswer = useCallback((answer: AnswerRecord) => {
setAnswerHistory(prev => [...prev, {
...answer,
timestamp: new Date(),
responseTime: answer.responseTime,
isCorrect: answer.isCorrect
}])
}, [])
return (
<div className="answer-history">
<h4>答題歷史</h4>
<div className="history-list">
{answerHistory.map((record, index) => (
<div key={index} className="history-item">
<span className="timestamp">{record.timestamp.toLocaleTimeString()}</span>
<span className="test-type">{record.testType}</span>
<span className="word">{record.word}</span>
<span className={`result ${record.isCorrect ? 'correct' : 'incorrect'}`}>
{record.isCorrect ? '✓' : '✗'}
</span>
<span className="response-time">{record.responseTime}ms</span>
</div>
))}
</div>
</div>
)
}
🎛️ 操作流程設計
理想的使用流程
1. 初始狀態 (空白畫面)
┌─────────────────────────────────┐
│ 複習系統設計工具 │
├─────────────────────────────────┤
│ │
│ 🔧 控制面板 │
│ ┌───────────────────────────┐ │
│ │ 📁 測試資料管理 │ │
│ │ ○ 選擇資料集... │ │
│ │ [ 匯入資料 ] [ 重置 ] │ │
│ └───────────────────────────┘ │
│ │
│ 💭 當前狀態: 無資料 │
│ 📊 Store 狀態: 空 │
└─────────────────────────────────┘
2. 匯入資料後
┌─────────────────────────────────┐
│ 🎯 模擬控制 │
│ [ 開始完整模擬 ] │
│ [ 單步執行 ] [ 自動完成 ] │
├─────────────────────────────────┤
│ 📊 即時狀態監控 │
│ TestQueue: 15 items loaded │
│ Current: flip-memory (0/15) │
│ Score: 0/0 (0%) │
└─────────────────────────────────┘
3. 模擬進行中
┌─────────────────────────────────┐
│ 🎮 複習模擬器 │
│ ┌─ 真實 ReviewRunner ─────┐ │
│ │ [Progress: 3/15 ████▒▒] │ │
│ │ │ │
│ │ 當前測驗: 翻卡記憶 │ │
│ │ 詞卡: "elaborate" │ │
│ │ [ 信心度選擇: 1-5 ] │ │
│ │ │ │
│ │ [ 跳過 ] [ 繼續 ] │ │
│ └─────────────────────────┘ │
├─────────────────────────────────┤
│ 🐛 調試面板 (即時更新) │
│ TestQueue: item 3→4 │
│ Score: 2✓ 1✗ (66.7%) │
│ LastAnswer: "elaborate" ✓ │
└─────────────────────────────────┘
3. 測試資料集設計
預定義資料集範例
{
"datasets": [
{
"id": "basic-flow",
"name": "基礎流程測試",
"description": "測試完整的答題→進度更新→下一題流程",
"flashcards": [
{
"id": "test-1",
"word": "hello",
"definition": "A greeting",
"cefr": "A1"
}
],
"scenarios": {
"completedTests": [],
"userProfile": {
"level": "A2",
"preferences": {}
}
}
},
{
"id": "error-handling",
"name": "錯誤處理測試",
"description": "測試各種錯誤情況的處理",
"flashcards": [],
"scenarios": {
"errorConditions": [
"api_failure",
"invalid_answer",
"network_timeout"
]
}
}
]
}
4. 開發者工作流程
典型調試場景
// 場景 1: 測試新測驗類型
1. 選擇「單一組件測試」模式
2. 匯入「基礎詞彙」資料集
3. 選擇特定測驗類型 (如 sentence-fill)
4. 逐步測試答題邏輯
5. 檢查 Store 狀態變化
6. 驗證UI反饋正確性
// 場景 2: 測試完整流程
1. 選擇「完整流程測試」模式
2. 匯入「混合難度」資料集
3. 開始自動模擬
4. 監控進度條更新
5. 檢查佇列重排邏輯
6. 驗證會話完成處理
// 場景 3: 測試錯誤處理
1. 選擇「錯誤情況測試」模式
2. 觸發各種錯誤條件
3. 驗證錯誤邊界處理
4. 檢查錯誤恢復機制
🔧 技術實施細節
Store 重置機制
const resetAllStores = () => {
// 重置所有複習相關 Store
const { resetSession } = useReviewSessionStore.getState()
const { resetQueue } = useTestQueueStore.getState()
const { resetScore } = useTestResultStore.getState()
const { resetData } = useReviewDataStore.getState()
resetSession()
resetQueue()
resetScore()
resetData()
console.log('✅ 所有 Store 已重置')
}
真實資料注入
const injectTestData = async (dataset: TestDataset) => {
// 模擬真實的資料載入流程
const { setDueCards, setLoadingCards } = useReviewDataStore.getState()
setLoadingCards(true)
// 模擬API延遲
await new Promise(resolve => setTimeout(resolve, 500))
setDueCards(dataset.flashcards)
setLoadingCards(false)
console.log(`✅ 已匯入 ${dataset.flashcards.length} 張測試詞卡`)
}
自動答題機器人
const createAnswerBot = () => {
return {
// 自動產生正確答案
generateCorrectAnswer: (cardData: any, testType: string): string => {
switch (testType) {
case 'vocab-choice':
case 'vocab-listening':
return cardData.word
case 'sentence-fill':
return cardData.word
case 'sentence-reorder':
case 'sentence-listening':
return cardData.example
default:
return ''
}
},
// 自動產生錯誤答案 (用於測試錯誤處理)
generateIncorrectAnswer: (cardData: any, testType: string): string => {
// 故意產生錯誤答案來測試錯誤處理邏輯
return 'incorrect_answer_' + Math.random().toString(36).substr(2, 9)
}
}
}
📊 成功指標
工具效能指標
- 資料匯入時間: < 1秒
- Store 重置時間: < 0.5秒
- 狀態監控更新: 即時 (< 100ms)
- 模擬流程完成: 15張卡片 < 30秒
開發者體驗指標
- 問題發現時間: 從數小時縮短到數分鐘
- UI設計驗證: 即時預覽和調整
- 邏輯調試效率: 提升 80%+
- 回歸測試: 自動化場景測試
🎯 總結
這個重構將把 review-design 從靜態組件展示升級為專業的複習系統開發工具,提供:
- 真實性: 完全模擬生產環境行為
- 靈活性: 動態資料管理和場景切換
- 可觀測性: 完整的狀態監控和調試信息
- 自動化: 自動測試和驗證功能
這樣的工具將大幅提升複習功能的開發和調試效率! 🛠️✨
規格版本: v1.0 設計目標: 專業複習系統開發工具 預計實施時間: 2-3 天