Files
biblical-guide.com/components/admin/layout/admin-layout.tsx
Andrei 9b5c0ed8bb build: production build with Phase 1 2025 Bible Reader implementation complete
Includes all Phase 1 features:
- Search-first navigation with auto-complete
- Responsive reading interface (desktop/tablet/mobile)
- 4 customization presets + full fine-tuning controls
- Layered details panel with notes, bookmarks, highlights
- Smart offline caching with IndexedDB and auto-sync
- Full accessibility (WCAG 2.1 AA)

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-11 20:38:01 +00:00

246 lines
6.6 KiB
TypeScript

'use client';
import { useState } from 'react';
import { useRouter } from 'next/navigation';
import {
Box,
Drawer,
AppBar,
Toolbar,
List,
Typography,
Divider,
ListItem,
ListItemButton,
ListItemIcon,
ListItemText,
IconButton,
Menu,
MenuItem,
Avatar,
Chip,
Button
} from '@mui/material';
import {
Dashboard,
People,
Gavel,
Analytics,
Chat,
Settings,
Logout,
AccountCircle,
AdminPanelSettings,
Launch as LaunchIcon,
Article as PageIcon,
Share,
Email as EmailIcon
} from '@mui/icons-material';
interface AdminLayoutProps {
children: React.ReactNode;
user?: {
id: string;
email: string;
name: string | null;
role: string;
};
}
const drawerWidth = 280;
const menuItems = [
{ text: 'Dashboard', icon: Dashboard, href: '/admin' },
{ text: 'Users', icon: People, href: '/admin/users' },
{ text: 'Pages', icon: PageIcon, href: '/admin/pages' },
{ text: 'Social Media', icon: Share, href: '/admin/social-media' },
{ text: 'Email Settings', icon: EmailIcon, href: '/admin/mailgun' },
{ text: 'Content Moderation', icon: Gavel, href: '/admin/content' },
{ text: 'Analytics', icon: Analytics, href: '/admin/analytics' },
// { text: 'Chat Monitoring', icon: Chat, href: '/admin/chat' }, // AI Chat disabled
{ text: 'Settings', icon: Settings, href: '/admin/settings' },
];
export function AdminLayout({ children, user }: AdminLayoutProps) {
const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null);
const router = useRouter();
const handleMenu = (event: React.MouseEvent<HTMLElement>) => {
setAnchorEl(event.currentTarget);
};
const handleClose = () => {
setAnchorEl(null);
};
const handleLogout = async () => {
try {
await fetch('/api/admin/auth/logout', {
method: 'POST',
credentials: 'include'
});
router.push('/admin/login');
router.refresh();
} catch (error) {
console.error('Logout error:', error);
}
handleClose();
};
const currentPath = typeof window !== 'undefined' ? window.location.pathname : '';
return (
<Box sx={{ display: 'flex' }}>
<AppBar
position="fixed"
sx={{
width: `calc(100% - ${drawerWidth}px)`,
ml: `${drawerWidth}px`,
backgroundColor: 'primary.main'
}}
>
<Toolbar>
<AdminPanelSettings sx={{ mr: 2 }} />
<Typography variant="h6" noWrap component="div" sx={{ flexGrow: 1 }}>
Biblical Guide Admin
</Typography>
{user && (
<Box sx={{ display: 'flex', alignItems: 'center', gap: 2 }}>
<Button
variant="outlined"
startIcon={<LaunchIcon />}
onClick={() => window.open('/', '_blank')}
sx={{
color: 'white',
borderColor: 'white',
'&:hover': {
borderColor: 'white',
backgroundColor: 'rgba(255, 255, 255, 0.1)'
}
}}
>
Visit Website
</Button>
<Chip
label={user.role}
color={user.role === 'admin' ? 'error' : 'warning'}
size="small"
variant="outlined"
sx={{ color: 'white', borderColor: 'white' }}
/>
<IconButton
size="large"
aria-label="account of current user"
aria-controls="menu-appbar"
aria-haspopup="true"
onClick={handleMenu}
color="inherit"
>
<Avatar sx={{ width: 32, height: 32 }}>
{user.name?.[0] || user.email[0]}
</Avatar>
</IconButton>
<Menu
id="menu-appbar"
anchorEl={anchorEl}
anchorOrigin={{
vertical: 'top',
horizontal: 'right',
}}
keepMounted
transformOrigin={{
vertical: 'top',
horizontal: 'right',
}}
open={Boolean(anchorEl)}
onClose={handleClose}
>
<MenuItem disabled>
<Box>
<Typography variant="body2" fontWeight="bold">
{user.name || 'Admin User'}
</Typography>
<Typography variant="caption" color="text.secondary">
{user.email}
</Typography>
</Box>
</MenuItem>
<Divider />
<MenuItem onClick={handleLogout}>
<ListItemIcon>
<Logout fontSize="small" />
</ListItemIcon>
Logout
</MenuItem>
</Menu>
</Box>
)}
</Toolbar>
</AppBar>
<Drawer
sx={{
width: drawerWidth,
flexShrink: 0,
'& .MuiDrawer-paper': {
width: drawerWidth,
boxSizing: 'border-box',
},
}}
variant="permanent"
anchor="left"
>
<Toolbar>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
<AdminPanelSettings color="primary" />
<Typography variant="h6" noWrap>
Admin Panel
</Typography>
</Box>
</Toolbar>
<Divider />
<List>
{menuItems.map((item) => (
<ListItem key={item.text} disablePadding>
<ListItemButton
selected={currentPath === item.href}
onClick={() => router.push(item.href)}
sx={{
'&.Mui-selected': {
backgroundColor: 'primary.main',
color: 'white',
'&:hover': {
backgroundColor: 'primary.dark',
},
'& .MuiListItemIcon-root': {
color: 'white',
},
},
}}
>
<ListItemIcon>
<item.icon />
</ListItemIcon>
<ListItemText primary={item.text} />
</ListItemButton>
</ListItem>
))}
</List>
</Drawer>
<Box
component="main"
sx={{
flexGrow: 1,
bgcolor: 'background.default',
p: 3,
minHeight: '100vh'
}}
>
<Toolbar />
{children}
</Box>
</Box>
);
}