feat: add error boundary

This commit is contained in:
Dan Ditomaso
2025-03-03 20:32:47 -05:00
parent f9066eced0
commit d28b4ce6d9
18 changed files with 342 additions and 194 deletions
+81
View File
@@ -0,0 +1,81 @@
import newGithubIssueUrl from "@app/core/utils/github";
import { ExternalLink } from "lucide-react";
import { Heading } from "./Typography/Heading";
import { Link } from "./Typography/Link";
import { P } from "./Typography/P";
export function ErrorPage({ error }: { error: Error }) {
if (!error) {
return null;
}
return (
<article>
<section className="prose mx-auto mb-20 mt-28 max-w-prose px-8 text-2xl transition-all duration-150 ease-linear space-y-2">
<Heading as="h2" className="text-text-primary">
This is a little embarrassing...
</Heading>
<P>
We are really sorry but an error occured in the web client that caused
it to crash. This is not supposed to happen and we are working hard to
fix it.
</P>
<P>
The best way to prevent this from happening again to you or anyone
else is to report the issue to us.
</P>
<P>Please include the following information in your report:</P>
<ul className="list-disc list-inside text-sm">
<li>What you were doing when the error occured</li>
<li>What you expected to happen</li>
<li>What actually happened</li>
<li>Any other information you think might be relevant</li>
</ul>
<P>
You can report the issue to our{" "}
<Link
href={newGithubIssueUrl({
repoUrl: "https://github.com/meshtastic/web",
template: "bug.yml",
title: "[Bug]: An unhandled error occurred. <Add details here>",
logs: error?.stack,
})}
>
Github
</Link>
<ExternalLink size={24} className="inline-block ml-2" />
</P>
<P>
Return to the <Link href="/">dashboard</Link>
</P>
<details className="mt-6 text-md">
<summary className="cursor-pointer">Error Details</summary>
<span className="block text-sm mt-4 overflow-auto">
{error?.message ? (
<>
<label htmlFor="message">Error message:</label>
<pre
id="message"
className="w-full text-slate-400"
>{`${error.message}`}</pre>
</>
) : null}
{error?.stack ? (
<>
<label htmlFor="stack">Stack trace:</label>
<pre
id="stack"
className="w-full text-slate-400"
>{`${error.stack}`}</pre>
</>
) : null}
{!error?.message && !error?.stack ? (
<pre className=" w-full text-slate-400">{error.toString()}</pre>
) : null}
</span>
</details>
</section>
</article>
);
}
+4 -2
View File
@@ -1,4 +1,4 @@
import { H4 } from "@components/UI/Typography/H4.tsx";
import { Heading } from "../Typography/Heading";
export interface SidebarSectionProps {
label: string;
@@ -11,7 +11,9 @@ export const SidebarSection = ({
children,
}: SidebarSectionProps) => (
<div className="px-4 py-2">
<H4 className="mb-3 ml-2">{title}</H4>
<Heading as="h4" className="mb-3 ml-2">
{title}
</Heading>
<div className="space-y-1">{children}</div>
</div>
);
-9
View File
@@ -1,9 +0,0 @@
export interface H1Props {
children: React.ReactNode;
}
export const H1 = ({ children }: H1Props) => (
<h1 className="scroll-m-20 text-4xl font-extrabold tracking-tight lg:text-5xl">
{children}
</h1>
);
-9
View File
@@ -1,9 +0,0 @@
export interface H2Props {
children: React.ReactNode;
}
export const H2 = ({ children }: H2Props) => (
<h2 className="scroll-m-20 border-b border-b-slate-200 pb-2 text-3xl font-semibold tracking-tight transition-colors first:mt-0 dark:border-b-slate-700">
{children}
</h2>
);
-9
View File
@@ -1,9 +0,0 @@
export interface H3Props {
children: React.ReactNode;
}
export const H3 = ({ children }: H3Props) => (
<h3 className="scroll-m-20 text-2xl font-semibold tracking-tight">
{children}
</h3>
);
-17
View File
@@ -1,17 +0,0 @@
import { cn } from "@app/core/utils/cn.ts";
export interface H4Props {
className?: string;
children: React.ReactNode;
}
export const H4 = ({ className, children }: H4Props) => (
<h4
className={cn(
"scroll-m-20 text-xl font-semibold tracking-tight",
className,
)}
>
{children}
</h4>
);
-14
View File
@@ -1,14 +0,0 @@
import { cn } from "@app/core/utils/cn.ts";
export interface H5Props {
className?: string;
children: React.ReactNode;
}
export const H5 = ({ className, children }: H5Props) => (
<h5
className={cn("scroll-m-20 text-lg font-medium tracking-tight", className)}
>
{children}
</h5>
);
+30
View File
@@ -0,0 +1,30 @@
import type React from "react";
const headingStyles = {
h1: "scroll-m-20 text-4xl font-extrabold tracking-tight lg:text-5xl",
h2: "scroll-m-20 border-b border-b-slate-200 pb-2 text-3xl font-semibold tracking-tight transition-colors first:mt-0 dark:border-b-slate-700",
h3: "scroll-m-20 text-2xl font-semibold tracking-tight",
h4: "scroll-m-20 text-xl font-semibold tracking-tight",
h5: "scroll-m-20 text-lg font-medium tracking-tight",
};
interface HeadingProps {
as?: "h1" | "h2" | "h3" | "h4" | "h5";
children: React.ReactNode;
className?: string;
}
export const Heading = ({
as: Component = "h1",
children,
className = "",
...props
}: HeadingProps) => {
const baseStyles = headingStyles[Component] || headingStyles.h1;
return (
<Component className={`${baseStyles} ${className}`} {...props}>
{children}
</Component>
);
};