Files
community-rule/app/(marketing)/how-it-works/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

136 lines
4.2 KiB
TypeScript

/**
* Figma: "How Community Rule works" (22078:806964)
* https://www.figma.com/design/agv0VBLiBlcnSAaiAORgPR/Community-Rule-System?node-id=22078-806964
*/
import type { Metadata } from "next";
import dynamic from "next/dynamic";
import messages from "../../../messages/en/index";
import { getAllBlogPosts } from "../../../lib/content";
import {
buildHowItWorksSyntheticPost,
HOW_IT_WORKS_SENTINEL_SLUG,
} from "../../../lib/howItWorksSyntheticPost";
import { getRequestSiteOrigin } from "../../../lib/requestSiteOrigin";
import { absoluteSiteUrl, routeMetadata } from "../../../lib/siteMetadata";
import ContentBanner from "../../components/sections/ContentBanner";
import ContentTemplateDecorativeShapes from "../_components/ContentTemplateDecorativeShapes";
import AskOrganizer from "../../components/sections/AskOrganizer";
import "../blog/blog.css";
const RelatedArticles = dynamic(
() => import("../../components/sections/RelatedArticles"),
{
loading: () => (
<section className="py-[var(--space-800)] min-h-[400px]" />
),
ssr: true,
},
);
export async function generateMetadata(): Promise<Metadata> {
const meta = messages.metadata.howItWorks;
const page = messages.pages.howItWorks;
return routeMetadata("/how-it-works", {
title: meta.title,
description: meta.description,
keywords: meta.keywords,
openGraph: {
title: page.banner.title,
description: meta.description,
type: "website",
siteName: messages.metadata.siteName,
},
});
}
export default async function HowItWorksPage() {
const origin = await getRequestSiteOrigin();
const page = messages.pages.howItWorks;
const syntheticPost = buildHowItWorksSyntheticPost(page);
const allPosts = getAllBlogPosts();
const relatedPosts = allPosts.slice(0, 8);
const slugOrder = allPosts.map((post) => post.slug);
const askOrganizerData = {
title: messages.pages.home.askOrganizer.title,
subtitle: messages.pages.home.askOrganizer.subtitle,
buttonText: messages.pages.home.askOrganizer.buttonText,
};
const structuredData = {
"@context": "https://schema.org",
"@type": "WebPage",
name: page.banner.title,
description: messages.metadata.howItWorks.description,
url: absoluteSiteUrl("/how-it-works", 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: page.banner.title,
item: absoluteSiteUrl("/how-it-works", origin),
},
],
};
return (
<>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{
__html: JSON.stringify(structuredData),
}}
/>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{
__html: JSON.stringify(breadcrumbData),
}}
/>
<div className="relative min-h-screen overflow-x-clip bg-transparent">
<ContentBanner post={syntheticPost} variant="guide" />
<div className="relative w-full overflow-x-clip">
<ContentTemplateDecorativeShapes variant="guide" />
<article className="relative z-10 p-[var(--space-600)] sm:py-[var(--space-800)] xl:px-[calc(268/1440*100%)]">
<div
className="post-body text-[var(--color-content-default-primary)] text-medium-paragraph sm:text-large-paragraph lg:text-x-large-paragraph xl:text-xx-large-paragraph sm:mx-auto sm:max-w-[390px] md:max-w-[472px] lg:max-w-[700px] xl:max-w-none"
dangerouslySetInnerHTML={{ __html: syntheticPost.htmlContent }}
/>
</article>
</div>
<RelatedArticles
relatedPosts={relatedPosts}
currentPostSlug={HOW_IT_WORKS_SENTINEL_SLUG}
slugOrder={slugOrder}
headingSurface="onLight"
heading={page.relatedArticles.title}
/>
<AskOrganizer {...askOrganizerData} />
</div>
</>
);
}