'use client'; import { useEffect, useState, useMemo } from 'react'; import Link from 'next/link'; import { useParams, useRouter } from 'next/navigation'; import { ArrowLeft, Edit, Trash2, ChevronLeft, ChevronRight, CheckCircle2, Circle, Lock, BookOpen, Clock, GraduationCap, ChevronDown, ChevronUp, Play, } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { Progress } from '@/components/ui/progress'; import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger, } from '@/components/ui/alert-dialog'; import { api } from '@/lib/api'; import { useAuth } from '@/contexts/auth-context'; import { LessonContentViewer } from '@/components/editor/lesson-content-viewer'; import { LessonQuiz } from '@/components/dashboard/lesson-quiz'; import { cn } from '@/lib/utils'; type Lesson = { id: string; title: string; durationMinutes?: number | null; order: number }; type Chapter = { id: string; title: string; description?: string | null; order: number; lessons: Lesson[] }; type CourseData = { id: string; title: string; description?: string | null; status: string; authorId: string; chapters: Chapter[]; }; export default function CoursePage() { const params = useParams(); const router = useRouter(); const { loading: authLoading, backendUser } = useAuth(); const id = params?.id as string; const [course, setCourse] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [deleting, setDeleting] = useState(false); const [selectedLessonId, setSelectedLessonId] = useState(null); const [lessonContent, setLessonContent] = useState | null>(null); const [lessonContentLoading, setLessonContentLoading] = useState(false); const [completedLessons, setCompletedLessons] = useState>(new Set()); const [expandedChapters, setExpandedChapters] = useState([]); const [sidebarOpen, setSidebarOpen] = useState(true); const [enrollmentProgress, setEnrollmentProgress] = useState(null); const [showQuiz, setShowQuiz] = useState(false); const [quizQuestions, setQuizQuestions] = useState([]); const [generatingCertificate, setGeneratingCertificate] = useState(false); // Flat list of all lessons const flatLessons = useMemo(() => { if (!course) return []; return course.chapters .sort((a, b) => a.order - b.order) .flatMap((ch) => ch.lessons.sort((a, b) => a.order - b.order).map((l) => ({ ...l, chapterId: ch.id, chapterTitle: ch.title })) ); }, [course]); const currentLessonIndex = flatLessons.findIndex((l) => l.id === selectedLessonId); const totalLessons = flatLessons.length; const completedCount = completedLessons.size; const progressPercent = totalLessons > 0 ? Math.round((completedCount / totalLessons) * 100) : 0; // Load course and progress useEffect(() => { if (!id || authLoading) return; let cancelled = false; (async () => { setLoading(true); setError(null); try { const [courseData, progressData] = await Promise.all([ api.getCourse(id), api.getEnrollmentProgress(id).catch(() => null), ]); if (!cancelled) { setCourse(courseData); setEnrollmentProgress(progressData); if (progressData?.lessons) { const completed: Set = new Set( progressData.lessons .filter((l: any) => l.completedAt) .map((l: any) => String(l.lessonId)) ); setCompletedLessons(completed); } const first = courseData.chapters?.[0]?.lessons?.[0]; if (first) setSelectedLessonId(first.id); setExpandedChapters(courseData.chapters.map((ch: Chapter) => ch.id)); } } catch (e: any) { if (!cancelled) setError(e?.message || 'Не удалось загрузить курс'); } finally { if (!cancelled) setLoading(false); } })(); return () => { cancelled = true; }; }, [id, authLoading]); // Load lesson content useEffect(() => { if (!id || !selectedLessonId) { setLessonContent(null); return; } let cancelled = false; setLessonContentLoading(true); (async () => { try { const data = await api.getLesson(id, selectedLessonId); const content = data?.content; if (!cancelled) setLessonContent( typeof content === 'object' && content !== null ? (content as Record) : null ); } catch { if (!cancelled) setLessonContent(null); } finally { if (!cancelled) setLessonContentLoading(false); } })(); return () => { cancelled = true; }; }, [id, selectedLessonId]); const handleDelete = async () => { if (!course?.id || deleting) return; setDeleting(true); try { await api.deleteCourse(course.id); router.push('/dashboard'); router.refresh(); } catch (e: any) { setError(e?.message || 'Не удалось удалить курс'); } finally { setDeleting(false); } }; const markComplete = async () => { if (!selectedLessonId || !id) return; try { await api.completeLesson(id, selectedLessonId); setCompletedLessons((prev) => new Set(prev).add(selectedLessonId)); } catch { setCompletedLessons((prev) => new Set(prev).add(selectedLessonId)); } }; const handleStartQuiz = async () => { if (!selectedLessonId || !id) return; try { const quiz = await api.getLessonQuiz(id, selectedLessonId); setQuizQuestions(quiz.questions || []); setShowQuiz(true); } catch { setQuizQuestions([]); } }; const handleQuizComplete = async (score: number) => { if (!selectedLessonId || !id) return; try { await api.submitQuizScore(id, selectedLessonId, score); markComplete(); } catch { markComplete(); } }; const handleGetCertificate = async () => { if (!id || generatingCertificate) return; setGeneratingCertificate(true); try { const { certificateUrl } = await api.getCertificate(id); window.open(certificateUrl, '_blank'); } catch { // silent } finally { setGeneratingCertificate(false); } }; const goToNextLesson = () => { if (currentLessonIndex < flatLessons.length - 1) { markComplete(); setSelectedLessonId(flatLessons[currentLessonIndex + 1].id); } }; const goToPrevLesson = () => { if (currentLessonIndex > 0) { setSelectedLessonId(flatLessons[currentLessonIndex - 1].id); } }; const toggleChapter = (chapterId: string) => { setExpandedChapters((prev) => prev.includes(chapterId) ? prev.filter((id) => id !== chapterId) : [...prev, chapterId] ); }; if (authLoading || loading) { return (

Загрузка курса...

); } if (error || !course) { return (

{error || 'Курс не найден'}

); } const activeLessonMeta = selectedLessonId ? flatLessons.find((l) => l.id === selectedLessonId) : null; const isAuthor = course && backendUser && course.authorId === backendUser.id; const courseCompleted = completedCount >= totalLessons && totalLessons > 0; return (
{/* Top bar */}
{course.title}
{/* Progress badge */}
{progressPercent}% пройдено
{/* Certificate button - show when course completed */} {courseCompleted && ( )} {/* Edit/Delete - only for author */} {isAuthor && ( <> Удалить курс? Курс «{course.title}» будет удалён безвозвратно. Отмена { e.preventDefault(); handleDelete(); }} className="bg-destructive text-destructive-foreground hover:bg-destructive/90" disabled={deleting} > {deleting ? 'Удаление...' : 'Удалить'} )}
{/* ─── Left sidebar: course navigation ─── */}
{sidebarOpen && (
{/* Course progress */}
Прогресс курса {completedCount}/{totalLessons}
{/* Chapters & lessons */}
{course.chapters .sort((a, b) => a.order - b.order) .map((chapter, chapterIdx) => { const isExpanded = expandedChapters.includes(chapter.id); const chapterLessons = chapter.lessons.sort((a, b) => a.order - b.order); const chapterComplete = chapterLessons.every((l) => completedLessons.has(l.id)); const chapterStarted = chapterLessons.some((l) => completedLessons.has(l.id)); return (
{isExpanded && (
{chapterLessons.map((lesson, lessonIdx) => { const isActive = selectedLessonId === lesson.id; const isCompleted = completedLessons.has(lesson.id); const globalIdx = flatLessons.findIndex((l) => l.id === lesson.id); // Lesson is locked if sequential mode: all previous must be complete // For now, don't lock (allow free navigation) const isLocked = false; return ( ); })}
)}
); })}
)}
{/* Sidebar toggle */} {/* ─── Main content area ─── */}
{/* Lesson content */}
{/* Chapter & lesson header */} {activeLessonMeta && (

{activeLessonMeta.chapterTitle}

{activeLessonMeta.title}

Урок {currentLessonIndex + 1} из {totalLessons} {activeLessonMeta.durationMinutes && ( {activeLessonMeta.durationMinutes} мин )}
)} {/* Content */} {lessonContentLoading ? (
) : selectedLessonId ? ( <> {!showQuiz && !completedLessons.has(selectedLessonId) && (

Проверьте свои знания

Пройдите тест, чтобы закрепить материал и получить сертификат

)} {showQuiz && ( )} ) : (

Выберите урок для начала обучения

)}
{/* Bottom navigation */}
{currentLessonIndex < flatLessons.length - 1 ? ( ) : (
{courseCompleted ? 'Курс пройден!' : 'Последний урок'}
)}
); }