import { useEffect, useState } from 'react'; import { useParams, useNavigate } from 'react-router-dom'; import { supabase } from '@/integrations/supabase/client'; import { useAuth } from '@/hooks/useAuth'; import { Button } from '@/components/ui/button'; import { Card, CardContent } from '@/components/ui/card'; import { Skeleton } from '@/components/ui/skeleton'; import { toast } from '@/hooks/use-toast'; import { formatDuration } from '@/lib/format'; import { ChevronLeft, ChevronRight, Check, Play, BookOpen, Clock, Menu, X } from 'lucide-react'; import { cn } from '@/lib/utils'; import { Sheet, SheetContent, SheetTrigger } from '@/components/ui/sheet'; interface Product { id: string; title: string; slug: string; } interface Module { id: string; title: string; position: number; lessons: Lesson[]; } interface Lesson { id: string; title: string; content: string | null; video_url: string | null; duration_seconds: number | null; position: number; release_at: string | null; } interface Progress { lesson_id: string; completed_at: string; } export default function Bootcamp() { const { slug } = useParams<{ slug: string }>(); const navigate = useNavigate(); const { user, loading: authLoading } = useAuth(); const [product, setProduct] = useState(null); const [modules, setModules] = useState([]); const [progress, setProgress] = useState([]); const [selectedLesson, setSelectedLesson] = useState(null); const [loading, setLoading] = useState(true); const [sidebarOpen, setSidebarOpen] = useState(true); const [mobileMenuOpen, setMobileMenuOpen] = useState(false); useEffect(() => { if (!authLoading && !user) { navigate('/auth'); } else if (user && slug) { checkAccessAndFetch(); } }, [user, authLoading, slug]); const checkAccessAndFetch = async () => { const { data: productData, error: productError } = await supabase .from('products') .select('id, title, slug') .eq('slug', slug) .eq('type', 'bootcamp') .maybeSingle(); if (productError || !productData) { toast({ title: 'Error', description: 'Bootcamp tidak ditemukan', variant: 'destructive' }); navigate('/dashboard'); return; } setProduct(productData); const { data: accessData } = await supabase .from('user_access') .select('id') .eq('user_id', user!.id) .eq('product_id', productData.id) .maybeSingle(); if (!accessData) { toast({ title: 'Akses ditolak', description: 'Anda tidak memiliki akses ke bootcamp ini', variant: 'destructive' }); navigate('/dashboard'); return; } const { data: modulesData } = await supabase .from('bootcamp_modules') .select(` id, title, position, bootcamp_lessons ( id, title, content, video_url, duration_seconds, position, release_at ) `) .eq('product_id', productData.id) .order('position'); if (modulesData) { const sortedModules = modulesData.map(m => ({ ...m, lessons: (m.bootcamp_lessons as Lesson[]).sort((a, b) => a.position - b.position) })); setModules(sortedModules); if (sortedModules.length > 0 && sortedModules[0].lessons.length > 0) { setSelectedLesson(sortedModules[0].lessons[0]); } } const { data: progressData } = await supabase .from('lesson_progress') .select('lesson_id, completed_at') .eq('user_id', user!.id); if (progressData) { setProgress(progressData); } setLoading(false); }; const isLessonCompleted = (lessonId: string) => { return progress.some(p => p.lesson_id === lessonId); }; const markAsCompleted = async () => { if (!selectedLesson || !user) return; const { error } = await supabase .from('lesson_progress') .insert({ user_id: user.id, lesson_id: selectedLesson.id }); if (error) { if (error.code === '23505') { toast({ title: 'Info', description: 'Pelajaran sudah ditandai selesai' }); } else { toast({ title: 'Error', description: 'Gagal menandai selesai', variant: 'destructive' }); } return; } setProgress([...progress, { lesson_id: selectedLesson.id, completed_at: new Date().toISOString() }]); toast({ title: 'Selesai!', description: 'Pelajaran ditandai selesai' }); goToNextLesson(); }; const goToNextLesson = () => { if (!selectedLesson) return; const allLessons = modules.flatMap(m => m.lessons); const currentIndex = allLessons.findIndex(l => l.id === selectedLesson.id); if (currentIndex < allLessons.length - 1) { setSelectedLesson(allLessons[currentIndex + 1]); } }; const goToPrevLesson = () => { if (!selectedLesson) return; const allLessons = modules.flatMap(m => m.lessons); const currentIndex = allLessons.findIndex(l => l.id === selectedLesson.id); if (currentIndex > 0) { setSelectedLesson(allLessons[currentIndex - 1]); } }; const getVideoEmbed = (url: string) => { const youtubeMatch = url.match(/(?:youtube\.com\/(?:watch\?v=|embed\/)|youtu\.be\/)([^&\s]+)/); if (youtubeMatch) return `https://www.youtube.com/embed/${youtubeMatch[1]}`; const vimeoMatch = url.match(/vimeo\.com\/(\d+)/); if (vimeoMatch) return `https://player.vimeo.com/video/${vimeoMatch[1]}`; return url; }; const completedCount = progress.length; const totalLessons = modules.reduce((sum, m) => sum + m.lessons.length, 0); const renderSidebarContent = () => (
{modules.map((module) => (

{module.title}

{module.lessons.map((lesson) => { const isCompleted = isLessonCompleted(lesson.id); const isSelected = selectedLesson?.id === lesson.id; const isReleased = !lesson.release_at || new Date(lesson.release_at) <= new Date(); return ( ); })}
))}
); if (authLoading || loading) { return (
{[...Array(5)].map((_, i) => ( ))}
); } return (
{/* Header */}

{product?.title}

{completedCount} / {totalLessons} selesai
0 ? (completedCount / totalLessons) * 100 : 0}%` }} />
{/* Mobile menu trigger */}
Kurikulum
{renderSidebarContent()}
{/* Desktop Sidebar */} {/* Toggle sidebar button */} {/* Main content */}
{selectedLesson ? (

{selectedLesson.title}

{selectedLesson.duration_seconds && ( {formatDuration(selectedLesson.duration_seconds)} )}
{selectedLesson.video_url && (