deckhearth/components/AdminProtected.js
Randall Stillwell 3478932c3c Fixed SSR and null user issues for production deployment
- Made admin pages client-side only using dynamic imports to prevent SSR issues
- Added proper null checks in Layout component to handle guest users
- Updated AdminProtected to support render prop pattern for user data
- Fixed card-editor and card-import pages to use proper authentication flow
- Eliminated hardcoded user data that was causing build failures
- All pages now build successfully and handle null user states gracefully
- Production deployment should now work without SSR errors
2025-07-24 17:04:06 -05:00

104 lines
No EOL
3.2 KiB
JavaScript

import { useEffect, useState } from 'react';
import { useRouter } from 'next/router';
import Layout from './Layout';
export default function AdminProtected({ children }) {
const router = useRouter();
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
const [accessDenied, setAccessDenied] = useState(false);
useEffect(() => {
checkAdminAccess();
}, []);
const checkAdminAccess = async () => {
try {
// Get token from localStorage
const token = localStorage.getItem('auth_token');
const headers = {
'Content-Type': 'application/json',
};
// Add authorization header if token exists
if (token) {
headers.Authorization = `Bearer ${token}`;
}
const response = await fetch('/api/auth/verify', { headers });
if (response.ok) {
const userData = await response.json();
if (userData.role === 'admin') {
setUser(userData);
setAccessDenied(false);
} else {
setAccessDenied(true);
}
} else {
setAccessDenied(true);
// Clear invalid token
if (token) {
localStorage.removeItem('auth_token');
}
}
} catch (error) {
console.error('Admin auth check failed:', error);
setAccessDenied(true);
} finally {
setLoading(false);
}
};
if (loading) {
return (
<Layout user={null}>
<div className="flex items-center justify-center min-h-screen">
<div className="text-center">
<div className="animate-spin rounded-full h-32 w-32 border-b-2 mx-auto mb-4" style={{ borderColor: 'var(--text-accent)' }}></div>
<p style={{ color: 'var(--text-secondary)' }}>Checking admin access...</p>
</div>
</div>
</Layout>
);
}
if (accessDenied) {
return (
<Layout user={user}>
<div className="flex items-center justify-center min-h-screen">
<div className="text-center">
<div className="text-6xl mb-4">🚫</div>
<h2 className="text-2xl font-bold mb-2" style={{ color: 'var(--text-primary)' }}>
Access Denied
</h2>
<p className="mb-6" style={{ color: 'var(--text-secondary)' }}>
You need administrator privileges to access this page.
</p>
<div className="space-x-4">
<button
onClick={() => router.push('/login')}
className="px-6 py-3 rounded-xl font-medium gradient-bg-purple text-white hover:shadow-lg transition-all duration-200"
>
Login as Admin
</button>
<button
onClick={() => router.push('/')}
className="px-6 py-3 rounded-xl font-medium border transition-all duration-200"
style={{
borderColor: 'var(--border)',
color: 'var(--text-primary)'
}}
>
Go Home
</button>
</div>
</div>
</div>
</Layout>
);
}
// Support both render prop and children patterns
return typeof children === 'function' ? children(user) : children;
}