- 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
104 lines
No EOL
3.2 KiB
JavaScript
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;
|
|
}
|