feat: Add language and theme context providers to App component, implement global search functionality in Dashboard, and enhance ASNs, Domains, and IPRanges managers with drag-and-drop import feature and confirmation modals for improved user experience
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 5m48s
Publish Fast Tabler Docker image / build-and-push-fast (push) Successful in 5m48s
This commit is contained in:
@@ -13,7 +13,8 @@ import {
|
||||
IconRefresh,
|
||||
IconFilter,
|
||||
IconDownload,
|
||||
IconCreditCard
|
||||
IconCreditCard,
|
||||
IconSearch
|
||||
} from '@tabler/icons-react';
|
||||
|
||||
function StatCard({ icon: Icon, color, value, title, subtitle, to }) {
|
||||
@@ -56,6 +57,7 @@ function MetricCard({ title, value, icon: Icon, color, description }) {
|
||||
}
|
||||
|
||||
function Dashboard() {
|
||||
const [globalQuery, setGlobalQuery] = useState('');
|
||||
const [stats, setStats] = useState({
|
||||
domainsCount: null,
|
||||
ipRangesCount: null,
|
||||
@@ -67,6 +69,7 @@ function Dashboard() {
|
||||
onlineServers: null,
|
||||
totalServers: null
|
||||
});
|
||||
const [raw, setRaw] = useState({ domains: [], ipRanges: [], asns: [], servers: [] });
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [error, setError] = useState(null);
|
||||
|
||||
@@ -92,7 +95,11 @@ function Dashboard() {
|
||||
const s3Res = results[4];
|
||||
|
||||
// Получаем данные серверов для подсчета дополнительной статистики
|
||||
const domains = domainsRes.status === 'fulfilled' ? domainsRes.value.data : [];
|
||||
const ipRanges = ipRangesRes.status === 'fulfilled' ? ipRangesRes.value.data : [];
|
||||
const asns = asnsRes.status === 'fulfilled' ? asnsRes.value.data : [];
|
||||
const servers = serversRes.status === 'fulfilled' ? serversRes.value.data : [];
|
||||
setRaw({ domains, ipRanges, asns, servers });
|
||||
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;
|
||||
@@ -103,9 +110,9 @@ function Dashboard() {
|
||||
const lastModified = lmRaw ? new Date(lmRaw).toLocaleString() : new Date().toLocaleString();
|
||||
|
||||
setStats({
|
||||
domainsCount: domainsRes.status === 'fulfilled' ? domainsRes.value.data.length : 0,
|
||||
ipRangesCount: ipRangesRes.status === 'fulfilled' ? ipRangesRes.value.data.length : 0,
|
||||
asnsCount: asnsRes.status === 'fulfilled' ? asnsRes.value.data.length : 0,
|
||||
domainsCount: domains.length,
|
||||
ipRangesCount: ipRanges.length,
|
||||
asnsCount: asns.length,
|
||||
serversCount: servers.length,
|
||||
lastModified,
|
||||
countriesCount: countries.size,
|
||||
@@ -151,6 +158,19 @@ function Dashboard() {
|
||||
|
||||
return (
|
||||
<div>
|
||||
{/* Глобальный поиск */}
|
||||
<div className="card mb-3">
|
||||
<div className="card-body d-flex align-items-center">
|
||||
<span className="input-icon-addon me-2"><IconSearch size={18} /></span>
|
||||
<input
|
||||
type="text"
|
||||
className="form-control"
|
||||
placeholder="Глобальный поиск по доменам, ASN, IP, серверам..."
|
||||
value={globalQuery}
|
||||
onChange={(e) => setGlobalQuery(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{/* Заголовок страницы */}
|
||||
<div className="page-header d-print-none mb-4">
|
||||
<div className="row align-items-center">
|
||||
@@ -172,7 +192,9 @@ function Dashboard() {
|
||||
<StatCard
|
||||
icon={IconWorld}
|
||||
color="blue"
|
||||
value={loading ? '...' : stats.domainsCount ?? '—'}
|
||||
value={loading ? '...' : (globalQuery
|
||||
? raw.domains.filter(d => `${d.domain} ${d.community || d.type || ''}`.toLowerCase().includes(globalQuery.toLowerCase())).length
|
||||
: (stats.domainsCount ?? '—'))}
|
||||
title="Доменов"
|
||||
subtitle="Всего доменов в системе"
|
||||
to="/domains"
|
||||
@@ -182,7 +204,9 @@ function Dashboard() {
|
||||
<StatCard
|
||||
icon={IconNetwork}
|
||||
color="green"
|
||||
value={loading ? '...' : stats.ipRangesCount ?? '—'}
|
||||
value={loading ? '...' : (globalQuery
|
||||
? raw.ipRanges.filter(x => `${x.ipRange} ${x.community || ''}`.toLowerCase().includes(globalQuery.toLowerCase())).length
|
||||
: (stats.ipRangesCount ?? '—'))}
|
||||
title="IP-диапазонов"
|
||||
subtitle="Всего IP диапазонов"
|
||||
to="/ip-ranges"
|
||||
@@ -192,7 +216,9 @@ function Dashboard() {
|
||||
<StatCard
|
||||
icon={IconNetwork}
|
||||
color="purple"
|
||||
value={loading ? '...' : stats.asnsCount ?? '—'}
|
||||
value={loading ? '...' : (globalQuery
|
||||
? raw.asns.filter(a => `${a.domain || a.asn} ${a.type || a.community || ''}`.toLowerCase().includes(globalQuery.toLowerCase())).length
|
||||
: (stats.asnsCount ?? '—'))}
|
||||
title="AS"
|
||||
subtitle="Всего Autonomous Systems"
|
||||
to="/asns"
|
||||
@@ -202,7 +228,9 @@ function Dashboard() {
|
||||
<StatCard
|
||||
icon={IconServer}
|
||||
color="orange"
|
||||
value={loading ? '...' : stats.serversCount ?? '—'}
|
||||
value={loading ? '...' : (globalQuery
|
||||
? raw.servers.filter(s => `${s.ip} ${s.dns} ${s.country} ${s.provider}`.toLowerCase().includes(globalQuery.toLowerCase())).length
|
||||
: (stats.serversCount ?? '—'))}
|
||||
title="Серверов"
|
||||
subtitle="Всего серверов"
|
||||
to="/servers"
|
||||
|
||||
Reference in New Issue
Block a user