126 lines
4.9 KiB
TypeScript
126 lines
4.9 KiB
TypeScript
import React from 'react';
|
|
import {
|
|
AreaChart,
|
|
Area,
|
|
XAxis,
|
|
YAxis,
|
|
CartesianGrid,
|
|
Tooltip,
|
|
ResponsiveContainer,
|
|
} from 'recharts';
|
|
import type { ActivityData } from '../hooks/use-activity-stats';
|
|
|
|
interface ActivityChartProps {
|
|
data: ActivityData[];
|
|
loading?: boolean;
|
|
}
|
|
|
|
const CustomTooltip = ({ active, payload }: any) => {
|
|
if (active && payload && payload.length) {
|
|
const data = payload[0].payload;
|
|
|
|
return (
|
|
<div className="glass p-3 rounded-lg border border-white/10 text-sm shadow-xl animate-in fade-in zoom-in duration-200">
|
|
<p className="font-semibold text-white/90 border-b border-white/10 pb-1 mb-2">
|
|
{data.displayTime}
|
|
</p>
|
|
<div className="space-y-1">
|
|
<p className="flex items-center justify-between gap-4">
|
|
<span className="text-primary font-medium">Commands</span>
|
|
<span className="font-mono">{payload[0].value}</span>
|
|
</p>
|
|
<p className="flex items-center justify-between gap-4">
|
|
<span className="text-[var(--chart-2)] font-medium">Transactions</span>
|
|
<span className="font-mono">{payload[1].value}</span>
|
|
</p>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
return null;
|
|
};
|
|
|
|
export const ActivityChart: React.FC<ActivityChartProps> = ({ data, loading }) => {
|
|
if (loading) {
|
|
return (
|
|
<div className="w-full h-[300px] flex items-center justify-center">
|
|
<div className="flex flex-col items-center gap-3">
|
|
<div className="w-8 h-8 border-4 border-primary/20 border-t-primary rounded-full animate-spin" />
|
|
<p className="text-muted-foreground animate-pulse text-sm">Aggregating stats...</p>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
// Format hour for XAxis (e.g., "HH:00")
|
|
const chartData = data.map(item => ({
|
|
...item,
|
|
displayTime: new Date(item.hour).getHours() + ':00'
|
|
}));
|
|
|
|
return (
|
|
<div className="w-full h-[300px] mt-4">
|
|
<ResponsiveContainer width="100%" height="100%">
|
|
<AreaChart
|
|
data={chartData}
|
|
margin={{ top: 10, right: 10, left: -20, bottom: 0 }}
|
|
>
|
|
<defs>
|
|
<linearGradient id="colorCommands" x1="0" y1="0" x2="0" y2="1">
|
|
<stop offset="5%" stopColor="var(--color-primary)" stopOpacity={0.3} />
|
|
<stop offset="95%" stopColor="var(--color-primary)" stopOpacity={0} />
|
|
</linearGradient>
|
|
<linearGradient id="colorTransactions" x1="0" y1="0" x2="0" y2="1">
|
|
<stop offset="5%" stopColor="var(--chart-2)" stopOpacity={0.3} />
|
|
<stop offset="95%" stopColor="var(--chart-2)" stopOpacity={0} />
|
|
</linearGradient>
|
|
</defs>
|
|
<CartesianGrid
|
|
strokeDasharray="3 3"
|
|
vertical={false}
|
|
stroke="rgba(255,255,255,0.05)"
|
|
/>
|
|
<XAxis
|
|
dataKey="hour"
|
|
fontSize={10}
|
|
tickFormatter={(str) => {
|
|
const date = new Date(str);
|
|
return date.getHours() % 4 === 0 ? `${date.getHours()}:00` : '';
|
|
}}
|
|
axisLine={false}
|
|
tickLine={false}
|
|
stroke="var(--muted-foreground)"
|
|
minTickGap={30}
|
|
/>
|
|
<YAxis
|
|
fontSize={10}
|
|
axisLine={false}
|
|
tickLine={false}
|
|
stroke="var(--muted-foreground)"
|
|
width={40}
|
|
/>
|
|
<Tooltip content={<CustomTooltip />} />
|
|
<Area
|
|
type="monotone"
|
|
dataKey="commands"
|
|
stroke="var(--color-primary)"
|
|
strokeWidth={2}
|
|
fillOpacity={1}
|
|
fill="url(#colorCommands)"
|
|
animationDuration={1500}
|
|
/>
|
|
<Area
|
|
type="monotone"
|
|
dataKey="transactions"
|
|
stroke="var(--chart-2)"
|
|
strokeWidth={2}
|
|
fillOpacity={1}
|
|
fill="url(#colorTransactions)"
|
|
animationDuration={1500}
|
|
/>
|
|
</AreaChart>
|
|
</ResponsiveContainer>
|
|
</div>
|
|
);
|
|
};
|