lms/resources/js/components/exam/exam-card-1.tsx
2025-12-15 12:26:23 +01:00

132 lines
5.5 KiB
TypeScript

import { Badge } from '@/components/ui/badge';
import { Button } from '@/components/ui/button';
import { Card, CardContent, CardFooter } from '@/components/ui/card';
import { cn, systemCurrency } from '@/lib/utils';
import { SharedData } from '@/types/global';
import { Link, usePage } from '@inertiajs/react';
import { Clock, Heart, ShoppingCart, Users } from 'lucide-react';
import RatingDisplay from './rating-display';
interface Props {
exam: Exam;
variant?: 'default' | 'compact';
viewType?: 'grid' | 'list';
onAddToCart?: (exam: Exam) => void;
onAddToWishlist?: (exam: Exam) => void;
className?: string;
}
const ExamCard1 = ({ exam, variant = 'default', viewType = 'grid', onAddToCart, onAddToWishlist, className }: Props) => {
const { props } = usePage<SharedData>();
const { translate } = props;
const { common } = translate;
const isCompact = variant === 'compact';
const examUrl = route('exams.details', { slug: exam.slug, id: exam.id });
const currency = systemCurrency(props.system.fields['selling_currency']);
return (
<Card className={cn('group', className, viewType === 'list' && 'flex flex-row')}>
<Link href={examUrl} className={cn(viewType === 'list' && 'w-1/3')}>
<div className={cn('relative overflow-hidden', viewType === 'grid' ? 'aspect-video' : 'h-full min-h-[200px]')}>
{exam.thumbnail ? (
<img src={exam.thumbnail} alt={exam.title} className="h-full w-full object-cover transition-transform group-hover:scale-105" />
) : (
<div className="from-primary/20 to-primary/5 flex h-full items-center justify-center bg-gradient-to-br">
<span className="text-primary/30 text-4xl font-bold">{exam.title.charAt(0)}</span>
</div>
)}
{exam.level && <Badge className="absolute top-2 left-2 capitalize">{exam.level}</Badge>}
</div>
</Link>
<div className={cn('flex flex-col', viewType === 'list' && 'flex-1')}>
<CardContent className="p-4">
<Link href={examUrl}>
<h3 className="group-hover:text-primary mb-2 line-clamp-2 text-lg font-semibold transition-colors">{exam.title}</h3>
</Link>
{!isCompact && exam.short_description && <p className="text-muted-foreground mb-3 line-clamp-2 text-sm">{exam.short_description}</p>}
<div className="text-muted-foreground mb-3 flex items-center gap-1 text-sm">
<span>by</span>
<span className="font-medium">{exam.instructor?.user?.name || 'Instructor'}</span>
</div>
<div className="text-muted-foreground mb-3 flex flex-wrap items-center gap-3 text-sm">
<div className="flex items-center gap-1">
<Clock className="h-4 w-4" />
<span>
{exam.duration_hours}h {exam.duration_minutes}m
</span>
</div>
<div className="flex items-center gap-1">
<Users className="h-4 w-4" />
<span>{exam.enrollments_count || 0} students</span>
</div>
</div>
{exam.average_rating !== undefined && (
<RatingDisplay rating={exam.average_rating} reviewCount={exam.reviews_count} size="sm" className="mb-3" />
)}
</CardContent>
<CardFooter className={cn('flex items-center justify-between border-t p-4', viewType === 'list' && 'mt-auto')}>
<div className="flex items-baseline gap-2">
{exam.pricing_type === 'free' ? (
common.free
) : exam.discount ? (
<>
<span className="font-semibold">
{currency?.symbol}
{exam.discount_price}
</span>
<span className="text-muted-foreground ml-2 text-sm font-medium line-through">
{currency?.symbol}
{exam.price}
</span>
</>
) : (
<>
<span className="font-semibold">
{currency?.symbol}
{exam.price}
</span>
</>
)}
</div>
<div className="flex items-center gap-2">
{onAddToWishlist && (
<Button
variant="outline"
size="icon"
onClick={(e) => {
e.preventDefault();
onAddToWishlist(exam);
}}
>
<Heart className="h-4 w-4" />
</Button>
)}
{onAddToCart && exam.pricing_type === 'paid' && (
<Button
variant="default"
size="icon"
onClick={(e) => {
e.preventDefault();
onAddToCart(exam);
}}
>
<ShoppingCart className="h-4 w-4" />
</Button>
)}
</div>
</CardFooter>
</div>
</Card>
);
};
export default ExamCard1;