1import React from "react";
2import { Line } from "react-chartjs-2";
3import { apiFetch } from "../../lib/api";
27export default function TabMetrics({ agentId, wsData }) {
28 const [history, setHistory] = React.useState([]);
29 const [loading, setLoading] = React.useState(true);
30 const [error, setError] = React.useState(null);
31 const [realtimeMetric, setRealtimeMetric] = React.useState(null);
33 // Load historical data from API
34 React.useEffect(() => {
37 apiFetch(`/agent/${agentId}/metrics?limit=100`)
38 .then((res) => res.json())
40 console.log('[TabMetrics] Received data:', d);
41 if (Array.isArray(d)) {
42 console.log('[TabMetrics] Data is array, length:', d.length);
43 // Reverse to show oldest first (chronological order)
44 setHistory(d.reverse());
46 console.log('[TabMetrics] Data is not array:', typeof d);
51 console.error("Failed to load metrics:", err);
52 setError(err.message);
54 .finally(() => setLoading(false));
57 // Update realtime metric from WebSocket
58 React.useEffect(() => {
60 console.log('[TabMetrics] WebSocket data received:', wsData);
62 timestamp: new Date().toISOString(),
63 cpu: wsData.cpuLoad1 || wsData.cpu || 0,
64 memory: wsData.memPercent || wsData.memory || 0,
65 disk: Array.isArray(wsData.disk) ? wsData.disk.reduce((sum, d) => sum + (d.used || 0), 0) : (wsData.disk || 0),
66 uptime: wsData.uptime || 0,
74 <div className="tab-container">
75 <h2>Performance Metrics</h2>
76 <div className="card">Loading metrics...</div>
83 <div className="tab-container">
84 <h2>Performance Metrics</h2>
85 <div className="card error">Error: {error}</div>
90 // Combine historical data with realtime metric for display
91 let displayData = history;
92 if (realtimeMetric && history.length > 0) {
93 // Append realtime data to history for charts
94 displayData = [...history, realtimeMetric];
95 } else if (realtimeMetric && history.length === 0) {
96 // Only have realtime data, use it alone
97 displayData = [realtimeMetric];
100 if (!Array.isArray(displayData) || displayData.length === 0) {
102 <div className="tab-container">
103 <h2>Performance Metrics</h2>
104 <div className="card">
105 <p style={{ textAlign: "center", color: "var(--text-secondary)" }}>
106 No metrics data available yet. Metrics are collected in real-time from the agent.
113 // Take last 50 data points for better readability
114 const recent = displayData.slice(-50);
116 const labels = recent.map((h) => {
117 const date = new Date(h.timestamp);
118 return date.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
121 const cpuData = recent.map((h) => parseFloat(h.cpu) || 0);
122 const memoryData = recent.map((h) => parseFloat(h.memory) || 0);
123 const diskData = recent.map((h) => {
124 // disk is in bytes, convert to GB
125 const diskBytes = parseFloat(h.disk) || 0;
126 return (diskBytes / (1024 * 1024 * 1024)).toFixed(2);
129 const cpuChartData = {
133 label: "CPU Usage (%)",
135 borderColor: "rgb(75, 192, 192)",
136 backgroundColor: "rgba(75, 192, 192, 0.1)",
143 const memoryChartData = {
147 label: "Memory Usage (%)",
149 borderColor: "rgb(255, 99, 132)",
150 backgroundColor: "rgba(255, 99, 132, 0.1)",
157 const diskChartData = {
161 label: "Disk Usage (GB)",
163 borderColor: "rgb(153, 102, 255)",
164 backgroundColor: "rgba(153, 102, 255, 0.1)",
171 const chartOptions = {
173 maintainAspectRatio: false,
188 color: "rgba(0, 0, 0, 0.05)",
199 const diskChartOptions = {
202 ...chartOptions.scales,
204 ...chartOptions.scales.y,
206 callback: function (value) {
207 return value + " GB";
214 // Calculate current stats - prefer realtime data if available
215 const latest = realtimeMetric || recent[recent.length - 1];
216 const currentCPU = parseFloat(latest?.cpu || 0).toFixed(1);
217 const currentMemory = parseFloat(latest?.memory || 0).toFixed(1);
218 const currentDisk = (parseFloat(latest?.disk || 0) / (1024 * 1024 * 1024)).toFixed(2);
219 const uptime = parseInt(latest?.uptime || 0);
220 const uptimeHours = Math.floor(uptime / 3600);
221 const uptimeDays = Math.floor(uptimeHours / 24);
224 <div className="tab-container">
225 <h2>Performance Metrics</h2>
227 {/* Current Stats Summary */}
228 <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fit, minmax(200px, 1fr))", gap: "16px", marginBottom: "24px" }}>
229 <div className="card" style={{ padding: "16px" }}>
230 <div style={{ fontSize: "12px", color: "var(--text-secondary)", marginBottom: "4px" }}>CPU Usage</div>
231 <div style={{ fontSize: "24px", fontWeight: "600", color: "var(--text)" }}>{currentCPU}%</div>
233 <div className="card" style={{ padding: "16px" }}>
234 <div style={{ fontSize: "12px", color: "var(--text-secondary)", marginBottom: "4px" }}>Memory Usage</div>
235 <div style={{ fontSize: "24px", fontWeight: "600", color: "var(--text)" }}>{currentMemory}%</div>
237 <div className="card" style={{ padding: "16px" }}>
238 <div style={{ fontSize: "12px", color: "var(--text-secondary)", marginBottom: "4px" }}>Disk Used</div>
239 <div style={{ fontSize: "24px", fontWeight: "600", color: "var(--text)" }}>{currentDisk} GB</div>
241 <div className="card" style={{ padding: "16px" }}>
242 <div style={{ fontSize: "12px", color: "var(--text-secondary)", marginBottom: "4px" }}>Uptime</div>
243 <div style={{ fontSize: "24px", fontWeight: "600", color: "var(--text)" }}>
244 {uptimeDays > 0 ? `${uptimeDays}d ${uptimeHours % 24}h` : `${uptimeHours}h`}
250 <div className="card" style={{ padding: "20px", marginBottom: "20px" }}>
251 <h3 style={{ marginTop: 0, marginBottom: "16px", fontSize: "16px", fontWeight: "600" }}>
254 <div style={{ height: "250px" }}>
255 <Line data={cpuChartData} options={chartOptions} />
260 <div className="card" style={{ padding: "20px", marginBottom: "20px" }}>
261 <h3 style={{ marginTop: 0, marginBottom: "16px", fontSize: "16px", fontWeight: "600" }}>
262 Memory Usage Over Time
264 <div style={{ height: "250px" }}>
265 <Line data={memoryChartData} options={chartOptions} />
270 <div className="card" style={{ padding: "20px" }}>
271 <h3 style={{ marginTop: 0, marginBottom: "16px", fontSize: "16px", fontWeight: "600" }}>
274 <div style={{ height: "250px" }}>
275 <Line data={diskChartData} options={diskChartOptions} />
279 <div style={{ marginTop: "16px", fontSize: "12px", color: "var(--text-secondary)", textAlign: "center" }}>
280 Showing last {recent.length} data points • Metrics collected every 2 minutes