From 7c6ceb66bc54562054a83e5d81d1e2034000c8a0 Mon Sep 17 00:00:00 2001 From: Denis Shatskiy Date: Tue, 5 Aug 2025 21:26:23 +0700 Subject: [PATCH] feat: Integrate Dashboard component into MainLayout and update routing to redirect home path to /dashboard --- frontend/src/App.jsx | 107 +------------ frontend/src/Dashboard.jsx | 297 +++++++++++++++++++++++++++++++++++++ 2 files changed, 302 insertions(+), 102 deletions(-) create mode 100644 frontend/src/Dashboard.jsx diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 55ef925..ee7e18b 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -32,6 +32,7 @@ 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 { @@ -107,7 +108,7 @@ function MainLayout() { id: 'home', title: 'Главная', icon: IconHome, - path: '/', + path: '/dashboard', single: true }, { @@ -136,11 +137,7 @@ function MainLayout() { title: 'Инструменты', icon: IconSettings, items: [ - { id: 'auto-urls', title: 'Авто URL', path: '/auto-urls', icon: IconDownload }, - { id: 'files', title: 'Файлы', path: '/files', icon: IconFileText }, - { id: 'cloud', title: 'Облако', path: '/cloud', icon: IconCloud }, - { id: 'security', title: 'Безопасность', path: '/security', icon: IconShield }, - { id: 'activity', title: 'Активность', path: '/activity', icon: IconActivity } + { id: 'auto-urls', title: 'Авто URL', path: '/auto-urls', icon: IconDownload } ] }, { @@ -288,6 +285,7 @@ function MainLayout() {
+ } /> } /> } /> } /> - } /> + } />
); } -function StatCard({ icon: Icon, color, value, title, subtitle }) { - return ( -
-
- - - -
-
{value} {title}
-
{subtitle}
-
-
-
- ); -} -function Dashboard() { - const [stats, setStats] = useState({ domainsCount: null, asnsCount: null, serversCount: null, lastModified: null }); - const [loading, setLoading] = useState(true); - - useEffect(() => { - async function fetchStats() { - setLoading(true); - try { - const [domainsRes, asnsRes, serversRes, s3Res] = await Promise.all([ - axios.get('/api/domains'), - axios.get('/api/asns'), - axios.get('/api/servers'), - axios.get('/api/s3/last-modified') - ]); - setStats({ - domainsCount: domainsRes.data.length, - asnsCount: asnsRes.data.length, - serversCount: serversRes.data.length, - lastModified: s3Res.data.domainsLastModified - }); - } catch (e) { - setStats({ domainsCount: null, asnsCount: null, serversCount: null, lastModified: null }); - } finally { - setLoading(false); - } - } - fetchStats(); - }, []); - - return ( -
-
-
- -
-
- -
-
- -
-
- -
-
-
-
-
-

Dashboard

-
Главная / Dashboard
-
-
-
-
- ); -} export default App; diff --git a/frontend/src/Dashboard.jsx b/frontend/src/Dashboard.jsx new file mode 100644 index 0000000..d37296f --- /dev/null +++ b/frontend/src/Dashboard.jsx @@ -0,0 +1,297 @@ +import { useState, useEffect } from 'react'; +import axios from 'axios'; +import { + IconWorld, + IconNetwork, + IconServer, + IconDatabase, + IconActivity, + IconTrendingUp, + IconTrendingDown, + IconClock, + IconMapPin, + IconCloud, + IconShield, + IconAlertTriangle +} from '@tabler/icons-react'; + +function StatCard({ icon: Icon, color, value, title, subtitle, trend, trendValue }) { + return ( +
+
+ + + +
+
{value} {title}
+
{subtitle}
+ {trend && ( +
+ {trend === 'up' ? : } + {trendValue} +
+ )} +
+
+
+ ); +} + +function MetricCard({ title, value, icon: Icon, color, description }) { + return ( +
+
+
+ + + +
+
{value}
+
{title}
+
+
+ {description && ( +
{description}
+ )} +
+
+ ); +} + +function Dashboard() { + const [stats, setStats] = useState({ + domainsCount: null, + ipRangesCount: null, + asnsCount: null, + serversCount: null, + lastModified: null, + countriesCount: null, + providersCount: null, + onlineServers: null, + totalServers: null + }); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + + useEffect(() => { + async function fetchStats() { + setLoading(true); + setError(null); + try { + const [domainsRes, ipRangesRes, asnsRes, serversRes, s3Res] = await Promise.all([ + axios.get('/api/domains'), + axios.get('/api/ip-ranges'), + axios.get('/api/asns'), + axios.get('/api/servers'), + axios.get('/api/s3/last-modified') + ]); + + // Подсчет уникальных стран и провайдеров из серверов + const servers = serversRes.data; + const countries = new Set(servers.map(server => server.country).filter(Boolean)); + const providers = new Set(servers.map(server => server.provider).filter(Boolean)); + const onlineServers = servers.filter(server => server.status === 'Онлайн').length; + + setStats({ + domainsCount: domainsRes.data.length, + ipRangesCount: ipRangesRes.data.length, + asnsCount: asnsRes.data.length, + serversCount: servers.length, + lastModified: s3Res.data?.domainsLastModified || null, + countriesCount: countries.size, + providersCount: providers.size, + onlineServers, + totalServers: servers.length + }); + } catch (e) { + console.error('Ошибка загрузки статистики:', e); + setError('Не удалось загрузить статистику'); + setStats({ + domainsCount: null, + ipRangesCount: null, + asnsCount: null, + serversCount: null, + lastModified: null, + countriesCount: null, + providersCount: null, + onlineServers: null, + totalServers: null + }); + } finally { + setLoading(false); + } + } + fetchStats(); + }, []); + + if (error) { + return ( +
+ + {error} +
+ ); + } + + return ( +
+ {/* Заголовок страницы */} +
+
+
+

Dashboard

+
Главная / Dashboard
+
+
+
+ +
+
+
+
+ + {/* Основные метрики */} +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ + {/* Дополнительные метрики */} +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ + {/* Графики и диаграммы (заглушки для будущего) */} +
+
+
+
+

Распределение по странам

+
+
+
+ +

График распределения серверов по странам

+ Будет добавлен в следующем обновлении +
+
+
+
+
+
+
+

Активность обновлений

+
+
+
+ +

График активности обновлений данных

+ Будет добавлен в следующем обновлении +
+
+
+
+
+
+ ); +} + +export default Dashboard; \ No newline at end of file