Drop Cookies Settings from the footer, link the Home features that already have pages, and give leftover routes their own descriptions.
141 lines
7.0 KiB
TypeScript
141 lines
7.0 KiB
TypeScript
import type { Metadata } from "next";
|
|
import Link from "next/link";
|
|
import messages from "../messages/en/index";
|
|
import { getTranslation } from "../lib/i18n/getTranslation";
|
|
import { NO_INDEX_ROBOTS, routeMetadata } from "../lib/siteMetadata";
|
|
import { getGovernanceTemplateCatalogEntry } from "../lib/templates/governanceTemplateCatalog";
|
|
import Icon from "./components/asset/icon";
|
|
import Button from "./components/buttons/Button";
|
|
import HeroDecor from "./components/sections/HeroBanner/HeroDecor";
|
|
|
|
export const metadata: Metadata = routeMetadata("/404", {
|
|
title: messages.metadata.notFound.title,
|
|
description: messages.metadata.notFound.description,
|
|
robots: NO_INDEX_ROBOTS,
|
|
});
|
|
|
|
const NOT_FOUND_TEMPLATE_SLUGS = [
|
|
"consensus",
|
|
"do-ocracy",
|
|
"devolution",
|
|
"solidarity-network",
|
|
] as const;
|
|
|
|
/**
|
|
* Figma: 404 page frame 22078-808557; 480px lockup 22078-808903; title + CTA group 22078-808908
|
|
* (filled / Go home left, outline / Browse right, 16px between).
|
|
* Same [HeroDecor](app/components/sections/HeroBanner/HeroDecor.tsx) SVG as home; 404 places it only behind the title stack.
|
|
* Shell: [app/layout.tsx](app/layout.tsx) `Top` only — no site footer.
|
|
* Template chip row: Figma 22078-809968 — one row, 16px gaps, 20px to hint (no inner scroll; page handles overflow if needed).
|
|
* Hero pattern: behind the 404 + bar + h1; wide SVG is painted with overflow-x-clip on `main` so it does not widen the scrollport.
|
|
*/
|
|
export default function NotFound() {
|
|
const t = (key: string) => getTranslation(messages, key);
|
|
|
|
const templateEntries = NOT_FOUND_TEMPLATE_SLUGS.map((slug) =>
|
|
getGovernanceTemplateCatalogEntry(slug),
|
|
).filter(
|
|
(e): e is NonNullable<typeof e> => e != null,
|
|
);
|
|
|
|
return (
|
|
<main
|
|
className="relative flex min-h-0 w-full min-w-0 max-w-full flex-1 flex-col overflow-x-clip bg-[var(--color-surface-default-primary)]"
|
|
aria-labelledby="not-found-heading"
|
|
>
|
|
<div
|
|
className="relative flex min-h-0 w-full min-w-0 max-w-full flex-1 flex-col overflow-x-clip px-[var(--space-200)] sm:px-[var(--space-250)] md:px-[var(--space-400)] lg:px-[var(--space-600)] xl:px-[var(--space-1200)]"
|
|
>
|
|
<div className="relative z-10 flex min-h-0 w-full max-w-full flex-1 flex-col items-center justify-center py-[var(--space-1000)] sm:py-[var(--space-1200)]">
|
|
{/*
|
|
Vertical rhythm: 22078-808903 + 22078-808908 — 404→bar 8px, bar→h1 32px, h1→body 16px,
|
|
body→CTAs 48px, CTA→templates 40px (lockup flex gap), template→hint 20px
|
|
*/}
|
|
<div className="mx-auto flex w-full max-w-[480px] flex-col items-center gap-[var(--space-1000)] text-center">
|
|
<div className="flex w-full min-w-0 flex-col items-center">
|
|
<div className="relative flex w-full flex-col items-center">
|
|
<HeroDecor
|
|
className="pointer-events-none absolute left-1/2 top-[40%] -z-10 h-[645px] w-[1540px] max-w-none
|
|
-translate-x-1/2 -translate-y-1/2
|
|
scale-[0.41] sm:scale-[0.45] md:scale-[0.47] lg:scale-[0.5] xl:scale-[0.53]"
|
|
/>
|
|
<p
|
|
className="w-full text-center font-bricolage-grotesque font-extrabold leading-none tracking-[-0.04em] text-[clamp(5.5rem,16vw,13.75rem)] text-[var(--color-content-default-brand-primary)]"
|
|
aria-hidden="true"
|
|
>
|
|
{t("pages.notFoundPage.codeTitle")}
|
|
</p>
|
|
<div
|
|
className="mt-[var(--space-200)] h-1.5 w-[120px] shrink-0 rounded-full bg-[var(--color-yellow-yellow200)]"
|
|
aria-hidden="true"
|
|
/>
|
|
<h1
|
|
id="not-found-heading"
|
|
className="mt-[var(--space-800)] max-w-full text-center font-bricolage-grotesque font-medium leading-[1.1] text-[40px] text-[var(--color-content-default-primary)] min-[400px]:text-medium-display"
|
|
>
|
|
{t("pages.notFoundPage.heading")}
|
|
</h1>
|
|
</div>
|
|
<p className="mt-[var(--space-400)] w-full max-w-[443px] text-center text-large-paragraph text-[var(--color-content-default-secondary)]">
|
|
{t("pages.notFoundPage.description")}
|
|
</p>
|
|
|
|
<div
|
|
dir="ltr"
|
|
className="mt-[var(--space-1200)] flex w-full min-w-0 flex-col items-center justify-center gap-[var(--space-400)] min-[400px]:flex-nowrap min-[400px]:flex-row min-[400px]:items-center min-[400px]:justify-center"
|
|
>
|
|
<Button
|
|
href="/"
|
|
size="large"
|
|
buttonType="filled"
|
|
palette="default"
|
|
className="inline-flex w-max max-w-full shrink-0 items-center justify-center gap-[var(--space-250)]"
|
|
>
|
|
<Icon
|
|
name="arrow_back"
|
|
size={20}
|
|
className="shrink-0"
|
|
/>
|
|
{t("pages.notFoundPage.goHomeCta")}
|
|
</Button>
|
|
<Button
|
|
href="/templates"
|
|
size="large"
|
|
buttonType="outline"
|
|
palette="default"
|
|
className="inline-flex w-max max-w-full shrink-0 items-center justify-center"
|
|
>
|
|
{t("pages.notFoundPage.browseTemplatesCta")}
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
|
|
{templateEntries.length > 0 ? (
|
|
<div className="flex w-full min-w-0 max-w-[36rem] flex-col items-center gap-[var(--space-500)] self-stretch">
|
|
<div
|
|
className="flex w-full min-w-0 max-md:flex-wrap md:flex-nowrap items-center justify-center gap-x-[var(--space-400)] gap-y-[var(--space-300)]"
|
|
role="list"
|
|
>
|
|
{templateEntries.map((entry) => (
|
|
<Link
|
|
key={entry.slug}
|
|
href={`/create/review-template/${entry.slug}`}
|
|
role="listitem"
|
|
className={`${entry.backgroundColor} inline-flex h-[37px] shrink-0 items-center justify-center rounded-full px-[20px] py-0 text-center font-bricolage-grotesque text-sm font-extrabold leading-[21px] text-[var(--color-content-invert-primary)] no-underline transition-opacity hover:opacity-90 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[var(--color-border-invert-primary)]`}
|
|
>
|
|
{entry.title}
|
|
</Link>
|
|
))}
|
|
</div>
|
|
<p className="w-full text-center text-[13px] font-normal leading-[1.2] text-[var(--color-gray-500)]">
|
|
{t("pages.notFoundPage.templateHint")}
|
|
</p>
|
|
</div>
|
|
) : null}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</main>
|
|
);
|
|
}
|