96 lines
2.6 KiB
TypeScript
96 lines
2.6 KiB
TypeScript
"use client";
|
|
|
|
import { useEffect, useState } from "react";
|
|
import { useTranslations } from "next-intl";
|
|
import {
|
|
AreaChart,
|
|
Area,
|
|
XAxis,
|
|
YAxis,
|
|
Tooltip,
|
|
ResponsiveContainer,
|
|
} from "recharts";
|
|
import { Card, CardHeader, CardTitle, CardContent } from "@/components/ui/card";
|
|
|
|
interface HourlyData {
|
|
hour: string;
|
|
count: number;
|
|
}
|
|
|
|
export function ActivityTimeline() {
|
|
const t = useTranslations("activityTimeline");
|
|
const [data, setData] = useState<HourlyData[]>([]);
|
|
|
|
useEffect(() => {
|
|
const fetchData = async () => {
|
|
try {
|
|
const res = await fetch("/api/v1/stats");
|
|
if (res.ok) {
|
|
const json = await res.json();
|
|
setData(json.hourlyActivity ?? []);
|
|
}
|
|
} catch {
|
|
// retry on next interval
|
|
}
|
|
};
|
|
|
|
fetchData();
|
|
const interval = setInterval(fetchData, 30000);
|
|
return () => clearInterval(interval);
|
|
}, []);
|
|
|
|
return (
|
|
<Card className="border-white/5">
|
|
<CardHeader className="pb-2">
|
|
<CardTitle>{t("title")}</CardTitle>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className="h-[160px] w-full">
|
|
<ResponsiveContainer width="100%" height="100%">
|
|
<AreaChart data={data}>
|
|
<defs>
|
|
<linearGradient id="activityGradient" x1="0" y1="0" x2="0" y2="1">
|
|
<stop offset="0%" stopColor="#00f0ff" stopOpacity={0.3} />
|
|
<stop offset="100%" stopColor="#00f0ff" stopOpacity={0} />
|
|
</linearGradient>
|
|
</defs>
|
|
<XAxis
|
|
dataKey="hour"
|
|
tick={{ fill: "#64748b", fontSize: 10 }}
|
|
axisLine={false}
|
|
tickLine={false}
|
|
interval="preserveStartEnd"
|
|
/>
|
|
<YAxis hide />
|
|
<Tooltip
|
|
contentStyle={{
|
|
backgroundColor: "#1a1f2e",
|
|
border: "1px solid rgba(255,255,255,0.1)",
|
|
borderRadius: "8px",
|
|
color: "#e2e8f0",
|
|
fontSize: 12,
|
|
}}
|
|
labelStyle={{ color: "#94a3b8" }}
|
|
/>
|
|
<Area
|
|
type="monotone"
|
|
dataKey="count"
|
|
stroke="#00f0ff"
|
|
strokeWidth={2}
|
|
fill="url(#activityGradient)"
|
|
dot={false}
|
|
activeDot={{
|
|
r: 4,
|
|
fill: "#00f0ff",
|
|
stroke: "#0a0e1a",
|
|
strokeWidth: 2,
|
|
}}
|
|
/>
|
|
</AreaChart>
|
|
</ResponsiveContainer>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
);
|
|
}
|