// 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 });