/**
* AdminSurface — root admin surface
*
* Reads globals:
* __SECTION__ — active section name (string)
* __BASE__ — base path
* __CONFIG_SECTIONS__
*
* Layout: topbar (back + category tabs) + body (sidebar nav + content area).
* All 24+ sections are native Preact components loaded lazily.
*/
const { html } = window;
const { useState, useEffect, useCallback, useMemo } = hooks;
const { render } = preact;
import { ToastContainer } from '../../primitives/toast.js';
import { DialogStack } from '../../shell/dialog-stack.js';
import { UserMenu } from '../../shell/user-menu.js';
// ── Category → Section mapping ──────────────
const ADMIN_SECTIONS = {
people: ['users', 'teams', 'groups'],
workflows: ['workflows'],
system: ['settings', 'storage', 'packages', 'connections', 'broadcast', 'backup'],
monitoring: ['audit'],
};
const ADMIN_LABELS = {
users: 'Users', teams: 'Teams', groups: 'Groups',
workflows: 'Workflows',
settings: 'Settings', storage: 'Storage', packages: 'Packages',
connections: 'Connections', broadcast: 'Broadcast',
backup: 'Backup',
audit: 'Audit',
};
// ── Extension config sections ──────
// Packages declare config_section in their manifest targeting "admin".
// We merge them into the appropriate category and section module map.
const _configSections = window.__CONFIG_SECTIONS__ || [];
const _base = window.__BASE__ || '';
for (const cs of _configSections) {
const pkgId = cs.package_id;
const cat = cs.category || 'system';
// Add to category (create if new — unlikely but safe)
if (!ADMIN_SECTIONS[cat]) ADMIN_SECTIONS[cat] = [];
if (!ADMIN_SECTIONS[cat].includes(pkgId)) ADMIN_SECTIONS[cat].push(pkgId);
ADMIN_LABELS[pkgId] = cs.label;
}
const CATEGORY_META = {
people: { label: 'People', first: 'users', icon: 'M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2|C9 7 4' },
workflows: { label: 'Workflows', first: 'workflows', icon: 'P16 3 21 3 21 8|L4 20 21 3|P21 16 21 21 16 21|L15 15 21 21|L4 4 9 9' },
system: { label: 'System', first: 'settings', icon: 'C12 12 3|M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09' },
monitoring: { label: 'Monitoring', first: 'audit', icon: 'L18 20 18 10|L12 20 12 4|L6 20 6 14' },
};
// ── Lazy section imports ────────────────────
// Version query busts browser module cache on upgrades.
const _v = window.__VERSION__ ? `?v=${window.__VERSION__}` : '';
const sectionModules = {
users: () => import(`./users.js${_v}`),
teams: () => import(`./teams.js${_v}`),
groups: () => import(`./groups.js${_v}`),
workflows: () => import(`./workflows.js${_v}`),
settings: () => import(`./settings.js${_v}`),
storage: () => import(`./storage.js${_v}`),
packages: () => import(`./packages.js${_v}`),
connections: () => import(`./connections.js${_v}`),
broadcast: () => import(`./broadcast.js${_v}`),
backup: () => import(`./backup.js${_v}`),
audit: () => import(`./audit.js${_v}`),
};
for (const cs of _configSections) {
const pkgId = cs.package_id;
const component = cs.component || 'js/config.js';
sectionModules[pkgId] = () => import(`${_base}/surfaces/${pkgId}/${component}`);
}
// ── Derive category from section ────────────
function catForSection(section) {
for (const [cat, sections] of Object.entries(ADMIN_SECTIONS)) {
if (sections.includes(section)) return cat;
}
return 'people';
}
// ── Simple SVG icon renderer ────────────────
function CatIcon({ paths }) {
// paths is a compact format: pipe-separated draw commands
// C = circle, L = line, P = polyline, R = rect, G = polygon, M = path
const els = paths.split('|').map((p, i) => {
const t = p[0], d = p.slice(1).trim();
if (t === 'C') { const [cx, cy, r] = d.split(' '); return html`