import { isRouteErrorResponse, Links, Meta, Outlet, Scripts, ScrollRestoration, useLocation, redirect, } from "react-router"; import type { Route } from "./+types/root"; import "./app.css"; import { auth } from "~/lib/auth.server"; import { Navbar } from "~/components/navbar"; import { NavigationProgress } from "~/components/NavigationProgress"; import { Toaster } from "~/components/ui/sonner"; import { BracktGradients } from "~/components/ui/BracktGradients"; import { Footer } from "~/components/marketing/Footer"; import { findUserById } from "~/models/user"; import { AlertCircle, FileQuestion, Lock, ShieldOff, ServerCrash } from "lucide-react"; import logoUrl from "../public/logo.svg?url"; // Paths that should never trigger the onboarding redirect. // Trailing-slash entries are matched as prefixes; others require exact match or sub-path. const ONBOARDING_EXEMPT = ["/onboarding", "/api/", "/admin", "/login", "/register", "/check-email", "/forgot-password", "/reset-password"]; export function isOnboardingExempt(pathname: string): boolean { return ONBOARDING_EXEMPT.some((p) => p.endsWith("/") ? pathname.startsWith(p) : pathname === p || pathname.startsWith(p + "/") ); } export async function loader({ request }: Route.LoaderArgs) { const session = await auth.api.getSession({ headers: request.headers }); const currentUser = session ? await findUserById(session.user.id) : null; const isAdmin = currentUser?.isAdmin ?? false; if (currentUser && !currentUser.username) { const { pathname } = new URL(request.url); if (!isOnboardingExempt(pathname)) { const redirectTo = pathname === "/" ? "" : `?redirectTo=${encodeURIComponent(pathname)}`; return redirect(`/onboarding${redirectTo}`); } } return { isAdmin, currentUser }; } export const links: Route.LinksFunction = () => [ { rel: "icon", href: "/favicon.ico?v=20260402", sizes: "48x48" }, { rel: "icon", href: "/favicon.svg?v=20260402", type: "image/svg+xml" }, { rel: "icon", href: "/favicon-96x96.png?v=20260402", type: "image/png", sizes: "96x96" }, { rel: "apple-touch-icon", href: "/apple-touch-icon.png?v=20260402" }, { rel: "manifest", href: "/site.webmanifest?v=20260402" }, { rel: "preconnect", href: "https://fonts.googleapis.com" }, { rel: "preconnect", href: "https://fonts.gstatic.com", crossOrigin: "anonymous", }, { rel: "stylesheet", href: "https://fonts.googleapis.com/css2?family=Barlow:ital,wght@0,300;0,400;0,500;0,600;0,700;0,800;1,300;1,400;1,500;1,600;1,700;1,800&display=swap", }, ]; export function Layout({ children }: { children: React.ReactNode }) { return ( {children} ); } export default function App({ loaderData }: Route.ComponentProps) { const location = useLocation(); const isDraftRoute = location.pathname.includes('/draft'); return ( <> {!isDraftRoute && ( )} {isDraftRoute ? ( ) : ( <>