Skip to content

Commit

Permalink
feat(dashboard): Wrap each route in an ErrorBoundary (#8674)
Browse files Browse the repository at this point in the history
**What**
- Updates the copy of the different error types
- Wraps each route (+ custom routes) in an ErrorBoundary to preserve app layout on error (sidebar and topbar)

![Skærmbillede 2024-08-20 kl  12 35 53](https://github.com/user-attachments/assets/0c589fc4-b279-4b66-9d66-1b99a7406696)

**Note**
If the user goes to a route that does not exist at all, e.g. `/some-weird-url`, then we have no way of knowing if the user is inside of a context where we can render the sidebar and topbar (as they require the user to be authenticated). So in this case we still show an ErrorBoundary where the two aren't included (see second picture), and include a button that takes the user to "/", which depending on whether the user is logged in will take them to "/login" or "/orders".

![image](https://github.com/user-attachments/assets/08dde48a-3bb8-41a1-9a0e-2c41716baf0b)

Resolves CC-248
  • Loading branch information
kasperkristensen authored Aug 20, 2024
1 parent b4d8e26 commit 3706bf5
Show file tree
Hide file tree
Showing 7 changed files with 106 additions and 56 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,24 @@ const Breadcrumbs = () => {
.map((match) => {
const handle = match.handle

let label: string | null = null

try {
label = handle.crumb!(match.data)
} catch (error) {
// noop
}

if (!label) {
return null
}

return {
label: handle.crumb!(match.data),
label: label,
path: match.pathname,
}
})
.filter(Boolean) as { label: string; path: string }[]

return (
<ol
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,7 @@ import { useTranslation } from "react-i18next"
import { Navigate, useLocation, useRouteError } from "react-router-dom"

import { isFetchError } from "../../../lib/is-fetch-error"
import { InlineTip } from "../../common/inline-tip"

// WIP - Need to allow wrapping <Outlet> with ErrorBoundary for more granular error handling.
export const ErrorBoundary = () => {
const error = useRouteError()
const location = useLocation()
Expand Down Expand Up @@ -45,47 +43,23 @@ export const ErrorBoundary = () => {
}

return (
<div className="flex size-full min-h-screen items-center justify-center">
<div className="text-ui-fg-subtle flex flex-col items-center gap-y-2">
<ExclamationCircle />
<Text size="small" leading="compact" weight="plus">
{title}
</Text>
<Text size="small" className="text-ui-fg-muted">
{message}
</Text>
<DevelopmentStack error={error} />
<div className="flex size-full min-h-[calc(100vh-57px-24px)] items-center justify-center">
<div className="flex flex-col gap-y-6">
<div className="text-ui-fg-subtle flex flex-col items-center gap-y-3">
<ExclamationCircle />
<div className="flex flex-col items-center justify-center gap-y-1">
<Text size="small" leading="compact" weight="plus">
{title}
</Text>
<Text
size="small"
className="text-ui-fg-muted text-balance text-center"
>
{message}
</Text>
</div>
</div>
</div>
</div>
)
}

/**
* Component that renders an error stack trace in development mode.
*
* We don't want to show stack traces in production, so this component is only
* rendered when the `NODE_ENV` is set to `development`.
*
* The reason for adding this is that `react-router-dom` can swallow certain types
* of errors, e.g. a missing export from a module that is exported, and will instead
* log a vague warning related to the route not exporting a Component.
*/
const DevelopmentStack = ({ error }: { error: unknown }) => {
const stack = error instanceof Error ? error.stack : null
const [stackType, stackMessage] = stack?.split(":") ?? ["", ""]
const isDevelopment = process.env.NODE_ENV === "development"

if (!isDevelopment) {
return null
}

return (
<InlineTip
variant="warning"
label={stackType}
className="mx-auto w-full max-w-[500px]"
>
<span className="font-mono">{stackMessage}</span>
</InlineTip>
)
}
16 changes: 9 additions & 7 deletions packages/admin-next/dashboard/src/i18n/translations/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -245,14 +245,16 @@
"addFilter": "Add filter"
},
"errorBoundary": {
"badRequestTitle": "Bad request",
"badRequestMessage": "The request was invalid.",
"notFoundTitle": "Not found",
"notFoundMessage": "The page you are looking for does not exist.",
"internalServerErrorTitle": "Internal server error",
"internalServerErrorMessage": "An error occurred on the server.",
"badRequestTitle": "400 - Bad request",
"badRequestMessage": "The request could not be understood by the server due to malformed syntax.",
"notFoundTitle": "404 - There is no page at this address",
"notFoundMessage": "Check the URL and try again, or use the search bar to find what you are looking for.",
"internalServerErrorTitle": "500 - Internal server error",
"internalServerErrorMessage": "An unexpected error occurred on the server. Please try again later.",
"defaultTitle": "An error occurred",
"defaultMessage": "An error occurred while rendering this page."
"defaultMessage": "An unexpected error occurred while rendering this page.",
"noMatchMessage": "The page you are looking for does not exist.",
"backToDashboard": "Back to dashboard"
},
"addresses": {
"shippingAddress": {
Expand Down
2 changes: 2 additions & 0 deletions packages/admin-next/dashboard/src/lib/extension-helpers.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { RouteObject } from "react-router-dom"
import { ErrorBoundary } from "../components/utilities/error-boundary"

/**
* Used to test if a route is a settings route.
Expand Down Expand Up @@ -32,6 +33,7 @@ export const createRouteMap = (
route.children ||= []
route.children.push({
path: "",
ErrorBoundary: ErrorBoundary,
async lazy() {
return { Component }
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export * from "./router-provider";
export * from "./router-provider"
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,6 @@ export const RouteMap: RouteObject[] = [
path: "/login",
lazy: () => import("../../routes/login"),
},
{
path: "/",
lazy: () => import("../../routes/home"),
},
{
path: "*",
lazy: () => import("../../routes/no-match"),
Expand All @@ -46,8 +42,14 @@ export const RouteMap: RouteObject[] = [
path: "/",
element: <MainLayout />,
children: [
{
index: true,
errorElement: <ErrorBoundary />,
lazy: () => import("../../routes/home"),
},
{
path: "/products",
errorElement: <ErrorBoundary />,
handle: {
crumb: () => "Products",
},
Expand Down Expand Up @@ -158,6 +160,7 @@ export const RouteMap: RouteObject[] = [
},
{
path: "/categories",
errorElement: <ErrorBoundary />,
handle: {
crumb: () => "Categories",
},
Expand Down Expand Up @@ -206,6 +209,7 @@ export const RouteMap: RouteObject[] = [
},
{
path: "/orders",
errorElement: <ErrorBoundary />,
handle: {
crumb: () => "Orders",
},
Expand Down Expand Up @@ -264,6 +268,7 @@ export const RouteMap: RouteObject[] = [
},
{
path: "/promotions",
errorElement: <ErrorBoundary />,
handle: {
crumb: () => "Promotions",
},
Expand Down Expand Up @@ -305,6 +310,7 @@ export const RouteMap: RouteObject[] = [
},
{
path: "/campaigns",
errorElement: <ErrorBoundary />,
handle: { crumb: () => "Campaigns" },
children: [
{
Expand Down Expand Up @@ -341,6 +347,7 @@ export const RouteMap: RouteObject[] = [
},
{
path: "/collections",
errorElement: <ErrorBoundary />,
handle: {
crumb: () => "Collections",
},
Expand Down Expand Up @@ -383,6 +390,7 @@ export const RouteMap: RouteObject[] = [
},
{
path: "/price-lists",
errorElement: <ErrorBoundary />,
handle: {
crumb: () => "Price Lists",
},
Expand Down Expand Up @@ -434,6 +442,7 @@ export const RouteMap: RouteObject[] = [
},
{
path: "/customers",
errorElement: <ErrorBoundary />,
handle: {
crumb: () => "Customers",
},
Expand Down Expand Up @@ -479,6 +488,7 @@ export const RouteMap: RouteObject[] = [
},
{
path: "/customer-groups",
errorElement: <ErrorBoundary />,
handle: {
crumb: () => "Customer Groups",
},
Expand Down Expand Up @@ -534,6 +544,7 @@ export const RouteMap: RouteObject[] = [
},
{
path: "/reservations",
errorElement: <ErrorBoundary />,
handle: {
crumb: () => "Reservations",
},
Expand Down Expand Up @@ -577,6 +588,7 @@ export const RouteMap: RouteObject[] = [
},
{
path: "/inventory",
errorElement: <ErrorBoundary />,
handle: {
crumb: () => "Inventory",
},
Expand Down Expand Up @@ -660,10 +672,12 @@ export const RouteMap: RouteObject[] = [
children: [
{
index: true,
errorElement: <ErrorBoundary />,
lazy: () => import("../../routes/settings"),
},
{
path: "profile",
errorElement: <ErrorBoundary />,
lazy: () => import("../../routes/profile/profile-detail"),
handle: {
crumb: () => "Profile",
Expand All @@ -677,6 +691,7 @@ export const RouteMap: RouteObject[] = [
},
{
path: "regions",
errorElement: <ErrorBoundary />,
element: <Outlet />,
handle: {
crumb: () => "Regions",
Expand Down Expand Up @@ -715,6 +730,7 @@ export const RouteMap: RouteObject[] = [
},
{
path: "store",
errorElement: <ErrorBoundary />,
lazy: () => import("../../routes/store/store-detail"),
handle: {
crumb: () => "Store",
Expand All @@ -736,6 +752,7 @@ export const RouteMap: RouteObject[] = [
},
{
path: "users",
errorElement: <ErrorBoundary />,
element: <Outlet />,
handle: {
crumb: () => "Users",
Expand Down Expand Up @@ -768,6 +785,7 @@ export const RouteMap: RouteObject[] = [
},
{
path: "sales-channels",
errorElement: <ErrorBoundary />,
element: <Outlet />,
handle: {
crumb: () => "Sales Channels",
Expand Down Expand Up @@ -814,6 +832,7 @@ export const RouteMap: RouteObject[] = [
},
{
path: "locations",
errorElement: <ErrorBoundary />,
element: <Outlet />,
handle: {
crumb: () => "Locations & Shipping",
Expand Down Expand Up @@ -865,6 +884,7 @@ export const RouteMap: RouteObject[] = [
},
{
path: "shipping-option-types",
errorElement: <ErrorBoundary />,
element: <Outlet />,
handle: {
crumb: () => "Shipping Option Types",
Expand Down Expand Up @@ -962,6 +982,7 @@ export const RouteMap: RouteObject[] = [
},
{
path: "product-tags",
errorElement: <ErrorBoundary />,
element: <Outlet />,
handle: {
crumb: () => "Product Tags",
Expand Down Expand Up @@ -999,6 +1020,7 @@ export const RouteMap: RouteObject[] = [
},
{
path: "workflows",
errorElement: <ErrorBoundary />,
element: <Outlet />,
handle: {
crumb: () => "Workflows",
Expand Down Expand Up @@ -1031,6 +1053,7 @@ export const RouteMap: RouteObject[] = [
},
{
path: "product-types",
errorElement: <ErrorBoundary />,
element: <Outlet />,
handle: {
crumb: () => "Product Types",
Expand Down Expand Up @@ -1069,6 +1092,7 @@ export const RouteMap: RouteObject[] = [

{
path: "publishable-api-keys",
errorElement: <ErrorBoundary />,
element: <Outlet />,
handle: {
crumb: () => "Publishable API Keys",
Expand Down Expand Up @@ -1128,6 +1152,7 @@ export const RouteMap: RouteObject[] = [
},
{
path: "secret-api-keys",
errorElement: <ErrorBoundary />,
element: <Outlet />,
handle: {
crumb: () => "Secret API Keys",
Expand Down Expand Up @@ -1180,6 +1205,7 @@ export const RouteMap: RouteObject[] = [
},
{
path: "tax-regions",
errorElement: <ErrorBoundary />,
element: <Outlet />,
handle: {
crumb: () => "Tax Regions",
Expand Down Expand Up @@ -1298,6 +1324,7 @@ export const RouteMap: RouteObject[] = [
},
{
path: "return-reasons",
errorElement: <ErrorBoundary />,
element: <Outlet />,
handle: {
crumb: () => "Return Reasons",
Expand Down
Loading

0 comments on commit 3706bf5

Please sign in to comment.