Files
front/src/components/training/ExerciseCard.tsx
2026-08-12 19:30:17 +03:00

112 lines
3.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { Link } from 'react-router-dom';
import { CheckCircle, Circle, Lock, Award, Clock } from 'lucide-react';
import { Exercise } from '../../api/types';
import { cn } from '../../utils/helpers';
import { DIFFICULTY_LABELS } from '../../utils/constants';
import { DIFFICULTY_COLORS } from '../../utils/colors';
interface ExerciseCardProps {
exercise: Exercise;
isCompleted?: boolean;
isLocked?: boolean;
className?: string;
}
export function ExerciseCard({
exercise,
isCompleted = false,
isLocked = false,
className,
}: ExerciseCardProps) {
const difficultyColor = DIFFICULTY_COLORS[exercise.difficulty as keyof typeof DIFFICULTY_COLORS];
return (
<div
className={cn(
'group rounded-xl border border-border bg-card p-6 transition-all',
isLocked && 'opacity-60',
isCompleted && 'border-green-500/50 bg-green-500/5',
!isLocked && 'hover:border-primary/50 hover:shadow-lg',
className
)}
>
{/* Header */}
<div className="mb-4 flex items-start justify-between">
<div className="flex items-center gap-3">
<div
className={cn(
'flex h-10 w-10 items-center justify-center rounded-lg',
isCompleted
? 'bg-green-500/20 text-green-400'
: isLocked
? 'bg-muted text-muted-foreground'
: 'bg-primary/20 text-primary'
)}
>
{isCompleted ? (
<CheckCircle className="h-6 w-6" />
) : isLocked ? (
<Lock className="h-6 w-6" />
) : (
<Circle className="h-6 w-6" />
)}
</div>
<div>
<h3 className={cn('font-semibold', isLocked && 'text-muted-foreground')}>
{exercise.title}
</h3>
<p className="text-sm text-muted-foreground">
Упражнение #{exercise.order}
</p>
</div>
</div>
{exercise.points > 0 && (
<div className="flex items-center gap-1 text-sm font-medium text-yellow-400">
<Award className="h-4 w-4" />
<span>{exercise.points}</span>
</div>
)}
</div>
{/* Description */}
<p className="mb-4 line-clamp-2 text-sm text-muted-foreground">
{exercise.description}
</p>
{/* Meta */}
<div className="flex flex-wrap items-center gap-4 text-sm text-muted-foreground">
<div className="flex items-center gap-1">
<Clock className="h-4 w-4" />
<span>~{exercise.estimatedMinutes} мин</span>
</div>
<span
className="rounded px-2 py-0.5 text-xs font-medium"
style={{ backgroundColor: `${difficultyColor}20`, color: difficultyColor }}
>
{DIFFICULTY_LABELS[exercise.difficulty]}
</span>
<span className="rounded bg-muted px-2 py-0.5 text-xs capitalize">
{exercise.type}
</span>
</div>
{/* Action */}
{!isLocked && (
<Link
to={`/training/${exercise.trackId}/exercises/${exercise.id}`}
className={cn(
'mt-4 flex items-center justify-center gap-2 rounded-lg px-4 py-2 text-sm font-medium transition-colors',
isCompleted
? 'bg-green-500/20 text-green-400 hover:bg-green-500/30'
: 'bg-primary text-primary-foreground hover:bg-primary/90'
)}
>
{isCompleted ? 'Повторить' : 'Начать'}
</Link>
)}
</div>
);
}
export default ExerciseCard;