返回 CodeWhale
route-state.tsx
根目录 / web / components / route-state.tsx
1 "use client";
2
3 import Image from "next/image";
4 import Link from "next/link";
5 import { usePathname } from "next/navigation";
6 import { defaultLocale } from "@/lib/i18n/config";
7 import { getStates } from "@/lib/i18n/dictionaries";
8 import { pathLocale } from "@/lib/i18n/path";
9 import { RetryAction } from "./retry-action";
10 import { ErrorState, LoadingState } from "./surface-state";
11
12 /**
13 * Route boundaries — `loading.tsx`, `error.tsx`, `not-found.tsx` — receive
14 * no params, so these thin client shims read the locale from the pathname
15 * and render the shared surface states with dictionary copy.
16 */
17 function useRouteLocale(): string {
18 const pathname = usePathname();
19 return pathLocale(pathname ?? "") ?? defaultLocale;
20 }
21
22 export function LoadingRoute() {
23 const locale = useRouteLocale();
24 return <LoadingState locale={locale} lines={4} />;
25 }
26
27 export function ErrorRoute({ reset, digest }: { reset: () => void; digest?: string }) {
28 const locale = useRouteLocale();
29 const t = getStates(locale);
30 return (
31 <ErrorState
32 locale={locale}
33 titleAs="h1"
34 body={digest ? `${t.errorBody} (${digest})` : t.errorBody}
35 action={
36 <>
37 <RetryAction label={t.retry} onRetry={reset} />
38 <Link href={`/${locale}`} className="btn btn-secondary">
39 {t.homeLink}
40 </Link>
41 </>
42 }
43 />
44 );
45 }
46
47 export function NotFoundRoute() {
48 const locale = useRouteLocale();
49 const t = getStates(locale);
50 // The founder's own joke: the Codwhale game poster, kept whole at every
51 // width, in a navy frame beside the way back.
52 return (
53 <section className="not-found" aria-labelledby="not-found-title">
54 <div className="not-found-copy">
55 <h1 id="not-found-title" className="not-found-title">
56 <span className="not-found-code">404.</span> {t.notFoundTitle}
57 </h1>
58 <p className="not-found-body">{t.notFoundBody}</p>
59 <div className="actions">
60 <Link href={`/${locale}`} className="btn btn-primary btn-lg">
61 {t.notFoundHomeLink}
62 </Link>
63 <Link href={`/${locale}/docs`} className="btn btn-secondary btn-lg">
64 {t.docsIndexLink}
65 </Link>
66 </div>
67 </div>
68 <div className="not-found-frame stage">
69 <Image
70 className="not-found-poster"
71 src="/codwhale-404.webp"
72 alt={t.notFoundPosterAlt}
73 width={1122}
74 height={1402}
75 priority
76 unoptimized
77 />
78 </div>
79 </section>
80 );
81 }
82
82 lines Plain Text