'use client' import * as React from 'react' import Link from 'next/link' import { useRouter } from 'next/navigation' import { Accordion, AccordionItem, AccordionTrigger, AccordionContent, } from '@/components/ui/accordion' import { Button } from '@/components/ui/button' import { ChevronRight, Play, CheckCircle, CheckCircle2, Circle, Clock, FileText, Film, Image as ImageIcon, Loader2, Music, HelpCircle, } from 'lucide-react' import { cn, formatDuration, formatTime } from '@/lib/utils' interface ModuleAccordionProps { courseId: string modules: Array<{ id: string name: string slug: string order: number lessons: Array<{ id: string title: string slug: string order: number type: string duration: number | null moduleId: string progress?: { completed: boolean position: number lastWatched: Date | string | null } | null }> }> currentLessonId?: string courseSlug: string defaultOpenModuleId?: string | null } const lessonTypeIcons: Record = { VIDEO: , AUDIO: , PDF: , MARKDOWN: , HTML: , IMAGE: , QUIZ: , OTHER: , } async function saveProgress(payload: { lessonId: string courseId: string moduleId: string position: number completed: boolean }) { const response = await fetch('/api/progress', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(payload), }) if (!response.ok) { throw new Error('Failed to save progress') } } export function ModuleAccordion({ modules, currentLessonId, courseSlug, courseId, defaultOpenModuleId }: ModuleAccordionProps) { const router = useRouter() const [savingLessonIds, setSavingLessonIds] = React.useState>({}) const [savingModuleIds, setSavingModuleIds] = React.useState>({}) const currentModuleId = React.useMemo(() => { if (defaultOpenModuleId) return defaultOpenModuleId if (!currentLessonId) return undefined for (const module of modules) { if (module.lessons.some(lesson => lesson.id === currentLessonId)) { return module.id } } return undefined }, [currentLessonId, modules, defaultOpenModuleId]) const openModuleId = React.useMemo(() => { if (!currentModuleId) return undefined return [currentModuleId] }, [currentModuleId]) const toggleLessonDone = React.useCallback(async ( lesson: { id: string moduleId: string duration: number | null progress?: { completed: boolean; position: number } | null } ) => { const wasCompleted = Boolean(lesson.progress?.completed) const nextCompleted = !wasCompleted const position = nextCompleted ? Math.max(lesson.progress?.position || 0, lesson.duration || 0) : (lesson.progress?.position || 0) setSavingLessonIds((prev) => ({ ...prev, [lesson.id]: true })) try { await saveProgress({ lessonId: lesson.id, courseId, moduleId: lesson.moduleId, position, completed: nextCompleted, }) router.refresh() } catch (error) { console.error('Failed to toggle lesson done:', error) } finally { setSavingLessonIds((prev) => { const next = { ...prev } delete next[lesson.id] return next }) } }, [courseId, router]) const toggleModuleDone = React.useCallback(async ( module: { id: string lessons: Array<{ id: string moduleId: string duration: number | null progress?: { completed: boolean; position: number } | null }> } ) => { const completedCount = module.lessons.filter((lesson) => lesson.progress?.completed).length const allCompleted = completedCount === module.lessons.length && module.lessons.length > 0 const nextCompleted = !allCompleted setSavingModuleIds((prev) => ({ ...prev, [module.id]: true })) try { for (const lesson of module.lessons) { const position = nextCompleted ? Math.max(lesson.progress?.position || 0, lesson.duration || 0) : (lesson.progress?.position || 0) await saveProgress({ lessonId: lesson.id, courseId, moduleId: lesson.moduleId, position, completed: nextCompleted, }) } router.refresh() } catch (error) { console.error('Failed to toggle module done:', error) } finally { setSavingModuleIds((prev) => { const next = { ...prev } delete next[module.id] return next }) } }, [courseId, router]) const [openModules, setOpenModules] = React.useState(openModuleId) React.useEffect(() => { setOpenModules(openModuleId) }, [openModuleId]) return ( {modules.map((module) => { const completedCount = module.lessons.filter((lesson) => lesson.progress?.completed).length const allCompleted = completedCount === module.lessons.length && module.lessons.length > 0 const isSavingModule = Boolean(savingModuleIds[module.id]) const isCurrentModule = currentModuleId === module.id return (
{String(module.order + 1).padStart(2, '0')} {module.name} {isCurrentModule ? ( Now Playing ) : ( {completedCount}/{module.lessons.length} )}

{completedCount} / {module.lessons.length} lessons done

{allCompleted ? 'All lessons in this module are marked done.' : 'Mark the whole module done or clear it in one click.'}

{module.lessons.map((lesson) => { const isCurrent = currentLessonId === lesson.id const isCompleted = Boolean(lesson.progress?.completed) const progress = lesson.progress const hasProgress = progress && progress.position > 0 && !isCompleted const progressPercent = lesson.duration && progress ? Math.round((progress.position / lesson.duration) * 100) : 0 const isSavingLesson = Boolean(savingLessonIds[lesson.id]) const watchHref = '/watch/' + lesson.id + '?course=' + courseSlug return (
{String(lesson.order + 1).padStart(2, '0')} {isCompleted ? ( ) : hasProgress ? ( ) : ( )}

{lesson.title}

{lessonTypeIcons[lesson.type] || lessonTypeIcons.OTHER} {lesson.type.toLowerCase()} {lesson.duration && ( <> {formatDuration(lesson.duration)} )} {hasProgress && lesson.duration && ( <> {formatTime(progress!.position)} / {formatDuration(lesson.duration)} )}
{hasProgress && lesson.duration && (
)}
) })}
) })} ) }