import { useState } from "react"; /* ═══════════════════════════════════════════════════════════════ SECTION MANIFEST A page is a list of sections, and a section is a view over data the database already holds, narrowed by a filter. The map of chapters and the vertical list of regions are the same rows seen two ways; the three bands of retreats are one view seen three times with a different filter each. So a page declares what it wants and this turns it into what PageShell takes. Every entry looks like: { id, title, blurb, accent, background, // the heading Component, // the view props: { ... }, // the filter views: { // optional toggle options: ["carousel", "grid"], default: "carousel", Toggle: EventCardsToggle, }, } Sections fetch their own data. The page never does, which is why there's no loading or error state here — each view handles its own, and one section failing doesn't blank the page. Every Component receives `accent` so its empty and error notices match the heading above them, and `view` when the entry declares a toggle. Both are ignored harmlessly by a section that doesn't want them. ═══════════════════════════════════════════════════════════════ */ export function useSectionManifest(manifest) { // One entry per section that has a toggle, seeded from its // declared default so the control is right before anything loads. const [views, setViews] = useState(() => Object.fromEntries( manifest .filter(entry => entry.views) .map(entry => [ entry.id, entry.views.default ?? entry.views.options?.[0] ?? null, ]), ), ); const setView = (id, value) => setViews(prev => ({ ...prev, [id]: value })); return manifest.map(entry => { const { Component, props, views: spec, ...heading } = entry; const view = views[entry.id]; const Toggle = spec?.Toggle; return { ...heading, actions: Toggle ? ( setView(entry.id, value)} accent={entry.accent} options={spec.options} /> ) : undefined, content: ( ), }; }); }