forge/frontend/components/AdminGuard.tsx
DJP 7a804e896d Initial commit - FORGE AI unified platform
Features:
- Image generation (OpenAI, Gemini, Leonardo, Bria, Stability, Flux)
- Nano Banana iterative editing
- Video generation and upscaling
- Audio TTS, STT, sound effects (ElevenLabs)
- Text prompt studio and alt text
- User authentication with JWT/cookies
- Admin panel with voice management
- Job queue with Celery
- PostgreSQL + Redis backend
- Next.js 15 + FastAPI architecture

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 (1M context) <noreply@anthropic.com>
2025-12-09 20:39:00 -05:00

59 lines
1.5 KiB
TypeScript

'use client';
import { useEffect, useState } from 'react';
import { useRouter } from 'next/navigation';
import { useStore } from '@/lib/store';
import { isAdmin } from '@/lib/auth';
import { ShieldX, Loader2 } from 'lucide-react';
interface AdminGuardProps {
children: React.ReactNode;
fallback?: React.ReactNode;
}
export default function AdminGuard({ children, fallback }: AdminGuardProps) {
const { user } = useStore();
const router = useRouter();
const [checking, setChecking] = useState(true);
useEffect(() => {
// Small delay to allow store to hydrate
const timer = setTimeout(() => {
setChecking(false);
}, 100);
return () => clearTimeout(timer);
}, []);
if (checking) {
return (
<div className="flex items-center justify-center h-64">
<Loader2 className="w-8 h-8 text-forge-yellow animate-spin" />
</div>
);
}
if (!isAdmin(user as any)) {
if (fallback) {
return <>{fallback}</>;
}
return (
<div className="flex flex-col items-center justify-center h-64 text-center">
<ShieldX className="w-16 h-16 text-red-400 mb-4" />
<h2 className="text-xl font-bold text-white mb-2">Access Denied</h2>
<p className="text-gray-400 mb-6">
You don't have permission to access this area.
</p>
<button
onClick={() => router.push('/')}
className="btn-secondary"
>
Go to Dashboard
</button>
</div>
);
}
return <>{children}</>;
}