/* JRN — App shell, navegação, controle de acesso e Tweaks */ const { useState: uS, useEffect } = React; const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{ "companyInteraction": "click", "sidebar": "navy", "showBrandBar": true }/*EDITMODE-END*/; /* Itens de navegação administrativos e a função que cada um exige */ const ADMIN_NAV = [ { id: 'admin-dashboards', label: 'Dashboards', icon: 'chart', fn: 'dash_cadastro' }, { id: 'admin-users', label: 'Usuários', icon: 'users', fn: 'user_edicao' }, { id: 'admin-companies', label: 'Empresas', icon: 'building', fn: null }, { id: 'admin-audit', label: 'Auditoria', icon: 'history', fn: 'auditoria' }, ]; function canSee(user, fn) { if (!user) return false; if (user.perfil === 'admin') return true; if (!fn) return false; const fns = user.funcoes || []; return fns.includes('*') || fns.includes(fn); } function Sidebar({ user, view, setView, tweaks, collapsed, onToggle, onLogout }) { const dark = tweaks.sidebar === 'navy'; const [userOpen, setUserOpen] = useState(false); const adminItems = ADMIN_NAV.filter(i => canSee(user, i.fn)); const C = { bg: dark ? 'linear-gradient(180deg,#15233f,#0d1730)' : '#fff', fg: dark ? '#fff' : 'var(--ink)', muted: dark ? '#8ea0c4' : 'var(--muted)', line: dark ? 'rgba(255,255,255,.1)' : 'var(--line)', activeBg: dark ? 'rgba(255,255,255,.12)' : 'var(--navy-100)', activeFg: dark ? '#fff' : 'var(--navy-800)', hover: dark ? 'rgba(255,255,255,.06)' : 'var(--navy-50)', }; const NavItem = ({ id, label, icon }) => { const I = Icons[icon]; const active = view === id || (id === 'portal' && (view === 'portal' || view === 'dashboard')); return ( ); }; return ( ); } function Topbar({ user, onLogout }) { return (
); } /* Tela de carregamento enquanto verifica a sessão */ function LoadingScreen() { return (
JRN
); } function App() { const [t, setTweak] = useTweaks(TWEAK_DEFAULTS); const [session, setSession] = useState(null); const [loadingSession, setLoadingSession] = useState(true); const [empresas, setEmpresas] = useState([]); // empresas visíveis ao usuário logado const [view, setView] = useState('portal'); const [collapsed, setCollapsed] = useState(() => localStorage.getItem('jrn-sidebar') === '1'); const toggleSidebar = () => setCollapsed(c => { localStorage.setItem('jrn-sidebar', c ? '0' : '1'); return !c; }); const [openDash, setOpenDash] = useState(null); const [toastNode, toast] = useToast(); // Verifica sessão existente ao carregar a página useEffect(() => { apiMe() .then(({ user, empresas: emp }) => { setSession(user); setEmpresas(emp); }) .catch(() => {}) .finally(() => setLoadingSession(false)); }, []); // Chamado após apiLogin() bem-sucedido const doLogin = async () => { const { user, empresas: emp } = await apiMe(); setSession(user); setEmpresas(emp); setView('portal'); setOpenDash(null); }; const logout = async () => { try { await apiLogout(); } catch (_) {} setSession(null); setEmpresas([]); setView('portal'); setOpenDash(null); }; if (loadingSession) return ; if (!session) return ( ); let content; if (view === 'dashboard' && openDash) { content = { setView('portal'); setOpenDash(null); }} />; } else if (view === 'admin-users' && canSee(session, 'user_edicao')) { content = ; } else if (view === 'admin-dashboards' && canSee(session, 'dash_cadastro')) { content = ; } else if (view === 'admin-companies' && session.perfil === 'admin') { content = ; } else if (view === 'admin-audit' && canSee(session, 'auditoria')) { content = ; } else { content = { setOpenDash(d); setView('dashboard'); }} />; } return (
{ setView(v); setOpenDash(null); }} tweaks={t} collapsed={collapsed} onToggle={toggleSidebar} onLogout={logout} />
{content}
{toastNode}
); } function TweaksHost({ t, setTweak }) { return ( setTweak('companyInteraction', v)} /> setTweak('sidebar', v)} /> ); } ReactDOM.createRoot(document.getElementById('root')).render();