-
-
Notifications
You must be signed in to change notification settings - Fork 6
perf: Improve error handling #731
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| "use client"; | ||
|
|
||
| import { | ||
| RouteErrorView, | ||
| type RouteErrorViewProps, | ||
| } from "@vitnode/core/views/error/route-error-view"; | ||
|
|
||
| export default function ErrorBoundary({ error, retry }: RouteErrorViewProps) { | ||
| return <RouteErrorView error={error} retry={retry} />; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| import { Loader } from "@vitnode/core/components/ui/loader"; | ||
|
|
||
| export default function Loading() { | ||
| return ( | ||
| <div className="container mx-auto flex items-center justify-center p-4"> | ||
| <Loader /> | ||
| <span className="sr-only">Loading</span> | ||
| </div> | ||
| ); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| "use client"; | ||
|
|
||
| import { | ||
| RouteErrorView, | ||
| type RouteErrorViewProps, | ||
| } from "@vitnode/core/views/error/route-error-view"; | ||
|
|
||
| export default function ErrorBoundary({ error, retry }: RouteErrorViewProps) { | ||
| return <RouteErrorView error={error} retry={retry} />; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| "use client"; | ||
|
|
||
| import { | ||
| RouteErrorView, | ||
| type RouteErrorViewProps, | ||
| } from "@vitnode/core/views/error/route-error-view"; | ||
|
|
||
| export default function ErrorBoundary({ error, retry }: RouteErrorViewProps) { | ||
| return <RouteErrorView error={error} retry={retry} />; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| "use client"; | ||
|
|
||
| import { | ||
| RouteErrorView, | ||
| type RouteErrorViewProps, | ||
| } from "@vitnode/core/views/error/route-error-view"; | ||
|
|
||
| export default function ErrorBoundary({ error, retry }: RouteErrorViewProps) { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
This and the other newly added AGENTS.md reference: AGENTS.md:L7-L7 Useful? React with 👍 / 👎. |
||
| return <RouteErrorView error={error} retry={retry} />; | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| import { Skeleton } from "@vitnode/core/components/ui/skeleton"; | ||
|
|
||
| const Row = ({ center }: { center?: boolean }) => ( | ||
| <div className="flex flex-row items-center justify-between"> | ||
| <Skeleton className="size-12" /> | ||
| <Skeleton className={center ? "size-16" : "size-12"} /> | ||
| <Skeleton className="size-12" /> | ||
| </div> | ||
| ); | ||
|
|
||
| export const AnimatedBeamHomeSkeleton = () => ( | ||
| <div | ||
| aria-busy="true" | ||
| className="relative flex w-full items-center justify-center overflow-hidden p-4 sm:max-w-md" | ||
| > | ||
| <div className="flex size-full max-w-lg flex-col items-stretch justify-between gap-10"> | ||
| <Row /> | ||
| <Row center /> | ||
| <Row /> | ||
| </div> | ||
|
|
||
| <span className="sr-only">Loading</span> | ||
| </div> | ||
| ); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| import { Loader } from "@/components/ui/loader"; | ||
|
|
||
| export default function Loading() { | ||
| return ( | ||
| <div className="container mx-auto flex items-center justify-center p-4"> | ||
| <Loader /> | ||
| <span className="sr-only">Loading</span> | ||
| </div> | ||
| ); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -272,7 +272,6 @@ export const DashboardBoardProvider = ({ | |
| } | ||
|
|
||
| setIsEditing(false); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
After a successful layout mutation, this path now exits editing and refreshes without confirming that the update was saved. Restore a AGENTS.md reference: AGENTS.md:L11-L11 Useful? React with 👍 / 👎. |
||
| toast.success(t("saved_title"), { description: t("saved_desc") }); | ||
| router.refresh(); | ||
| }); | ||
| }; | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -121,14 +121,6 @@ export const WidgetSettingsDialog = ({ | |
| } | ||
|
|
||
| setOpen(false); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When widget settings save successfully, the dialog now closes and schedules the card refresh without showing any confirmation; users cannot distinguish a completed save from a dismissal until they inspect the widget. Restore the removed AGENTS.md reference: AGENTS.md:L11-L11 Useful? React with 👍 / 👎. |
||
| toast.success(t("settings.saved_title"), { | ||
| description: t("settings.saved_desc"), | ||
| }); | ||
|
|
||
| // Left until the dialog has finished closing. The card suspends | ||
| // while it is re-rendered, and a suspended render mid-animation | ||
| // strands the overlay on screen - batched into this transition it | ||
| // would also hold the close back until the new card was ready. | ||
| setTimeout(onSaved, 300); | ||
| } finally { | ||
| resolve(); | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,17 +1,35 @@ | ||
| "use client"; | ||
|
|
||
| import type { Metadata } from "next/dist/types"; | ||
|
|
||
| import { HomeIcon, RefreshCwIcon } from "lucide-react"; | ||
| // eslint-disable-next-line no-restricted-imports | ||
| import Link from "next/link"; | ||
| import { useTransition } from "react"; | ||
|
|
||
| import { LogoVitNode } from "@/components/logo-vitnode"; | ||
| import { ThemeProvider } from "@/components/theme-provider"; | ||
| import { Button, buttonVariants } from "@/components/ui/button"; | ||
| import { Card, CardContent } from "@/components/ui/card"; | ||
| import { cn } from "@/lib/utils"; | ||
|
|
||
| export const metadata: Metadata = { | ||
| title: "Error 500!", | ||
| }; | ||
|
|
||
| export const GlobalErrorView = ({ className }: { className?: string }) => { | ||
| export interface GlobalErrorViewProps { | ||
| className?: string; | ||
| error: Error & { digest?: string }; | ||
| retry: () => void; | ||
| } | ||
|
|
||
| export const GlobalErrorView = ({ | ||
| className, | ||
| error, | ||
| retry, | ||
| }: GlobalErrorViewProps) => { | ||
| const [isRetrying, startRetry] = useTransition(); | ||
|
|
||
| return ( | ||
| <html lang="en" suppressHydrationWarning> | ||
| <body className={className}> | ||
|
|
@@ -22,16 +40,49 @@ export const GlobalErrorView = ({ className }: { className?: string }) => { | |
| <LogoVitNode className="mb-4 h-12 w-auto" /> | ||
| </Link> | ||
|
|
||
| <h1 className="text-3xl font-semibold tracking-tight"> | ||
| <h1 className="text-3xl font-semibold tracking-tight text-balance"> | ||
| Oops! Something went wrong. | ||
| </h1> | ||
|
|
||
| <Card className="w-full"> | ||
| <CardContent> | ||
| <p className="text-muted-foreground"> | ||
| An unexpected error occurred. Please try refreshing the page | ||
| or come back later. | ||
| <CardContent className="flex flex-col gap-4"> | ||
| <p className="text-muted-foreground leading-relaxed text-pretty"> | ||
| An unexpected error occurred. Try again, and if it keeps | ||
| happening come back a little later. | ||
| </p> | ||
|
|
||
| {error.digest ? ( | ||
| <p className="text-muted-foreground/70 font-mono text-xs"> | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The error digest is rendered with Tailwind's AGENTS.md reference: AGENTS.md:L50-L50 Useful? React with 👍 / 👎. |
||
| Error reference: {error.digest} | ||
| </p> | ||
| ) : null} | ||
|
|
||
| <div className="flex flex-col items-center justify-center gap-3 sm:flex-row"> | ||
| <Button | ||
| isLoading={isRetrying} | ||
| onClick={() => { | ||
| // `retry` runs its own transition; wrapping it keeps | ||
| // the button in a pending state until it settles. | ||
| startRetry(() => { | ||
| retry(); | ||
| }); | ||
| }} | ||
| size="lg" | ||
| > | ||
| <RefreshCwIcon /> | ||
| Try again | ||
| </Button> | ||
|
|
||
| <Link | ||
| className={cn( | ||
| buttonVariants({ size: "lg", variant: "ghost" }), | ||
| )} | ||
| href="/" | ||
| > | ||
| <HomeIcon /> | ||
| Back to home | ||
| </Link> | ||
| </div> | ||
| </CardContent> | ||
| </Card> | ||
| </div> | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When any new route boundary, modified global boundary, or generated-app boundary renders, Next.js supplies a
resetcallback rather thanretry; destructuringretrytherefore passesundefinedinto the view, and clicking “Try again” throws instead of rerendering the failed segment. Acceptresetand pass it through, optionally renaming it during destructuring.Useful? React with 👍 / 👎.