108 lines
4.2 KiB
TypeScript
108 lines
4.2 KiB
TypeScript
import React from 'react';
|
|
import { Card } from '../ui/card';
|
|
import { ProgressBar } from '../ui/progress';
|
|
import { Course } from '../../types';
|
|
import { PlayCircle, Clock, Star, Users, CheckCircle } from 'lucide-react';
|
|
|
|
interface CourseCardProps {
|
|
course: Course;
|
|
onClick: (course: Course) => void;
|
|
showProgress?: boolean;
|
|
}
|
|
|
|
export const CourseCard: React.FC<CourseCardProps> = ({
|
|
course,
|
|
onClick,
|
|
showProgress = false,
|
|
}) => {
|
|
return (
|
|
<Card
|
|
variant="default"
|
|
className="group p-0 overflow-hidden cursor-pointer hover:border-kodo-cyan/50 transition-all flex flex-col h-full"
|
|
onClick={() => onClick(course)}
|
|
>
|
|
{/* Cover */}
|
|
<div className="relative aspect-video bg-gray-900 overflow-hidden">
|
|
<img
|
|
src={course.thumbnailUrl}
|
|
className="w-full h-full object-cover transition-transform duration-700 group-hover:scale-110 opacity-90 group-hover:opacity-100"
|
|
alt={course.title}
|
|
/>
|
|
<div className="absolute inset-0 bg-black/40 opacity-0 group-hover:opacity-100 transition-opacity flex items-center justify-center backdrop-blur-sm">
|
|
<PlayCircle className="w-12 h-12 text-white fill-current opacity-80" />
|
|
</div>
|
|
{course.certificateAvailable && (
|
|
<div className="absolute top-2 right-2 bg-kodo-gold/90 text-black text-[10px] font-bold px-2 py-0.5 rounded shadow-lg flex items-center gap-1">
|
|
<Star className="w-3 h-3 fill-current" /> CERTIFIED
|
|
</div>
|
|
)}
|
|
<div className="absolute bottom-2 left-2 bg-black/70 text-white text-xs px-2 py-1 rounded font-mono flex items-center gap-1">
|
|
<Clock className="w-3 h-3" /> {course.duration}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Content */}
|
|
<div className="p-4 flex flex-col flex-1">
|
|
<div className="flex justify-between items-start mb-2">
|
|
<span
|
|
className={`text-[10px] px-2 py-0.5 rounded uppercase font-bold ${
|
|
course.level === 'Advanced'
|
|
? 'bg-kodo-red/20 text-kodo-red'
|
|
: course.level === 'Intermediate'
|
|
? 'bg-kodo-gold/20 text-kodo-gold'
|
|
: 'bg-kodo-lime/20 text-kodo-lime'
|
|
}`}
|
|
>
|
|
{course.level}
|
|
</span>
|
|
{course.rating && (
|
|
<div className="flex items-center gap-1 text-xs text-kodo-gold font-bold">
|
|
<Star className="w-3 h-3 fill-current" /> {course.rating}
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
<h3 className="font-bold text-white text-base mb-1 line-clamp-2 group-hover:text-kodo-cyan transition-colors">
|
|
{course.title}
|
|
</h3>
|
|
<p className="text-gray-400 text-xs mb-3">by {course.instructor}</p>
|
|
|
|
<div className="mt-auto pt-2">
|
|
{showProgress && course.progress !== undefined ? (
|
|
<div className="space-y-2">
|
|
<div className="flex justify-between text-xs text-gray-400">
|
|
<span>Progress</span>
|
|
<span
|
|
className={
|
|
course.progress === 100 ? 'text-kodo-lime' : 'text-white'
|
|
}
|
|
>
|
|
{course.progress}%
|
|
</span>
|
|
</div>
|
|
<ProgressBar
|
|
value={course.progress}
|
|
color={course.progress === 100 ? 'lime' : 'cyan'}
|
|
/>
|
|
{course.progress === 100 && (
|
|
<div className="flex items-center gap-1 text-xs text-kodo-lime mt-1 font-bold">
|
|
<CheckCircle className="w-3 h-3" /> Completed
|
|
</div>
|
|
)}
|
|
</div>
|
|
) : (
|
|
<div className="flex justify-between items-center border-t border-white/5 pt-3">
|
|
<div className="flex items-center gap-1 text-xs text-gray-500">
|
|
<Users className="w-3 h-3" />{' '}
|
|
{(course.studentCount || 0).toLocaleString()} students
|
|
</div>
|
|
<span className="font-mono font-bold text-white">
|
|
{course.price && course.price > 0 ? `$${course.price}` : 'Free'}
|
|
</span>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</Card>
|
|
);
|
|
};
|