This repository has been archived on 2026-04-09. You can view files and clone it, but cannot push or open issues or pull requests.
Files
KLC-Learners-Portal-Fronten…/src/components/learner/CourseCard.tsx
2025-08-28 19:53:36 +05:30

331 lines
11 KiB
TypeScript

import React from 'react';
import { Card, CardContent, CardHeader } from '../ui/card';
import { Button } from '../ui/button';
import { Badge } from '../ui/badge';
import { Progress } from '../ui/progress';
import { Avatar, AvatarFallback, AvatarImage } from '../ui/avatar';
import {
Star,
Clock,
Users,
Play,
CheckCircle,
Calendar,
Award,
Eye,
Heart,
Share2,
Video,
FileText,
Headphones,
Monitor,
AlertCircle
} from 'lucide-react';
import { Course } from '../../pages/learner/data/libraryData';
const navigate = useNavigate();
interface CourseCardProps {
course: Course;
userType: 'individual' | 'corporate';
onEnroll?: (courseId: string) => void;
onContinue?: (courseId: string) => void;
onBookmark?: (courseId: string) => void;
}
const getTypeIcon = (type: string) => {
switch (type) {
case 'video': return Video;
case 'article': return FileText;
case 'audio': return Headphones;
case 'interactive': return Monitor;
default: return Video;
}
};
const getPriorityColor = (priority?: string) => {
switch (priority) {
case 'high': return 'text-destructive bg-destructive/10';
case 'medium': return 'text-orange-600 bg-orange-100';
case 'low': return 'text-success bg-success/10';
default: return '';
}
};
const getLevelColor = (level: string) => {
switch (level) {
case 'Beginner': return 'text-success bg-success/10';
case 'Intermediate': return 'text-primary bg-primary/10';
case 'Advanced': return 'text-destructive bg-destructive/10';
default: return 'text-muted-foreground bg-muted';
}
};
export function CourseCard({ course, userType, onEnroll, onContinue, onBookmark }: CourseCardProps) {
const TypeIcon = getTypeIcon(course.type);
const isOverdue = course.deadline && new Date(course.deadline) < new Date();
// Navigate to course details page with proper query parameters
const handleCourseNavigation = () => {
navigate(`/course?view=${userType}&courseId=${course.id}`);
};
return (
<Card className={`group hover:shadow-lg transition-all duration-300 ${
course.isFeatured ? 'ring-2 ring-primary/20' : ''
} ${isOverdue ? 'border-destructive/20 bg-destructive/5' : ''}`}>
{/* Course Thumbnail */}
<div className="relative overflow-hidden rounded-t-lg">
<img
src={course.thumbnail}
alt={course.title}
className="w-full h-48 object-cover group-hover:scale-105 transition-transform duration-300"
/>
{/* Overlay Badges */}
<div className="absolute top-3 left-3 flex flex-wrap gap-2">
{course.isFeatured && (
<Badge className="bg-primary text-primary-foreground text-xs">
Featured
</Badge>
)}
{course.isPremium && (
<Badge variant="secondary" className="text-xs">
Premium
</Badge>
)}
{course.organizationAssigned && userType === 'corporate' && (
<Badge variant="outline" className="text-xs bg-background/90">
Assigned
</Badge>
)}
</div>
{/* Priority Badge for Corporate */}
{userType === 'corporate' && course.priority && (
<div className="absolute top-3 right-3">
<Badge variant="outline" className={`text-xs ${getPriorityColor(course.priority)}`}>
{course.priority.toUpperCase()}
</Badge>
</div>
)}
{/* Type Icon */}
<div className="absolute bottom-3 left-3">
<div className="bg-background/90 backdrop-blur-sm p-2 rounded-full">
<TypeIcon className="h-4 w-4 text-primary" />
</div>
</div>
{/* Course Status */}
<div className="absolute bottom-3 right-3">
{course.status === 'completed' && (
<div className="bg-success/90 backdrop-blur-sm p-2 rounded-full">
<CheckCircle className="h-4 w-4 text-white" />
</div>
)}
{course.status === 'bookmarked' && (
<div className="bg-orange-500/90 backdrop-blur-sm p-2 rounded-full">
<Heart className="h-4 w-4 text-white fill-current" />
</div>
)}
</div>
</div>
<CardHeader className="pb-3">
<div className="flex items-start justify-between gap-3">
<div className="flex-1 min-w-0">
<h3 className="text-lg font-semibold line-clamp-2 group-hover:text-primary transition-colors">
{course.title}
</h3>
<p className="text-base text-muted-foreground mt-2 line-clamp-2">
{course.description}
</p>
</div>
</div>
{/* Instructor Info */}
<div className="flex items-center gap-3 mt-3">
<Avatar className="h-8 w-8">
<AvatarImage src={course.instructor.avatar} />
<AvatarFallback className="text-xs">
{course.instructor.name.split(' ').map(n => n[0]).join('')}
</AvatarFallback>
</Avatar>
<div className="flex-1 min-w-0">
<p className="text-base font-medium">{course.instructor.name}</p>
<p className="text-base text-muted-foreground">{course.instructor.title}</p>
</div>
</div>
{/* Course Metadata */}
<div className="flex items-center gap-4 mt-3 text-base text-muted-foreground">
<div className="flex items-center gap-1">
<Clock className="h-4 w-4" />
<span>{course.duration}</span>
</div>
<div className="flex items-center gap-1">
<Eye className="h-4 w-4" />
<span>{course.lessonsCount} lessons</span>
</div>
<div className="flex items-center gap-1">
<Users className="h-4 w-4" />
<span>{course.enrolledCount.toLocaleString()}</span>
</div>
</div>
{/* Rating */}
<div className="flex items-center gap-2 mt-2">
<div className="flex items-center gap-1">
{[...Array(5)].map((_, i) => (
<Star
key={i}
className={`h-4 w-4 ${
i < Math.floor(course.rating)
? 'text-yellow-500 fill-current'
: 'text-muted-foreground'
}`}
/>
))}
</div>
<span className="text-base font-medium">{course.rating}</span>
<span className="text-base text-muted-foreground">
({course.completionRate}% completion rate)
</span>
</div>
{/* Level and Category */}
<div className="flex items-center gap-2 mt-2">
<Badge variant="outline" className={`text-xs ${getLevelColor(course.level)}`}>
{course.level}
</Badge>
<Badge variant="secondary" className="text-xs">
{course.category}
</Badge>
</div>
{/* Tags */}
<div className="flex flex-wrap gap-1 mt-2">
{course.tags.slice(0, 3).map((tag) => (
<Badge key={tag} variant="outline" className="text-xs">
{tag}
</Badge>
))}
{course.tags.length > 3 && (
<Badge variant="outline" className="text-xs">
+{course.tags.length - 3} more
</Badge>
)}
</div>
</CardHeader>
<CardContent className="pt-0">
{/* Progress Bar for In-Progress Courses */}
{course.status === 'in-progress' && course.progress !== undefined && (
<div className="space-y-2 mb-4">
<div className="flex justify-between text-base">
<span>Progress</span>
<span>{course.progress}%</span>
</div>
<Progress value={course.progress} className="h-2" />
{course.lastAccessed && (
<p className="text-base text-muted-foreground">
Last accessed: {course.lastAccessed}
</p>
)}
</div>
)}
{/* Deadline Warning for Corporate */}
{userType === 'corporate' && course.deadline && (
<div className={`p-3 rounded-lg mb-4 ${
isOverdue
? 'bg-destructive/5 border border-destructive/20'
: 'bg-orange-50 border border-orange-200'
}`}>
<div className="flex items-center gap-2">
<AlertCircle className={`h-4 w-4 ${isOverdue ? 'text-destructive' : 'text-orange-600'}`} />
<span className={`text-base font-medium ${isOverdue ? 'text-destructive' : 'text-orange-600'}`}>
{isOverdue ? 'Overdue' : 'Due'}: {new Date(course.deadline).toLocaleDateString()}
</span>
</div>
</div>
)}
{/* Action Buttons */}
<div className="flex items-center gap-2">
{course.status === 'not-started' && (
<Button
onClick={handleCourseNavigation}
className="flex-1 text-base min-h-[44px]"
>
<Play className="h-4 w-4 mr-2" />
Start Course
</Button>
)}
{course.status === 'in-progress' && (
<Button
onClick={handleCourseNavigation}
className="flex-1 text-base min-h-[44px]"
>
<Play className="h-4 w-4 mr-2" />
Continue Learning
</Button>
)}
{course.status === 'completed' && (
<Button
variant="outline"
onClick={handleCourseNavigation}
className="flex-1 text-base min-h-[44px]"
>
<Eye className="h-4 w-4 mr-2" />
Review Course
</Button>
)}
{course.status === 'bookmarked' && (
<Button
onClick={handleCourseNavigation}
className="flex-1 text-base min-h-[44px]"
>
<Play className="h-4 w-4 mr-2" />
Start Course
</Button>
)}
{/* Secondary Actions */}
<Button
variant="outline"
size="icon"
onClick={() => onBookmark?.(course.id)}
className="min-h-[44px] min-w-[44px]"
aria-label="Bookmark course"
>
<Heart className={`h-4 w-4 ${course.status === 'bookmarked' ? 'fill-current text-red-500' : ''}`} />
</Button>
<Button
variant="outline"
size="icon"
className="min-h-[44px] min-w-[44px]"
aria-label="Share course"
>
<Share2 className="h-4 w-4" />
</Button>
{course.certificate && course.status === 'completed' && (
<Button
variant="outline"
size="icon"
className="min-h-[44px] min-w-[44px]"
aria-label="Download certificate"
>
<Award className="h-4 w-4" />
</Button>
)}
</div>
</CardContent>
</Card>
);
}