dramaling-vocab-learning/複習系統設計工具重構規格.md

20 KiB
Raw Blame History

複習系統設計工具重構規格

📋 現況問題分析

當前 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 狀態不會改變
  • 無法發現狀態同步問題

🎯 重構設計規格

目標:打造專業的複習系統開發工具

核心需求:

  1. 真實模擬: 完整模擬生產環境的複習流程
  2. 動態資料: 可動態匯入、重置、切換測試資料
  3. 狀態監控: 即時顯示所有 Store 狀態
  4. 調試功能: 提供開發者需要的調試工具

🏗️ 新架構設計

整體頁面架構

複習系統設計工具
├── 控制面板 (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靜態組件展示升級為專業的複習系統開發工具,提供:

  1. 真實性: 完全模擬生產環境行為
  2. 靈活性: 動態資料管理和場景切換
  3. 可觀測性: 完整的狀態監控和調試信息
  4. 自動化: 自動測試和驗證功能

這樣的工具將大幅提升複習功能的開發和調試效率! 🛠️


規格版本: v1.0 設計目標: 專業複習系統開發工具 預計實施時間: 2-3 天