import React, { useEffect, useState } from 'react'; import { Layout, Menu, Button, theme, notification, Avatar, Dropdown, Space, Typography, Badge } from 'antd'; import { Outlet, useNavigate, useLocation } from 'react-router-dom'; import { useAuthStore } from '../store/authStore'; import { useAdminWebSocket } from '../hooks/useAdminWebSocket'; import { DashboardOutlined, UserOutlined, CalendarOutlined, WarningOutlined, StarOutlined, StopOutlined, BugOutlined, DollarOutlined, TeamOutlined, AuditOutlined, LogoutOutlined, SettingOutlined, MenuFoldOutlined, MenuUnfoldOutlined, } from '@ant-design/icons'; const { Header, Sider, Content } = Layout; const { Text } = Typography; const AdminLayout: React.FC = () => { useAdminWebSocket(); const navigate = useNavigate(); const location = useLocation(); const { user, logout } = useAuthStore(); const { token: { colorBgContainer } } = theme.useToken(); const [collapsed, setCollapsed] = useState(false); // Обработка WebSocket-уведомлений useEffect(() => { const handler = (event: CustomEvent) => { const msg = event.detail; if (msg.type === 'report_created') { const { report_id, target_type, target_id, reason } = msg.data || {}; notification.info({ title: 'Новая жалоба', description: ( Жалоба{' '} {report_id ? ( { e.preventDefault(); navigate(`/reports/${report_id}`); }} style={{ fontWeight: 600 }} > #{report_id} ) : ( '#?' )}{' '} на {target_type || 'неизвестный тип'}{' '} {target_id ? ( { e.preventDefault(); navigate(`/${target_type}s/${target_id}`); }} style={{ fontWeight: 600 }} > {target_id} ) : ( '?' )}{' '} {reason ? `(${reason})` : ''} ), placement: 'topRight', }); } else if (msg.type === 'ticket_created') { const { ticket_id } = msg.data || {}; notification.info({ title: 'Новый тикет', description: ( Тикет{' '} {ticket_id ? ( { e.preventDefault(); navigate(`/tickets/${ticket_id}`); }} style={{ fontWeight: 600 }} > #{ticket_id} ) : ( 'без ID' )}{' '} создан ), placement: 'topRight', }); } }; window.addEventListener('admin-ws-message', handler as EventListener); return () => window.removeEventListener('admin-ws-message', handler as EventListener); }, [navigate]); const menuItems = [ { key: '/dashboard', icon: , label: 'Дашборд' }, { key: '/users', icon: , label: 'Пользователи', roles: ['superadmin', 'admin'] }, { key: '/events', icon: , label: 'События', roles: ['superadmin', 'admin'] }, { key: '/reports', icon: , label: 'Жалобы', roles: ['superadmin', 'admin', 'moderator'] }, { key: '/reviews', icon: , label: 'Отзывы', roles: ['superadmin', 'admin', 'moderator'] }, { key: '/banned-words', icon: , label: 'Бан-слова', roles: ['superadmin', 'admin'] }, { key: '/tickets', icon: , label: 'Тикеты', roles: ['superadmin', 'admin', 'support'] }, { key: '/subscriptions', icon: , label: 'Подписки', roles: ['superadmin', 'admin'] }, { key: '/admins', icon: , label: 'Администраторы', roles: ['superadmin'] }, { key: '/audit', icon: , label: 'Аудит', roles: ['superadmin'] }, ]; const filteredMenu = menuItems.filter( (item) => !item.roles || (user && item.roles.includes(user.role)) ); const handleLogout = async () => { await logout(); navigate('/login'); }; const getDisplayName = () => { const nick = user?.nickname; const email = user?.email; if (nick && nick !== '-' && nick !== 'undefined') return nick; if (email && email !== '-' && email !== 'undefined') return email; return user?.id ?? '—'; }; return (
{collapsed ? 'EH' : import.meta.env.VITE_APP_TITLE}
navigate(key)} style={{ flex: 1, overflowY: 'auto', overflowX: 'hidden', marginBottom: 60, // отступ, чтобы меню не заходило под нижний блок }} />
, label: 'Мой профиль', onClick: () => navigate('/profile'), }, { type: 'divider' }, { key: 'logout', icon: , label: 'Выйти', onClick: handleLogout, }, ], }} trigger={['click']} placement="topLeft" >
{/* Резерв */}
); }; export default AdminLayout;