diff --git a/dashboard/app/components/layout/Error.module.css b/dashboard/app/components/layout/Error.module.css index a9cb86f..ee8390b 100644 --- a/dashboard/app/components/layout/Error.module.css +++ b/dashboard/app/components/layout/Error.module.css @@ -19,6 +19,12 @@ max-width: 500px; margin: auto; margin-bottom: 30px; + + color: var(--text-muted); + font-size: 18px; + text-align: center; + + line-height: 1.5; } .title { @@ -42,3 +48,10 @@ color: var(--text-dark); } } + +.error { + font-size: 18px; + color: var(--text-red); + + line-height: 1.5; +} diff --git a/dashboard/app/components/layout/Error.tsx b/dashboard/app/components/layout/Error.tsx index 9102e1f..affde63 100644 --- a/dashboard/app/components/layout/Error.tsx +++ b/dashboard/app/components/layout/Error.tsx @@ -1,4 +1,3 @@ -import { Anchor, Container, Text } from '@mantine/core'; import { Link } from '@remix-run/react'; import type { ReactNode } from 'react'; @@ -14,19 +13,34 @@ interface InternalServerErrorProps { error?: string; } +interface ErrorProps { + message?: string; +} + +class StatusError extends Error { + status: number; + + constructor(status: number, message: string) { + super(message); + this.status = status; + } +} + +const isStatusError = (error: unknown): error is StatusError => { + return error instanceof StatusError; +}; + const ErrorPage = ({ label, title, description }: ErrorPageProps) => { return ( - +
{label}

{title}

- - {description} - - +

{description}

+
); }; -const BadRequestError = () => ( +const BadRequestError = ({ message }: ErrorProps) => ( ( <> Please check the URL and try again. If you think this is an error, please{' '} - report this issue - {' '} + {' '} to the developers. + {message && ( +
+
+ + {message ? `Error: ${message}` : ''} + +
+ )} } /> @@ -80,16 +103,16 @@ const NotFoundError = () => ( description={ <> The page you're looking for isn't here. Feel free to return to the{' '} - + home page - {' '} + {' '} or browse the docs to find what you need. } /> ); -const ConflictError = () => ( +const ConflictError = ({ message }: ErrorProps) => ( ( <> There was a conflict while processing your request. Did you try to create something that already exists? + {message && ( +
+
+ + {message ? `Error: ${message}` : ''} + +
+ )} } /> @@ -109,18 +140,21 @@ const InternalServerError = ({ error }: InternalServerErrorProps) => ( description={ <> We encountered an unexpected error while processing your request. Please{' '} - report this issue - {' '} + {' '} to the developers. -
- - {error ? `Error: ${error}` : ''} - +
+
+ + {error ? `Error: ${error}` : ''} + +
} /> @@ -132,4 +166,6 @@ export { ForbiddenError, InternalServerError, NotFoundError, + StatusError, + isStatusError, }; diff --git a/dashboard/app/root.tsx b/dashboard/app/root.tsx index ca2b7e5..1210452 100644 --- a/dashboard/app/root.tsx +++ b/dashboard/app/root.tsx @@ -73,6 +73,7 @@ import { ForbiddenError, InternalServerError, NotFoundError, + isStatusError, } from '@/components/layout/Error'; import theme from '@/styles/theme'; import { EXPIRE_LOGGED_IN, hasSession } from '@/utils/cookies'; @@ -215,6 +216,45 @@ export const ErrorBoundary = () => { ); } + if (isStatusError(error)) { + switch (error.status) { + case 400: { + return ( + + + + ); + } + case 403: { + return ( + + + + ); + } + case 404: { + return ( + + + + ); + } + case 409: { + return ( + + + + ); + } + } + + return ( + + + + ); + } + if (error instanceof Error) { // If the error is due to a loader mismatch, reload the page as it may be // related to a bad cookie cache from the API restarting. This is probably diff --git a/dashboard/app/styles/global.css b/dashboard/app/styles/global.css index 3d14cc0..e2436bf 100644 --- a/dashboard/app/styles/global.css +++ b/dashboard/app/styles/global.css @@ -5,6 +5,7 @@ --text-disabled: #8e9cb4; --text-grey: #39414e; --text-muted: #7e8c94; + --text-red: #fa5252; /* Background Colors */ --bg-light: #fcfdfe; diff --git a/dashboard/app/utils/filters.ts b/dashboard/app/utils/filters.ts index f2604fb..3f081de 100644 --- a/dashboard/app/utils/filters.ts +++ b/dashboard/app/utils/filters.ts @@ -13,6 +13,8 @@ import { sub, } from 'date-fns'; +import { StatusError } from '@/components/layout/Error'; + interface FilterOptions { start?: string; end?: string; @@ -94,7 +96,7 @@ const generatePeriods = (searchParams: URLSearchParams) => { startPeriod = startOfHour(sub(currentDate, { hours })); endPeriod = endOfHour(currentDate); } else { - throw new Error(`Invalid period: ${period}`); + throw new StatusError(400, `Invalid time period: ${period}`); } } }