import React, { useState, useEffect } from 'react'; import { ArrowLeft, BookOpen, ChevronRight, FileText, CheckCircle2, Circle, MessageSquare, AlertCircle, Award, Download } from 'lucide-react'; import VideoPlayer from './VideoPlayer'; import CourseActivity from './CourseActivity'; interface LessonProgress { watchedPercentage: number; completed: boolean; } interface Lesson { id: string; moduleId: string; title: string; videoUrl: string; videoType: 'youtube' | 'direct'; order: number; content: string; progress: LessonProgress; note: string; } interface Module { id: string; courseId: string; title: string; order: number; lessons: Lesson[]; } interface Course { id: string; title: string; description: string; thumbnail: string; category: string; studyMaterialUrl?: string; } interface CourseViewProps { courseId: string; token: string | null; onBack: () => void; initialLessonId?: string | null; } export default function CourseView({ courseId, token, onBack, initialLessonId }: CourseViewProps) { const [course, setCourse] = useState(null); const [modules, setModules] = useState([]); const [activeLesson, setActiveLesson] = useState(null); const [expandedModules, setExpandedModules] = useState([]); const [noteContent, setNoteContent] = useState(''); const [isLoading, setIsLoading] = useState(true); const [error, setError] = useState(null); const [saveStatus, setSaveStatus] = useState<'idle' | 'saving' | 'saved' | 'error'>('idle'); const [activeActivityModuleId, setActiveActivityModuleId] = useState(null); useEffect(() => { fetchCourseDetails(); }, [courseId]); const fetchCourseDetails = async () => { setIsLoading(true); setError(null); try { const res = await fetch(`/api/courses/${courseId}`, { headers: { 'Authorization': `Bearer ${token}` } }); if (!res.ok) { throw new Error('Falha ao carregar detalhes do curso'); } const data = await res.json(); setCourse(data.course); setModules(data.modules); // Default to the selected initial lesson, or the first lesson of the first module if (data.modules && data.modules.length > 0) { let selectedLsn = null; if (initialLessonId) { for (const mod of data.modules) { const found = mod.lessons?.find((l: any) => l.id === initialLessonId); if (found) { selectedLsn = found; break; } } } if (!selectedLsn) { const firstMod = data.modules[0]; if (firstMod.lessons && firstMod.lessons.length > 0) { selectedLsn = firstMod.lessons[0]; } } if (selectedLsn) { handleSelectLesson(selectedLsn, data.modules.find((m: any) => m.lessons.some((l: any) => l.id === selectedLsn.id))?.id); } } } catch (err: any) { setError(err.message || 'Erro inesperado.'); } finally { setIsLoading(false); } }; const handleSelectLesson = (lesson: Lesson, moduleId?: string) => { setActiveLesson(lesson); setNoteContent(lesson.note || ''); setSaveStatus('idle'); if (moduleId) { setExpandedModules(prev => prev.includes(moduleId) ? prev : [...prev, moduleId]); } else { // Find module if not passed const parentMod = modules.find(m => m.lessons.some(l => l.id === lesson.id)); if (parentMod) { setExpandedModules(prev => prev.includes(parentMod.id) ? prev : [...prev, parentMod.id]); } } }; const toggleModule = (moduleId: string) => { setExpandedModules(prev => prev.includes(moduleId) ? prev.filter(id => id !== moduleId) : [...prev, moduleId] ); }; const totalLessonsCount = modules.reduce((acc, mod) => acc + mod.lessons.length, 0); const completedLessonsCount = modules.reduce((acc, mod) => acc + mod.lessons.filter(l => l.progress?.completed).length, 0); const overallProgress = totalLessonsCount === 0 ? 0 : Math.round((completedLessonsCount / totalLessonsCount) * 100); const handleReact = async (type: string | null) => { if (!activeLesson) return; try { const res = await fetch(`/api/lessons/${activeLesson.id}/react`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` }, body: JSON.stringify({ type }) }); if (res.ok) { // Update local state for lesson reaction setModules(prev => prev.map(mod => { return { ...mod, lessons: mod.lessons.map(lsn => { if (lsn.id === activeLesson.id) { return { ...lsn, reaction: type }; } return lsn; }) }; })); } } catch (err) { console.error('Error saving reaction:', err); } }; const handleProgressUpdate = async (watchedPercentage: number) => { if (!activeLesson) return; try { const res = await fetch(`/api/lessons/${activeLesson.id}/progress`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` }, body: JSON.stringify({ watchedPercentage }) }); if (res.ok) { const data = await res.json(); // Update local state for lesson progress setModules(prev => prev.map(mod => { return { ...mod, lessons: mod.lessons.map(lsn => { if (lsn.id === activeLesson.id) { return { ...lsn, progress: { watchedPercentage: Math.max(lsn.progress.watchedPercentage, watchedPercentage), completed: data.completed || lsn.progress.completed } }; } return lsn; }) }; })); // Also update the active lesson progress locally setActiveLesson(prev => { if (!prev) return null; return { ...prev, progress: { watchedPercentage: Math.max(prev.progress.watchedPercentage, watchedPercentage), completed: data.completed || prev.progress.completed } }; }); } } catch (err) { console.error('Error saving progress:', err); } }; const handleSaveNote = async () => { if (!activeLesson) return; setSaveStatus('saving'); try { const res = await fetch(`/api/lessons/${activeLesson.id}/notes`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` }, body: JSON.stringify({ content: noteContent }) }); if (res.ok) { setSaveStatus('saved'); // Update local modules array so state matches setModules(prev => prev.map(mod => { return { ...mod, lessons: mod.lessons.map(lsn => { if (lsn.id === activeLesson.id) { return { ...lsn, note: noteContent }; } return lsn; }) }; })); setTimeout(() => setSaveStatus('idle'), 3000); } else { setSaveStatus('error'); } } catch (err) { setSaveStatus('error'); } }; if (isLoading) { return (

Preparando seu ambiente de estudos...

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

Oops! Algo deu errado

{error || 'Não foi possível carregar as informações deste curso.'}

); } if (activeActivityModuleId) { return ( setActiveActivityModuleId(null)} onCompleted={(passed) => { if (passed) { // Re-fetch course details to update any unlocked certificates if applicable fetchCourseDetails(); } }} /> ); } return (
{/* Back Header */}
{course.category}
{/* Main Grid: Player on left, lessons sidebar on right */}
{/* Left Hand: Video & Notes */}
{activeLesson ? (

{activeLesson.title}

{/* Lesson Description Content */} {activeLesson.content && (

Recursos & Descrição da Aula

{activeLesson.content}

)} {/* Notes & Doubts panel */}

Suas Anotações e Dúvidas

{saveStatus === 'saving' && Salvando...} {saveStatus === 'saved' && Salvo com sucesso!} {saveStatus === 'error' && Erro ao salvar.}