// /admin.html → WEBSITE tab. Per-product operational state editor backed by
// /api/admin/product-states. Writes propagate to every visitor immediately
// because the catalog/spotlight/modal/status page all read from the same
// /api/site/product-states feed via window.ExecStates.
(function () {
const { useState, useEffect, useMemo, useCallback } = React;
const STATE_OPTIONS = [
{ id: 'online', label: 'ONLINE' },
{ id: 'updating', label: 'UPDATING' },
{ id: 'offline', label: 'OFFLINE' },
];
function AdminSiteApp() {
// Re-render whenever ExecStates emits.
window.ExecStates && window.ExecStates.useStates();
window.ExecCatalog && window.ExecCatalog.useCatalog();
window.ExecCategories && window.ExecCategories.useCategories();
const products = window.ExecCatalog ? window.ExecCatalog.list() : [];
const counts = window.ExecStates
? window.ExecStates.counts(products)
: { online: 0, offline: 0, updating: 0, total: products.length };
const [category, setCategory] = useState('all');
const categories = useMemo(() => {
const counts = {};
for (const p of products) counts[p.category] = (counts[p.category] || 0) + 1;
const live = window.ExecCategories ? window.ExecCategories.list() : [];
return [
{ id: 'all', label: 'ALL', count: products.length },
...live.map((c) => ({ ...c, count: counts[c.id] || 0 })),
];
}, [products]);
const filtered = category === 'all' ? products : products.filter((p) => p.category === category);
return (
<>