feat(frontend): slightly better error page
This commit is contained in:
		
							parent
							
								
									0f51f01b34
								
							
						
					
					
						commit
						bb76c24017
					
				
					 4 changed files with 72 additions and 40 deletions
				
			
		
							
								
								
									
										22
									
								
								Foxnouns.Frontend/app/components/nav/BaseNavbar.tsx
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								Foxnouns.Frontend/app/components/nav/BaseNavbar.tsx
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,22 @@ | ||||||
|  | import { ReactNode } from "react"; | ||||||
|  | import { Nav, Navbar } from "react-bootstrap"; | ||||||
|  | import { Link } from "@remix-run/react"; | ||||||
|  | import Logo from "~/components/nav/Logo"; | ||||||
|  | 
 | ||||||
|  | export default function BaseNavbar({ children, theme }: { children?: ReactNode; theme: string }) { | ||||||
|  | 	return ( | ||||||
|  | 		<Navbar expand="lg" className={`mb-4 mx-2 bg-${theme}`} color={theme} variant={theme}> | ||||||
|  | 			<Navbar.Brand to="/" as={Link}> | ||||||
|  | 				<Logo /> | ||||||
|  | 			</Navbar.Brand> | ||||||
|  | 			{children && ( | ||||||
|  | 				<> | ||||||
|  | 					<Navbar.Toggle aria-controls="main-navbar" /> | ||||||
|  | 					<Navbar.Collapse id="main-navbar"> | ||||||
|  | 						<Nav className="ms-auto">{children}</Nav> | ||||||
|  | 					</Navbar.Collapse> | ||||||
|  | 				</> | ||||||
|  | 			)} | ||||||
|  | 		</Navbar> | ||||||
|  | 	); | ||||||
|  | } | ||||||
|  | @ -1,11 +1,11 @@ | ||||||
| import { Link, useFetcher } from "@remix-run/react"; | import { Link, useFetcher } from "@remix-run/react"; | ||||||
| import Meta from "~/lib/api/meta"; | import Meta from "~/lib/api/meta"; | ||||||
| import { User, UserSettings } from "~/lib/api/user"; | import { User, UserSettings } from "~/lib/api/user"; | ||||||
| import Logo from "./Logo"; |  | ||||||
| 
 | 
 | ||||||
| import { Nav, Navbar, NavDropdown } from "react-bootstrap"; | import { Nav, NavDropdown } from "react-bootstrap"; | ||||||
| import { BrightnessHigh, BrightnessHighFill, MoonFill } from "react-bootstrap-icons"; | import { BrightnessHigh, BrightnessHighFill, MoonFill } from "react-bootstrap-icons"; | ||||||
| import { useTranslation } from "react-i18next"; | import { useTranslation } from "react-i18next"; | ||||||
|  | import BaseNavbar from "~/components/nav/BaseNavbar"; | ||||||
| 
 | 
 | ||||||
| export default function MainNavbar({ | export default function MainNavbar({ | ||||||
| 	user, | 	user, | ||||||
|  | @ -49,36 +49,28 @@ export default function MainNavbar({ | ||||||
| 	const theme = settings.dark_mode ? "dark" : "light"; | 	const theme = settings.dark_mode ? "dark" : "light"; | ||||||
| 
 | 
 | ||||||
| 	return ( | 	return ( | ||||||
| 		<Navbar expand="lg" className={`mb-4 mx-2 bg-${theme}`} color={theme} variant={theme}> | 		<BaseNavbar theme={theme}> | ||||||
| 			<Navbar.Brand to="/" as={Link}> | 			{userMenu} | ||||||
| 				<Logo /> | 			<fetcher.Form method="POST" action="/dark-mode"> | ||||||
| 			</Navbar.Brand> | 				<NavDropdown | ||||||
| 			<Navbar.Toggle aria-controls="main-navbar" /> | 					title={ | ||||||
| 			<Navbar.Collapse id="main-navbar"> | 						<> | ||||||
| 				<Nav className="ms-auto"> | 							<ThemeIcon /> {t("navbar.theme")} | ||||||
| 					{userMenu} | 						</> | ||||||
| 					<fetcher.Form method="POST" action="/dark-mode"> | 					} | ||||||
| 						<NavDropdown | 					align="end" | ||||||
| 							title={ | 				> | ||||||
| 								<> | 					<NavDropdown.Item as="button" name="theme" value="auto" type="submit"> | ||||||
| 									<ThemeIcon /> {t("navbar.theme")} | 						{t("navbar.theme-auto")} | ||||||
| 								</> | 					</NavDropdown.Item> | ||||||
| 							} | 					<NavDropdown.Item as="button" name="theme" value="dark" type="submit"> | ||||||
| 							align="end" | 						{t("navbar.theme-dark")} | ||||||
| 						> | 					</NavDropdown.Item> | ||||||
| 							<NavDropdown.Item as="button" name="theme" value="auto" type="submit"> | 					<NavDropdown.Item as="button" name="theme" value="light" type="submit"> | ||||||
| 								{t("navbar.theme-auto")} | 						{t("navbar.theme-light")} | ||||||
| 							</NavDropdown.Item> | 					</NavDropdown.Item> | ||||||
| 							<NavDropdown.Item as="button" name="theme" value="dark" type="submit"> | 				</NavDropdown> | ||||||
| 								{t("navbar.theme-dark")} | 			</fetcher.Form> | ||||||
| 							</NavDropdown.Item> | 		</BaseNavbar> | ||||||
| 							<NavDropdown.Item as="button" name="theme" value="light" type="submit"> |  | ||||||
| 								{t("navbar.theme-light")} |  | ||||||
| 							</NavDropdown.Item> |  | ||||||
| 						</NavDropdown> |  | ||||||
| 					</fetcher.Form> |  | ||||||
| 				</Nav> |  | ||||||
| 			</Navbar.Collapse> |  | ||||||
| 		</Navbar> |  | ||||||
| 	); | 	); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -24,6 +24,8 @@ import getLocalSettings from "./lib/settings.server"; | ||||||
| import { LANGUAGE } from "~/env.server"; | import { LANGUAGE } from "~/env.server"; | ||||||
| import { errorCodeDesc } from "./components/ErrorAlert"; | import { errorCodeDesc } from "./components/ErrorAlert"; | ||||||
| import { Container } from "react-bootstrap"; | import { Container } from "react-bootstrap"; | ||||||
|  | import { ReactNode } from "react"; | ||||||
|  | import BaseNavbar from "~/components/nav/BaseNavbar"; | ||||||
| 
 | 
 | ||||||
| export const loader = async ({ request }: LoaderFunctionArgs) => { | export const loader = async ({ request }: LoaderFunctionArgs) => { | ||||||
| 	const meta = await serverRequest<Meta>("GET", "/meta"); | 	const meta = await serverRequest<Meta>("GET", "/meta"); | ||||||
|  | @ -54,7 +56,7 @@ export const loader = async ({ request }: LoaderFunctionArgs) => { | ||||||
| 	); | 	); | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| export function Layout({ children }: { children: React.ReactNode }) { | export function Layout({ children }: { children: ReactNode }) { | ||||||
| 	const { locale, settings } = useRouteLoaderData<typeof loader>("root") || { | 	const { locale, settings } = useRouteLoaderData<typeof loader>("root") || { | ||||||
| 		meta: { | 		meta: { | ||||||
| 			users: { | 			users: { | ||||||
|  | @ -94,6 +96,8 @@ export function Layout({ children }: { children: React.ReactNode }) { | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export function ErrorBoundary() { | export function ErrorBoundary() { | ||||||
|  | 	const data = useRouteLoaderData<typeof loader>("root"); | ||||||
|  | 
 | ||||||
| 	// eslint-disable-next-line @typescript-eslint/no-explicit-any
 | 	// eslint-disable-next-line @typescript-eslint/no-explicit-any
 | ||||||
| 	const error: any = useRouteError(); | 	const error: any = useRouteError(); | ||||||
| 	const { t } = useTranslation(); | 	const { t } = useTranslation(); | ||||||
|  | @ -110,14 +114,17 @@ export function ErrorBoundary() { | ||||||
| 	return ( | 	return ( | ||||||
| 		<html lang="en"> | 		<html lang="en"> | ||||||
| 			<head> | 			<head> | ||||||
| 				<title> | 				<title>{t("error.title")}</title> | ||||||
| 					<>{t("error.title")} - pronouns.cc</> |  | ||||||
| 				</title> |  | ||||||
| 				<MetaComponent /> | 				<MetaComponent /> | ||||||
| 				<Links /> | 				<Links /> | ||||||
| 			</head> | 			</head> | ||||||
| 			<body> | 			<body> | ||||||
| 				{errorElem} | 				{data?.meUser && data?.settings && data?.meta ? ( | ||||||
|  | 					<Navbar meta={data.meta} user={data.meUser} settings={data.settings} /> | ||||||
|  | 				) : ( | ||||||
|  | 					<BaseNavbar theme="light" /> | ||||||
|  | 				)} | ||||||
|  | 				<Container>{errorElem}</Container> | ||||||
| 				<Scripts /> | 				<Scripts /> | ||||||
| 			</body> | 			</body> | ||||||
| 		</html> | 		</html> | ||||||
|  | @ -130,8 +137,14 @@ function ApiErrorElem({ error }: { error: ApiError }) { | ||||||
| 
 | 
 | ||||||
| 	return ( | 	return ( | ||||||
| 		<> | 		<> | ||||||
| 			<h4>{t("error.heading")}</h4> | 			<h1>{t("error.heading")}</h1> | ||||||
| 			<p>{errorDesc}</p> | 			<p>{errorDesc}</p> | ||||||
|  | 			<details> | ||||||
|  | 				<summary>{t("error.more-info")}</summary> | ||||||
|  | 				<pre> | ||||||
|  | 					<code>{JSON.stringify(error, null, "  ")}</code> | ||||||
|  | 				</pre> | ||||||
|  | 			</details> | ||||||
| 		</> | 		</> | ||||||
| 	); | 	); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -18,7 +18,8 @@ | ||||||
|       "member-not-found": "Member not found, please check your spelling and try again.", |       "member-not-found": "Member not found, please check your spelling and try again.", | ||||||
|       "user-not-found": "User not found, please check your spelling and try again." |       "user-not-found": "User not found, please check your spelling and try again." | ||||||
|     }, |     }, | ||||||
|     "title": "Error" |     "title": "An error occurred", | ||||||
|  |     "more-info": "Click here for a more detailed error" | ||||||
|   }, |   }, | ||||||
|   "navbar": { |   "navbar": { | ||||||
|     "view-profile": "View profile", |     "view-profile": "View profile", | ||||||
|  | @ -66,9 +67,13 @@ | ||||||
|   "welcome": { |   "welcome": { | ||||||
|     "title": "Welcome", |     "title": "Welcome", | ||||||
|     "header": "Welcome to pronouns.cc!", |     "header": "Welcome to pronouns.cc!", | ||||||
|  |     "blurb": "{welcome.blurb}", | ||||||
|     "customize-profile": "Customize your profile", |     "customize-profile": "Customize your profile", | ||||||
|  |     "customize-profile-blurb": "{welcome.customize-profile-blurb}", | ||||||
|     "create-members": "Create members", |     "create-members": "Create members", | ||||||
|  |     "create-members-blurb": "{welcome.create-members-blurb}", | ||||||
|     "custom-preferences": "Customize your preferences", |     "custom-preferences": "Customize your preferences", | ||||||
|  |     "custom-preferences-blurb": "{welcome.custom-preferences-blurb}", | ||||||
|     "profile-button": "Go to your profile" |     "profile-button": "Go to your profile" | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue