// admin.jsx — internal operations console (cookie-auth, separate from the customer app). // Loaded from index.html as an ordered `text/babel` classic script (position 7 of 8). // Classic scripts share one global scope — see docs/engineering/Patterns.md §9. // ─── Admin Form Primitives ────────────────────────────────────────────────── // One copy of the field chrome — it used to be re-declared, identically, in // four separate sections. AdminField owns the label↔control wiring so a new // field cannot be added without one: the generated id lands on both halves, // and a child that carries its own `id` keeps it. const ADMIN_FIELD_STYLE = { width: "100%", padding: "9px 12px", background: "#0d0d0d", border: "1px solid #2a2a2a", borderRadius: 7, color: "#e8e8f0", fontSize: 13, boxSizing: "border-box", fontFamily: "'DM Sans', sans-serif" }; const ADMIN_SELECT_STYLE = { ...ADMIN_FIELD_STYLE, appearance: "none" }; const ADMIN_LABEL_STYLE = { display: "block", fontSize: 11, color: "#888", marginBottom: 5, textTransform: "uppercase", letterSpacing: "0.05em" }; function AdminField({ label, style, children }) { const generatedId = useId(); // Children.only so a future conditional child ({cond && }) fails with // React's own invariant rather than a bare "cannot read property of false". const control = React.Children.only(children); const id = control.props.id || generatedId; return (
{React.cloneElement(control, { id })}
); } // Sidebar entries and the overview's shortcut cards are )} ); } // ─── Admin Overview Section ────────────────────────────────────────────────── function AdminOverviewSection({ setSection, admin, adminApi }) { const [stats, setStats] = useState(null); useEffect(() => { (async () => { try { const [cust, qs, feed] = await Promise.all([ adminApi("/admin/customers"), fetch(`${API_BASE}/community/questions?limit=100`).then(r => { if (!r.ok) throw new Error(r.status); return r.json(); }), fetch(`${API_BASE}/community/feed?limit=1`).then(r => { if (!r.ok) throw new Error(r.status); return r.json(); }), ]); const customers = cust?.customers || []; const questions = qs?.questions || []; const faqs = questions.filter(q => q.category === "faq").length; const featureReqs = questions.filter(q => q.category === "feature_request").length; const active = customers.filter(c => c.subscription_status === "active" || c.subscription_status === "trialing" || !c.subscription_status).length; const latestFeed = (feed?.posts || [])[0] || null; setStats({ total: customers.length, active, faqs, featureReqs, totalQuestions: questions.length, latestFeed }); } catch(e) { console.error(e); } })(); }, [adminApi]); const cardStyle = { background: "#111", border: "1px solid #222", borderRadius: 12, padding: 20 }; // Same chrome as cardStyle, but as a real
{stats.active} Active Customers
{stats.latestFeed && ( )} )}

Quick Access

{[["Customers", "customers"], ["Community", "community"], ["Feed", "feed"]].map(([label, sec]) => ( ))}
); } // ─── Admin Community Section ───────────────────────────────────────────────── function AdminCommunitySection({ adminApi, load, data }) { const [faqOpen, setFaqOpen] = useState(false); const [frOpen, setFrOpen] = useState(false); const [faqTitle, setFaqTitle] = useState(""); const [answerQuestion, setAnswerQuestion] = useState(null); const [answerBody, setAnswerBody] = useState(""); const [answerSubmitting, setAnswerSubmitting] = useState(false); const [answerError, setAnswerError] = useState(""); const [faqBody, setFaqBody] = useState(""); const [frTitle, setFrTitle] = useState(""); const [frDesc, setFrDesc] = useState(""); const submitFaq = async () => { if (!faqTitle || !faqBody) return; await adminApi("/admin/community/questions", { method: "POST", body: JSON.stringify({ title: faqTitle, body: faqBody, category: "faq", pinned: true }) }); const qs = await fetch(`${API_BASE}/community/questions?category=faq&limit=1`).then(r => { if (!r.ok) throw new Error(r.status); return r.json(); }); if (qs.questions && qs.questions.length > 0) { await adminApi("/admin/community/answers", { method: "POST", body: JSON.stringify({ question_id: qs.questions[0].id, body: faqBody }) }); } setFaqTitle(""); setFaqBody(""); setFaqOpen(false); load("community"); }; const submitFr = async () => { if (!frTitle) return; await adminApi("/admin/community/features", { method: "POST", body: JSON.stringify({ title: frTitle, description: frDesc }) }); setFrTitle(""); setFrDesc(""); setFrOpen(false); load("community"); }; return (

Community Management

Post FAQs, answer questions, and manage feature requests.

{!faqOpen && !frOpen && } {!faqOpen && !frOpen && }
{faqOpen && (

New FAQ

setFaqTitle(e.target.value)} placeholder="How do I enroll a device?" />