| 1 | "use client"; |
| 2 | |
| 3 | import Link from "next/link"; |
| 4 | import { usePathname } from "next/navigation"; |
| 5 | import { defaultLocale } from "@/lib/i18n/config"; |
| 6 | import { getStates } from "@/lib/i18n/dictionaries"; |
| 7 | import { pathLocale } from "@/lib/i18n/path"; |
| 8 | import { RetryAction } from "./retry-action"; |
| 9 | import { EmptyState, ErrorState, LoadingState } from "./surface-state"; |
| 10 | |
| 11 | /** |
| 12 | * Route boundaries — `loading.tsx`, `error.tsx`, `not-found.tsx` — receive |
| 13 | * no params, so these thin client shims read the locale from the pathname |
| 14 | * and render the shared surface states with dictionary copy. |
| 15 | */ |
| 16 | function useRouteLocale(): string { |
| 17 | const pathname = usePathname(); |
| 18 | return pathLocale(pathname ?? "") ?? defaultLocale; |
| 19 | } |
| 20 | |
| 21 | export function LoadingRoute() { |
| 22 | const locale = useRouteLocale(); |
| 23 | return <LoadingState locale={locale} lines={4} />; |
| 24 | } |
| 25 | |
| 26 | export function ErrorRoute({ reset, digest }: { reset: () => void; digest?: string }) { |
| 27 | const locale = useRouteLocale(); |
| 28 | const t = getStates(locale); |
| 29 | return ( |
| 30 | <ErrorState |
| 31 | locale={locale} |
| 32 | titleAs="h1" |
| 33 | body={digest ? `${t.errorBody} (${digest})` : t.errorBody} |
| 34 | action={ |
| 35 | <> |
| 36 | <RetryAction label={t.retry} onRetry={reset} /> |
| 37 | <Link href={`/${locale}`} className="portal-button portal-button-secondary"> |
| 38 | {t.homeLink} |
| 39 | </Link> |
| 40 | </> |
| 41 | } |
| 42 | /> |
| 43 | ); |
| 44 | } |
| 45 | |
| 46 | export function NotFoundRoute() { |
| 47 | const locale = useRouteLocale(); |
| 48 | const t = getStates(locale); |
| 49 | // The plate is the whole page, so its title is the page's <h1>. The body |
| 50 | // names the documentation index; the primary action is that index, and |
| 51 | // the home link is the secondary way out. |
| 52 | return ( |
| 53 | <EmptyState |
| 54 | locale={locale} |
| 55 | title={t.notFoundTitle} |
| 56 | body={t.notFoundBody} |
| 57 | titleAs="h1" |
| 58 | action={ |
| 59 | <> |
| 60 | <Link href={`/${locale}/docs`} className="portal-button portal-button-primary"> |
| 61 | {t.docsIndexLink} |
| 62 | </Link> |
| 63 | <Link href={`/${locale}`} className="portal-button portal-button-secondary"> |
| 64 | {t.homeLink} |
| 65 | </Link> |
| 66 | </> |
| 67 | } |
| 68 | /> |
| 69 | ); |
| 70 | } |
| 71 |