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>
This commit is contained in:
1 parent
0b6e8b00dd
commit
c180e79c38
52 files changed
+363
-209
No files matched your search
@@ -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 }) {
|
||||
|
||||
@@ -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<Metadata> {
|
||||
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;
|
||||
}
|
||||
@@ -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: () => (
|
||||
<footer className="w-full min-h-[200px] bg-[var(--color-surface-default-primary)]" />
|
||||
),
|
||||
ssr: true,
|
||||
});
|
||||
|
||||
export default function ProfileLayout({ children }: { children: ReactNode }) {
|
||||
return (
|
||||
<>
|
||||
{children}
|
||||
<Footer />
|
||||
<Suspense
|
||||
fallback={
|
||||
<footer className="min-h-[200px] w-full bg-[var(--color-surface-default-primary)]" />
|
||||
}
|
||||
>
|
||||
<FooterWithOrigin />
|
||||
</Suspense>
|
||||
</>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user