/* 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 (
setTweak('role', r)} theme={t.theme} onThemeToggle={() => setTweak('theme', t.theme === 'dark' ? 'light' : 'dark')} realtime={realtime} onOpenCmdK={() => setCmdkOpen(true)} />
{route.page === 'dashboard' && } {route.page === 'messaging' && } {route.page === 'queues' && } {route.page === 'logs' && } {route.page === 'alerts' && } {route.page === 'tenants' && navigate('/tenants/' + id)} />} {route.page === 'tenant_detail' && navigate('/tenants')} />} {route.page === 'apikeys' && } {route.page === 'templates' && } {route.page === 'audit' && } {route.page === 'isps' && } {route.page === 'pagos' && }
setCmdkOpen(false)} onNav={navigate} /> {/* Tweaks panel */} setTweak('theme', v)} options={[{ value: 'dark', label: 'Dark' }, { value: 'light', label: 'Light' }]} /> setTweak('accent', v)} options={['#FF5A1F', '#F000D8', '#7A3CFF', '#1E7BFF', '#2ECC5A']} /> setTweak('density', v)} options={[{ value: 'compact', label: 'Compact' }, { value: 'cozy', label: 'Cozy' }, { value: 'comfortable', label: 'Roomy' }]} /> setTweak('role', v)} options={[ { value: 'superadmin', label: 'Superadmin — acceso total' }, { value: 'operaciones', label: 'Operaciones — CRUD + retries' }, { value: 'soporte', label: 'Soporte — read + retries' }, { value: 'readonly', label: 'Read-only' } ]} /> setTweak('systemHealth', v)} options={[ { value: 'healthy', label: 'Healthy' }, { value: 'degraded', label: 'Degraded' }, { value: 'outage', label: 'Outage' } ]} /> {/* Gate de conexión al backend real (se auto-oculta al conectar). */}
); } ReactDOM.createRoot(document.getElementById('root')).render();