Initial project structure cleanup
This commit is contained in:
@@ -0,0 +1,169 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
|
||||
import {
|
||||
Area,
|
||||
AreaChart,
|
||||
CartesianGrid,
|
||||
ResponsiveContainer,
|
||||
Tooltip,
|
||||
XAxis,
|
||||
YAxis,
|
||||
} from 'recharts';
|
||||
|
||||
import { Card } from '@/components/ui/Card';
|
||||
|
||||
import { vpsApi } from '@/services/vpsApi';
|
||||
|
||||
type TrafficPoint = {
|
||||
time: string;
|
||||
downloadMbps: number;
|
||||
uploadMbps: number;
|
||||
};
|
||||
|
||||
const MAX_POINTS = 24;
|
||||
|
||||
export function NetworkTrafficChart() {
|
||||
const [points, setPoints] = useState<
|
||||
TrafficPoint[]
|
||||
>([]);
|
||||
|
||||
const [error, setError] = useState('');
|
||||
|
||||
useEffect(() => {
|
||||
let mounted = true;
|
||||
|
||||
async function loadTraffic() {
|
||||
try {
|
||||
const response =
|
||||
await vpsApi.networkTraffic();
|
||||
|
||||
if (!mounted) {
|
||||
return;
|
||||
}
|
||||
|
||||
const point: TrafficPoint = {
|
||||
time: new Date(
|
||||
response.updatedAt,
|
||||
).toLocaleTimeString(),
|
||||
downloadMbps:
|
||||
response.downloadMbps,
|
||||
uploadMbps:
|
||||
response.uploadMbps,
|
||||
};
|
||||
|
||||
setPoints((currentPoints) => [
|
||||
...currentPoints.slice(
|
||||
-(MAX_POINTS - 1),
|
||||
),
|
||||
point,
|
||||
]);
|
||||
|
||||
setError('');
|
||||
} catch (err) {
|
||||
if (mounted) {
|
||||
setError(String(err));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
loadTraffic();
|
||||
|
||||
const intervalId = window.setInterval(
|
||||
loadTraffic,
|
||||
3000,
|
||||
);
|
||||
|
||||
return () => {
|
||||
mounted = false;
|
||||
window.clearInterval(intervalId);
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Card className="h-full">
|
||||
<div className="mb-4 flex items-center justify-between">
|
||||
<div>
|
||||
<h3 className="font-semibold text-white">
|
||||
Network Traffic
|
||||
</h3>
|
||||
|
||||
<p className="text-xs text-slate-500">
|
||||
Live wg0 RX/TX throughput
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<span className="rounded-lg border border-white/10 bg-slate-900 px-3 py-1 text-xs text-slate-400">
|
||||
3s refresh
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{error && (
|
||||
<div className="mb-3 rounded-xl border border-red-500/20 bg-red-500/10 p-3 text-xs text-red-300">
|
||||
{error}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="h-72">
|
||||
<ResponsiveContainer
|
||||
width="100%"
|
||||
height="100%"
|
||||
>
|
||||
<AreaChart data={points}>
|
||||
<CartesianGrid
|
||||
strokeDasharray="3 3"
|
||||
stroke="rgba(148,163,184,.12)"
|
||||
/>
|
||||
|
||||
<XAxis
|
||||
dataKey="time"
|
||||
stroke="#94a3b8"
|
||||
fontSize={12}
|
||||
minTickGap={24}
|
||||
/>
|
||||
|
||||
<YAxis
|
||||
stroke="#94a3b8"
|
||||
fontSize={12}
|
||||
tickFormatter={(value) =>
|
||||
`${value} Mbps`
|
||||
}
|
||||
/>
|
||||
|
||||
<Tooltip
|
||||
contentStyle={{
|
||||
background: '#0b1522',
|
||||
border:
|
||||
'1px solid rgba(255,255,255,.1)',
|
||||
borderRadius: 12,
|
||||
}}
|
||||
formatter={(value) => [
|
||||
`${Number(value).toFixed(
|
||||
3,
|
||||
)} Mbps`,
|
||||
'',
|
||||
]}
|
||||
/>
|
||||
|
||||
<Area
|
||||
type="monotone"
|
||||
dataKey="downloadMbps"
|
||||
name="Download"
|
||||
stroke="#19d16f"
|
||||
fill="#19d16f"
|
||||
fillOpacity={0.2}
|
||||
/>
|
||||
|
||||
<Area
|
||||
type="monotone"
|
||||
dataKey="uploadMbps"
|
||||
name="Upload"
|
||||
stroke="#3b82f6"
|
||||
fill="#3b82f6"
|
||||
fillOpacity={0.16}
|
||||
/>
|
||||
</AreaChart>
|
||||
</ResponsiveContainer>
|
||||
</div>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user