+ {/* Metric Cards + System Status */}
+
{cards.map((card, index) => (
))}
+
{/* Traffic Chart */}
diff --git a/web/src/app/home/monitoring/components/overview-cards/SystemStatusCards.tsx b/web/src/app/home/monitoring/components/overview-cards/SystemStatusCards.tsx
index 5181fed1..a69f6991 100644
--- a/web/src/app/home/monitoring/components/overview-cards/SystemStatusCards.tsx
+++ b/web/src/app/home/monitoring/components/overview-cards/SystemStatusCards.tsx
@@ -1,75 +1,36 @@
import React, { useEffect, useState, useCallback } from 'react';
import { useTranslation } from 'react-i18next';
-import { Plug, Box, CircleCheck, CircleX, Loader2 } from 'lucide-react';
+import {
+ Plug,
+ Box,
+ CircleCheck,
+ CircleX,
+ Loader2,
+ ChevronDown,
+} from 'lucide-react';
+import { Card, CardHeader, CardTitle, CardContent } from '@/components/ui/card';
+import {
+ Popover,
+ PopoverContent,
+ PopoverTrigger,
+} from '@/components/ui/popover';
import {
ApiRespPluginSystemStatus,
ApiRespBoxStatus,
} from '@/app/infra/entities/api';
import { httpClient } from '@/app/infra/http/HttpClient';
-interface StatusCardProps {
- icon: React.ReactNode;
- title: string;
- connected: boolean | null;
- connectedLabel: string;
- disconnectedLabel: string;
- details?: { label: string; value: string }[];
- loading: boolean;
-}
-
-function StatusCard({
- icon,
- title,
- connected,
- connectedLabel,
- disconnectedLabel,
- details,
- loading,
-}: StatusCardProps) {
- return (
-
-
- {icon}
-
-
-
{title}
- {loading ? (
-
-
-
- ) : connected === null ? (
-
—
- ) : connected ? (
-
-
-
- {connectedLabel}
-
-
- ) : (
-
-
-
- {disconnectedLabel}
-
-
- )}
- {!loading && details && details.length > 0 && (
-
- {details.map((d) => (
-
- {d.label}:{' '}
- {d.value}
-
- ))}
-
- )}
-
-
+function StatusDot({ ok }: { ok: boolean | null }) {
+ if (ok === null)
+ return
;
+ return ok ? (
+
+ ) : (
+
);
}
-export default function SystemStatusCards() {
+export default function SystemStatusCard() {
const { t } = useTranslation();
const [pluginStatus, setPluginStatus] =
useState
(null);
@@ -94,64 +55,138 @@ export default function SystemStatusCards() {
fetchStatus();
}, [fetchStatus]);
- const pluginConnected = pluginStatus
+ const pluginOk = pluginStatus
? pluginStatus.is_enable && pluginStatus.is_connected
: null;
+ const boxOk = boxStatus ? boxStatus.available : null;
- const pluginDetails: { label: string; value: string }[] = [];
- if (pluginStatus && !pluginStatus.is_enable) {
- pluginDetails.push({
- label: t('monitoring.statusDetail'),
- value: t('monitoring.pluginDisabled'),
- });
- }
-
- const boxConnected = boxStatus ? boxStatus.available : null;
-
- const boxDetails: { label: string; value: string }[] = [];
- if (boxStatus) {
- if (boxStatus.backend) {
- boxDetails.push({
- label: t('monitoring.boxBackend'),
- value: `${boxStatus.backend.name}`,
- });
- }
- boxDetails.push({
- label: t('monitoring.boxProfile'),
- value: boxStatus.profile,
- });
- if (boxStatus.available && boxStatus.active_sessions !== undefined) {
- boxDetails.push({
- label: t('monitoring.boxSandboxes'),
- value: String(boxStatus.active_sessions),
- });
- }
+ if (loading) {
+ return (
+
+
+
+ {t('monitoring.systemStatus')}
+
+
+
+
+
+
+
+
+ );
}
return (
-
- }
- title={t('monitoring.pluginRuntime')}
- connected={pluginConnected}
- connectedLabel={t('monitoring.connected')}
- disconnectedLabel={
- pluginStatus && !pluginStatus.is_enable
- ? t('monitoring.disabled')
- : t('monitoring.disconnected')
- }
- details={pluginDetails}
- loading={loading}
- />
- }
- title={t('monitoring.boxRuntime')}
- connected={boxConnected}
- connectedLabel={t('monitoring.connected')}
- disconnectedLabel={t('monitoring.disconnected')}
- details={boxDetails}
- loading={loading}
- />
-
+
+
+
+
+
+ {t('monitoring.systemStatus')}
+
+
+
+
+
+
+
+
{t('monitoring.pluginRuntime')}
+
+
+
+
+ {t('monitoring.boxRuntime')}
+
+
+
+
+
+
+ {/* Plugin Runtime */}
+
+
+
+
+ {t('monitoring.pluginRuntime')}
+
+
+
+
+ {pluginOk ? (
+
+ ) : (
+
+ )}
+
+ {pluginOk
+ ? t('monitoring.connected')
+ : pluginStatus && !pluginStatus.is_enable
+ ? t('monitoring.disabled')
+ : t('monitoring.disconnected')}
+
+
+ {pluginStatus && !pluginStatus.is_enable && (
+
+ {t('monitoring.pluginDisabled')}
+
+ )}
+
+
+
+
+
+ {/* Box Runtime */}
+
+
+
+
+ {t('monitoring.boxRuntime')}
+
+
+
+
+ {boxOk ? (
+
+ ) : (
+
+ )}
+
+ {boxOk
+ ? t('monitoring.connected')
+ : t('monitoring.disconnected')}
+
+
+ {boxStatus && (
+
+ {boxStatus.backend && (
+
+ {t('monitoring.boxBackend')}:{' '}
+
+ {boxStatus.backend.name}
+
+
+ )}
+
+ {t('monitoring.boxProfile')}:{' '}
+
+ {boxStatus.profile}
+
+
+ {boxOk && boxStatus.active_sessions !== undefined && (
+
+ {t('monitoring.boxSandboxes')}:{' '}
+
+ {boxStatus.active_sessions}
+
+
+ )}
+
+ )}
+
+
+
+
+
);
}
diff --git a/web/src/app/home/monitoring/page.tsx b/web/src/app/home/monitoring/page.tsx
index 387506bb..a86b83ae 100644
--- a/web/src/app/home/monitoring/page.tsx
+++ b/web/src/app/home/monitoring/page.tsx
@@ -12,7 +12,6 @@ import {
CheckCircle2,
} from 'lucide-react';
import OverviewCards from './components/overview-cards/OverviewCards';
-import SystemStatusCards from './components/overview-cards/SystemStatusCards';
import MonitoringFilters from './components/filters/MonitoringFilters';
import { ExportDropdown } from './components/ExportDropdown';
import { useMonitoringFilters } from './hooks/useMonitoringFilters';
@@ -297,9 +296,6 @@ function MonitoringPageContent() {
{/* Content Area */}
- {/* System Status */}
-
-
{/* Overview Section */}