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
This commit is contained in:
Randall Stillwell 2025-07-24 17:04:06 -05:00
parent 53423509f0
commit 3478932c3c
4 changed files with 66 additions and 25 deletions

View file

@ -99,5 +99,6 @@ export default function AdminProtected({ children }) {
);
}
return children;
// Support both render prop and children patterns
return typeof children === 'function' ? children(user) : children;
}

View file

@ -180,12 +180,16 @@ export default function Layout({ children, user = { email: 'me@randallstillwell.
</button>
<div className="flex items-center space-x-3">
<div className="text-right">
<p className="text-sm font-medium" style={{ color: 'var(--text-primary)' }}>{user.email}</p>
<p className="text-xs" style={{ color: 'var(--text-secondary)' }}>{user.role}</p>
<p className="text-sm font-medium" style={{ color: 'var(--text-primary)' }}>
{user?.email || 'Guest'}
</p>
<p className="text-xs" style={{ color: 'var(--text-secondary)' }}>
{user?.role || 'visitor'}
</p>
</div>
<div className="h-10 w-10 logo-container">
<span className="text-white text-sm font-medium">
{user.email.charAt(0).toUpperCase()}
{user?.email?.charAt(0).toUpperCase() || 'G'}
</span>
</div>
</div>

View file

@ -1,16 +1,29 @@
import { useState, useEffect } from 'react';
import { useRouter } from 'next/router';
import dynamic from 'next/dynamic';
import Layout from '../../components/Layout';
import AdminProtected from '../../components/AdminProtected';
export default function CardEditor() {
// Make this component client-side only to avoid SSR issues
const CardEditor = () => {
const router = useRouter();
const { id } = router.query;
const user = {
email: 'admin@tcgvault.com',
role: 'admin'
};
const [mounted, setMounted] = useState(false);
useEffect(() => {
setMounted(true);
}, []);
if (!mounted) {
return (
<Layout user={null}>
<div className="flex items-center justify-center min-h-screen">
<div className="animate-spin rounded-full h-32 w-32 border-b-2" style={{ borderColor: 'var(--text-accent)' }}></div>
</div>
</Layout>
);
}
const [card, setCard] = useState(null);
const [loading, setLoading] = useState(true);
@ -178,18 +191,21 @@ export default function CardEditor() {
if (loading) {
return (
<AdminProtected>
<Layout user={user}>
<div className="flex items-center justify-center min-h-screen">
<div className="animate-spin rounded-full h-32 w-32 border-b-2" style={{ borderColor: 'var(--text-accent)' }}></div>
</div>
</Layout>
{(user) => (
<Layout user={user}>
<div className="flex items-center justify-center min-h-screen">
<div className="animate-spin rounded-full h-32 w-32 border-b-2" style={{ borderColor: 'var(--text-accent)' }}></div>
</div>
</Layout>
)}
</AdminProtected>
);
}
return (
<AdminProtected>
<Layout user={user}>
{(user) => (
<Layout user={user}>
<div className="container mx-auto px-6 py-8">
{/* Admin Navigation */}
<div className="mb-8 p-4 rounded-xl" style={{ backgroundColor: 'var(--bg-secondary)' }}>
@ -753,6 +769,10 @@ export default function CardEditor() {
)}
</div>
</Layout>
)}
</AdminProtected>
);
}
};
// Export with dynamic import to disable SSR
export default dynamic(() => Promise.resolve(CardEditor), { ssr: false });

View file

@ -1,9 +1,25 @@
import { useState } from 'react';
import { useState, useEffect } from 'react';
import { useRouter } from 'next/router';
import dynamic from 'next/dynamic';
import Layout from '../../components/Layout';
import AdminProtected from '../../components/AdminProtected';
export default function CardImport() {
const CardImport = () => {
const [mounted, setMounted] = useState(false);
useEffect(() => {
setMounted(true);
}, []);
if (!mounted) {
return (
<Layout user={null}>
<div className="flex items-center justify-center min-h-screen">
<div className="animate-spin rounded-full h-32 w-32 border-b-2" style={{ borderColor: 'var(--text-accent)' }}></div>
</div>
</Layout>
);
}
const router = useRouter();
const [importType, setImportType] = useState('mtg');
const [setCode, setSetCode] = useState('');
@ -85,14 +101,10 @@ export default function CardImport() {
]
};
const user = {
email: 'admin@tcgvault.com',
role: 'admin'
};
return (
<AdminProtected>
<Layout user={user}>
{(user) => (
<Layout user={user}>
<div className="container mx-auto px-6 py-8">
{/* Admin Navigation */}
<div className="mb-8 p-4 rounded-xl" style={{ backgroundColor: 'var(--bg-secondary)' }}>
@ -288,6 +300,10 @@ export default function CardImport() {
</div>
</div>
</Layout>
)}
</AdminProtected>
);
}
};
// Export with dynamic import to disable SSR
export default dynamic(() => Promise.resolve(CardImport), { ssr: false });