import React, { useState, useEffect, useRef, useCallback } from 'react'; import { Play, Shield, User, GraduationCap, ArrowRight, Lock, Mail, Plus, CheckCircle, ChevronRight, HelpCircle, Film, Laptop, Eye, EyeOff } from 'lucide-react'; import Navbar from './components/Navbar'; import HeroBanner from './components/HeroBanner'; import CourseCarousel from './components/CourseCarousel'; import CourseView from './components/CourseView'; import SubscriptionView from './components/SubscriptionView'; import CourseUnlockModal from './components/CourseUnlockModal'; import CertificatesTab from './components/CertificatesTab'; import ProfileView from './components/ProfileView'; import HelpView from './components/HelpView'; import CustomModal from './components/CustomModal'; import { User as UserType, Course as CourseType, SubscriptionStatus } from './types'; export default function App() { const [token, setToken] = useState(localStorage.getItem('app_token')); const [user, setUser] = useState<{ name: string; email: string; role: 'admin' | 'student'; subscriptionStatus: SubscriptionStatus; } | null>(null); // App routing const [activeView, setView] = useState('home'); // home, course-view, subscription, admin-dashboard, admin-content const [selectedCourseId, setSelectedCourseId] = useState(null); const [unlockingCourse, setUnlockingCourse] = useState(null); const [expandedCourseId, setExpandedCourseId] = useState(null); const [selectedLessonId, setSelectedLessonId] = useState(null); const lessonShelfRefs = useRef>({}); // Authentication forms const [isRegister, setIsRegister] = useState(false); const [isForgotPassword, setIsForgotPassword] = useState(false); const [resetToken, setResetToken] = useState(null); const [loginEmail, setLoginEmail] = useState(''); const [loginPassword, setLoginPassword] = useState(''); const [showPassword, setShowPassword] = useState(false); const [showRegPassword, setShowRegPassword] = useState(false); const [regName, setRegName] = useState(''); const [regEmail, setRegEmail] = useState(''); const [regPassword, setRegPassword] = useState(''); const [regCpf, setRegCpf] = useState(''); const [regBirthDate, setRegBirthDate] = useState(''); const [regWhatsapp, setRegWhatsapp] = useState(''); const [authError, setAuthError] = useState(null); const [authSuccess, setAuthSuccess] = useState(null); const [authLoading, setAuthLoading] = useState(false); // Course catalogue const [courses, setCourses] = useState<(CourseType & { progress?: number; totalLessons?: number; completedLessons?: number })[]>([]); const [isLoadingCourses, setIsLoadingCourses] = useState(false); const [settings, setSettings] = useState<{ brandName?: string; brandSlogan?: string; cnpj?: string; address?: string; termsOfUse?: string; privacyPolicy?: string; featuredCourseId?: string | null; heroImageUrl?: string | null; heroTitle?: string | null; heroDescription?: string | null } | null>(null); const [infoModal, setInfoModal] = useState<{isOpen: boolean, title: string, content: string}>({isOpen: false, title: '', content: ''}); useEffect(() => { const urlParams = new URLSearchParams(window.location.search); const tokenParam = urlParams.get('token'); if (window.location.pathname === '/reset-password' && tokenParam) { setResetToken(tokenParam); setIsForgotPassword(true); } fetch('/api/settings') .then(res => res.json()) .then(data => { setSettings(data); if (data.brandName) { document.title = `${data.brandName}FLIX - Plataforma de Estudos`; } }) .catch(err => console.error('Error fetching settings:', err)); }, []); useEffect(() => { if (token) { fetchUserProfile(); } }, [token]); useEffect(() => { if (user) { fetchCourses(); } }, [user]); const fetchUserProfile = async () => { try { const res = await fetch('/api/auth/me', { headers: { 'Authorization': `Bearer ${token}` } }); if (res.ok) { const data = await res.json(); setUser(data); } else { // Stale token handleLogout(); } } catch (err) { console.error('Failed to load profile:', err); } }; const fetchCourses = async () => { setIsLoadingCourses(true); try { const res = await fetch('/api/courses', { headers: { 'Authorization': `Bearer ${token}` } }); if (res.ok) { const data = await res.json(); setCourses(data); } } catch (err) { console.error('Failed to fetch courses:', err); } finally { setIsLoadingCourses(false); } }; const handleForgotPassword = async (e: React.FormEvent) => { e.preventDefault(); setAuthError(null); setAuthSuccess(null); setAuthLoading(true); try { const res = await fetch('/api/auth/forgot-password', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ identifier: loginEmail, channel: loginEmail.includes('@') ? 'email' : 'whatsapp' }) }); const data = await res.json(); if (!res.ok) throw new Error(data.error || 'Erro ao solicitar recuperação.'); setAuthSuccess(data.message || 'Link de recuperação enviado.'); } catch (err: any) { setAuthError(err.message); } finally { setAuthLoading(false); } }; const handleResetPassword = async (e: React.FormEvent) => { e.preventDefault(); if (regPassword !== regPassword.trim() || regPassword.length < 6) { setAuthError('A senha deve ter no mínimo 6 caracteres.'); return; } setAuthError(null); setAuthSuccess(null); setAuthLoading(true); try { const res = await fetch('/api/auth/reset-password', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ token: resetToken, newPassword: regPassword }) }); const data = await res.json(); if (!res.ok) throw new Error(data.error || 'Erro ao redefinir senha.'); setAuthSuccess(data.message || 'Senha redefinida com sucesso! Redirecionando...'); setTimeout(() => { window.location.href = '/'; }, 3000); } catch (err: any) { setAuthError(err.message); } finally { setAuthLoading(false); } }; const handleLogin = async (e: React.FormEvent) => { e.preventDefault(); setAuthError(null); setAuthLoading(true); try { const res = await fetch('/api/auth/login', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email: loginEmail, password: loginPassword }) }); const data = await res.json(); if (!res.ok) { throw new Error(data.error || 'Erro ao efetuar login'); } localStorage.setItem('app_token', data.token); setToken(data.token); setUser(data.user); setLoginEmail(''); setLoginPassword(''); } catch (err: any) { setAuthError(err.message); } finally { setAuthLoading(false); } }; const handleRegister = async (e: React.FormEvent) => { e.preventDefault(); setAuthError(null); setAuthSuccess(null); setAuthLoading(true); try { const res = await fetch('/api/auth/register', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ name: regName, email: regEmail, password: regPassword, cpf: regCpf, birthDate: regBirthDate, whatsapp: regWhatsapp }) }); const data = await res.json(); if (!res.ok) { throw new Error(data.error || 'Erro ao efetuar cadastro'); } localStorage.setItem('app_token', data.token); setToken(data.token); setUser(data.user); setRegName(''); setRegEmail(''); setRegPassword(''); } catch (err: any) { setAuthError(err.message); } finally { setAuthLoading(false); } }; const handleLogout = () => { localStorage.removeItem('app_token'); setToken(null); setUser(null); setView('home'); setSelectedCourseId(null); setExpandedCourseId(null); setSelectedLessonId(null); }; const handleSelectCourse = (courseId: string) => { const selected = courses.find(c => c.id === courseId); if (selected && (selected as any).isUnlocked === false) { setUnlockingCourse(selected); return; } if (expandedCourseId === courseId) { setExpandedCourseId(null); } else { setExpandedCourseId(courseId); // Smooth scroll to lesson shelf after brief render delay setTimeout(() => { const el = lessonShelfRefs.current[courseId]; if (el) { el.scrollIntoView({ behavior: 'smooth', block: 'center' }); } }, 80); } }; // Group courses by Category const categories = Array.from(new Set(courses.map(c => c.category))) as string[]; // Preset quick-fill logins for easy testing const quickFill = (email: string, pass: string) => { setLoginEmail(email); setLoginPassword(pass); setAuthError(null); }; // Check if active student is blocked (overdue or cancelled) const isBlocked = user?.subscriptionStatus === 'OVERDUE' || user?.subscriptionStatus === 'CANCELED'; return (
{token && user ? ( /* --- AUTHENTICATED AREA --- */ <> { setView(v); setSelectedCourseId(null); }} onLogout={handleLogout} settings={settings} />
{/* RENDER VIEW: HOME CATALOGUE */} {activeView === 'home' && (
{/* Hero Banner (Featured last course or first catalogue course) */} c.id === settings.featuredCourseId)) || (courses.length > 0 ? courses[0] : null) } customHeroTitle={settings?.heroTitle} customHeroDescription={settings?.heroDescription} customHeroImage={settings?.heroImageUrl} onPlay={(courseId) => { const selected = courses.find(c => c.id === courseId); if (selected && (selected as any).isUnlocked === false) { setUnlockingCourse(selected); return; } setSelectedCourseId(courseId); const firstLesson = selected?.lessons && selected.lessons.length > 0 ? selected.lessons[0].id : null; setSelectedLessonId(firstLesson); setView('course-view'); }} onDetail={handleSelectCourse} subscriptionStatus={user.subscriptionStatus} setView={setView} /> {/* Categories Carousels */}
{isLoadingCourses ? (
) : courses.length === 0 ? (
Nenhum curso disponível no momento.
) : ( categories.map(cat => { const categoryCourses = courses.filter(c => c.category === cat); const expandedCourse = categoryCourses.find(c => c.id === expandedCourseId); return (
setUnlockingCourse(course)} subscriptionStatus={user.subscriptionStatus} /> {/* Expanded Lessons Shelf */} {expandedCourse && expandedCourse.lessons && expandedCourse.lessons.length > 0 && (
{ lessonShelfRefs.current[expandedCourse.id] = el; }} className="lesson-shelf-enter bg-[#141414]/95 border border-white/5 rounded-2xl p-5 space-y-4 shadow-2xl relative overflow-hidden" > {/* Red accent left bar */}
{/* Header: course thumbnail + title + close */}
{/* Course miniature */} {expandedCourse.title}

Aulas do Curso: {expandedCourse.title}

{expandedCourse.lessons.length} aulas disponíveis

{(expandedCourse as any).studyMaterialUrl && ( Baixar Material (PDF) )}
{/* Lessons carousel - portrait cards */}
{expandedCourse.lessons.map(lesson => (
{ setSelectedCourseId(expandedCourse.id); setSelectedLessonId(lesson.id); setView('course-view'); }} className="flex-shrink-0 w-44 glass-card glass-card-hover rounded-xl overflow-hidden cursor-pointer snap-start transition-all duration-300 group/lesson flex flex-col" > {/* Portrait thumbnail */}
{lesson.thumbnailUrl ? ( {lesson.title} ) : (
Aula {lesson.order} {lesson.title}
)} {/* Play overlay */}
{/* Order badge */} #{lesson.order} {/* Progress Bar Overlay */} {(lesson as any).progress !== undefined && (
)}
{/* Info below */}

{lesson.title}

))}
)}
); }) )}
)} {/* RENDER VIEW: COURSE STUDY ROOM */} {activeView === 'course-view' && selectedCourseId && ( { setView('home'); setSelectedCourseId(null); setSelectedLessonId(null); fetchCourses(); }} /> )} {/* RENDER VIEW: ACCOUNT & BILLING / SUBSCRIPTION */} {activeView === 'subscription' && ( setUser(prev => prev ? { ...prev, subscriptionStatus: newStatus } : null)} /> )} {/* RENDER VIEW: CERTIFICATES */} {activeView === 'certificates' && ( )} {/* RENDER VIEW: PROFILE */} {activeView === 'profile' && ( setView('home')} onUpdateUser={(updated) => setUser(updated)} /> )} {/* RENDER VIEW: HELP */} {activeView === 'help' && ( setView('home')} /> )}
{/* Unlock Modal */} {unlockingCourse && ( setUnlockingCourse(null)} onSuccess={() => { setUnlockingCourse(null); fetchCourses(); fetchUserProfile(); }} token={token || ''} /> )} {/* Footer */}

{(settings?.brandName || 'Plataforma')}FLIX - Plataforma de Cursos Profissionalizantes

Desenvolvido com foco em alta experiência de estudo para Windows, Pacote Office (Word, Excel, PowerPoint) e informática profissionalizante.

{settings?.cnpj && (

CNPJ: {settings.cnpj} • Endereço: {settings.address}

)}

© 2026 {(settings?.brandName || 'Plataforma')}FLIX Inc. Todos os direitos reservados. Integrado oficialmente com a API Asaas.

{/* Info Modal for Terms and Privacy */} setInfoModal({...infoModal, isOpen: false})} title={infoModal.title} isGlass={true} >
{infoModal.content}
) : ( /* --- UNAUTHENTICATED PORTAL (LOGIN / REGISTER) --- */
{/* Login Container */}
{/* Logo */}

{(settings?.brandName || 'Plataforma')}FLIX

{settings?.brandSlogan || 'Acelere seus estudos em informática e pacote Office no estilo Netflix.'}

{authError && (
{authError}
)} {authSuccess && (
{authSuccess}
)} {resetToken ? ( /* RESET PASSWORD FORM */
setRegPassword(e.target.value)} placeholder="Mínimo 6 caracteres" className="w-full glass-input rounded-lg p-3 pl-10 pr-10 text-xs text-white focus:outline-none" />
) : isForgotPassword ? ( /* FORGOT PASSWORD FORM */
setLoginEmail(e.target.value)} placeholder="email@exemplo.com ou 5511999999999" className="w-full glass-input rounded-lg p-3 pl-10 text-xs text-white focus:outline-none" />

Lembrou a senha?{' '}

) : isRegister ? ( /* REGISTER FORM */
setRegName(e.target.value)} placeholder="Seu nome" className="w-full glass-input rounded-lg p-3 text-xs text-white focus:outline-none" />
setRegEmail(e.target.value)} placeholder="email@exemplo.com" className="w-full glass-input rounded-lg p-3 text-xs text-white focus:outline-none" />
setRegCpf(e.target.value)} placeholder="000.000.000-00" className="w-full glass-input rounded-lg p-3 text-xs text-white focus:outline-none" />
setRegBirthDate(e.target.value)} className="w-full glass-input rounded-lg p-3 text-xs text-white focus:outline-none" />
setRegWhatsapp(e.target.value)} placeholder="(11) 99999-9999" className="w-full glass-input rounded-lg p-3 text-xs text-white focus:outline-none" />
setRegPassword(e.target.value)} placeholder="Mínimo 6 caracteres" className="w-full glass-input rounded-lg p-3 pr-10 text-xs text-white focus:outline-none" />

Já possui cadastro?{' '}

) : ( /* LOGIN FORM */
setLoginEmail(e.target.value)} placeholder="email@exemplo.com ou 5585981145217" className="w-full glass-input rounded-lg p-3 pl-10 text-xs text-white focus:outline-none" />
setLoginPassword(e.target.value)} placeholder="Digite sua senha" className="w-full glass-input rounded-lg p-3 pl-10 pr-10 text-xs text-white focus:outline-none" />

Novo por aqui?{' '}

{/* QUICK PRESETS FOR SANDBOX DEMO */}

Acesso de Teste Rápido (Iframe Sandbox)

)}
)}
); }