diff --git a/web/NavPanel.tsx b/web/NavPanel.tsx index bb6e97f..4841208 100644 --- a/web/NavPanel.tsx +++ b/web/NavPanel.tsx @@ -1,9 +1,11 @@ +import { useEffect, useRef } from 'react'; import { Link } from 'react-router'; +import { useClient } from 'hooks/useClient'; import { Lock } from 'lucide-react'; -import { LAYOUT_BANDS, SECTIONS, sectionOpen } from './sections'; +import { SECTIONS, sectionOpen } from './sections'; import { href, useNav } from './routes'; import { useReady } from './useReady'; -import { pageParam, usePages } from './usePages'; +import { pageParam, usePages, type Page } from './usePages'; /** * The left column: which part of the fofos site the detail panel is showing. @@ -50,12 +52,34 @@ const rowClass = (active: boolean) => }`; export const NavPanel = () => { - // Announced by PagesPanel rather than fetched here — see usePages. Null until it has spoken, which - // is why nothing is drawn instead of an empty list. - const { pages } = usePages(); + const { pages, announce } = usePages(); const { section: active, item } = useNav(); const { ready, rootSet } = useReady(); + /* + The pages, fetched HERE when nobody has said yet. + + They were announced by PagesPanel and only by PagesPanel — which mounts when Pages is the open + section, so the site's pages were invisible in the nav until you had already been to the page that + lists them. The one place they most need to be is the one place they were not. + + The channel stays and is still the reason the nav renames a page the moment the editor does: two + fetchers would agree until somebody typed. This only fills a silence. + */ + const client = useClient(); + const clientRef = useRef(client); + clientRef.current = client; + + useEffect(() => { + if (pages !== null || !ready) return; + void clientRef.current + .get<{ data: Page[] }>('/fofos-admin/pages') + .then((body) => announce(body.data)) + // Left null: the nav then draws no children, which is what it did before and is not a failure + // worth a message in a list of links. + .catch(() => {}); + }, [pages, ready, announce]); + const renderRow = (section: (typeof SECTIONS)[number]) => { const Icon = section.icon; // Each section asks its own question now — Media wants a root location, the site's pages want a @@ -118,42 +142,6 @@ export const NavPanel = () => { Drawn whether or not Pages is the open section. The list of pages IS the site, and hiding it until you click makes the nav a menu of menus. */ - /* - Layout's three bands, as rows under Layout. - - The layout IS a page — a header, the slot a page goes in, and a footer — and those three are the - only things on this screen. They were reachable by scrolling and by knowing which tab held what; - as rows they are the same shape as the pages under Pages, which is the shape the nav already - teaches. - - Fixed rather than fetched: unlike pages, these three are structural. A band cannot be added or - removed from the admin, only the code that draws it can, so a list read from the database would - be a query that can only ever return the same three rows. - */ - if (section.key === 'layout') { - return ( -
- {row} - {LAYOUT_BANDS.map((band) => { - const current = active === 'layout' && item === band.key; - - return ( - - {band.label} - - ); - })} -
- ); - } - if (section.key !== 'pages' || !pages?.length) return row; return ( diff --git a/web/sections.ts b/web/sections.ts index 7e30292..13a58d4 100644 --- a/web/sections.ts +++ b/web/sections.ts @@ -70,27 +70,13 @@ export type Section = { pinned?: boolean; }; -/** - * The three bands the layout is made of, in the order they appear on the page. - * - * The layout IS a page: a header, the slot a page goes in, and a footer. These are the nav rows under - * Layout, and the `band` in the query string. - * - * FIXED, unlike the pages under Pages. A band cannot be added or removed from the admin — only the - * code that draws it can — so reading them from the database would be a query that can only ever - * return the same three rows in the same order. - */ -export const LAYOUT_BANDS = [ - { key: 'header', label: 'Top Menu' }, - { key: 'content', label: 'Content' }, - { key: 'footer', label: 'Footer' }, -] as const; - -export type LayoutBand = (typeof LAYOUT_BANDS)[number]['key']; - -export const isLayoutBand = (value: string | null): value is LayoutBand => - value !== null && LAYOUT_BANDS.some((band) => band.key === value); +/* + Layout had three rows under it — Top Menu, Content, Footer — and they are gone. + They were a hierarchy the screen did not have: all three are edited in the same panel, by the same + controls, at the same time. Clicking one changed the URL and nothing else, which teaches that the + nav does not mean anything. Pages keeps its children because a page IS a destination. +*/ export const SECTIONS: Section[] = [ { /*