feat: migrate agent charts to ThemedChart + Recharts
Replace custom LineChart/AreaChart/BarChart usage with ThemedChart wrapper. Data format changed from ChartSeries[] to Recharts-native flat objects. Uses DS v0.1.47. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -2,7 +2,7 @@ import { useMemo, useState } from 'react';
|
||||
import { useParams, Link } from 'react-router';
|
||||
import { RefreshCw, ChevronRight } from 'lucide-react';
|
||||
import {
|
||||
StatCard, StatusDot, Badge, LineChart, AreaChart, BarChart,
|
||||
StatCard, StatusDot, Badge, ThemedChart, Line, Area, ReferenceLine, CHART_COLORS,
|
||||
EventFeed, Spinner, EmptyState, SectionHeader, MonoText,
|
||||
LogViewer, ButtonGroup, useGlobalFilters,
|
||||
} from '@cameleer/design-system';
|
||||
@@ -100,46 +100,42 @@ export default function AgentInstance() {
|
||||
return eventSortAsc ? mapped.toReversed() : mapped;
|
||||
}, [events, instanceId, eventSortAsc]);
|
||||
|
||||
// JVM chart series helpers
|
||||
const cpuSeries = useMemo(() => {
|
||||
const formatTime = (t: string) =>
|
||||
new Date(t).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
|
||||
|
||||
const cpuData = useMemo(() => {
|
||||
const pts = jvmMetrics?.metrics?.['process.cpu.usage.value'];
|
||||
if (!pts?.length) return null;
|
||||
return [{ label: 'CPU %', data: pts.map((p: any) => ({ x: new Date(p.time), y: p.value * 100 })) }];
|
||||
if (!pts?.length) return [];
|
||||
return pts.map((p: any) => ({ time: p.time, cpu: p.value * 100 }));
|
||||
}, [jvmMetrics]);
|
||||
|
||||
const heapSeries = useMemo(() => {
|
||||
const heapData = useMemo(() => {
|
||||
const pts = jvmMetrics?.metrics?.['jvm.memory.used.value'];
|
||||
if (!pts?.length) return null;
|
||||
return [{ label: 'Heap MB', data: pts.map((p: any) => ({ x: new Date(p.time), y: p.value / (1024 * 1024) })) }];
|
||||
if (!pts?.length) return [];
|
||||
return pts.map((p: any) => ({ time: p.time, heap: p.value / (1024 * 1024) }));
|
||||
}, [jvmMetrics]);
|
||||
|
||||
const threadSeries = useMemo(() => {
|
||||
const threadData = useMemo(() => {
|
||||
const pts = jvmMetrics?.metrics?.['jvm.threads.live.value'];
|
||||
if (!pts?.length) return null;
|
||||
return [{ label: 'Threads', data: pts.map((p: any) => ({ x: new Date(p.time), y: p.value })) }];
|
||||
if (!pts?.length) return [];
|
||||
return pts.map((p: any) => ({ time: p.time, threads: p.value }));
|
||||
}, [jvmMetrics]);
|
||||
|
||||
const gcSeries = useMemo(() => {
|
||||
const gcData = useMemo(() => {
|
||||
const pts = jvmMetrics?.metrics?.['jvm.gc.pause.total_time'];
|
||||
if (!pts?.length) return null;
|
||||
return [{ label: 'GC ms', data: pts.map((p: any) => ({ x: new Date(p.time), y: p.value })) }];
|
||||
if (!pts?.length) return [];
|
||||
return pts.map((p: any) => ({ time: p.time, gc: p.value }));
|
||||
}, [jvmMetrics]);
|
||||
|
||||
const throughputSeries = useMemo(
|
||||
() =>
|
||||
chartData.length
|
||||
? [{ label: 'msg/s', data: chartData.map((d: any) => ({ x: d.date, y: d.throughput })) }]
|
||||
: null,
|
||||
[chartData],
|
||||
);
|
||||
const throughputData = useMemo(() => {
|
||||
if (!chartData.length) return [];
|
||||
return chartData.map((d: any) => ({ time: d.date.toISOString(), throughput: d.throughput }));
|
||||
}, [chartData]);
|
||||
|
||||
const errorSeries = useMemo(
|
||||
() =>
|
||||
chartData.length
|
||||
? [{ label: 'Error %', data: chartData.map((d: any) => ({ x: d.date, y: d.errorPct })) }]
|
||||
: null,
|
||||
[chartData],
|
||||
);
|
||||
const errorData = useMemo(() => {
|
||||
if (!chartData.length) return [];
|
||||
return chartData.map((d: any) => ({ time: d.date.toISOString(), errorPct: d.errorPct }));
|
||||
}, [chartData]);
|
||||
|
||||
// Application logs
|
||||
const { data: rawLogs } = useApplicationLogs(appId, instanceId, { toOverride: logRefreshTo, source: logSource || undefined });
|
||||
@@ -315,13 +311,14 @@ export default function AgentInstance() {
|
||||
{cpuDisplay != null ? `${cpuDisplay}% current` : ''}
|
||||
</span>
|
||||
</div>
|
||||
{cpuSeries ? (
|
||||
<AreaChart
|
||||
series={cpuSeries}
|
||||
height={160}
|
||||
yLabel="%"
|
||||
threshold={{ value: 85, label: 'Alert' }}
|
||||
/>
|
||||
{cpuData.length ? (
|
||||
<ThemedChart data={cpuData} height={160} xDataKey="time"
|
||||
xTickFormatter={formatTime} yLabel="%">
|
||||
<Area dataKey="cpu" name="CPU %" stroke={CHART_COLORS[0]}
|
||||
fill={CHART_COLORS[0]} fillOpacity={0.1} strokeWidth={2} dot={false} />
|
||||
<ReferenceLine y={85} stroke="var(--error)" strokeDasharray="5 3"
|
||||
label={{ value: 'Alert', position: 'right', fill: 'var(--error)', fontSize: 9 }} />
|
||||
</ThemedChart>
|
||||
) : (
|
||||
<EmptyState title="No data" description="No CPU metrics available" />
|
||||
)}
|
||||
@@ -336,13 +333,16 @@ export default function AgentInstance() {
|
||||
: ''}
|
||||
</span>
|
||||
</div>
|
||||
{heapSeries ? (
|
||||
<AreaChart
|
||||
series={heapSeries}
|
||||
height={160}
|
||||
yLabel="MB"
|
||||
threshold={heapMax != null ? { value: heapMax / (1024 * 1024), label: 'Max Heap' } : undefined}
|
||||
/>
|
||||
{heapData.length ? (
|
||||
<ThemedChart data={heapData} height={160} xDataKey="time"
|
||||
xTickFormatter={formatTime} yLabel="MB">
|
||||
<Area dataKey="heap" name="Heap MB" stroke={CHART_COLORS[0]}
|
||||
fill={CHART_COLORS[0]} fillOpacity={0.1} strokeWidth={2} dot={false} />
|
||||
{heapMax != null && (
|
||||
<ReferenceLine y={heapMax / (1024 * 1024)} stroke="var(--error)" strokeDasharray="5 3"
|
||||
label={{ value: 'Max Heap', position: 'right', fill: 'var(--error)', fontSize: 9 }} />
|
||||
)}
|
||||
</ThemedChart>
|
||||
) : (
|
||||
<EmptyState title="No data" description="No heap metrics available" />
|
||||
)}
|
||||
@@ -355,8 +355,12 @@ export default function AgentInstance() {
|
||||
{agent?.tps != null ? `${agent.tps.toFixed(1)} msg/s` : ''}
|
||||
</span>
|
||||
</div>
|
||||
{throughputSeries ? (
|
||||
<LineChart series={throughputSeries} height={160} yLabel="msg/s" />
|
||||
{throughputData.length ? (
|
||||
<ThemedChart data={throughputData} height={160} xDataKey="time"
|
||||
xTickFormatter={formatTime} yLabel="msg/s">
|
||||
<Line dataKey="throughput" name="msg/s" stroke={CHART_COLORS[0]}
|
||||
strokeWidth={2} dot={false} />
|
||||
</ThemedChart>
|
||||
) : (
|
||||
<EmptyState title="No data" description="No throughput data in range" />
|
||||
)}
|
||||
@@ -369,8 +373,12 @@ export default function AgentInstance() {
|
||||
{agent?.errorRate != null ? `${(agent.errorRate * 100).toFixed(1)}%` : ''}
|
||||
</span>
|
||||
</div>
|
||||
{errorSeries ? (
|
||||
<LineChart series={errorSeries} height={160} yLabel="%" />
|
||||
{errorData.length ? (
|
||||
<ThemedChart data={errorData} height={160} xDataKey="time"
|
||||
xTickFormatter={formatTime} yLabel="%">
|
||||
<Line dataKey="errorPct" name="Error %" stroke={CHART_COLORS[0]}
|
||||
strokeWidth={2} dot={false} />
|
||||
</ThemedChart>
|
||||
) : (
|
||||
<EmptyState title="No data" description="No error data in range" />
|
||||
)}
|
||||
@@ -380,13 +388,17 @@ export default function AgentInstance() {
|
||||
<div className={styles.chartHeader}>
|
||||
<span className={styles.chartTitle}>Thread Count</span>
|
||||
<span className={styles.chartMeta}>
|
||||
{threadSeries
|
||||
? `${threadSeries[0].data[threadSeries[0].data.length - 1]?.y.toFixed(0)} active`
|
||||
{threadData.length
|
||||
? `${threadData[threadData.length - 1].threads.toFixed(0)} active`
|
||||
: ''}
|
||||
</span>
|
||||
</div>
|
||||
{threadSeries ? (
|
||||
<LineChart series={threadSeries} height={160} yLabel="threads" />
|
||||
{threadData.length ? (
|
||||
<ThemedChart data={threadData} height={160} xDataKey="time"
|
||||
xTickFormatter={formatTime} yLabel="threads">
|
||||
<Line dataKey="threads" name="Threads" stroke={CHART_COLORS[0]}
|
||||
strokeWidth={2} dot={false} />
|
||||
</ThemedChart>
|
||||
) : (
|
||||
<EmptyState title="No data" description="No thread metrics available" />
|
||||
)}
|
||||
@@ -397,8 +409,12 @@ export default function AgentInstance() {
|
||||
<span className={styles.chartTitle}>GC Pauses</span>
|
||||
<span className={styles.chartMeta} />
|
||||
</div>
|
||||
{gcSeries ? (
|
||||
<AreaChart series={gcSeries} height={160} yLabel="ms" />
|
||||
{gcData.length ? (
|
||||
<ThemedChart data={gcData} height={160} xDataKey="time"
|
||||
xTickFormatter={formatTime} yLabel="ms">
|
||||
<Area dataKey="gc" name="GC ms" stroke={CHART_COLORS[1]}
|
||||
fill={CHART_COLORS[1]} fillOpacity={0.1} strokeWidth={2} dot={false} />
|
||||
</ThemedChart>
|
||||
) : (
|
||||
<EmptyState title="No data" description="No GC metrics available" />
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user