import type { ReactNode } from "react"; import type { Metadata } from "next"; import Link from "next/link"; import { notFound } from "next/navigation"; import { SiteTabs } from "./components/SiteTabs"; import { getSiteCached } from "./lib/siteCache"; // Shared chrome for every route of one site: the breadcrumb, the site's title // and the Settings | Charts | Search aliases | Tags | Reports | Publish switch. // A layout now that the site has more than one page — Charts, Search aliases // and Publish were three sidebar entries reading the site from a ?site= param. // // Next 16 caveats this file is written around (see // node_modules/next/dist/docs/01-app/03-api-reference/03-file-conventions/layout.md): // // - LAYOUTS DO NOT RERENDER on navigation and cannot read searchParams or the // pathname. So the active-tab check lives in a client component (SiteTabs). // - NO loading.tsx HERE. In this app it turns notFound() 404s into 200s and // never shows on a client-side sibling navigation anyway. The notFound() // below is the one 404 for /sites/ and every tab under it. // // force-dynamic for the same reason page.tsx carries it: this reads site.json // off disk on every request and must never be statically prerendered. export const dynamic = "force-dynamic"; export async function generateMetadata({ params, }: { params: Promise<{ siteId: string }>; }): Promise { const { siteId } = await params; // The index page (the settings form) exports no title and gets `default`; // each tab exports a bare title and gets the template // (generate-metadata.md: a layout's template applies to CHILD segments). return { title: { default: `${siteId} — Sites`, template: `%s — ${siteId} — Sites` }, }; } export default async function SiteLayout({ children, params, }: { children: ReactNode; params: Promise<{ siteId: string }>; }) { const { siteId } = await params; // Deduped with each tab page's read of the same file via React cache(). const site = getSiteCached(siteId); if (!site) notFound(); return (
Sites / {siteId}

{site.siteTitle || siteId}

{children}
); }