From eaffde0f8914e74d45452c02f77be4565261b3a0 Mon Sep 17 00:00:00 2001 From: Junyan Qin Date: Sun, 19 Apr 2026 13:54:23 +0800 Subject: [PATCH] refactor(web): compact system status into a single card alongside metrics Replace the separate two-card row with a single compact 'System Status' card placed as the 5th column in the metrics grid. Shows green/red dots for Plugin Runtime and Box Runtime. Click to expand a popover with connection details (backend, profile, sandbox count). --- .../overview-cards/OverviewCards.tsx | 6 +- .../overview-cards/SystemStatusCards.tsx | 263 ++++++++++-------- web/src/app/home/monitoring/page.tsx | 4 - web/src/i18n/locales/en-US.ts | 1 + web/src/i18n/locales/es-ES.ts | 1 + web/src/i18n/locales/ja-JP.ts | 1 + web/src/i18n/locales/ru-RU.ts | 1 + web/src/i18n/locales/th-TH.ts | 1 + web/src/i18n/locales/vi-VN.ts | 1 + web/src/i18n/locales/zh-Hans.ts | 1 + web/src/i18n/locales/zh-Hant.ts | 1 + 11 files changed, 161 insertions(+), 120 deletions(-) diff --git a/web/src/app/home/monitoring/components/overview-cards/OverviewCards.tsx b/web/src/app/home/monitoring/components/overview-cards/OverviewCards.tsx index 5ef5226d..a116cc5e 100644 --- a/web/src/app/home/monitoring/components/overview-cards/OverviewCards.tsx +++ b/web/src/app/home/monitoring/components/overview-cards/OverviewCards.tsx @@ -2,6 +2,7 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; import { MessageSquare, Sparkles, Check, Users } from 'lucide-react'; import MetricCard from './MetricCard'; +import SystemStatusCard from './SystemStatusCards'; import TrafficChart from './TrafficChart'; import { OverviewMetrics, @@ -81,8 +82,8 @@ export default function OverviewCards({ return (
- {/* Metric Cards */} -
+ {/* 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 */}