import { useEffect, useState } from 'react'; import { useParams, useNavigate, Link } from 'react-router-dom'; import { supabase } from '@/integrations/supabase/client'; import { AppLayout } from '@/components/AppLayout'; import { Card, CardContent } from '@/components/ui/card'; import { Badge } from '@/components/ui/badge'; import { Button } from '@/components/ui/button'; import { useCart } from '@/contexts/CartContext'; import { useAuth } from '@/hooks/useAuth'; import { toast } from '@/hooks/use-toast'; import { Skeleton } from '@/components/ui/skeleton'; import { formatIDR, formatDuration } from '@/lib/format'; import { Video, Calendar, BookOpen, Play, Clock, ChevronDown, ChevronRight, Star, CheckCircle, Lock } from 'lucide-react'; import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '@/components/ui/collapsible'; import { ReviewModal } from '@/components/reviews/ReviewModal'; import { ProductReviews } from '@/components/reviews/ProductReviews'; interface Product { id: string; title: string; slug: string; type: string; description: string; content: string; price: number; sale_price: number | null; meeting_link: string | null; recording_url: string | null; m3u8_url: string | null; mp4_url: string | null; video_host: 'youtube' | 'adilo' | 'unknown' | null; event_start: string | null; duration_minutes: number | null; chapters?: { time: number; title: string; }[]; created_at: string; } interface Module { id: string; title: string; position: number; lessons: Lesson[]; } interface Lesson { id: string; title: string; duration_seconds: number | null; position: number; chapters?: { time: number; title: string; }[]; } interface UserReview { id: string; rating: number; title: string; body: string; is_approved: boolean; created_at: string; } export default function ProductDetail() { const { slug } = useParams<{ slug: string }>(); const navigate = useNavigate(); const [product, setProduct] = useState(null); const [modules, setModules] = useState([]); const [loading, setLoading] = useState(true); const [hasAccess, setHasAccess] = useState(false); const [checkingAccess, setCheckingAccess] = useState(true); const [expandedModules, setExpandedModules] = useState>(new Set()); const [expandedLessonChapters, setExpandedLessonChapters] = useState>(new Set()); const [userReview, setUserReview] = useState(null); const [reviewModalOpen, setReviewModalOpen] = useState(false); const { addItem, items } = useCart(); const { user } = useAuth(); useEffect(() => { if (slug) fetchProduct(); }, [slug]); useEffect(() => { if (product && user) { checkUserAccess(); checkUserReview(); } else { setCheckingAccess(false); } }, [product, user]); useEffect(() => { if (product?.type === 'bootcamp') { fetchCurriculum(); } }, [product]); const fetchProduct = async () => { const { data, error } = await supabase .from('products') .select('*') .eq('slug', slug) .eq('is_active', true) .maybeSingle(); if (error || !data) { toast({ title: 'Error', description: 'Produk tidak ditemukan', variant: 'destructive' }); } else { setProduct(data); } setLoading(false); }; const fetchCurriculum = async () => { if (!product) return; const { data: modulesData } = await supabase .from('bootcamp_modules') .select(` id, title, position, bootcamp_lessons ( id, title, duration_seconds, position, chapters ) `) .eq('product_id', product.id) .order('position'); if (modulesData) { const sorted = modulesData.map(m => ({ ...m, lessons: (m.bootcamp_lessons as Lesson[]).sort((a, b) => a.position - b.position) })); setModules(sorted); // Expand first module by default if (sorted.length > 0) { setExpandedModules(new Set([sorted[0].id])); } // Keep all lesson timelines collapsed by default for cleaner UX setExpandedLessonChapters(new Set()); } }; const checkUserAccess = async () => { if (!product || !user) return; // Check user_access table first const { data: accessData } = await supabase .from('user_access') .select('id') .eq('user_id', user.id) .eq('product_id', product.id) .maybeSingle(); if (accessData) { setHasAccess(true); setCheckingAccess(false); return; } // Also check for paid orders containing this product const { data: paidOrders } = await supabase .from('orders') .select(` id, order_items!inner (product_id) `) .eq('user_id', user.id) .eq('payment_status', 'paid') .eq('payment_provider', 'pakasir') .eq('order_items.product_id', product.id) .limit(1); setHasAccess(!!(paidOrders && paidOrders.length > 0)); setCheckingAccess(false); }; const checkUserReview = async () => { if (!product || !user) return; const { data } = await supabase .from('reviews') .select('id, rating, title, body, is_approved, created_at') .eq('user_id', user.id) .eq('product_id', product.id) .order('created_at', { ascending: false }) .limit(1); if (data && data.length > 0) { setUserReview(data[0] as UserReview); } else { setUserReview(null); } }; // Check if webinar has ended (eligible for review) const isWebinarEnded = () => { if (!product || product.type !== 'webinar' || !product.event_start) return false; const eventStart = new Date(product.event_start); const durationMs = (product.duration_minutes || 60) * 60 * 1000; const eventEnd = new Date(eventStart.getTime() + durationMs); return new Date() > eventEnd; }; // Check if webinar is currently running or about to start (can join) const isWebinarJoinable = () => { if (!product || product.type !== 'webinar' || !product.event_start) return false; const eventStart = new Date(product.event_start); const durationMs = (product.duration_minutes || 60) * 60 * 1000; const eventEnd = new Date(eventStart.getTime() + durationMs); const now = new Date(); // Can join if webinar hasn't ended yet (even if it's already started) return now <= eventEnd; }; const handleAddToCart = () => { if (!product) return; addItem({ id: product.id, title: product.title, price: product.price, sale_price: product.sale_price, type: product.type }); toast({ title: 'Ditambahkan', description: `${product.title} sudah ditambahkan ke keranjang` }); }; const isInCart = product ? items.some(item => item.id === product.id) : false; const formatChapterTime = (seconds: number) => { const hours = Math.floor(seconds / 3600); const mins = Math.floor((seconds % 3600) / 60); const secs = Math.floor(seconds % 60); if (hours > 0) { return `${hours}:${String(mins).padStart(2, '0')}:${String(secs).padStart(2, '0')}`; } return `${mins}:${String(secs).padStart(2, '0')}`; }; const isLastTimelineItem = (length: number, chapterIndex: number)=> { const calcLength = length - 1; return calcLength !== chapterIndex; } const renderWebinarChapters = () => { if (product?.type !== 'webinar' || !product.chapters || product.chapters.length === 0) return null; return (

Daftar isi Webinar

{product.chapters.map((chapter, index) => (
{formatChapterTime(chapter.time)}

{chapter.title}

))}
); }; 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 totalDuration = modules.reduce((total, m) => total + m.lessons.reduce((sum, l) => sum + (l.duration_seconds || 0), 0), 0 ); const totalLessons = modules.reduce((sum, m) => sum + m.lessons.length, 0); const toggleModule = (id: string) => { const newSet = new Set(expandedModules); if (newSet.has(id)) newSet.delete(id); else newSet.add(id); setExpandedModules(newSet); }; const toggleLessonChapters = (lessonId: string) => { const newSet = new Set(expandedLessonChapters); if (newSet.has(lessonId)) { newSet.delete(lessonId); } else { newSet.add(lessonId); } setExpandedLessonChapters(newSet); }; // Check if product has any recording (YouTube, M3U8, or MP4) const hasRecording = () => { if (!product) return false; return !!(product.recording_url || product.m3u8_url || product.mp4_url); }; if (loading) { return (
); } if (!product) { return (

Produk tidak ditemukan

); } const renderActionButtons = () => { if (checkingAccess) return ; if (!hasAccess) { return ( ); } switch (product.type) { case 'consulting': return ( ); case 'webinar': if (hasRecording()) { return (

Rekaman webinar tersedia

Akses rekaman webinar kapan saja. Pelajari materi sesuai kecepatan Anda.

); } // Show "Gabung Webinar" if webinar hasn't ended yet (can join even if already started) if (isWebinarJoinable() && product.meeting_link) { return ( ); } // Webinar has ended but no recording yet if (isWebinarEnded()) { return Rekaman segera tersedia; } return null; case 'bootcamp': return ( ); default: return null; } }; const renderCurriculumPreview = () => { if (product.type !== 'bootcamp' || modules.length === 0) return null; return (

Kurikulum

{totalLessons} Pelajaran {totalDuration > 0 && ( {formatDuration(totalDuration)} )}
{modules.map((module) => ( toggleModule(module.id)} > {module.title}
{module.lessons.length} pelajaran {expandedModules.has(module.id) ? : }
{module.lessons.map((lesson) => (
{/* Lesson header */}
{lesson.title}
{lesson.duration_seconds && ( {formatDuration(lesson.duration_seconds)} )}
{/* Lesson chapters (if any) */} {lesson.chapters && lesson.chapters.length > 0 && ( toggleLessonChapters(lesson.id)} > {lesson.chapters.length} timeline item{lesson.chapters.length > 1 ? 's' : ''} {expandedLessonChapters.has(lesson.id) ? ( ) : ( )}
{lesson.chapters.map((chapter, chapterIndex) => (
{formatChapterTime(chapter.time)}
))}
)}
))}
))}
); }; return (
{/* Ownership Banner - shown at top for purchased users */} {hasAccess && (

Anda memiliki akses ke produk ini

{product.type === 'webinar' && 'Selamat menonton rekaman webinar!'} {product.type === 'bootcamp' && 'Mulai belajar sekarang!'} {product.type === 'consulting' && 'Jadwalkan sesi konsultasi Anda.'}

)}

{product.title}

{product.type} {product.type === 'webinar' && hasRecording() && ( Rekaman Tersedia )} {product.type === 'webinar' && !hasRecording() && product.event_start && new Date(product.event_start) > new Date() && ( Segera Hadir )} {product.type === 'webinar' && !hasRecording() && product.event_start && new Date(product.event_start) <= new Date() && ( Telah Lewat )}
{product.sale_price ? (
{formatIDR(product.sale_price)} {formatIDR(product.price)}
) : ( {formatIDR(product.price)} )} {product.type === 'bootcamp' && totalDuration > 0 && (

Total: {formatDuration(totalDuration)} waktu belajar

)}
{product.description && (
)} {renderCurriculumPreview()} {product.content && (
)} {renderWebinarChapters()}
{renderActionButtons()}
{/* Webinar review prompt */} {hasAccess && product.type === 'webinar' && isWebinarEnded() && ( {userReview ? ( userReview.is_approved ? ( // Approved review - celebratory display

Ulasan Anda Terbit!

Disetujui

Terima kasih telah berbagi pengalaman Anda. Ulasan Anda membantu peserta lain!

{/* User's review display */}
{[1, 2, 3, 4, 5].map((i) => ( ))}

{userReview.title}

{userReview.body && (

{userReview.body}

)}
Diterbitkan pada {new Date(userReview.created_at).toLocaleDateString('id-ID', { day: 'numeric', month: 'long', year: 'numeric' })}
) : ( // Pending review

Ulasan Anda sedang ditinjau

Terima kasih! Ulasan akan muncul setelah disetujui admin.

) ) : ( // No review yet - prompt to review

Bagaimana pengalaman webinar ini?

Ulasan Anda membantu peserta lain

)}
)} {/* Product reviews section */}
{/* Review Modal */} {user && ( checkUserReview()} /> )} ); }