// GuinchoFlow — Layout (Sidebar + Topbar) const NAV_ITEMS = [ { id: 'dashboard', label: 'Dashboard', icon: 'home' }, { id: 'assistencias', label: 'Assistências', icon: 'truck' }, { id: 'vistoria', label: 'Vistoria Digital', icon: 'search' }, { id: 'frota', label: 'Frota de Guinchos',icon: 'wrench' }, { id: 'motoristas', label: 'Motoristas', icon: 'users' }, { id: 'seguradoras', label: 'Seguradoras', icon: 'shield' }, { id: 'financeiro', label: 'Financeiro', icon: 'dollar' }, { id: 'relatorios', label: 'Relatórios', icon: 'fileText' }, { id: 'configuracoes',label: 'Configurações', icon: 'settings' }, ]; const Sidebar = ({ active, onNav, collapsed, onToggle, session, onLogout }) => { const [hov, setHov] = React.useState(null); const [userInfo, setUserInfo] = React.useState(null); React.useEffect(() => { if (!session?.user) { setUserInfo(null); return; } db.one('usuarios', session.user.id).then(u => setUserInfo(u)); }, [session?.user?.id]); const userNome = userInfo?.nome || session?.user?.email?.split('@')[0] || 'Usuário'; const userCargo = userInfo?.cargo || '—'; const userInits = userNome.split(' ').map(w => w[0]).join('').slice(0, 2).toUpperCase(); return ( ); }; const BREADCRUMBS = { dashboard: ['Dashboard'], assistencias: ['Assistências'], vistoria: ['Vistoria Digital'], frota: ['Frota de Guinchos'], motoristas: ['Cadastros', 'Motoristas'], seguradoras: ['Cadastros', 'Seguradoras'], financeiro: ['Financeiro'], relatorios: ['Relatórios'], configuracoes: ['Configurações'], }; const Topbar = ({ active, onMenuToggle, isMobile, onNav, session }) => { const [searchOpen, setSearchOpen] = React.useState(false); const [searchVal, setSearchVal] = React.useState(''); const [notifOpen, setNotifOpen] = React.useState(false); const [alerts, setAlerts] = React.useState([]); const crumbs = BREADCRUMBS[active] || ['Dashboard']; React.useEffect(() => { if (!notifOpen) return; (async () => { const abertas = await db.list('assistencias', { eq: { status: 'aberta' } }); const manut = await db.list('frota', { eq: { status: 'manutencao' } }); const list = [ ...abertas.slice(0, 3).map(a => ({ id: 'a' + a.id, type: 'danger', msg: `Assistência ${a.numero || '#' + a.id} aberta`, link: 'assistencias' })), ...manut.slice(0, 2).map(g => ({ id: 'f' + g.id, type: 'warning', msg: `Guincho ${g.code} (${g.placa}) em manutenção`, link: 'frota' })), ]; setAlerts(list); })(); }, [notifOpen]); const userEmail = session?.user?.email || ''; const userInits = userEmail.split('@')[0].slice(0, 2).toUpperCase() || 'U'; const userShort = userEmail.split('@')[0]; return (
{isMobile && ( )} {/* Breadcrumb */}
{crumbs.map((c, i) => ( {i > 0 && } {c} ))}
{/* Search */}
{searchOpen ? ( setSearchVal(e.target.value)} onBlur={() => { setSearchOpen(false); setSearchVal(''); }} placeholder="Buscar placa, ID, cliente... (Ctrl+K)" style={{ width: 280, padding: '7px 12px', borderRadius: 8, fontSize: 13, border: '1px solid #3B82F6', outline: 'none', color: '#0F172A', fontFamily: 'Inter, sans-serif', boxShadow: '0 0 0 3px rgba(59,130,246,0.12)' }} /> ) : ( )}
{/* Badge global de lembretes da frota */} onNav && onNav('lembretes')} /> {/* Notif */}
{notifOpen && (
Notificações {alerts.length > 0 && {alerts.length}}
{alerts.length === 0 ? (
Nenhum alerta no momento
) : alerts.map(a => (
{ setNotifOpen(false); onNav && onNav(a.link); }} style={{ padding: '12px 16px', borderBottom: '1px solid #F8FAFC', display: 'flex', gap: 10, alignItems: 'flex-start', cursor: 'pointer', transition: 'background 0.15s' }} onMouseEnter={e => e.currentTarget.style.background = '#FFF7ED'} onMouseLeave={e => e.currentTarget.style.background = ''}> {a.msg}
))}
)}
{/* User */}
{!isMobile && {userShort}}
); }; // Mobile overlay const SidebarOverlay = ({ open, onClose, active, onNav, session, onLogout }) => { if (!open) return null; return (
e.stopPropagation()}> { onNav(id); onClose(); }} collapsed={false} session={session} onLogout={onLogout} />
); }; Object.assign(window, { Sidebar, Topbar, SidebarOverlay, NAV_ITEMS });