"use client"; import { Loader2 } from "lucide-react"; import { usePathname, useRouter } from "next/navigation"; import { useEffect } from "react"; import { AchievementUnlockNotifier } from "@/components/gamification/achievement-unlock-notifier"; import { MaintainershipBanner } from "@/components/i18n/maintainership-banner"; import { useAuth } from "@/hooks/use-auth"; import { useTranslations } from "@/lib/i18n/context"; import { BottomNav } from "./bottom-nav"; import { SidebarNav } from "./sidebar-nav"; import { TopBar } from "./top-bar"; const PUBLIC_PATHS = ["/auth/login", "/auth/register", "/privacy"]; export function AuthShell({ children }: { children: React.ReactNode }) { const { user, isAuthenticated, isLoading } = useAuth(); const { t } = useTranslations(); const pathname = usePathname(); const router = useRouter(); const isPublicPage = PUBLIC_PATHS.some((p) => pathname.startsWith(p)); // v1.4.26 — `/privacy` is a long-form legal page that brings its own // header + footer. Centering it inside the public-page wrapper would // squash a 3000-word policy into a "login card" layout, so we let it // render edge-to-edge while still resolving as a public page for the // auth gate above. const isStandalonePublicPage = pathname.startsWith("/privacy"); const isAdminPage = pathname.startsWith("/admin"); const isOnboardingPage = pathname === "/onboarding"; const showUnlockNotifier = isAuthenticated && !isPublicPage && !!user?.id; useEffect(() => { if (!isLoading && !isAuthenticated && !isPublicPage) { router.replace("/auth/login"); } }, [isLoading, isAuthenticated, isPublicPage, router]); // Redirect non-admins away from /admin useEffect(() => { if ( !isLoading && isAuthenticated && isAdminPage && user?.role !== "ADMIN" ) { router.replace("/"); } }, [isLoading, isAuthenticated, isAdminPage, user?.role, router]); // v1.4.22 C4 — onboarding redirect moved to proxy.ts so it lands on // the first server response instead of post-hydration. The proxy // reads the `hl_onboarding` cookie (mirrored from the DB by the auth // routes) and 307s to /onboarding before this shell ever paints. The // previous `useEffect`-based redirect caused a brief dashboard flash // for users with `onboardingCompletedAt === null`. // Loading state if (isLoading) { return (