## Social Media Management System - Add SocialMediaLink database model with platform, URL, icon, and ordering - Create complete CRUD API endpoints for admin social media management - Implement admin social media management page with Material-UI DataGrid - Add "Social Media" menu item to admin navigation - Update footer to dynamically load and display enabled social media links - Support multiple platforms: Facebook, Twitter, Instagram, YouTube, LinkedIn, GitHub, TikTok - Include proper icon mapping and fallback handling ## Admin Pages Improvements - Replace broken TinyMCE editor with working WYSIWYG rich text editor - Create SimpleRichEditor component with toolbar for formatting - Fix admin authentication to use cookies instead of localStorage tokens - Update all admin API calls to use credentials: 'include' - Increase content editor height to 800px for better editing experience - Add Lexical editor component as alternative (not currently used) ## Footer Pages System - Create 8 comprehensive footer pages: About, Blog, Support, API Docs, Terms, Privacy, Cookies, GDPR - Implement dynamic footer link management with smart categorization - Separate Quick Links and Legal sections with automatic filtering - Remove duplicate hardcoded links and use database-driven system - All footer pages are fully written with professional content ## Database & Dependencies - Add uuid package for ID generation - Update Prisma schema with new SocialMediaLink model and relations - Seed default social media links for Facebook, Twitter, Instagram, YouTube - Add Lexical rich text editor packages (@lexical/react, etc.) ## Technical Improvements - Fix async params compatibility for Next.js 15 - Update MUI DataGrid deprecated props - Improve admin layout navigation structure - Add proper TypeScript interfaces for all new components - Implement proper error handling and user feedback 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
244 lines
6.5 KiB
TypeScript
244 lines
6.5 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
|
|
} 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: 'Content Moderation', icon: Gavel, href: '/admin/content' },
|
|
{ text: 'Analytics', icon: Analytics, href: '/admin/analytics' },
|
|
{ text: 'Chat Monitoring', icon: Chat, href: '/admin/chat' },
|
|
{ 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>
|
|
);
|
|
} |