diff --git a/src/pages/AdminRemnawave.tsx b/src/pages/AdminRemnawave.tsx
index a4e7d7f..ea66de4 100644
--- a/src/pages/AdminRemnawave.tsx
+++ b/src/pages/AdminRemnawave.tsx
@@ -36,13 +36,7 @@ import {
BackIcon,
ChevronRightIcon,
} from '../components/icons';
-import {
- PiCpuDuotone,
- PiMemoryDuotone,
- PiTimerDuotone,
- PiDevicesDuotone,
- PiPulseDuotone,
-} from 'react-icons/pi';
+import { PiCpu, PiMemory, PiTimer, PiDevices, PiPulse } from 'react-icons/pi';
const formatBytes = (bytes: number): string => {
if (bytes === 0) return '0 B';
@@ -294,16 +288,18 @@ const formatUptimeSince = (iso: string): string => {
function BreakdownCard({
title,
items,
+ wide = false,
}: {
title: string;
items: { label: string; count: number }[];
+ wide?: boolean;
}) {
const max = Math.max(1, ...items.map((i) => i.count));
return (
{title}
-
- {items.slice(0, 8).map((it) => (
+
+ {items.slice(0, wide ? 16 : 8).map((it) => (
{it.label}
@@ -377,7 +373,7 @@ function OverviewTab({
{t('admin.remnawave.overview.system', 'System')}
-
+
{t('admin.remnawave.overview.bandwidth', 'Inbound Traffic')}
-
+
{t('admin.remnawave.overview.server', 'Server')}
-
+
}
+ icon={
}
color="accent"
/>
}
+ icon={
}
color={memoryUsedPercent > 80 ? 'red' : memoryUsedPercent > 60 ? 'orange' : 'green'}
/>
}
+ icon={
}
color="blue"
/>
@@ -468,7 +464,7 @@ function OverviewTab({
{t('admin.remnawave.overview.traffic', 'Traffic Statistics')}
-
+
{t('admin.remnawave.overview.usersByStatus', 'Users by Status')}
-
+
{Object.entries(stats.users_by_status).map(([status, count]) => (
{t('admin.remnawave.overview.panel', 'Панель')}
-
+
0 || devicesStats.by_app.length > 0) && (
-
+
{t('admin.remnawave.overview.devices', 'Устройства')} ·{' '}
{devicesStats.total_hwid_devices} ({devicesStats.average_devices_per_user.toFixed(1)}/
{t('admin.remnawave.overview.perUser', 'юзер')})
@@ -629,11 +625,11 @@ function OverviewTab({
{t('admin.remnawave.overview.panelHealth', 'Здоровье панели')}
{health.instances > 1 ? ` · ${health.instances}` : ''}
-
+
}
+ icon={
}
color="blue"
/>
}
+ icon={
}
color={health.event_loop_p99_ms > 50 ? 'red' : 'green'}
/>
}
+ icon={
}
color="accent"
/>
@@ -664,14 +660,14 @@ function OverviewTab({
- {t('admin.remnawave.overview.subRequests', 'Запросы подписки (по клиентам)')}
+ {t('admin.remnawave.overview.subRequests', 'Запросы подписки (по клиентам)')} ·{' '}
+ {subRequests.by_app.reduce((acc, a) => acc + a.count, 0)}
-
- ({ label: a.app, count: a.count }))}
- />
-
+
({ label: a.app, count: a.count }))}
+ />
)}
@@ -719,7 +715,7 @@ function NodesTab({
return (
{/* Stats */}
-
+
{/* Stats */}
-
+
adminRemnawaveApi.getTopConsumers(7, 10),
enabled: activeTab === 'overview',
+ refetchInterval: 60000,
staleTime: 60000,
});
@@ -1222,6 +1221,7 @@ export default function AdminRemnawave() {
queryKey: ['admin-remnawave-sub-requests'],
queryFn: adminRemnawaveApi.getSubscriptionRequests,
enabled: activeTab === 'overview',
+ refetchInterval: 60000,
staleTime: 60000,
});