import type { ReactNode } from "react"; import type { HousekeepingResult } from "../../../foundation/contracts"; import type { ContentQueryData } from "../queries/content-queries"; export interface ContentPageProps { readonly title?: string; readonly search?: string; readonly context: import("../../../foundation/contracts").HousekeepingCapabilityContext; readonly result?: HousekeepingResult; readonly routeId: import("../routes").ContentRouteId | null; } export function ContentPageFrame({ title, description, result, forms, }: { readonly title: string; readonly description: string; readonly result?: HousekeepingResult; readonly forms?: ReactNode; }) { if (!result) { return (

{title}

{description}

Loading content…

); } if (!result.ok) { const state = result.error.code === "FORBIDDEN" ? "forbidden" : "error"; return (

{title}

{result.error.messageKey}

); } const state = result.data.items.length === 0 ? "empty" : "ready"; return (

{title}

{description}

{forms} {result.data.items.length === 0 ? (

No matching content.

) : (
    {result.data.items.map((item) => (
  • {item.href ? {item.title} : item.title} {item.status ? — {item.status} : null} {item.description ?

    {item.description}

    : null}
  • ))}
)}
); } export function parseContentListInput( searchParams: Readonly< Record >, ) { const first = (value: string | readonly string[] | undefined) => Array.isArray(value) ? value[0] : value; const text = (value: string | readonly string[] | undefined, limit: number) => String(first(value) ?? "") .normalize("NFC") .trim() .slice(0, limit); const bounded = ( value: string | readonly string[] | undefined, fallback: number, minimum: number, maximum: number, ) => { const parsed = Number(first(value)); return Number.isSafeInteger(parsed) ? Math.min(maximum, Math.max(minimum, parsed)) : fallback; }; return { search: text(searchParams.search, 128), status: text(searchParams.status, 32).toLocaleLowerCase(), pageSize: bounded(searchParams.pageSize, 25, 1, 100), offset: bounded(searchParams.offset, 0, 0, 100_000), responseQuestionId: text(searchParams.responseQuestionId, 32), responsePageSize: bounded(searchParams.responsePageSize, 25, 1, 50), responseOffset: bounded(searchParams.responseOffset, 0, 0, 100_000), }; }