'use client'; /** * DevSectionPage — full-page wrapper for a dev-center section * (`/roadmap`, `/bug-fixes-and-enhancements`, `/releases`). * * Mounts the lib's canonical `PageLayout` directly (no in-app wrapper) * so the back-button affordance stays in lockstep with whatever the * design system ships — any future lib change to BackButton / TitleBlock * propagates automatically. * * Composition: `PageShell` → `PageLayout` (back-to-home wired) → * `DevSectionView` (icon hero + search + filter pills) → list body. * * Adding a new section is one entry in `OPENFRAME_DEV_SECTIONS` plus a * single-line page file mounting this factory with the new key. */ import type { ReactNode } from 'react'; import { useRouter } from '../../../embed-shims/next-navigation'; import { PageShell, PageLayout } from '../../ui'; import { DevSectionView } from './dev-section-view'; import { OPENFRAME_DEV_SECTIONS, type OpenframeDevSectionKey, } from '../../../utils/dev-sections/openframe-dev-sections'; export interface DevSectionPageProps { sectionKey: OpenframeDevSectionKey; /** The page-specific list body (e.g. ``). */ children: ReactNode; /** Optional slot rendered BETWEEN the hero and search/filter — see * `DevSectionView.preControls`. Used by surfaces that want an entry * action (e.g. Help Center's "Open a new ticket" form) above the * controls instead of below them. */ preControls?: ReactNode; /** Back-button config — same shape as `LegalDocumentPage` / * `ReleaseDetailPage`. Pass `false` to hide entirely. Default * `{ label: 'Back to home', href: '/' }` — embedders whose "home" isn't `/` * should override `href`, or pass `false` if the embed has no home page. */ backButton?: { label?: string; href?: string } | false; /** Override the hero title. Defaults to the (OpenFrame-specific) copy in * `OPENFRAME_DEV_SECTIONS[sectionKey].hero.title`. Set this to brand the * section for a non-OpenFrame embed. */ title?: string; /** Override the hero subtitle/description. Defaults to * `OPENFRAME_DEV_SECTIONS[sectionKey].hero.description`. */ subtitle?: string; /** Render the standalone `` (own `
` + bg + max-width). Default * `true` — the contract for marketing/hub surfaces with no app shell. Pass * `false` when the host layout already provides the page container (e.g. * openframe-frontend's `AppLayout` `
`): then only the * `page-shell-content` padding box is rendered, avoiding a nested `
`. */ shell?: boolean; } export function DevSectionPage({ sectionKey, children, preControls, backButton, title, subtitle, shell = true, }: DevSectionPageProps) { const router = useRouter(); const section = OPENFRAME_DEV_SECTIONS[sectionKey]; // Back-button config — mirrors LegalDocumentPage / ReleaseDetailPage. // Default: { label: 'Back to home', href: '/' }. Pass `false` to hide. // After `backButton &&` narrowing, inner type is `{ label?, href? } | // undefined`; don't re-compare to `false` (TS2367). const backCfg = backButton === false ? undefined : { label: (backButton ? backButton.label : undefined) ?? 'Back to home', onClick: () => router.push((backButton ? backButton.href : undefined) ?? '/'), }; const inner = ( // Unified header: title/description route through the canonical (frozen) // `PageLayout` `TitleBlock` (text-h2) — same as FAQ / Legal / detail pages — // so every help-center surface shares one header. `DevSectionView` then // renders ONLY its search + filter controls (`showHeading={false}`), no // duplicate title. (The hero icon is intentionally dropped: TitleBlock is // frozen and renders title-only.) {children} ); // `shell` true → standalone `` (own
+ bg + max-width). // false → padding-only box (no nested
) for hosts whose layout already // provides the container; both consume the host's `--page-shell-*` vars. return shell ? {inner} :
{inner}
; }