/* global React, ReactDOM */ // ======================================================================= // BlazeConnector Admin v3 — App root // ======================================================================= const { useState: as_, useEffect: ae_, useMemo: am_, useCallback: ac_ } = React; function App() { // Tweaks (persisted) const TWEAKS = window.useTweaks(/*EDITMODE-BEGIN*/{ "theme": "dark", "role": "superadmin", "density": "cozy", "accent": "#FF5A1F", "systemHealth": "healthy" }/*EDITMODE-END*/); const t = TWEAKS[0]; const setTweak = TWEAKS[1]; // Suscribe la App al store live → re-render cuando llegan datos reales. window.useBC(); // Al arrancar: si hay config persistida, reconecta al backend real. ae_(() => { if (window.BC.isConfigured()) window.BC.reconnect(); }, []); // Apply theme + accent globally ae_(() => { document.documentElement.dataset.theme = t.theme; document.documentElement.style.setProperty('--blaze-orange', t.accent); document.documentElement.style.setProperty('--accent', t.accent); }, [t.theme, t.accent]); // Routing (hash) const [path, setPath] = as_(window.location.hash.replace('#', '') || '/dashboard'); ae_(() => { const h = () => setPath(window.location.hash.replace('#', '') || '/dashboard'); window.addEventListener('hashchange', h); return () => window.removeEventListener('hashchange', h); }, []); const navigate = ac_((to) => { window.location.hash = to; }, []); // Tenant scope const [tenant, setTenant] = as_({ id: '*', name: 'Todos los tenants' }); // ⌘K const [cmdkOpen, setCmdkOpen] = as_(false); ae_(() => { const fn = (e) => { if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === 'k') { e.preventDefault(); setCmdkOpen((o) => !o); } if ((e.metaKey || e.ctrlKey) && e.shiftKey && e.key.toLowerCase() === 'l') { e.preventDefault(); setTweak('theme', t.theme === 'dark' ? 'light' : 'dark'); } }; window.addEventListener('keydown', fn); return () => window.removeEventListener('keydown', fn); }, [t.theme, setTweak]); // SSE simulation const [realtime, setRealtime] = as_({ status: 'connected', rate: 14.2 }); ae_(() => { const id = setInterval(() => { setRealtime((r) => ({ status: t.systemHealth === 'outage' ? 'down' : t.systemHealth === 'degraded' ? 'degraded' : 'connected', rate: t.systemHealth === 'outage' ? 0 : t.systemHealth === 'degraded' ? Math.max(2, 8 + Math.sin(Date.now() / 4000) * 3) : 12 + Math.sin(Date.now() / 4000) * 3 + Math.random() * 1.5 })); }, 1500); return () => clearInterval(id); }, [t.systemHealth]); // Resolve route const route = am_(() => { if (path === '/' || path === '/dashboard') return { page: 'dashboard', crumb: ['Dashboard'] }; if (path.startsWith('/messaging')) return { page: 'messaging', crumb: ['Operaciones', 'Mensajería'] }; if (path.startsWith('/queues')) return { page: 'queues', crumb: ['Operaciones', 'Colas'] }; if (path.startsWith('/logs')) return { page: 'logs', crumb: ['Operaciones', 'Logs'] }; if (path.startsWith('/alerts')) return { page: 'alerts', crumb: ['Operaciones', 'Alertas'] }; if (path.startsWith('/tenants/')) { const id = path.split('/')[2]; const tnt = window.TENANTS.find(x => x.id === id); return { page: 'tenant_detail', tenantId: id, crumb: ['Administración', 'Tenants', tnt ? tnt.name : id] }; } if (path.startsWith('/tenants')) return { page: 'tenants', crumb: ['Administración', 'Tenants'] }; if (path.startsWith('/apikeys')) return { page: 'apikeys', crumb: ['Administración', 'API Keys'] }; if (path.startsWith('/templates')) return { page: 'templates', crumb: ['Administración', 'Templates'] }; if (path.startsWith('/audit')) return { page: 'audit', crumb: ['Administración', 'Auditoría'] }; if (path.startsWith('/isps')) return { page: 'isps', crumb: ['Integraciones', 'ISP Providers'] }; if (path.startsWith('/pagos')) return { page: 'pagos', crumb: ['Integraciones', 'Pagos'] }; return { page: 'dashboard', crumb: ['Dashboard'] }; }, [path]); const sidebarBadges = { alerts: { value: window.ALERTS.filter(a => !a.ack).length, tone: 'is-warn' }, messaging: { value: '1.2k' } }; return (