mirror of
https://github.com/TheRealOwenRees/chess-scribe-website.git
synced 2026-07-23 01:46:57 +00:00
generic error view with try again
This commit is contained in:
@@ -0,0 +1,68 @@
|
||||
import { useNavigate, useRouter } from "@tanstack/react-router";
|
||||
|
||||
interface GenericErrorViewProps {
|
||||
error?: Error | unknown;
|
||||
resetErrorBoundary?: () => void;
|
||||
}
|
||||
|
||||
const GenericErrorView = ({
|
||||
error,
|
||||
resetErrorBoundary,
|
||||
}: GenericErrorViewProps) => {
|
||||
const router = useRouter();
|
||||
const navigate = useNavigate();
|
||||
|
||||
const errorMessage =
|
||||
error instanceof Error
|
||||
? error.message
|
||||
: "An unexpected system error occurred.";
|
||||
|
||||
const handleTryAgain = async () => {
|
||||
if (resetErrorBoundary) {
|
||||
resetErrorBoundary();
|
||||
}
|
||||
|
||||
await router.invalidate();
|
||||
const currentSearch = router.state.location.search;
|
||||
await navigate({ to: ".", search: currentSearch });
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex min-h-[calc(100vh-194px)] flex-col items-center justify-center p-6">
|
||||
<div className="relative flex max-w-md flex-col items-center text-center">
|
||||
<h1 className="text-2xl font-bold tracking-tight mb-2 text-(--accent)">
|
||||
Something went wrong
|
||||
</h1>
|
||||
<p className="text-sm text-(--neutral-content) mb-6 max-w-sm">
|
||||
The application encountered an issue it couldn't recover from.
|
||||
</p>
|
||||
|
||||
<div className="w-full mb-8 rounded-md bg-slate-300/60 border border-slate-800/80 p-4 text-left font-mono text-xs text-red-400 max-h-32 overflow-y-auto">
|
||||
<span className="text-(--base-content) block mb-1">Error Logs:</span>
|
||||
{errorMessage}
|
||||
</div>
|
||||
|
||||
<div className="flex w-full gap-3 justify-center">
|
||||
{resetErrorBoundary && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={handleTryAgain}
|
||||
className="btn btn-secondary"
|
||||
>
|
||||
Try Again
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => navigate({ to: "/" })}
|
||||
className="btn btn-primary"
|
||||
>
|
||||
Go Home
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default GenericErrorView;
|
||||
+31
-14
@@ -1,4 +1,3 @@
|
||||
import { useNavigate } from "@tanstack/react-router";
|
||||
import { useServerFn } from "@tanstack/react-start";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useLichessUser } from "#/context/LichessUserContext.tsx";
|
||||
@@ -14,6 +13,8 @@ import {
|
||||
} from "#/server/lichess.ts";
|
||||
import { getHeaders } from "#/utils/pgnUtils.ts";
|
||||
|
||||
type Status = "loading" | "success" | "error";
|
||||
|
||||
interface ITokenData {
|
||||
access_token: string;
|
||||
expires_in: number;
|
||||
@@ -85,24 +86,40 @@ export const useLichess = () => {
|
||||
return getSessionFn();
|
||||
};
|
||||
|
||||
const useLichessCallback = async ({ code }: { code: string }) => {
|
||||
const navigate = useNavigate();
|
||||
const useLichessCallback = ({ code }: { code: string }) => {
|
||||
const [status, setStatus] = useState<Status>("loading");
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!code) return;
|
||||
|
||||
(async () => {
|
||||
const tokenData = await lichessAccessToken({ code });
|
||||
const userData = await getLichessUser({ tokenData });
|
||||
const isMounted = true;
|
||||
|
||||
await setLichessUser({
|
||||
username: userData.username,
|
||||
id: userData.id,
|
||||
token: tokenData.access_token,
|
||||
});
|
||||
await navigate({ to: "/chessboard" });
|
||||
})();
|
||||
}, [navigate, code]);
|
||||
try {
|
||||
(async () => {
|
||||
const tokenData = await lichessAccessToken({ code });
|
||||
const userData = await getLichessUser({ tokenData });
|
||||
|
||||
if (!isMounted) return;
|
||||
|
||||
await setLichessUser({
|
||||
username: userData.username,
|
||||
id: userData.id,
|
||||
token: tokenData.access_token,
|
||||
});
|
||||
|
||||
setStatus("success");
|
||||
})();
|
||||
} catch (err: unknown) {
|
||||
if (isMounted && err instanceof Error) {
|
||||
console.error("Lichess Authentication Error:", err);
|
||||
setError(err.message || "Failed to authenticate with Lichess");
|
||||
setStatus("error");
|
||||
}
|
||||
}
|
||||
}, [code]);
|
||||
|
||||
return { status, error };
|
||||
};
|
||||
|
||||
const getLichessUserStudies = async () => {
|
||||
|
||||
@@ -1,14 +1,19 @@
|
||||
import { getRouteApi } from "@tanstack/react-router";
|
||||
import { getRouteApi, useNavigate } from "@tanstack/react-router";
|
||||
import { useLichess } from "#/hooks/useLichess.ts";
|
||||
|
||||
const routeApi = getRouteApi("/callback");
|
||||
|
||||
const Callback = () => {
|
||||
const navigate = useNavigate();
|
||||
const { useLichessCallback } = useLichess();
|
||||
const { code } = routeApi.useSearch();
|
||||
const { status, error } = useLichessCallback({ code });
|
||||
|
||||
// TODO deal with error if code is invalid
|
||||
useLichessCallback({ code }).then();
|
||||
if (status === "success") navigate({ to: "/chessboard" });
|
||||
|
||||
if (status === "error") {
|
||||
console.error(error);
|
||||
}
|
||||
|
||||
return <div>Loading...</div>;
|
||||
};
|
||||
|
||||
@@ -3,6 +3,7 @@ import { createRootRoute, HeadContent, Scripts } from "@tanstack/react-router";
|
||||
import { TanStackRouterDevtoolsPanel } from "@tanstack/react-router-devtools";
|
||||
import { type ReactNode, Suspense } from "react";
|
||||
import { ToastContainer } from "react-toastify";
|
||||
import GenericErrorView from "#/components/Error/GenericErrorView.tsx";
|
||||
import { MatomoAnalytics } from "#/components/MatomoAnalytics.tsx";
|
||||
import { LichessUserProvider } from "#/context/LichessUserContext.tsx";
|
||||
import Footer from "../components/Footer";
|
||||
@@ -43,6 +44,7 @@ export const Route = createRootRoute({
|
||||
],
|
||||
}),
|
||||
shellComponent: RootDocument,
|
||||
errorComponent: ({ error }) => <GenericErrorView error={error} />,
|
||||
});
|
||||
|
||||
function RootDocument({ children }: { children: ReactNode }) {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { createFileRoute, notFound } from "@tanstack/react-router";
|
||||
import { z } from "zod";
|
||||
import GenericErrorView from "#/components/Error/GenericErrorView.tsx";
|
||||
import Callback from "#/pages/Callback.tsx";
|
||||
|
||||
const callbackSchema = z.object({
|
||||
@@ -14,7 +15,11 @@ export const Route = createFileRoute("/callback")({
|
||||
throw notFound();
|
||||
}
|
||||
},
|
||||
|
||||
component: RouteComponent,
|
||||
errorComponent: ({ error, reset }) => (
|
||||
<GenericErrorView error={error} resetErrorBoundary={reset} />
|
||||
),
|
||||
});
|
||||
|
||||
function RouteComponent() {
|
||||
|
||||
Reference in New Issue
Block a user