Files
zopu-code/apps/web/src/root.tsx
T

121 lines
3.1 KiB
TypeScript

import { WebAuthProvider } from "@code/auth/web";
import { Toaster } from "@code/ui/components/sonner";
import "./index.css";
import { LoaderCircle } from "lucide-react";
import {
isRouteErrorResponse,
Links,
Meta,
Outlet,
Scripts,
ScrollRestoration,
useNavigation,
} from "react-router";
import type { Route } from "./+types/root";
import { ThemeProvider } from "./components/theme-provider";
import { loadAuthToken } from "./lib/auth.server";
export const loader = ({ request }: Route.LoaderArgs) => loadAuthToken(request);
export const links: Route.LinksFunction = () => [
{ href: "https://fonts.googleapis.com", rel: "preconnect" },
{
crossOrigin: "anonymous",
href: "https://fonts.gstatic.com",
rel: "preconnect",
},
{
href: "https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap",
rel: "stylesheet",
},
];
export const Layout = ({ children }: { children: React.ReactNode }) => (
<html lang="en">
<head>
<meta charSet="utf-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1, interactive-widget=resizes-content"
/>
<Meta />
<Links />
</head>
<body className="bg-[#0e0e0d]">
{children}
<ScrollRestoration />
<Scripts />
</body>
</html>
);
const RouteProgress = () => {
const navigation = useNavigation();
if (!navigation.location) {
return null;
}
return (
<output
aria-label="Loading page"
className="fixed inset-x-0 top-0 z-50 h-0.5 overflow-hidden bg-[#d7d3c7]"
>
<div className="route-progress-bar h-full w-1/3 bg-[#7f9130]" />
</output>
);
};
export const HydrateFallback = () => (
<main className="grid min-h-svh place-items-center bg-[#f2f0e7] text-[#69675f]">
<div className="flex items-center gap-2 text-sm">
<LoaderCircle className="size-4 animate-spin" /> Loading Zopu…
</div>
</main>
);
const App = ({ loaderData }: Route.ComponentProps) => (
<WebAuthProvider initialToken={loaderData.token}>
<ThemeProvider
attribute="class"
defaultTheme="dark"
forcedTheme="dark"
disableTransitionOnChange
storageKey="vite-ui-theme"
>
<RouteProgress />
<Outlet />
<Toaster richColors />
</ThemeProvider>
</WebAuthProvider>
);
export default App;
export const ErrorBoundary = ({ error }: Route.ErrorBoundaryProps) => {
let message = "Oops!";
let details = "An unexpected error occurred.";
let stack: string | undefined;
if (isRouteErrorResponse(error)) {
const { status, statusText } = error;
message = status === 404 ? "404" : "Error";
details =
status === 404
? "The requested page could not be found."
: statusText || details;
} else if (import.meta.env.DEV && error && error instanceof Error) {
({ message: details, stack } = error);
}
return (
<main className="pt-16 p-4 container mx-auto">
<h1>{message}</h1>
<p>{details}</p>
{stack && (
<pre className="w-full p-4 overflow-x-auto">
<code>{stack}</code>
</pre>
)}
</main>
);
};