Phase 1 (Foundation): - Project restructure (presenton-main → backend/ + frontend/) - Database schema (8 new models, Alembic config, seed script) - Auth (Azure AD SSO + dev bypass, JWT sessions, AuthMiddleware) - RBAC (access_service, rbac_middleware, admin routers) - Audit logging (fire-and-forget, AuditMiddleware, admin router) - i18n (react-i18next with 5 namespace files) Phase 2 (Admin Panel & Client Management): - Admin panel shell (sidebar layout, role guard, 12 pages) - Redux admin slice with 18 async thunks - User management (role changes, deactivation) - Client management (CRUD, brand config, team management) - Brand config editor (colors, fonts, logos, voice rules) - Master deck upload & parser (PPTX → HTML → React pipeline) - Audit log viewer with filters and CSV/JSON export Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
63 lines
2.1 KiB
TypeScript
63 lines
2.1 KiB
TypeScript
'use client';
|
|
|
|
import { useEffect } from 'react';
|
|
import { useDispatch, useSelector } from 'react-redux';
|
|
import { fetchCurrentUser, checkDevMode } from '@/store/slices/authSlice';
|
|
import { RootState, AppDispatch } from '@/store/store';
|
|
import { usePathname } from 'next/navigation';
|
|
|
|
const PUBLIC_PATHS = ['/login'];
|
|
|
|
export function AuthGuard({ children }: { children: React.ReactNode }) {
|
|
const dispatch = useDispatch<AppDispatch>();
|
|
const { isLoading, isAuthenticated } = useSelector(
|
|
(state: RootState) => state.auth
|
|
);
|
|
const pathname = usePathname();
|
|
|
|
useEffect(() => {
|
|
dispatch(fetchCurrentUser());
|
|
dispatch(checkDevMode());
|
|
}, [dispatch]);
|
|
|
|
// Allow public paths without auth
|
|
if (PUBLIC_PATHS.includes(pathname)) {
|
|
return <>{children}</>;
|
|
}
|
|
|
|
if (isLoading) {
|
|
return (
|
|
<div className="min-h-screen bg-gradient-to-br from-slate-50 to-slate-100 flex items-center justify-center p-4">
|
|
<div className="max-w-md w-full">
|
|
<div className="bg-white/80 backdrop-blur-sm rounded-2xl shadow-xl border border-white/20 p-8 text-center">
|
|
<div className="mb-6">
|
|
<h2 className="text-xl font-semibold text-gray-800 font-inter">
|
|
OLIVER DeckForge
|
|
</h2>
|
|
<div className="w-16 h-1 bg-gradient-to-r from-blue-500 to-purple-600 mx-auto rounded-full mt-2"></div>
|
|
</div>
|
|
<div className="space-y-2">
|
|
<p className="text-sm text-gray-600 font-inter">
|
|
Loading...
|
|
</p>
|
|
</div>
|
|
<div className="mt-6 flex space-x-1 justify-center">
|
|
<div className="w-2 h-2 bg-blue-500 rounded-full animate-pulse"></div>
|
|
<div className="w-2 h-2 bg-purple-500 rounded-full animate-pulse" style={{ animationDelay: '0.2s' }}></div>
|
|
<div className="w-2 h-2 bg-blue-500 rounded-full animate-pulse" style={{ animationDelay: '0.4s' }}></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
if (!isAuthenticated) {
|
|
if (typeof window !== 'undefined') {
|
|
window.location.href = '/login';
|
|
}
|
|
return null;
|
|
}
|
|
|
|
return <>{children}</>;
|
|
}
|