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/HorizontalCourseCard.tsx
2025-08-28 19:53:36 +05:30

299 lines
12 KiB
TypeScript

import React from 'react';
import { Button } from '../ui/button';
import { Badge } from '../ui/badge';
import { Avatar, AvatarFallback, AvatarImage } from '../ui/avatar';
import {
Star,
Clock,
Users,
Play,
CheckCircle,
Heart,
BookOpen,
ChevronRight,
MoreHorizontal
} from 'lucide-react';
import { Course } from '../../pages/learner/data/libraryData';
import { ImageWithFallback } from '../figma/ImageWithFallback';
import { useNavigate } from 'react-router-dom';
interface HorizontalCourseCardProps {
course: Course;
userType: 'individual' | 'corporate';
onEnroll?: (courseId: string) => void;
onContinue?: (courseId: string) => void;
onBookmark?: (courseId: string) => void;
}
// Course images based on course category
const getCourseImage = (category: string) => {
const patterns = {
'Leadership': (
<div className="absolute inset-0 overflow-hidden">
<div className="absolute inset-0 opacity-100">
<ImageWithFallback
src="https://images.unsplash.com/photo-1658198420916-951923730cdd?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxsZWFkZXJzaGlwJTIwYm9va3N8ZW58MXx8fHwxNzU1ODQzNDIxfDA&ixlib=rb-4.1.0&q=80&w=1080&utm_source=figma&utm_medium=referral"
alt=""
className="w-full h-full object-cover object-right"
/>
</div>
</div>
),
'Personal Development': (
<div className="absolute inset-0 overflow-hidden">
<div className="absolute inset-0 opacity-100">
<ImageWithFallback
src="https://images.unsplash.com/photo-1668092547893-6402c0387885?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxidXNpbmVzcyUyMGVkdWNhdGlvbiUyMGxlYXJuaW5nfGVufDF8fHx8MTc1NTg0MzQwOHww&ixlib=rb-4.1.0&q=80&w=1080&utm_source=figma&utm_medium=referral"
alt=""
className="w-full h-full object-cover object-center"
/>
</div>
</div>
),
'Team Management': (
<div className="absolute inset-0 overflow-hidden">
<div className="absolute inset-0 opacity-100">
<ImageWithFallback
src="https://images.unsplash.com/photo-1668092547893-6402c0387885?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHhidXNpbmVzcyUyMGVkdWNhdGlvbiUyMGxlYXJuaW5nfGVufDF8fHx8MTc1NTg0MzQwOHww&ixlib=rb-4.1.0&q=80&w=1080&utm_source=figma&utm_medium=referral"
alt=""
className="w-full h-full object-cover object-center"
/>
</div>
</div>
),
'Digital Leadership': (
<div className="absolute inset-0 overflow-hidden">
<div className="absolute inset-0 opacity-100">
<ImageWithFallback
src="https://images.unsplash.com/photo-1588912914078-2fe5224fd8b8?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxvbmxpbmUlMjBjb3Vyc2UlMjBsYXB0b3B8ZW58MXx8fHwxNzU1NzIwMTYyfDA&ixlib=rb-4.1.0&q=80&w=1080&utm_source=figma&utm_medium=referral"
alt=""
className="w-full h-full object-cover object-center"
/>
</div>
</div>
),
'Communication': (
<div className="absolute inset-0 overflow-hidden">
<div className="absolute inset-0 opacity-100">
<ImageWithFallback
src="https://images.unsplash.com/photo-1668092547893-6402c0387885?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=M3w3Nzg4Nzd8MHwxfHNlYXJjaHwxfHxidXNpbmVzcyUyMGVkdWNhdGlvbiUyMGxlYXJuaW5nfGVufDF8fHx8MTc1NTg0MzQwOHww&ixlib=rb-4.1.0&q=80&w=1080&utm_source=figma&utm_medium=referral"
alt=""
className="w-full h-full object-cover object-center"
/>
</div>
</div>
)
};
return patterns[category as keyof typeof patterns] || patterns.Leadership;
};
export function HorizontalCourseCard({ course, userType, onEnroll, onContinue, onBookmark }: HorizontalCourseCardProps) {
const courseImage = getCourseImage(course.category);
const navigate = useNavigate();
// Navigate to course details page with proper query parameters
const handleCourseNavigation = () => {
navigate(`/course?view=${userType}&courseId=${course.id}`);
};
return (
<div className="bg-background border border-border rounded-lg overflow-hidden hover:shadow-lg transition-all duration-200"
style={{ height: '260px', minHeight: '260px' }}>
<div className="flex h-full">
{/* Left Image Section - 50% width, full height */}
<div className="w-1/2 relative overflow-hidden">
{/* Course image overlay */}
{courseImage}
{/* Status indicators - Positioned better for larger image area */}
<div className="absolute top-3 left-3 flex flex-wrap gap-1.5 z-10 max-w-[70%]">
{course.isFeatured && (
<Badge className="text-sm bg-white/90 text-foreground hover:bg-white font-medium">
Featured
</Badge>
)}
{course.isPremium && (
<Badge variant="secondary" className="text-sm bg-[#F8C301]/90 text-[#26231A] font-medium">
Premium
</Badge>
)}
</div>
{/* Course status indicator */}
<div className="absolute top-3 right-3 z-10">
{course.status === 'completed' && (
<div className="bg-success/90 backdrop-blur-sm p-1.5 rounded-full">
<CheckCircle className="h-4 w-4 text-white" />
</div>
)}
{course.status === 'bookmarked' && (
<div className="bg-[#F8C301]/90 backdrop-blur-sm p-1.5 rounded-full">
<Heart className="h-4 w-4 text-[#26231A] fill-current" />
</div>
)}
</div>
</div>
{/* Right Content Section - 50% width with increased spacing */}
<div className="w-1/2 p-5 flex flex-col justify-between">
{/* Top Content - Title and Description stay with original spacing */}
<div>
{/* Course Title - Increased to text-xl (20px) as requested */}
<h3 className="text-xl font-semibold text-foreground line-clamp-2 leading-tight mb-2">
{course.title}
</h3>
{/* Description with 8px spacing from title as requested */}
<p className="text-sm text-muted-foreground line-clamp-2 leading-relaxed mb-4">
{course.description}
</p>
{/* Elements after description - Increased vertical spacing to 12px (space-y-3) */}
<div className="space-y-3">
{/* Created by section - Updated font size for Avatar text */}
<div className="flex items-center gap-2">
<Avatar className="w-5 h-5 flex-shrink-0">
<AvatarImage src={course.instructor.avatar} />
<AvatarFallback className="text-sm">
{course.instructor.name.split(' ').map(n => n[0]).join('')}
</AvatarFallback>
</Avatar>
<span className="text-sm text-muted-foreground">Created by:</span>
<span className="text-sm font-medium text-foreground truncate">{course.instructor.name}</span>
</div>
{/* Badges below created by - Updated to text-sm (14px minimum) with brand colors */}
<div className="flex flex-wrap gap-1.5">
<Badge
variant="outline"
className={`text-sm font-medium ${
course.level === 'Beginner' ? 'border-success text-success bg-success/5' :
course.level === 'Intermediate' ? 'border-primary text-primary bg-primary/5' :
course.level === 'Advanced' ? 'border-[#26231A] text-[#26231A] bg-[#26231A]/5' :
'border-[#F8C301] text-[#26231A] bg-[#F8C301]/10'
}`}
>
{course.level}
</Badge>
<Badge
variant="outline"
className={`text-sm font-medium ${
course.category === 'Leadership' ? 'border-primary text-primary bg-primary/5' :
course.category === 'Digital Leadership' ? 'border-[#F8C301] text-[#26231A] bg-[#F8C301]/10' :
course.category === 'Strategy' ? 'border-[#26231A] text-[#26231A] bg-[#26231A]/5' :
'border-[#F8C301] text-[#26231A] bg-[#F8C301]/10'
}`}
>
{course.category}
</Badge>
</div>
{/* Combined lessons, duration, and rating section - All in one container */}
<div className="flex items-center justify-between text-sm text-muted-foreground">
{/* Left side: Duration and Lessons */}
<div className="flex items-center gap-4">
<div className="flex items-center gap-1">
<Clock className="h-3.5 w-3.5 flex-shrink-0" />
<span>{course.duration}</span>
</div>
<div className="flex items-center gap-1">
<BookOpen className="h-3.5 w-3.5 flex-shrink-0" />
<span>{course.lessonsCount} lessons</span>
</div>
</div>
{/* Right side: Rating stars */}
<div className="flex items-center gap-1 flex-shrink-0">
{[...Array(5)].map((_, i) => (
<Star
key={i}
className={`h-3.5 w-3.5 ${
i < Math.floor(course.rating)
? 'text-[#F8C301] fill-current'
: 'text-muted-foreground'
}`}
/>
))}
<span className="text-sm font-medium ml-1 text-foreground">{course.rating}</span>
</div>
</div>
</div>
</div>
{/* Bottom Section - CTA buttons only with reduced spacing */}
<div className="mt-auto">
{/* CTA buttons below everything else - wider for consistency */}
<div className="flex items-center gap-2">
{course.status === 'not-started' && (
<Button
onClick={handleCourseNavigation}
size="sm"
className="flex-1 text-base font-medium min-h-[36px]"
>
Start Course
<ChevronRight className="h-4 w-4 ml-1" />
</Button>
)}
{course.status === 'in-progress' && (
<Button
onClick={handleCourseNavigation}
size="sm"
className="flex-1 text-base font-medium min-h-[36px]"
>
Continue
<Play className="h-4 w-4 ml-1" />
</Button>
)}
{course.status === 'completed' && (
<Button
variant="outline"
onClick={handleCourseNavigation}
size="sm"
className="flex-1 text-base font-medium min-h-[36px]"
>
Review
<ChevronRight className="h-4 w-4 ml-1" />
</Button>
)}
{course.status === 'bookmarked' && (
<Button
onClick={handleCourseNavigation}
size="sm"
className="flex-1 text-base font-medium min-h-[36px]"
>
Start Course
<ChevronRight className="h-4 w-4 ml-1" />
</Button>
)}
{/* Secondary action buttons - optimized sizing */}
<Button
variant="ghost"
size="icon"
onClick={() => onBookmark?.(course.id)}
className="min-h-[36px] min-w-[36px] hover:bg-muted flex-shrink-0"
aria-label="Bookmark course"
>
<Heart className={`h-4 w-4 ${course.status === 'bookmarked' ? 'fill-current text-[#F8C301]' : 'text-muted-foreground'}`} />
</Button>
<Button
variant="ghost"
size="icon"
className="min-h-[36px] min-w-[36px] hover:bg-muted flex-shrink-0"
aria-label="More options"
>
<MoreHorizontal className="h-4 w-4 text-muted-foreground" />
</Button>
</div>
</div>
</div>
</div>
</div>
);
}