From c180e79c384e28808b7d97ec4762cf3fb3d0354d Mon Sep 17 00:00:00 2001 From: adilallo <39313955+adilallo@users.noreply.github.com> Date: Wed, 4 Nov 2026 11:04:52 -0700 Subject: [PATCH] Give every public route its own description, and keep canonicals and structured data on the host that serves the page. Decorative images on the marketing pages stay quiet, and every dialog close button is named Close. Co-authored-by: Cursor --- app/(app)/create/layout.tsx | 1 + .../create/review-template/[slug]/layout.tsx | 36 +++++++++++++++ app/(app)/profile/layout.tsx | 19 ++++---- .../_components/LegalDocumentPage.tsx | 13 +++--- app/(marketing)/blog/[slug]/page.tsx | 18 +++++--- app/(marketing)/how-it-works/page.tsx | 18 ++++---- app/(marketing)/layout.tsx | 20 ++++----- app/(marketing)/page.tsx | 13 +++++- app/(marketing)/templates/page.tsx | 1 + app/(marketing)/use-cases/[slug]/page.tsx | 14 +++--- .../cards/CaseStudy/CaseStudy.view.tsx | 6 ++- .../ContentContainer.container.tsx | 3 +- .../navigation/ConditionalNavigation.tsx | 9 +++- .../ConditionalNavigationClient.tsx | 7 ++- app/components/navigation/Footer.tsx | 5 ++- .../navigation/FooterWithOrigin.tsx | 8 ++++ .../navigation/Top/Top.container.tsx | 35 ++++++--------- app/components/navigation/Top/Top.types.ts | 12 ++--- app/components/navigation/Top/Top.view.tsx | 4 +- .../sections/CardSteps/CardSteps.view.tsx | 19 ++++---- .../sections/QuoteBlock/QuoteBlock.view.tsx | 6 +-- app/components/sections/SectionNumber.tsx | 4 +- .../type/TripleStep/TripleStep.view.tsx | 6 ++- app/hooks/useSchemaData.ts | 18 +------- lib/requestSiteOrigin.ts | 12 +++++ lib/siteMetadata.ts | 45 ++++++++++++++++--- lib/templates/governanceTemplateCatalog.ts | 18 ++++++++ messages/en/components/controlsChrome.json | 6 +-- messages/en/components/footer.json | 2 +- messages/en/components/quoteBlock.json | 2 +- messages/en/components/topNav.json | 7 +-- messages/en/metadata.json | 16 ++++--- messages/en/modals/share.json | 2 +- messages/en/pages/howItWorks.json | 2 +- messages/en/pages/learn.json | 6 +-- messages/en/pages/workshops.json | 4 +- tests/components/Alert.test.tsx | 6 +-- .../ConfirmStakeholdersPage.test.tsx | 2 +- tests/components/Create.test.tsx | 4 +- tests/components/Dialog.test.tsx | 2 +- tests/components/FinalReviewPage.test.tsx | 8 ++-- tests/components/Login.test.tsx | 4 +- tests/components/Logo.test.tsx | 10 ++--- tests/components/SectionNumber.test.tsx | 17 ++++--- tests/e2e/critical-journeys.spec.ts | 2 +- tests/pages/communication-methods.test.jsx | 2 +- tests/pages/decision-approaches.test.jsx | 6 +-- tests/unit/ContentContainer.test.jsx | 27 ++++++----- tests/unit/QuoteBlock.test.jsx | 2 +- tests/unit/governanceTemplateCatalog.test.ts | 16 +++++++ tests/unit/hooks/useSchemaData.test.jsx | 12 +---- tests/unit/siteMetadata.test.ts | 35 ++++++++++++++- 52 files changed, 363 insertions(+), 209 deletions(-) create mode 100644 app/(app)/create/review-template/[slug]/layout.tsx create mode 100644 app/components/navigation/FooterWithOrigin.tsx create mode 100644 lib/requestSiteOrigin.ts diff --git a/app/(app)/create/layout.tsx b/app/(app)/create/layout.tsx index fddfe16..018228c 100644 --- a/app/(app)/create/layout.tsx +++ b/app/(app)/create/layout.tsx @@ -6,6 +6,7 @@ import { routeMetadata } from "../../../lib/siteMetadata"; export const metadata: Metadata = routeMetadata("/create", { title: messages.metadata.create.title, + description: messages.metadata.create.description, }); export default function CreateFlowLayout({ children }: { children: ReactNode }) { diff --git a/app/(app)/create/review-template/[slug]/layout.tsx b/app/(app)/create/review-template/[slug]/layout.tsx new file mode 100644 index 0000000..873bea2 --- /dev/null +++ b/app/(app)/create/review-template/[slug]/layout.tsx @@ -0,0 +1,36 @@ +import type { Metadata } from "next"; +import type { ReactNode } from "react"; +import messages from "../../../../../messages/en/index"; +import { routeMetadata } from "../../../../../lib/siteMetadata"; +import { getRuleTemplateBySlug } from "../../../../../lib/server/ruleTemplates"; +import { templateReviewDocument } from "../../../../../lib/templates/governanceTemplateCatalog"; + +type LayoutProps = { + children: ReactNode; + params: Promise<{ slug: string }>; +}; + +export async function generateMetadata({ + params, +}: LayoutProps): Promise { + const { slug: raw } = await params; + const slug = decodeURIComponent(raw); + const row = await getRuleTemplateBySlug(slug); + const document = templateReviewDocument(slug, row); + + const title = document?.title ?? messages.metadata.create.title; + const description = + document?.description || messages.metadata.create.description; + return routeMetadata(`/create/review-template/${encodeURIComponent(slug)}`, { + title: { + absolute: messages.metadata.titleTemplate.replace("%s", title), + }, + description, + openGraph: { title, description }, + twitter: { title, description }, + }); +} + +export default function TemplateReviewLayout({ children }: LayoutProps) { + return children; +} diff --git a/app/(app)/profile/layout.tsx b/app/(app)/profile/layout.tsx index cc647aa..0b5d20f 100644 --- a/app/(app)/profile/layout.tsx +++ b/app/(app)/profile/layout.tsx @@ -1,19 +1,18 @@ -import dynamic from "next/dynamic"; -import type { ReactNode } from "react"; +import { Suspense, type ReactNode } from "react"; +import FooterWithOrigin from "../../components/navigation/FooterWithOrigin"; /** Profile uses the public marketing footer; other `(app)` routes stay footer-free. */ -const Footer = dynamic(() => import("../../components/navigation/Footer"), { - loading: () => ( -