Files
imphnen-frontend-service/apps/hackathon/src/app/layout.tsx
T

109 lines
3.0 KiB
TypeScript
Raw Normal View History

2025-11-27 23:46:52 +07:00
import {
Outlet,
ScrollRestoration,
useLocation,
useNavigate,
} from 'react-router-dom';
2025-11-25 01:47:56 +07:00
import { useEffect, useState } from 'react';
import { useAuthStore, useUserMe } from '@imphnen-frontend-service/service';
2025-11-25 01:47:56 +07:00
// Define onboarding routes
const ONBOARDING_ROUTES = new Set(['/onboarding/user']);
2025-11-24 12:51:41 +07:00
export default function RootLayout() {
2025-11-25 01:47:56 +07:00
const location = useLocation();
const navigate = useNavigate();
const { session } = useAuthStore();
const { data: userData, isLoading: isUserLoading } = useUserMe();
2025-11-25 01:47:56 +07:00
const [isChecking, setIsChecking] = useState(true);
useEffect(() => {
const checkAuth = async () => {
const pathname = location.pathname;
// Allow hackathon pages without checks
if (pathname.startsWith('/hackathons')) {
setIsChecking(false);
return;
}
// Allow auth callback without checks
if (pathname === '/auth/callback') {
setIsChecking(false);
return;
}
2025-11-28 16:02:35 +07:00
// Public auth pages - allow unauthenticated access
if (pathname.startsWith('/auth')) {
// If already authenticated and not on password reset pages, redirect to dashboard
if (session && pathname !== '/auth/reset-password') {
2025-11-25 01:47:56 +07:00
navigate('/dashboard', { replace: true });
setIsChecking(false);
return;
}
2025-11-28 16:02:35 +07:00
// Allow unauthenticated access to auth pages
2025-11-25 01:47:56 +07:00
setIsChecking(false);
return;
}
// Home page - allow everyone to view the landing page
2025-11-25 01:47:56 +07:00
if (pathname === '/') {
setIsChecking(false);
return;
}
// Certificate page - allow public access
if (pathname.startsWith('/certificate/')) {
setIsChecking(false);
return;
}
2025-11-25 01:47:56 +07:00
// Require authentication for all other routes
if (!session) {
2025-11-28 16:02:35 +07:00
navigate('/auth/login', { replace: true });
2025-11-25 01:47:56 +07:00
setIsChecking(false);
return;
}
// Wait for user data to load before checking onboarding
if (isUserLoading) {
return;
}
2025-11-25 01:47:56 +07:00
// Check if user has completed onboarding (skip for onboarding routes)
if (!ONBOARDING_ROUTES.has(pathname)) {
const hasLocation = !!userData?.data?.location || !!session?.user?.location;
2025-11-25 01:47:56 +07:00
if (!hasLocation) {
navigate('/onboarding/user', { replace: true });
setIsChecking(false);
return;
2025-11-25 01:47:56 +07:00
}
}
setIsChecking(false);
};
checkAuth();
}, [location.pathname, navigate, session, userData, isUserLoading]);
2025-11-25 01:47:56 +07:00
// Show loading state while checking auth
if (isChecking) {
return (
<div className="flex justify-center items-center min-h-screen bg-gray-50 dark:bg-neutral-950">
2025-11-25 01:47:56 +07:00
<div className="text-center">
<div className="inline-block animate-spin rounded-full h-12 w-12 border-b-2 border-primary-600 mb-4"></div>
<p className="text-gray-600 dark:text-neutral-400">Loading...</p>
2025-11-25 01:47:56 +07:00
</div>
</div>
);
}
2025-11-24 12:51:41 +07:00
return (
<>
<Outlet />
<ScrollRestoration />
</>
);
}