import React, { useEffect, useMemo, useState } from "react"; import { Alert, Button, Card, Empty, Space, Spin, Tag, Typography } from "antd"; import { DashboardOutlined, PauseCircleOutlined, PlayCircleOutlined, ReloadOutlined } from "@ant-design/icons"; import { useI18n } from "../i18n/provider"; import { useStore } from "../store"; import type { JVMMonitoringSessionState, TabData } from "../types"; import { buildRpcConnectionConfig } from "../utils/connectionRpcConfig"; import { buildMonitoringAvailabilityText, normalizeMonitoringProviderMode, type JVMMonitoringProviderMode, } from "../utils/jvmMonitoringPresentation"; import { resolveJVMModeMeta } from "../utils/jvmRuntimePresentation"; import JVMMonitoringCharts from "./jvm/JVMMonitoringCharts"; import JVMMonitoringDetailPanel from "./jvm/JVMMonitoringDetailPanel"; import JVMMonitoringStatusCards from "./jvm/JVMMonitoringStatusCards"; const { Paragraph, Text, Title } = Typography; const POLL_INTERVAL_MS = 2000; type JVMMonitoringDashboardProps = { tab: TabData; }; const isMonitoringSessionMissing = (message: string): boolean => /monitoring session not found/i.test(String(message || "")); const createEmptySession = ( connectionId: string, providerMode: JVMMonitoringProviderMode, ): JVMMonitoringSessionState => ({ connectionId, providerMode, running: false, points: [], recentGcEvents: [], availableMetrics: [], missingMetrics: [], providerWarnings: [], }); const normalizeMonitoringSession = ( payload: any, connectionId: string, providerMode: JVMMonitoringProviderMode, ): JVMMonitoringSessionState => ({ connectionId: String(payload?.connectionId || connectionId), providerMode: normalizeMonitoringProviderMode(payload?.providerMode, providerMode), running: payload?.running === true, points: Array.isArray(payload?.points) ? payload.points : [], recentGcEvents: Array.isArray(payload?.recentGcEvents) ? payload.recentGcEvents : [], availableMetrics: Array.isArray(payload?.availableMetrics) ? payload.availableMetrics : [], missingMetrics: Array.isArray(payload?.missingMetrics) ? payload.missingMetrics : [], providerWarnings: Array.isArray(payload?.providerWarnings) ? payload.providerWarnings : [], }); const resolveBackendApp = () => typeof window === "undefined" ? undefined : (window as any).go?.app?.App; const JVMMonitoringDashboard: React.FC = ({ tab }) => { const { t, language } = useI18n(); const theme = useStore((state) => state.theme); const connection = useStore((state) => state.connections.find((item) => item.id === tab.connectionId), ); const darkMode = theme === "dark"; const providerMode = normalizeMonitoringProviderMode( tab.providerMode, normalizeMonitoringProviderMode(connection?.config.jvm?.preferredMode, "jmx"), ); const [session, setSession] = useState(() => createEmptySession(tab.connectionId, providerMode), ); const [loading, setLoading] = useState(true); const [error, setError] = useState(""); const [actionLoading, setActionLoading] = useState(false); const [pollSeed, setPollSeed] = useState(0); const rpcConnectionConfig = useMemo(() => { if (!connection) { return null; } return buildRpcConnectionConfig(connection.config, { database: "", jvm: { ...(connection.config.jvm || {}), preferredMode: providerMode, allowedModes: [providerMode], }, }); }, [connection, providerMode]); const latestPoint = useMemo(() => { const points = session.points || []; return points.length > 0 ? points[points.length - 1] : undefined; }, [session.points]); useEffect(() => { setSession(createEmptySession(tab.connectionId, providerMode)); }, [tab.connectionId, providerMode]); useEffect(() => { if (!connection || !rpcConnectionConfig) { setLoading(false); return; } let cancelled = false; let timer: ReturnType | null = null; const backendApp = resolveBackendApp(); const poll = async () => { if (cancelled) { return; } setLoading(true); if (typeof backendApp?.JVMGetMonitoringHistory !== "function") { setError(t("jvm_monitoring_dashboard.error.history_unavailable")); setLoading(false); return; } try { const result = await backendApp.JVMGetMonitoringHistory( rpcConnectionConfig, providerMode, ); if (cancelled) { return; } if (result?.success === false) { const message = String( result?.message || t("jvm_monitoring_dashboard.error.history_load_failed"), ); if (isMonitoringSessionMissing(message)) { setSession(createEmptySession(tab.connectionId, providerMode)); setError(""); setLoading(false); return; } throw new Error(message); } const nextSession = normalizeMonitoringSession( result?.data, tab.connectionId, providerMode, ); setSession(nextSession); setError(""); setLoading(false); if (nextSession.running) { timer = setTimeout(poll, POLL_INTERVAL_MS); } } catch (fetchError: any) { if (!cancelled) { setError( fetchError?.message || t("jvm_monitoring_dashboard.error.history_load_failed"), ); setLoading(false); } } }; void poll(); return () => { cancelled = true; if (timer) { clearTimeout(timer); } }; }, [connection, providerMode, rpcConnectionConfig, tab.connectionId, pollSeed, t]); if (!connection) { return ( ); } const backendApp = resolveBackendApp(); const availabilityText = buildMonitoringAvailabilityText(session, language); const modeMeta = resolveJVMModeMeta(providerMode); const emptyState = !session.running && (session.points || []).length === 0; const handleStart = async () => { if (!rpcConnectionConfig || typeof backendApp?.JVMStartMonitoring !== "function") { setError(t("jvm_monitoring_dashboard.error.start_unavailable")); return; } setActionLoading(true); setError(""); try { const result = await backendApp.JVMStartMonitoring(rpcConnectionConfig); if (result?.success === false) { throw new Error( String(result?.message || t("jvm_monitoring_dashboard.error.start_failed")), ); } setSession( normalizeMonitoringSession(result?.data, tab.connectionId, providerMode), ); setPollSeed((current) => current + 1); } catch (startError: any) { setError(startError?.message || t("jvm_monitoring_dashboard.error.start_failed")); } finally { setActionLoading(false); } }; const handleStop = async () => { if (!rpcConnectionConfig || typeof backendApp?.JVMStopMonitoring !== "function") { setError(t("jvm_monitoring_dashboard.error.stop_unavailable")); return; } setActionLoading(true); setError(""); try { const result = await backendApp.JVMStopMonitoring( rpcConnectionConfig, providerMode, ); if (result?.success === false) { throw new Error( String(result?.message || t("jvm_monitoring_dashboard.error.stop_failed")), ); } setSession((current) => ({ ...current, running: false })); setPollSeed((current) => current + 1); } catch (stopError: any) { setError(stopError?.message || t("jvm_monitoring_dashboard.error.stop_failed")); } finally { setActionLoading(false); } }; return (
<DashboardOutlined style={{ color: "#1677ff", marginRight: 8 }} /> {t("jvm_monitoring_dashboard.title")} {connection.name} {" "} ยท {connection.config.host}:{connection.config.port}
{modeMeta.label} {session.running ? ( {t("jvm_monitoring_dashboard.status.sampling")} ) : ( {t("jvm_monitoring_dashboard.status.stopped")} )} {session.running ? ( ) : ( )}
{(session.missingMetrics?.length || session.providerWarnings?.length) ? ( ) : null} {error ? : null}
{loading && emptyState ? (
) : null} {emptyState ? (
{t("jvm_monitoring_dashboard.empty.description")}
) : (
)}
); }; export default JVMMonitoringDashboard;