ppt-tool/frontend/components/AuthGuard.tsx
Vadym Samoilenko cf21ba4516 Phase 1-2: Foundation + Admin Panel & Client Management
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>
2026-02-26 15:37:17 +00:00

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}</>;
}