mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-09-18 20:32:30 +03:00
fix(nodes): chart node net throughput in KB/s, not percent
The node history panel passed its Net Up / Net Down series to Sparkline without valueMax or yFormatter, so they inherited the percentage defaults: a fixed 0-100 scale and a "%" label. Any node above 100 KB/s drew off the top of the chart and every axis tick and tooltip read as a percentage. A Sparkline fed non-percentage data has to declare its own scale and unit; every other call site already did, only the two node net series did not.
This commit is contained in:
@@ -25,6 +25,8 @@ interface ApiMsg<T = unknown> {
|
||||
|
||||
const REFRESH_MS = 15000;
|
||||
|
||||
const formatKbps = (v: number) => v.toLocaleString(undefined, { maximumFractionDigits: 1 });
|
||||
|
||||
export default function NodeHistoryPanel({ node, bucket = 30 }: NodeHistoryPanelProps) {
|
||||
const { t } = useTranslation();
|
||||
const [cpuPoints, setCpuPoints] = useState<number[]>([]);
|
||||
@@ -51,7 +53,7 @@ export default function NodeHistoryPanel({ node, bucket = 30 }: NodeHistoryPanel
|
||||
};
|
||||
|
||||
// cpu/mem are percentages (clamp 0-100); net throughput is bytes/sec shown
|
||||
// as KB/s (no upper clamp, the sparkline auto-scales).
|
||||
// as KB/s, which must opt out of Sparkline's 0-100 "%" defaults.
|
||||
const fetchSeries = async (metric: string, kind: 'pct' | 'rate') => {
|
||||
try {
|
||||
const url = `/panel/api/nodes/history/${node.id}/${metric}/${bucket}`;
|
||||
@@ -148,6 +150,8 @@ export default function NodeHistoryPanel({ node, bucket = 30 }: NodeHistoryPanel
|
||||
fillOpacity={0.18}
|
||||
markerRadius={2.6}
|
||||
showTooltip
|
||||
valueMax={null}
|
||||
yFormatter={formatKbps}
|
||||
/>
|
||||
</div>
|
||||
<div className="series">
|
||||
@@ -164,6 +168,8 @@ export default function NodeHistoryPanel({ node, bucket = 30 }: NodeHistoryPanel
|
||||
fillOpacity={0.18}
|
||||
markerRadius={2.6}
|
||||
showTooltip
|
||||
valueMax={null}
|
||||
yFormatter={formatKbps}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
54
frontend/src/test/node-history-panel.test.tsx
Normal file
54
frontend/src/test/node-history-panel.test.tsx
Normal file
@@ -0,0 +1,54 @@
|
||||
import { render, screen, waitFor } from '@testing-library/react';
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import NodeHistoryPanel from '@/pages/nodes/NodeHistoryPanel';
|
||||
import { HttpUtil, Msg } from '@/utils';
|
||||
|
||||
const plots = vi.hoisted(() => [] as { scales: { y: { range: () => [number, number] } } }[]);
|
||||
|
||||
vi.mock('uplot', () => ({
|
||||
default: class {
|
||||
static paths = { spline: () => undefined };
|
||||
static pxRatio = 1;
|
||||
constructor(opts: (typeof plots)[number]) {
|
||||
plots.push(opts);
|
||||
}
|
||||
setData() {}
|
||||
setSize() {}
|
||||
redraw() {}
|
||||
destroy() {}
|
||||
},
|
||||
}));
|
||||
|
||||
// The net series fell through to Sparkline's percentage defaults: a 0-100 scale
|
||||
// and a "%" label, so 512 KB/s rendered as "512%" far above the chart.
|
||||
describe('NodeHistoryPanel', () => {
|
||||
it('charts net throughput in KB/s on its own scale', async () => {
|
||||
const samples: Record<string, number> = {
|
||||
cpu: 40,
|
||||
mem: 60,
|
||||
netUp: 512 * 1024,
|
||||
netDown: 200 * 1024,
|
||||
};
|
||||
vi.spyOn(HttpUtil, 'get').mockImplementation(async (url: string) => {
|
||||
const metric = url.split('/').at(-2) ?? '';
|
||||
return new Msg(true, '', [{ t: 1_700_000_000, v: samples[metric] }]);
|
||||
});
|
||||
|
||||
render(<NodeHistoryPanel node={{ id: 7 }} />);
|
||||
|
||||
await waitFor(() => expect(screen.getAllByRole('img')).toHaveLength(4));
|
||||
expect(screen.getAllByRole('img').map((el) => el.getAttribute('aria-label'))).toEqual([
|
||||
'40%',
|
||||
'60%',
|
||||
'512',
|
||||
'200',
|
||||
]);
|
||||
expect(plots.map((p) => p.scales.y.range())).toEqual([
|
||||
[0, 100],
|
||||
[0, 100],
|
||||
[0, 512 * 1.1],
|
||||
[0, 200 * 1.1],
|
||||
]);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user