From c8ee8a3ec2ed4007809276cd59d7f74b18fa6b9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Markovi=C4=87?= Date: Thu, 23 Apr 2026 21:15:22 +0400 Subject: [PATCH] Add 404 and error boundary components wired into router --- frontend/src/components/error-state.tsx | 25 +++++++++++++++++++++++++ frontend/src/components/not-found.tsx | 14 ++++++++++++++ frontend/src/routes/$slug.tsx | 2 ++ frontend/src/routes/__root.tsx | 12 ++++++++++++ 4 files changed, 53 insertions(+) create mode 100644 frontend/src/components/error-state.tsx create mode 100644 frontend/src/components/not-found.tsx diff --git a/frontend/src/components/error-state.tsx b/frontend/src/components/error-state.tsx new file mode 100644 index 0000000..a078c02 --- /dev/null +++ b/frontend/src/components/error-state.tsx @@ -0,0 +1,25 @@ +import { AlertTriangle } from 'lucide-react' +import { Alert, AlertDescription, AlertTitle } from '~/components/ui/alert' +import { Button } from '~/components/ui/button' + +type Props = { + error: Error + reset?: () => void +} + +export function ErrorState({ error, reset }: Props) { + return ( + +
+ + Something went wrong + {error.message} + {reset && ( + + )} +
+
+ ) +} diff --git a/frontend/src/components/not-found.tsx b/frontend/src/components/not-found.tsx new file mode 100644 index 0000000..92cc70d --- /dev/null +++ b/frontend/src/components/not-found.tsx @@ -0,0 +1,14 @@ +import { Frown } from 'lucide-react' +import { Alert, AlertDescription, AlertTitle } from '~/components/ui/alert' + +export function NotFound({ title = '404: Page Not Found', message = 'Try going back or something …' }) { + return ( + +
+ + {title} + {message} +
+
+ ) +} diff --git a/frontend/src/routes/$slug.tsx b/frontend/src/routes/$slug.tsx index 29a4709..af08999 100644 --- a/frontend/src/routes/$slug.tsx +++ b/frontend/src/routes/$slug.tsx @@ -1,6 +1,7 @@ import { useSuspenseQuery } from '@tanstack/react-query' import { createFileRoute, notFound } from '@tanstack/react-router' import { Helmet } from 'react-helmet-async' +import { NotFound } from '~/components/not-found' import { PageView } from '~/components/page-view' import { pageBySlugQuery } from '~/lib/queries' @@ -10,6 +11,7 @@ export const Route = createFileRoute('/$slug')({ if (!page) throw notFound() }, component: CmsPage, + notFoundComponent: () => , }) function CmsPage() { diff --git a/frontend/src/routes/__root.tsx b/frontend/src/routes/__root.tsx index 4d3f49f..6b0bcc9 100644 --- a/frontend/src/routes/__root.tsx +++ b/frontend/src/routes/__root.tsx @@ -3,7 +3,9 @@ import { useSuspenseQuery } from '@tanstack/react-query' import { createRootRouteWithContext, Outlet } from '@tanstack/react-router' import { lazy, Suspense } from 'react' import { Helmet } from 'react-helmet-async' +import { ErrorState } from '~/components/error-state' import { Shell } from '~/components/layout/shell' +import { NotFound } from '~/components/not-found' import { globalsQuery, menusQuery } from '~/lib/queries' const TanStackRouterDevtools = import.meta.env.PROD @@ -19,6 +21,16 @@ export const Route = createRootRouteWithContext<{ queryClient: QueryClient }>()( await Promise.all([queryClient.ensureQueryData(globalsQuery), queryClient.ensureQueryData(menusQuery)]) }, component: RootComponent, + notFoundComponent: () => ( + + + + ), + errorComponent: ({ error, reset }) => ( + + + + ), }) function RootComponent() {