Files
router-lists-ui/frontend/src/App.jsx
T
2025-08-05 22:01:56 +07:00

304 lines
10 KiB
React

import { useState, useEffect } from 'react';
import { Container } from 'react-bootstrap';
import {
IconBrandTabler,
IconWorld,
IconNetwork,
IconSettings,
IconHome,
IconFileText,
IconCloud,
IconShield,
IconActivity,
IconBell,
IconChevronDown,
IconUser,
IconDatabase,
IconAlertTriangle,
IconAlertCircle,
IconServer,
IconFilter,
IconDownload,
IconCreditCard,
IconMoon,
IconHeart,
IconCode
} from '@tabler/icons-react';
import ServerManager from './ServerManager';
import FilterManager from './FilterManager';
import DomainsNewManager from './DomainsNewManager';
import IPRangesManager from './IPRangesManager';
import ASNsNewManager from './ASNsNewManager';
import AutoUrlManager from './AutoUrlManager';
import BillingManager from './BillingManager';
import Dashboard from './Dashboard';
import './App.css';
import axios from 'axios';
import {
BrowserRouter as Router,
Routes,
Route,
Link,
useLocation,
Navigate
} from 'react-router-dom';
function App() {
return (
<Router>
<MainLayout />
</Router>
);
}
function MainLayout() {
const location = useLocation();
const [notifications] = useState([
{ id: 1, title: 'Новый домен добавлен', message: 'example.com был добавлен в список', time: '2 мин назад' },
{ id: 2, title: 'Изменения сохранены', message: 'Список AS обновлен в S3', time: '5 мин назад' }
]);
// Состояние для управления выпадающими меню
const [dropdownStates, setDropdownStates] = useState({
data: false,
management: false,
tools: false,
settings: false
});
// Закрытие всех выпадающих меню при клике вне их области
useEffect(() => {
const handleClickOutside = (event) => {
if (!event.target.closest('.nav-item.dropdown')) {
setDropdownStates({
data: false,
management: false,
tools: false,
settings: false
});
}
};
document.addEventListener('click', handleClickOutside);
return () => {
document.removeEventListener('click', handleClickOutside);
};
}, []);
const toggleDropdown = (dropdown) => {
setDropdownStates(prev => {
// Закрываем все выпадающие меню
const newState = {
data: false,
management: false,
tools: false,
settings: false
};
// Открываем только выбранное меню, если оно было закрыто
if (!prev[dropdown]) {
newState[dropdown] = true;
}
return newState;
});
};
const navCategories = [
{
id: 'home',
title: 'Главная',
icon: IconHome,
path: '/dashboard',
single: true
},
{
id: 'data',
title: 'Данные',
icon: IconDatabase,
items: [
{ id: 'domains', title: 'Домены', path: '/domains', icon: IconWorld },
{ id: 'ip-ranges', title: 'IP-диапазоны', path: '/ip-ranges', icon: IconNetwork },
{ id: 'asns', title: 'AS', path: '/asns', icon: IconNetwork }
]
},
{
id: 'management',
title: 'Управление',
icon: IconServer,
items: [
{ id: 'servers', title: 'Серверы', path: '/servers', icon: IconServer },
{ id: 'filters', title: 'Фильтры', path: '/filters', icon: IconFilter },
{ id: 'billing', title: 'Биллинг', path: '/billing', icon: IconCreditCard }
]
},
{
id: 'tools',
title: 'Инструменты',
icon: IconSettings,
items: [
{ id: 'auto-urls', title: 'Авто URL', path: '/auto-urls', icon: IconDownload }
]
},
{
id: 'settings',
title: 'Настройки',
icon: IconSettings,
path: '/settings',
single: true
}
];
// Определяем активную вкладку по адресу
const getActiveTab = () => {
for (const category of navCategories) {
if (category.single && location.pathname === category.path) {
return category.id;
}
if (category.items) {
for (const item of category.items) {
if (location.pathname.startsWith(item.path)) {
return item.id;
}
}
}
}
return 'home';
};
const activeTab = getActiveTab();
return (
<div className="page">
{/* Верхнее меню Tabler Admin */}
<header className="navbar navbar-expand-md navbar-light d-print-none" style={{boxShadow: '0 1px 0 0 #e9ecef'}}>
<div className="container-xl">
{/* Логотип */}
<div className="navbar-brand navbar-brand-autodark d-none-navbar-horizontal pe-0 pe-md-3">
<Link to="/" className="navbar-brand d-flex align-items-center">
<IconBrandTabler className="navbar-brand-icon me-2" />
<span className="fw-bold">S3 Lists Manager</span>
</Link>
</div>
{/* Основное меню */}
<div className="collapse navbar-collapse" id="navbar-menu">
<div className="d-flex flex-column flex-md-row flex-fill align-items-stretch align-items-md-center">
<ul className="navbar-nav">
{navCategories.map(category => (
<li key={category.id} className="nav-item dropdown">
{category.single ? (
<Link
to={category.path}
className={`nav-link${activeTab === category.id ? ' active' : ''}`}
>
<category.icon className="icon icon-tabler" />
<span className="nav-link-title">{category.title}</span>
</Link>
) : (
<>
<a
className={`nav-link dropdown-toggle${activeTab.startsWith(category.id) ? ' active' : ''}`}
href="#"
onClick={(e) => {
e.preventDefault();
e.stopPropagation();
toggleDropdown(category.id);
}}
>
<category.icon className="icon icon-tabler" />
<span className="nav-link-title">{category.title}</span>
</a>
{dropdownStates[category.id] && (
<div className="dropdown-menu show" onClick={(e) => e.stopPropagation()}>
<div className="dropdown-menu-columns">
<div className="dropdown-menu-column">
{category.items.map(item => (
<Link
key={item.id}
to={item.path}
className={`dropdown-item${activeTab === item.id ? ' active' : ''}`}
>
<item.icon className="icon icon-tabler" />
{item.title}
</Link>
))}
</div>
</div>
</div>
)}
</>
)}
</li>
))}
</ul>
</div>
</div>
{/* Правая часть навбара */}
<div className="navbar-nav flex-row order-md-last">
{/* Иконки */}
<div className="nav-item dropdown">
<a href="#" className="nav-link d-flex lh-1 text-reset p-0 ms-3" aria-label="Source code">
<IconCode className="icon icon-tabler" />
</a>
</div>
<div className="nav-item dropdown">
<a href="#" className="nav-link d-flex lh-1 text-reset p-0 ms-3" aria-label="Sponsor">
<IconHeart className="icon icon-tabler" />
</a>
</div>
<div className="nav-item dropdown">
<a href="#" className="nav-link d-flex lh-1 text-reset p-0 ms-3" aria-label="Dark mode">
<IconMoon className="icon icon-tabler" />
</a>
</div>
<div className="nav-item dropdown">
<a href="#" className="nav-link d-flex lh-1 text-reset p-0 ms-3" aria-label="Notifications">
<IconBell className="icon icon-tabler" />
</a>
</div>
<div className="nav-item dropdown">
<a href="#" className="nav-link d-flex lh-1 text-reset p-0 ms-3" aria-label="Apps">
<IconActivity className="icon icon-tabler" />
</a>
</div>
<div className="nav-item dropdown">
<a href="#" className="nav-link d-flex lh-1 text-reset p-0 ms-3" aria-label="Theme settings">
<IconSettings className="icon icon-tabler" />
<span className="badge bg-red"></span>
</a>
</div>
{/* Пользователь */}
<div className="nav-item dropdown">
<a href="#" className="nav-link d-flex lh-1 text-reset p-0 ms-3" aria-label="User menu">
<span className="avatar avatar-sm" style={{backgroundImage: 'url(./static/avatars/000m.jpg)'}}></span>
<div className="d-none d-xl-block ps-2">
<div>Admin User</div>
<div className="mt-1 small text-muted">Administrator</div>
</div>
</a>
</div>
</div>
</div>
</header>
<div className="container-xl mt-4">
<Routes>
<Route path="/dashboard" element={<Dashboard />} />
<Route path="/domains" element={<DomainsNewManager />} />
<Route path="/ip-ranges" element={<IPRangesManager />} />
<Route path="/asns" element={<ASNsNewManager />} />
<Route path="/auto-urls" element={<AutoUrlManager />} />
<Route path="/servers" element={<ServerManager />} />
<Route path="/billing" element={<BillingManager />} />
<Route path="/filters" element={<FilterManager />} />
<Route path="/" element={<Navigate to="/dashboard" replace />} />
</Routes>
</div>
</div>
);
}
export default App;