import React, { memo } from 'react' import { TestItem } from '@/store/review/useTestQueueStore' /** * 測驗狀態指示器 * 根據PRD US-009要求實現視覺化狀態顯示: * ✅ 已答對(綠色)- 已從當日清單移除 * ❌ 已答錯(紅色)- 移到隊列最後 * ⏭️ 已跳過(黃色)- 移到隊列最後 * ⚪ 未完成(灰色)- 優先處理 */ export interface TestStatusIndicatorProps { test: TestItem showDetails?: boolean size?: 'sm' | 'md' | 'lg' className?: string } export const TestStatusIndicator: React.FC = memo(({ test, showDetails = false, size = 'md', className = '' }) => { // 獲取狀態配置 const getStatusConfig = () => { if (test.isCompleted) { return { icon: '✅', label: '已答對', bgColor: 'bg-green-100', textColor: 'text-green-800', borderColor: 'border-green-300', description: '已從當日清單移除' } } if (test.isIncorrect) { return { icon: '❌', label: '已答錯', bgColor: 'bg-red-100', textColor: 'text-red-800', borderColor: 'border-red-300', description: '移到隊列最後重複練習' } } if (test.isSkipped) { return { icon: '⏭️', label: '已跳過', bgColor: 'bg-yellow-100', textColor: 'text-yellow-800', borderColor: 'border-yellow-300', description: '移到隊列最後稍後處理' } } if (test.isCurrent) { return { icon: '▶️', label: '進行中', bgColor: 'bg-blue-100', textColor: 'text-blue-800', borderColor: 'border-blue-300', description: '當前正在進行的測驗' } } return { icon: '⚪', label: '未完成', bgColor: 'bg-gray-100', textColor: 'text-gray-800', borderColor: 'border-gray-300', description: '優先處理的測驗' } } // 獲取尺寸樣式 const getSizeClasses = () => { switch (size) { case 'sm': return { container: 'px-2 py-1 text-xs', icon: 'text-sm', text: 'text-xs' } case 'lg': return { container: 'px-4 py-3 text-base', icon: 'text-lg', text: 'text-base' } default: // md return { container: 'px-3 py-2 text-sm', icon: 'text-base', text: 'text-sm' } } } const statusConfig = getStatusConfig() const sizeClasses = getSizeClasses() return (
{/* 狀態圖示 */} {statusConfig.icon} {/* 狀態文字 */} {statusConfig.label} {/* 優先級顯示(開發模式) */} {process.env.NODE_ENV === 'development' && ( P{test.priority} )} {/* 詳細資訊 */} {showDetails && (
({test.testName})
)}
) }) TestStatusIndicator.displayName = 'TestStatusIndicator' /** * 測驗狀態統計元件 */ interface TestStatsProps { stats: { total: number completed: number skipped: number incorrect: number remaining: number } className?: string } export const TestStats: React.FC = memo(({ stats, className = '' }) => { const items = [ { key: 'completed', label: '已完成', count: stats.completed, color: 'text-green-600', icon: '✅' }, { key: 'incorrect', label: '需重做', count: stats.incorrect, color: 'text-red-600', icon: '❌' }, { key: 'skipped', label: '已跳過', count: stats.skipped, color: 'text-yellow-600', icon: '⏭️' }, { key: 'remaining', label: '剩餘', count: stats.remaining, color: 'text-gray-600', icon: '⚪' } ] return (
{items.map(({ key, label, count, color, icon }) => (
{icon} {count} {label}
))}
) }) TestStats.displayName = 'TestStats' /** * 測驗狀態進度條 */ interface TestProgressBarProps { stats: { total: number completed: number skipped: number incorrect: number remaining: number } className?: string } export const TestProgressBar: React.FC = memo(({ stats, className = '' }) => { const { total, completed, skipped, incorrect, remaining } = stats if (total === 0) return null const completedPercent = (completed / total) * 100 const incorrectPercent = (incorrect / total) * 100 const skippedPercent = (skipped / total) * 100 return (
{/* 進度條 */}
{/* 已完成 */} {completed > 0 && (
)} {/* 答錯 */} {incorrect > 0 && (
)} {/* 跳過 */} {skipped > 0 && (
)}
{/* 數據統計 */}
已完成: {completed}/{total} {((completed / total) * 100).toFixed(0)}%
) }) TestProgressBar.displayName = 'TestProgressBar' /** * 測驗狀態列表 */ interface TestStatusListProps { tests: TestItem[] groupByCard?: boolean className?: string } export const TestStatusList: React.FC = memo(({ tests, groupByCard = true, className = '' }) => { if (groupByCard) { // 按詞卡分組 const groupedTests = tests.reduce((acc, test) => { if (!acc[test.cardId]) { acc[test.cardId] = { word: test.word, tests: [] } } acc[test.cardId].tests.push(test) return acc }, {} as Record) return (
{Object.entries(groupedTests).map(([cardId, { word, tests }]) => (

{word}

{tests.map(test => ( ))}
))}
) } // 平面列表 return (
{tests.map(test => (
{test.word} {test.testName}
))}
) }) TestStatusList.displayName = 'TestStatusList'