import { useEffect, useRef, useState } from 'react'; import { useSearchParams } from 'react-router'; import { useClient } from 'hooks/useClient'; import { readableError } from '../errors'; import { SitePreview, useDevPort } from '../SitePreview'; import { usePages, type Page } from '../usePages'; /** * One page, being built. * * ── Why this is a different panel from Pages ── * * Pages is the LIST: what exists, what it is called, where it lives, in what order. This is one PAGE: * what is on it. They are different jobs and the nav already says so — Pages is a section and the pages * are rows underneath it — so the detail panel follows the same shape rather than showing a list with * one row highlighted. * * ── It renders the real page ── * * The preview is the site's own page at its own address, through the platform's dev proxy. Whatever * the blocks end up being, this is what they will be judged against, and it is already true today. */ export const PageBuilderPanel = () => { const client = useClient(); const clientRef = useRef(client); clientRef.current = client; const [params] = useSearchParams(); const id = Number(params.get('page')) || null; const devPort = useDevPort(); const { pages, announce } = usePages(); const [error, setError] = useState(null); /* The list, if nobody has announced it yet. Landing here directly — a bookmark, a reload, a URL typed with `?page=4` — means Pages has never rendered and the channel is empty. So this fetches once and announces, which also fills the nav. Arriving from the nav, `pages` is already there and nothing is requested. */ useEffect(() => { if (pages) return; void clientRef.current .get<{ data: Page[] }>('/fofos-admin/pages') .then((response) => announce(response.data)) .catch((failure: unknown) => setError(readableError(failure))); }, [pages, announce]); const page = pages?.find((row) => row.id === id) ?? null; if (error) return
{error}
; if (!pages) return
Loading…
; if (!page) { // A page that was deleted in another panel, or an id somebody typed. Said plainly rather than // redirected: a silent bounce to the list leaves you wondering whether you clicked the wrong thing. return
That page no longer exists.
; } return (

{page.title}

{page.path}
{/* The real page, at its own address. `path` has no leading slash — Home's is the empty string, which the proxy resolves to the site's root. */} {/* Where the blocks go. Said rather than left blank, because an empty panel under a working preview reads as something that failed to load. This is the next thing to build and the panel should say so. */}

The blocks that make up this page go here.

); };