dramaling-vocab-learning/frontend/components/review/ui/MasteryIndicator.tsx

91 lines
2.8 KiB
TypeScript

'use client'
import { getMasteryLevelInfo } from '@/lib/utils/masteryCalculator'
interface MasteryIndicatorProps {
level: number; // 0-100
isDecaying?: boolean; // 是否正在衰減
showPercentage?: boolean; // 是否顯示百分比數字
size?: 'small' | 'medium' | 'large';
baseMasteryLevel?: number; // 基礎熟悉度,用於判斷是否衰減
}
export const MasteryIndicator: React.FC<MasteryIndicatorProps> = ({
level,
isDecaying = false,
showPercentage = true,
size = 'medium',
baseMasteryLevel
}) => {
// 自動判斷是否衰減
const actualIsDecaying = isDecaying || (baseMasteryLevel !== undefined && level < baseMasteryLevel);
const { label, color, bgColor } = getMasteryLevelInfo(level);
const getColor = (level: number, isDecaying: boolean) => {
if (isDecaying) return '#ff9500'; // 橙色表示衰減中
if (level >= 80) return '#34c759'; // 綠色表示熟悉
if (level >= 60) return '#007aff'; // 藍色表示良好
if (level >= 40) return '#ff9500'; // 橙色表示一般
return '#ff3b30'; // 紅色表示需要加強
};
const sizeClasses = {
small: 'w-8 h-8',
medium: 'w-12 h-12',
large: 'w-16 h-16'
};
const textSizes = {
small: 'text-xs',
medium: 'text-sm',
large: 'text-base'
};
return (
<div className={`mastery-indicator ${size} flex items-center gap-3`}>
<div className={`progress-circle relative ${sizeClasses[size]}`}>
<svg viewBox="0 0 36 36" className="w-full h-full transform -rotate-90">
{/* 背景圓圈 */}
<circle
cx="18" cy="18" r="15.915"
fill="transparent"
stroke="#e5e5e7"
strokeWidth="2"
/>
{/* 進度圓圈 */}
<circle
cx="18" cy="18" r="15.915"
fill="transparent"
stroke={getColor(level, actualIsDecaying)}
strokeWidth="2"
strokeDasharray={`${level} 100`}
className="transition-all duration-500 ease-out"
/>
</svg>
{showPercentage && (
<div className="absolute inset-0 flex items-center justify-center">
<div className={`text-center ${textSizes[size]}`}>
<div className="font-bold text-gray-900">{level}%</div>
{actualIsDecaying && (
<div className="text-orange-500 text-xs animate-pulse"></div>
)}
</div>
</div>
)}
</div>
<div className="flex flex-col">
<div className={`${bgColor} ${color} px-2 py-1 rounded-full text-xs font-medium`}>
{label}
</div>
{actualIsDecaying && (
<div className="text-xs text-orange-600 mt-1"></div>
)}
</div>
</div>
)
}
export default MasteryIndicator