import type { Metadata } from "next" export const SITE_NAME = "Property Management Network" // The generated Open Graph card (app/opengraph-image.tsx), served at this path. // Declared explicitly because a page that sets its own `openGraph` object // replaces the inherited one — including the image Next.js attaches from the // file convention — which silently leaves that page with no share image. const OG_IMAGE = "/opengraph-image" type PageSeo = { /** * Page title. Rendered through the root layout's "%s | Property Management * Network" template unless `absoluteTitle` is set. */ title: string /** Render `title` verbatim, without the site-name suffix. */ absoluteTitle?: boolean description: string /** Canonical path, root-relative and without a trailing slash, e.g. "/terms". */ path: string /** * Headline used for the Open Graph / Twitter card when the share copy should * differ from the page title. Defaults to the resolved page title. */ socialTitle?: string /** Set false for pages that must stay out of the index. */ index?: boolean } /** * Builds a complete, self-consistent metadata block for a public page. * * Every public page must go through this helper so that canonical, og:url, * og:title, og:image and the Twitter card always agree with each other and with * the page's real URL. Setting these ad hoc per page is what previously left the * landing page with no share image and every legal page pointing og:url at "/". */ export function pageMetadata({ title, absoluteTitle = false, description, path, socialTitle, index = true, }: PageSeo): Metadata { const fullTitle = absoluteTitle ? title : `${title} | ${SITE_NAME}` const social = socialTitle ?? fullTitle return { title: absoluteTitle ? { absolute: title } : title, description, alternates: { canonical: path }, openGraph: { title: social, description, url: path, siteName: SITE_NAME, locale: "en_US", type: "website", images: [OG_IMAGE], }, twitter: { card: "summary_large_image", title: social, description, images: [OG_IMAGE], }, ...(index ? {} : { robots: { index: false, follow: true } }), } }