"use client"; import { usePathname, useRouter } from "next/navigation"; import { useEffect, useState } from "react"; import { Navbar } from "@/components/navbar"; import { ChatWidget } from "@/components/mentor-chat"; import { BrandMark } from "@/components/brand-mark"; import { Skeleton } from "@/components/ui/skeleton"; import { useAuth, useAuthHydrated } from "@/lib/auth-store"; import { http } from "@/lib/api"; import type { User } from "@/lib/types"; import { hasInfiniteHearts } from "@/lib/hearts"; import { BottomNav } from "@/components/bottom-nav"; function SiteFooter() { return (

بوت‌کمپ ۱۲ هفته‌ای · توسعه‌یافته توسط امیرحسین پناهی‌فر

); } function isPublic(p: string) { return p === "/" || p === "/login"; } const PROTECTED_PREFIXES = [ "/cap", "/unwrap", "/leaderboard", "/admin", "/player", "/quiz", "/notifications", "/lockout", "/profile", "/settings", ]; function needsAuth(p: string) { if (isPublic(p)) return false; return PROTECTED_PREFIXES.some((prefix) => p === prefix || p.startsWith(prefix + "/")); } export function AppShell({ children }: { children: React.ReactNode }) { const pathname = usePathname(); const router = useRouter(); const hydrated = useAuthHydrated(); const { user, setUser, accessToken, clear } = useAuth(); const [bootDone, setBootDone] = useState(false); // Wait for persist hydration, then optionally refresh /me useEffect(() => { if (!hydrated) return; let cancelled = false; const boot = async () => { if (!accessToken) { if (!cancelled) setBootDone(true); return; } try { const { user: me } = await http.get<{ user: User }>("/api/auth/me"); if (!cancelled) setUser(me); } catch { if (!cancelled) clear(); } finally { if (!cancelled) setBootDone(true); } }; void boot(); return () => { cancelled = true; }; }, [hydrated, accessToken, clear, setUser]); const ready = hydrated && bootDone; const requiresAuth = needsAuth(pathname); // Redirects only in effects — never during render useEffect(() => { if (!ready) return; if (!user && requiresAuth) { router.replace("/login"); return; } if (user && pathname === "/login") { router.replace("/cap"); return; } if ( user?.isLocked && !hasInfiniteHearts(user.role) && pathname !== "/lockout" && pathname !== "/unwrap" && requiresAuth ) { router.replace("/lockout"); } }, [ready, user, requiresAuth, pathname, router]); if (!ready) { return (
{Array.from({ length: 6 }).map((_, i) => ( ))}
); } // Protected routes: never mount children when logged out. Unknown URLs (404) stay visible. const allowChildren = !requiresAuth || !!user; const showLockBanner = !!user?.isLocked && !hasInfiniteHearts(user.role) && pathname !== "/lockout"; return (
{showLockBanner && (
حساب شما قفل شده است — رفع محدودیت فقط با بررسی مدیریت انجام می‌شود.
)}
{allowChildren ? children : null}
{user && } {user && !pathname.startsWith("/admin") && }
); }