Files
community-rule/app/(marketing)/use-cases/[slug]/page.tsx
T
adilalloandCursor c180e79c38 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 <cursoragent@cursor.com>
2026-11-04 11:04:52 -07:00

157 lines
4.4 KiB
TypeScript

/**
* Figma: use case detail (22015:42619)
* https://www.figma.com/design/agv0VBLiBlcnSAaiAORgPR/Community-Rule-System?node-id=22015-42619
*/
import type { Metadata } from "next";
import { notFound } from "next/navigation";
import messages from "../../../../messages/en/index";
import {
buildUseCaseSyntheticPost,
getUseCaseDetailEntry,
isUseCaseDetailSlug,
USE_CASE_DETAIL_SLUGS,
useCaseContentKeyForSlug,
} from "../../../../lib/useCaseSyntheticPost";
import { getRequestSiteOrigin } from "../../../../lib/requestSiteOrigin";
import { absoluteSiteUrl, routeMetadata } from "../../../../lib/siteMetadata";
import ContentBanner from "../../../components/sections/ContentBanner";
import AskOrganizer from "../../../components/sections/AskOrganizer";
import type { AskOrganizerVariant } from "../../../components/sections/AskOrganizer/AskOrganizer.types";
import "../use-cases.css";
type PageProps = {
params: Promise<{ slug: string }>;
};
export function generateStaticParams() {
return USE_CASE_DETAIL_SLUGS.map((slug) => ({ slug }));
}
export async function generateMetadata({
params,
}: PageProps): Promise<Metadata> {
const { slug } = await params;
if (!isUseCaseDetailSlug(slug)) {
return {};
}
const contentKey = useCaseContentKeyForSlug(slug);
const meta = messages.metadata.useCasesDetail[contentKey];
return routeMetadata(`/use-cases/${slug}`, {
title: meta.title,
description: meta.description,
keywords: meta.keywords,
openGraph: {
title: meta.title,
description: meta.description,
type: "website",
siteName: messages.metadata.siteName,
},
});
}
export default async function UseCaseDetailPage({ params }: PageProps) {
const { slug } = await params;
if (!isUseCaseDetailSlug(slug)) {
notFound();
}
const origin = await getRequestSiteOrigin();
const detail = messages.pages.useCasesDetail;
const entry = getUseCaseDetailEntry(slug, detail);
const syntheticPost = buildUseCaseSyntheticPost(slug, detail);
const { ruleCard, askOrganizer } = entry;
const askVariant = (askOrganizer.variant ??
"use-case-detail") as AskOrganizerVariant;
const structuredData = {
"@context": "https://schema.org",
"@type": "WebPage",
name: entry.banner.title,
description: entry.banner.description,
url: absoluteSiteUrl(`/use-cases/${slug}`, origin),
publisher: {
"@type": "Organization",
name: "CommunityRule",
url: absoluteSiteUrl("/", origin),
},
};
const breadcrumbData = {
"@context": "https://schema.org",
"@type": "BreadcrumbList",
itemListElement: [
{
"@type": "ListItem",
position: 1,
name: "Home",
item: absoluteSiteUrl("/", origin),
},
{
"@type": "ListItem",
position: 2,
name: "Learn",
item: absoluteSiteUrl("/learn", origin),
},
{
"@type": "ListItem",
position: 3,
name: entry.banner.title,
item: absoluteSiteUrl(`/use-cases/${slug}`, origin),
},
],
};
return (
<>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{
__html: JSON.stringify(structuredData),
}}
/>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{
__html: JSON.stringify(breadcrumbData),
}}
/>
<div
className="min-h-screen"
style={{ background: entry.pageBackground }}
>
<ContentBanner
post={syntheticPost}
variant="useCase"
rulePreview={{
title: ruleCard.title,
description: ruleCard.description,
backgroundColor: ruleCard.backgroundColor,
iconPath: ruleCard.iconPath,
href: `/use-cases/${slug}/rule`,
}}
/>
<article
data-figma-node="22015:42622"
className="flex w-full items-center justify-center self-stretch px-[var(--space-600)] py-[var(--space-800)] sm:px-[var(--space-800)] md:px-[var(--space-1200)] lg:px-[var(--space-1800)] xl:px-[256px]"
>
<div
className="use-case-body"
dangerouslySetInnerHTML={{ __html: syntheticPost.htmlContent }}
/>
</article>
<AskOrganizer
title={askOrganizer.title}
subtitle={askOrganizer.subtitle}
buttonText={askOrganizer.buttonText}
variant={askVariant}
/>
</div>
</>
);
}