veza/apps/web/src/components/education/CourseDetailView.tsx

349 lines
13 KiB
TypeScript
Raw Normal View History

import React, { useState } from 'react';
import { Button } from '../ui/button';
import { Card } from '../ui/card';
import { Course } from '../../types';
import {
PlayCircle,
Star,
Users,
CheckCircle,
Clock,
Globe,
ShieldCheck,
Lock,
ChevronDown,
ChevronUp,
} from 'lucide-react';
import { useToast } from '../../components/feedback/ToastProvider';
interface CourseDetailViewProps {
course: Course;
onBack: () => void;
onEnroll: () => void;
isEnrolled?: boolean;
}
export const CourseDetailView: React.FC<CourseDetailViewProps> = ({
course,
onBack,
onEnroll,
isEnrolled,
}) => {
const { addToast: _addToast } = useToast();
const [activeTab, setActiveTab] = useState<
'overview' | 'curriculum' | 'reviews'
>('overview');
const [expandedModule, setExpandedModule] = useState<string | null>(
course.modules?.[0].id || null,
);
const toggleModule = (id: string) => {
setExpandedModule(expandedModule === id ? null : id);
};
return (
<div className="animate-fadeIn pb-20 max-w-7xl mx-auto">
{/* Breadcrumb */}
<div className="mb-6">
<Button
variant="ghost"
onClick={onBack}
className="pl-0 text-kodo-content-dim hover:text-white"
>
Back to Courses
</Button>
</div>
<div className="grid grid-cols-1 lg:grid-cols-3 gap-8">
{/* Left Content */}
<div className="lg:col-span-2 space-y-8">
{/* Header */}
<div>
<h1 className="text-3xl md:text-4xl font-display font-bold text-white mb-4">
{course.title}
</h1>
<p className="text-xl text-kodo-text-main mb-6 font-light">
{course.description}
</p>
<div className="flex flex-wrap items-center gap-6 text-sm text-kodo-content-dim mb-6">
{course.rating && (
<span className="flex items-center gap-1 text-kodo-gold font-bold">
<Star className="w-4 h-4 fill-current" /> {course.rating}
</span>
)}
<span className="flex items-center gap-1">
<Users className="w-4 h-4" />{' '}
{(course.studentCount || 0).toLocaleString()} students
</span>
<span className="flex items-center gap-1">
<Clock className="w-4 h-4" /> {course.duration} total
</span>
<span className="flex items-center gap-1">
<Globe className="w-4 h-4" /> English
</span>
</div>
<div className="flex items-center gap-4">
<img
src={`https://ui-avatars.com/api/?name=${course.instructor}&background=random`}
className="w-10 h-10 rounded-full"
/>
<div>
<div className="text-xs text-kodo-content-dim uppercase">
Created by
</div>
<div className="text-sm font-bold text-white text-kodo-cyan cursor-pointer hover:underline">
{course.instructor}
</div>
</div>
</div>
</div>
{/* Tabs */}
<div className="border-b border-kodo-steel flex gap-6">
{['overview', 'curriculum', 'reviews'].map((tab) => (
<button
key={tab}
onClick={() => setActiveTab(tab as any)}
className={`pb-3 text-sm font-bold uppercase tracking-wider border-b-2 transition-colors ${activeTab === tab ? 'border-kodo-cyan text-white' : 'border-transparent text-kodo-content-dim hover:text-kodo-text-main'}`}
>
{tab}
</button>
))}
</div>
{/* Tab Content */}
{activeTab === 'overview' && (
<div className="space-y-8 animate-fadeIn">
<Card variant="default">
<h3 className="font-bold text-white text-lg mb-4">
What you'll learn
</h3>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
{course.whatYouWillLearn?.map((item, i) => (
<div key={i} className="flex gap-4 text-sm text-kodo-text-main">
<CheckCircle className="w-4 h-4 text-kodo-lime flex-shrink-0 mt-0.5" />
<span>{item}</span>
</div>
))}
</div>
</Card>
<div>
<h3 className="font-bold text-white text-lg mb-4">
Requirements
</h3>
<ul className="list-disc pl-5 space-y-1 text-sm text-kodo-content-dim">
{course.requirements?.map((req, i) => (
<li key={i}>{req}</li>
))}
</ul>
</div>
</div>
)}
{activeTab === 'curriculum' && (
<div className="space-y-4 animate-fadeIn">
<div className="flex justify-between items-center text-sm text-kodo-content-dim mb-2">
<span>
{course.modules?.length} Modules {' '}
{course.modules?.reduce(
(acc, m) => acc + m.lessons.length,
0,
)}{' '}
Lessons
</span>
<button
className="text-kodo-cyan hover:underline"
onClick={() =>
setExpandedModule(expandedModule ? null : 'all')
}
>
{expandedModule === 'all' ? 'Collapse All' : 'Expand All'}
</button>
</div>
{course.modules?.map((module) => (
<div
key={module.id}
className="border border-kodo-steel rounded-lg overflow-hidden bg-kodo-ink/30"
>
<div
className="p-4 flex justify-between items-center cursor-pointer hover:bg-white/5 transition-colors"
onClick={() => toggleModule(module.id)}
>
<h4 className="font-bold text-white flex items-center gap-4">
{expandedModule === module.id ||
expandedModule === 'all' ? (
<ChevronUp className="w-4 h-4" />
) : (
<ChevronDown className="w-4 h-4" />
)}
{module.title}
</h4>
<span className="text-xs text-kodo-content-dim">
{module.lessons.length} lectures
</span>
</div>
{(expandedModule === module.id ||
expandedModule === 'all') && (
<div className="border-t border-kodo-steel">
{module.lessons.map((lesson) => (
<div
key={lesson.id}
className="p-4 pl-8 flex justify-between items-center hover:bg-white/5 border-b border-kodo-steel/30 last:border-0"
>
<div className="flex items-center gap-4 text-sm text-kodo-text-main">
{lesson.type === 'video' ? (
<PlayCircle className="w-4 h-4" />
) : (
<ShieldCheck className="w-4 h-4" />
)}
{lesson.title}
</div>
<div className="flex items-center gap-4">
{lesson.isLocked && !isEnrolled && (
<Lock className="w-3 h-3 text-kodo-content-dim" />
)}
<span className="text-xs text-kodo-content-dim">
{lesson.duration}
</span>
</div>
</div>
))}
</div>
)}
</div>
))}
</div>
)}
{activeTab === 'reviews' && (
<div className="space-y-6 animate-fadeIn">
{course.reviews?.map((review) => (
<div
key={review.id}
className="border-b border-kodo-steel/50 pb-6"
>
<div className="flex items-center gap-4 mb-2">
<img
src={review.avatar}
className="w-10 h-10 rounded-full"
/>
<div>
<div className="font-bold text-white text-sm">
{review.username}
</div>
<div className="flex text-kodo-gold text-xs">
{[...Array(5)].map((_, i) => (
<Star
key={i}
className={`w-3 h-3 ${i < review.rating ? 'fill-current' : 'text-kodo-text-main'}`}
/>
))}
</div>
</div>
<span className="ml-auto text-xs text-kodo-content-dim">
{review.date}
</span>
</div>
<p className="text-sm text-kodo-text-main">{review.comment}</p>
</div>
))}
</div>
)}
</div>
{/* Right Sidebar */}
<div className="relative">
<div className="sticky top-24 space-y-6">
<Card
variant="default"
className="p-0 overflow-hidden border-kodo-steel/30"
>
{/* Preview Video Placeholder */}
<div className="relative aspect-video bg-black group cursor-pointer">
<img
src={course.thumbnailUrl}
className="w-full h-full object-cover opacity-80"
/>
<div className="absolute inset-0 flex items-center justify-center">
<div className="w-16 h-16 bg-white/90 rounded-full flex items-center justify-center shadow-lg group-hover:scale-110 transition-transform">
<PlayCircle className="w-8 h-8 text-black fill-current" />
</div>
</div>
<div className="absolute bottom-4 text-center w-full text-white font-bold text-sm drop-shadow-md">
Preview Course
</div>
</div>
<div className="p-6">
<div className="text-3xl font-display font-bold text-white mb-2">
{isEnrolled
? 'Enrolled'
: course.price && course.price > 0
? `$${course.price}`
: 'Free'}
</div>
{course.price && course.price > 0 && !isEnrolled && (
<p className="text-kodo-content-dim text-xs mb-6 line-through">
$199.99 (85% off)
</p>
)}
{isEnrolled ? (
<Button
variant="primary"
className="w-full h-12 text-lg"
onClick={onEnroll}
>
CONTINUE LEARNING
</Button>
) : (
<div className="space-y-4">
<Button
variant="primary"
className="w-full h-12 text-lg"
onClick={onEnroll}
>
ENROLL NOW
</Button>
<p className="text-center text-xs text-kodo-content-dim">
30-Day Money-Back Guarantee
</p>
</div>
)}
<div className="mt-6 space-y-4">
<h4 className="font-bold text-white text-sm">
This course includes:
</h4>
<ul className="text-sm text-kodo-content-dim space-y-2">
<li className="flex items-center gap-4">
<PlayCircle className="w-4 h-4" /> {course.duration}{' '}
on-demand video
</li>
<li className="flex items-center gap-4">
<ShieldCheck className="w-4 h-4" /> Full lifetime access
</li>
<li className="flex items-center gap-4">
<Globe className="w-4 h-4" /> Access on mobile and TV
</li>
{course.certificateAvailable && (
<li className="flex items-center gap-4">
<Star className="w-4 h-4" /> Certificate of completion
</li>
)}
</ul>
</div>
</div>
</Card>
</div>
</div>
</div>
</div>
);
};