'use client'; import { useEffect, useState, useCallback, Suspense } from 'react'; const MOBILE_BREAKPOINT = 767; function useIsMobile() { const [isMobile, setIsMobile] = useState(false); useEffect(() => { const mq = window.matchMedia(`(max-width: ${MOBILE_BREAKPOINT}px)`); setIsMobile(mq.matches); const listener = () => setIsMobile(mq.matches); mq.addEventListener('change', listener); return () => mq.removeEventListener('change', listener); }, []); return isMobile; } import { useRouter, usePathname } from 'next/navigation'; import { BottomNavigationBar } from '@/components/navigation/BottomNavigationBar'; import { TopNavigationBar } from '@/components/navigation/TopNavigationBar'; import { NotificationBell } from '@/components/notifications/NotificationBell'; import { useAuth } from '@/contexts/AuthContext'; import { LoadingSpinner } from '@/components/common/LoadingSpinner'; import { NavBadgesProvider } from '@/contexts/NavBadgesContext'; import { SelectedChildProvider } from '@/contexts/SelectedChildContext'; import { getNavBadges } from '@/api/navBadges'; import { getActiveSubscription } from '@/api/subscriptions'; import { setReferrer, REFERRAL_STORAGE_KEY } from '@/api/referrals'; import type { NavBadges } from '@/api/navBadges'; export default function ProtectedLayout({ children, }: { children: React.ReactNode; }) { const router = useRouter(); const pathname = usePathname(); const { user, loading } = useAuth(); const isMobile = useIsMobile(); const [navBadges, setNavBadges] = useState(null); const [subscriptionChecked, setSubscriptionChecked] = useState(false); const refreshNavBadges = useCallback(async () => { try { const next = await getNavBadges(); setNavBadges(next); } catch { setNavBadges(null); } }, []); useEffect(() => { if (!user) return; refreshNavBadges(); }, [user, refreshNavBadges]); // После входа: если в localStorage сохранён реферальный код (переход по ссылке /register?ref=...), привязываем реферера useEffect(() => { if (!user) return; const code = typeof window !== 'undefined' ? localStorage.getItem(REFERRAL_STORAGE_KEY) : null; if (!code || !code.trim()) return; setReferrer(code.trim()) .then(() => { localStorage.removeItem(REFERRAL_STORAGE_KEY); }) .catch(() => {}); }, [user]); // Для ментора: редирект на /payment, если нет активной подписки (кроме самой страницы /payment) useEffect(() => { if (!user || user.role !== 'mentor' || pathname === '/payment') { if (user?.role === 'mentor' && pathname === '/payment') setSubscriptionChecked(true); return; } let cancelled = false; setSubscriptionChecked(false); getActiveSubscription() .then((sub) => { if (cancelled) return; setSubscriptionChecked(true); if (!sub) router.replace('/payment'); }) .catch(() => { if (!cancelled) setSubscriptionChecked(true); }); return () => { cancelled = true; }; }, [user, pathname, router]); useEffect(() => { // Проверяем токен в localStorage напрямую, чтобы избежать race condition const token = typeof window !== 'undefined' ? localStorage.getItem('access_token') : null; console.log('[ProtectedLayout] Auth state:', { user: !!user, loading, hasToken: !!token, pathname }); if (!loading && !user && !token) { console.log('[ProtectedLayout] Redirecting to login'); router.push('/login'); } // eslint-disable-next-line react-hooks/exhaustive-deps }, [user, loading]); if (loading) { return (
); } if (!user) { return null; } // Ментор не на /payment: ждём результат проверки подписки, чтобы не показывать контент перед редиректом const isMentorCheckingSubscription = user.role === 'mentor' && pathname !== '/payment' && !subscriptionChecked; if (isMentorCheckingSubscription) { return (
); } // Не показываем навигацию на страницах авторизации if (pathname?.startsWith('/login') || pathname?.startsWith('/register')) { return <>{children}; } // Для dashboard, schedule, chat, students, materials не показываем header и используем полную ширину const isDashboard = pathname === '/dashboard'; const isSchedule = pathname === '/schedule'; const isChat = pathname === '/chat'; const isStudents = pathname === '/students'; const isMaterials = pathname === '/materials'; const isProfile = pathname === '/profile'; const isPayment = pathname === '/payment'; const isAnalytics = pathname === '/analytics'; const isReferrals = pathname === '/referrals'; const isFeedback = pathname === '/feedback'; const isHomework = pathname === '/homework'; const isLiveKit = pathname?.startsWith('/livekit'); const isMyProgress = pathname === '/my-progress'; const isRequestMentor = pathname === '/request-mentor'; const isFullWidthPage = isDashboard || isSchedule || isChat || isStudents || isMaterials || isProfile || isPayment || isAnalytics || isReferrals || isFeedback || isHomework || isLiveKit || isMyProgress || isRequestMentor; return (
{!isFullWidthPage && }
{children}
{!isLiveKit && ( : null} /> )} {!isLiveKit && user && !isMobile && ( )}
); }