// Shell: sidebar, topbar, command palette, toasts const { useState, useEffect, useRef, useMemo } = React; const NAV = [ { id:'dashboard', label:'Dashboard', icon:'BarChart' }, { id:'inventory', label:'Inventario', icon:'Box', badge:42 }, { id:'sales', label:'Ventas', icon:'Cart' }, { id:'forecasts', label:'Pronósticos', icon:'Trend' }, { id:'alerts', label:'Alertas', icon:'Bell', badge:7 }, { id:'reports', label:'Reportes', icon:'Doc' }, { id:'stores', label:'Tiendas', icon:'Store' }, { id:'products', label:'Productos', icon:'Tag' }, { id:'suppliers', label:'Proveedores', icon:'Truck' }, { id:'users', label:'Usuarios', icon:'Users' }, { id:'integrations', label:'Integraciones', icon:'Plug' }, { id:'settings', label:'Configuración', icon:'Cog' }, ]; function Sidebar({active, onNav, open, onClose}){ const I = window.Icons; return ( <>
); } function Topbar({active, onCmdK, onTheme, theme, onToggleCopilot, onAlerts, onMenuToggle}){ const I = window.Icons; const labels = { dashboard:'Dashboard', inventory:'Inventario', sales:'Ventas', forecasts:'Pronósticos', alerts:'Alertas', reports:'Reportes', stores:'Tiendas', products:'Productos', suppliers:'Proveedores', users:'Usuarios', integrations:'Integraciones', settings:'Configuración' }; return (
2026 {labels[active] || active}
Buscar productos, tiendas, SKUs... ⌘K
); } // Command palette (Cmd+K) function CmdPalette({open, onClose, onNav, onAction}){ const I = window.Icons; const [q, setQ] = useState(''); const [sel, setSel] = useState(0); const inputRef = useRef(null); useEffect(()=>{ if(open){ setQ(''); setSel(0); setTimeout(()=>inputRef.current?.focus(), 30); } }, [open]); const items = useMemo(()=>{ const navItems = NAV.map(n=>({type:'nav',id:n.id,label:n.label,icon:n.icon,group:'Navegar'})); const skuItems = window.FLUXData.skus.slice(0,6).map(s=>({type:'sku',id:s.sku,label:`${s.sku} — ${s.name}`,sub:s.category,icon:'Tag',group:'Productos'})); const storeItems = window.FLUXData.stores.slice(0,5).map(s=>({type:'store',id:s.id,label:s.name,sub:s.city,icon:'Store',group:'Tiendas'})); const actions = [ {type:'action',id:'export',label:'Exportar reporte mensual',icon:'Download',group:'Acciones'}, {type:'action',id:'sync',label:'Forzar sincronización ahora',icon:'Sync',group:'Acciones'}, {type:'action',id:'newsku',label:'Crear nuevo SKU',icon:'Plus',group:'Acciones'}, {type:'action',id:'invite',label:'Invitar usuario',icon:'Users',group:'Acciones'}, {type:'action',id:'copilot',label:'Abrir FLUX Copilot',icon:'Sparkles',group:'Acciones'}, ]; const all = [...navItems, ...actions, ...skuItems, ...storeItems]; if(!q) return all; const ql = q.toLowerCase(); return all.filter(i => i.label.toLowerCase().includes(ql) || (i.sub||'').toLowerCase().includes(ql)); }, [q]); const grouped = useMemo(()=>{ const m = {}; items.forEach(i=>{ (m[i.group] = m[i.group] || []).push(i); }); return m; }, [items]); useEffect(()=>{ if(!open) return; const onKey = (e)=>{ if(e.key==='Escape'){ onClose(); } else if(e.key==='ArrowDown'){ e.preventDefault(); setSel(s=>Math.min(s+1, items.length-1)); } else if(e.key==='ArrowUp'){ e.preventDefault(); setSel(s=>Math.max(s-1, 0)); } else if(e.key==='Enter'){ const it = items[sel]; if(!it) return; if(it.type==='nav') onNav(it.id); else onAction(it); onClose(); } }; window.addEventListener('keydown', onKey); return ()=> window.removeEventListener('keydown', onKey); },[open,items,sel]); if(!open) return null; let idx = -1; return (
e.stopPropagation()}>
{setQ(e.target.value);setSel(0)}} placeholder="Buscar o ejecutar comando..." /> esc
{Object.entries(grouped).map(([g,its])=>(
{g}
{its.map((it)=>{ idx++; const myIdx = idx; const Ic = I[it.icon] || I.Search; return (
setSel(myIdx)} onClick={()=>{ if(it.type==='nav') onNav(it.id); else onAction(it); onClose(); }}> {it.label} {it.sub && {it.sub}} {it.type==='nav'?'Ir a':it.type==='action'?'Ejecutar':'Abrir'}
); })}
))} {items.length===0 &&
Sin resultados
}
); } // Toast manager function useToasts(){ const [toasts, setToasts] = useState([]); const push = (msg) => { const id = Math.random(); setToasts(t=>[...t,{id,msg}]); setTimeout(()=> setToasts(t=>t.filter(x=>x.id!==id)), 2800); }; return { toasts, push }; } function Toasts({toasts}){ const I = window.Icons; return (
{toasts.map(t=>(
{t.msg}
))}
); } Object.assign(window, { Sidebar, Topbar, CmdPalette, Toasts, useToasts, NAV });