/* global React, ReactDOM, Icon, Avatar, LoginScreen, SignupScreen, RecoverScreen, Dashboard, Jornada, LessonPlayer, Materiais, LivesScreen, Comunidade, Perfil, AdminPanel, TweaksPanel, useTweaks, TweakSection, TweakRadio, TweakSelect, TweakColor, TweakToggle, useHashRoute, useDB */ const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{ "navLayout": "sidebar", "cardStyle": "default", "fontHeading": "Archivo", "fontBody": "Manrope", "theme": "dark", "accent": "#2a6cff" }/*EDITMODE-END*/; const FONT_HEADING_OPTIONS = ['Archivo', 'Archivo Black', 'Manrope', 'Inter', 'Playfair Display', 'IBM Plex Serif']; const FONT_BODY_OPTIONS = ['Manrope', 'Inter', 'IBM Plex Sans', 'Source Sans 3', 'Archivo']; function App() { useDB(); // re-render on DB change const session = DB.getSession(); const [user, setUser] = React.useState(session?.user || null); const { hash, nav } = useHashRoute(); const [tweaks, setTweak] = useTweaks(TWEAK_DEFAULTS); // Apply tweaks to root React.useEffect(() => { const r = document.documentElement; r.setAttribute('data-theme', tweaks.theme); r.setAttribute('data-card-style', tweaks.cardStyle); r.style.setProperty('--font-heading', `'${tweaks.fontHeading}'`); r.style.setProperty('--font-body', `'${tweaks.fontBody}'`); r.style.setProperty('--blue', tweaks.accent); }, [tweaks.theme, tweaks.cardStyle, tweaks.fontHeading, tweaks.fontBody, tweaks.accent]); // Load fonts dynamically when changed React.useEffect(() => { const families = new Set([tweaks.fontHeading, tweaks.fontBody, 'Archivo', 'Manrope', 'Archivo Black']); const id = 'dynamic-fonts'; let link = document.getElementById(id); const familyParam = [...families].map(f => `family=${f.replace(/ /g, '+')}:wght@300;400;500;600;700;800;900`).join('&'); const href = `https://fonts.googleapis.com/css2?${familyParam}&display=swap`; if (!link) { link = document.createElement('link'); link.id = id; link.rel = 'stylesheet'; document.head.appendChild(link); } link.href = href; }, [tweaks.fontHeading, tweaks.fontBody]); // ---- public legal docs (accessible w/ or w/o session) ---- // (returns moved below — must come after all useEffect calls) // ---- auth: redirect when no session ---- React.useEffect(() => { if (!user) { const ok = ['/login', '/cadastro', '/recuperar', '/termos', '/privacidade']; if (!ok.some(p => hash.startsWith(p))) nav('/login'); } else { if (hash === '/login' || hash === '/cadastro' || hash === '/recuperar' || hash === '/') { nav('/dashboard'); } } }, [user, hash, nav]); // ---- public legal docs (must come AFTER all useEffect calls — hooks must run unconditionally) ---- if (hash.startsWith('/termos')) { return ; } if (hash.startsWith('/privacidade')) { return ; } const onLogin = (u) => { setUser(u); nav('/dashboard'); }; const onLogout = () => { DB.logout(); setUser(null); nav('/login'); }; // ---- routes ---- if (!user) { if (hash.startsWith('/cadastro')) return ; if (hash.startsWith('/recuperar')) return ; return ; } // refresh user from DB (e.g. after profile edit) const liveUser = DB.userById(user.id) || user; let screen = null; if (hash.startsWith('/jornada/')) { const slug = hash.split('/jornada/')[1].split('/')[0]; screen = ; } else if (hash.startsWith('/jornada')) { screen = ; } else if (hash.startsWith('/aula/')) { const id = hash.split('/aula/')[1]; screen = ; } else if (hash.startsWith('/materiais')) { screen = ; } else if (hash.startsWith('/lives')) { screen = ; } else if (hash.startsWith('/comunidade')) { screen = ; } else if (hash.startsWith('/perfil')) { screen = ; } else if (hash.startsWith('/admin')) { if (liveUser.role !== 'admin') { nav('/dashboard'); return null; } screen = ; } else { screen = ; } const isTopbar = tweaks.navLayout === 'topbar'; return ( <>
{isTopbar ? : } {screen}
setTweak('theme', v)} options={[{value:'dark',label:'Escuro'},{value:'light',label:'Claro'}]}/> setTweak('accent', v)} options={['#2a6cff', '#3d82ff', '#d4a55a', '#e85751', '#38b676', '#9a4ddf']}/> setTweak('cardStyle', v)} options={['default', 'flat', 'glass', 'bordered']}/> setTweak('navLayout', v)} options={[{value:'sidebar',label:'Sidebar'},{value:'topbar',label:'Top bar'}]}/> setTweak('fontHeading', v)} options={FONT_HEADING_OPTIONS}/> setTweak('fontBody', v)} options={FONT_BODY_OPTIONS}/> ); } // ----- Sidebar nav ----- const NAV_ITEMS = [ { path: '/dashboard', label: 'Dashboard', icon: 'home' }, { path: '/jornada', label: 'Jornada', icon: 'journey' }, { path: '/lives', label: 'Lives', icon: 'video' }, { path: '/materiais', label: 'Materiais', icon: 'file' }, { path: '/comunidade', label: 'Comunidade', icon: 'chat' } ]; function Sidebar({ user, hash, nav, onLogout }) { const isActive = (path) => hash === path || (path !== '/dashboard' && hash.startsWith(path)); return ( ); } function TopBar({ user, hash, nav, onLogout }) { const isActive = (path) => hash === path || (path !== '/dashboard' && hash.startsWith(path)); return (
Edson Lima
nav('/perfil')} style={{ cursor:'pointer' }}>
{user.name.split(' ')[0]}
{user.role === 'admin' ? 'Admin' : 'Mentorado'}
); } const root = ReactDOM.createRoot(document.getElementById('root')); root.render();