From 22b6af685ac8facca487d47f800b948fcb959c8f Mon Sep 17 00:00:00 2001 From: Aarnav Tale Date: Fri, 29 Mar 2024 16:14:35 -0400 Subject: [PATCH] feat: add proper error component --- app/components/Code.tsx | 11 ++++--- app/components/Error.tsx | 66 ++++++++++++++++++++++++++++++++++++++++ app/root.tsx | 40 ++---------------------- app/routes/_data.tsx | 25 ++++++++++++++- 4 files changed, 100 insertions(+), 42 deletions(-) create mode 100644 app/components/Error.tsx diff --git a/app/components/Code.tsx b/app/components/Code.tsx index 80275bf..0d8fb60 100644 --- a/app/components/Code.tsx +++ b/app/components/Code.tsx @@ -1,9 +1,12 @@ -import { type ReactNode } from 'react' +import clsx from 'clsx' +import { type HTMLProps } from 'react' -export default function Code({ children }: { readonly children: ReactNode }) { +type Properties = HTMLProps + +export default function Code(properties: Properties) { return ( - - {children} + + {properties.children} ) } diff --git a/app/components/Error.tsx b/app/components/Error.tsx new file mode 100644 index 0000000..01c664f --- /dev/null +++ b/app/components/Error.tsx @@ -0,0 +1,66 @@ +import { Transition } from '@headlessui/react' +import { ExclamationTriangleIcon } from '@heroicons/react/24/outline' +import { isRouteErrorResponse, useRouteError } from '@remix-run/react' +import clsx from 'clsx' +import { Fragment, useEffect, useState } from 'react' + +import Code from './Code' + +type Properties = { + readonly type?: 'full' | 'embedded'; +} + +export function ErrorPopup({ type = 'full' }: Properties) { + const [isOpen, setIsOpen] = useState(false) + const error = useRouteError() + const routing = isRouteErrorResponse(error) + const message = (error instanceof Error ? error.message : 'An unexpected error occurred') + console.error(error) + + // Debounce the error modal so it doesn't show up for a split second + // when the user navigates to a new page. + useEffect(() => { + setTimeout(() => { + setIsOpen(true) + }, 150) + }, []) + + return ( + + + +
+
+ +

+ {routing ? error.status : 'Error'} +

+ {routing ? ( +

+ {error.statusText} +

+ ) : ( + + {message} + + )} +
+
+
+
+ ) +} diff --git a/app/root.tsx b/app/root.tsx index 5fc53a3..9f5a19c 100644 --- a/app/root.tsx +++ b/app/root.tsx @@ -1,17 +1,13 @@ -import { ExclamationTriangleIcon, QuestionMarkCircleIcon } from '@heroicons/react/24/outline' import type { LinksFunction, MetaFunction } from '@remix-run/node' import { - isRouteErrorResponse, Links, Meta, Outlet, Scripts, - ScrollRestoration, - useRouteError + ScrollRestoration } from '@remix-run/react' -import clsx from 'clsx' -import Code from '~/components/Code' +import { ErrorPopup } from '~/components/Error' import Toaster from '~/components/Toaster' import stylesheet from '~/tailwind.css?url' import { getContext } from '~/utils/config' @@ -68,37 +64,7 @@ export function Layout({ children }: { readonly children: React.ReactNode }) { } export function ErrorBoundary() { - const error = useRouteError() - const routing = isRouteErrorResponse(error) - const message = (error instanceof Error ? error.message : 'An unexpected error occurred') - return ( -
-
- {routing ? ( - <> - -

{error.status}

-

{error.statusText}

- - ) : ( - <> - -

Error

- - {message} - -

- If you are the administrator of this site, please check your logs for information. -

- - )} -
-
- ) + return } export default function App() { diff --git a/app/routes/_data.tsx b/app/routes/_data.tsx index 0bea678..8f2d9a9 100644 --- a/app/routes/_data.tsx +++ b/app/routes/_data.tsx @@ -1,7 +1,8 @@ import { Cog8ToothIcon, CpuChipIcon, GlobeAltIcon, LockClosedIcon, ServerStackIcon, UsersIcon } from '@heroicons/react/24/outline' import { type LoaderFunctionArgs, redirect } from '@remix-run/node' -import { Outlet } from '@remix-run/react' +import { Outlet, useRouteError } from '@remix-run/react' +import { ErrorPopup } from '~/components/Error' import TabLink from '~/components/TabLink' import { HeadscaleError, pull } from '~/utils/headscale' import { destroySession, getSession } from '~/utils/sessions' @@ -61,3 +62,25 @@ export default function Layout() { ) } +export function ErrorBoundary() { + return ( + <> +
+ +
+ + + ) +}