Skip to content

Commit fa2a50e

Browse files
committed
feat: better error page
1 parent ed26d46 commit fa2a50e

File tree

2 files changed

+97
-37
lines changed

2 files changed

+97
-37
lines changed

packages/actions-app/app/root.tsx

Lines changed: 96 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
import { AnimatePresence } from 'framer-motion';
22
import React, { useContext, useEffect } from 'react';
33
import { withEmotionCache } from '@emotion/react';
4-
import { ChakraProvider } from '@chakra-ui/react';
5-
import { Links, LiveReload, Meta, Outlet, Scripts, ScrollRestoration, useCatch, useLoaderData } from '@remix-run/react';
4+
import { Box, Button, ChakraProvider, Heading, Text, VStack } from '@chakra-ui/react';
5+
import { Link, Links, LiveReload, Meta, Outlet, Scripts, ScrollRestoration, useCatch, useLoaderData } from '@remix-run/react';
66
import { MetaFunction, LinksFunction, json } from '@remix-run/node';
77

88
import { ServerStyleContext, ClientStyleContext } from './context';
9+
import { getBaseUrl } from './utils/routes';
910

1011
export const meta: MetaFunction = () => ({
1112
charset: 'utf-8',
@@ -33,13 +34,14 @@ export const links: LinksFunction = () => {
3334
};
3435

3536
interface DocumentProps {
37+
head?: React.ReactNode;
3638
children: React.ReactNode;
3739
}
3840

39-
const Document = withEmotionCache(({ children }: DocumentProps, emotionCache) => {
41+
const Document = withEmotionCache(({ children, head }: DocumentProps, emotionCache) => {
42+
const data = useLoaderData();
4043
const serverStyleData = useContext(ServerStyleContext);
4144
const clientStyleData = useContext(ClientStyleContext);
42-
const data = useLoaderData();
4345

4446
// Only executed on client
4547
useEffect(() => {
@@ -58,18 +60,21 @@ const Document = withEmotionCache(({ children }: DocumentProps, emotionCache) =>
5860
return (
5961
<html lang="en">
6062
<head>
63+
{head}
6164
<Meta />
6265
<Links />
6366
{serverStyleData?.map(({ key, ids, css }) => (
6467
<style key={key} data-emotion={`${key} ${ids.join(' ')}`} dangerouslySetInnerHTML={{ __html: css }} />
6568
))}
6669
</head>
6770
<body>
68-
{children}
69-
<script dangerouslySetInnerHTML={{ __html: `window.ENV = ${JSON.stringify(data.ENV)}` }} />
71+
<ChakraProvider>
72+
{children}
73+
</ChakraProvider>
74+
<script dangerouslySetInnerHTML={{ __html: `window.ENV = ${JSON.stringify(data?.ENV)}` }} />
7075
<ScrollRestoration />
7176
<Scripts />
72-
<LiveReload />
77+
{process.env.NODE_ENV === 'development' ? <LiveReload /> : null}
7378
</body>
7479
</html>
7580
);
@@ -78,47 +83,102 @@ const Document = withEmotionCache(({ children }: DocumentProps, emotionCache) =>
7883
export default function App() {
7984
return (
8085
<Document>
81-
<ChakraProvider>
82-
<AnimatePresence exitBeforeEnter>
83-
<Outlet />
84-
</AnimatePresence>
85-
</ChakraProvider>
86+
<AnimatePresence exitBeforeEnter>
87+
<Outlet />
88+
</AnimatePresence>
8689
</Document>
8790
);
8891
}
8992

9093
export function ErrorBoundary({ error }: { error: Error }) {
9194
console.error(error);
9295
return (
93-
<html>
94-
<head>
95-
<title>Oh no!</title>
96-
<Meta />
97-
<Links />
98-
</head>
99-
<body>
100-
<div>An unexpected error occurred: {error.message}</div>
101-
<Scripts />
102-
</body>
103-
</html>
96+
<Document>
97+
<VStack h="100vh" justify="center">
98+
<Heading>There was an error</Heading>
99+
<Text>{error.message}</Text>
100+
<hr />
101+
<Text>
102+
<div>An unexpected error occurred: {error.message}</div>
103+
</Text>
104+
</VStack>
105+
</Document>
104106
);
105107
}
106108

107109
export function CatchBoundary() {
108110
const caught = useCatch();
111+
let message;
112+
switch (caught.status) {
113+
case 401: {
114+
message = (
115+
<Box textAlign="center" py={10} px={6}>
116+
<Heading
117+
display="inline-block"
118+
as="h2"
119+
size="2xl"
120+
bgGradient="linear(to-r, purple.400, purple.600)"
121+
backgroundClip="text">
122+
No Access
123+
</Heading>
124+
<Text color={'gray.500'} mb={6}>
125+
The page you are looking for does not seem to exist
126+
</Text>
127+
128+
<Button
129+
colorScheme="purple"
130+
bgGradient="linear(to-r, purple.400, purple.500, purple.600)"
131+
color="white"
132+
variant="solid">
133+
Go to Home
134+
</Button>
135+
</Box>
136+
);
137+
break;
138+
}
139+
case 404: {
140+
message = (
141+
<Box textAlign="center" py={10} px={6}>
142+
<Heading
143+
display="inline-block"
144+
as="h1"
145+
size="4xl"
146+
bgGradient="linear(to-r, purple.400, purple.600)"
147+
backgroundClip="text">
148+
404
149+
</Heading>
150+
<Text fontSize="24px" mt={3} mb={2}>
151+
Page Not Found
152+
</Text>
153+
<Text color={'gray.500'} mb={6}>
154+
The page you are looking for does not seem to exist
155+
</Text>
156+
157+
<Button
158+
as={Link}
159+
colorScheme="purple"
160+
bgGradient="linear(to-r, purple.400, purple.500, purple.600)"
161+
color="white"
162+
variant="solid"
163+
to={getBaseUrl()}
164+
>
165+
Go to Home
166+
</Button>
167+
</Box>
168+
);
169+
break;
170+
}
171+
172+
default: {
173+
throw new Error(caught.data || caught.statusText);
174+
}
175+
}
176+
109177
return (
110-
<html>
111-
<head>
112-
<title>Oops!</title>
113-
<Meta />
114-
<Links />
115-
</head>
116-
<body>
117-
<h1>
118-
{caught.status} {caught.statusText}
119-
</h1>
120-
<Scripts />
121-
</body>
122-
</html>
178+
<Document>
179+
<VStack h="100vh" justify="center">
180+
{message}
181+
</VStack>
182+
</Document>
123183
);
124184
}

packages/actions-app/app/utils/routes.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ declare global {
77
}
88

99
export function getBaseUrl(): string {
10-
const envUrl = typeof window === 'undefined' ? process.env['ACTIONS_BASE_URL'] : window.ENV['ACTIONS_BASE_URL'];
10+
const envUrl = typeof window === 'undefined' ? process.env['ACTIONS_BASE_URL'] : window.ENV?.['ACTIONS_BASE_URL'];
1111
const base = envUrl || '/admin';
1212
if (base === '/') {
1313
return '';

0 commit comments

Comments
 (0)