1import { useState, FC } from 'react';
2import { useTranslation } from 'react-i18next';
3import {
4 SectionWidget,
5 ChartContainer,
6 Awaiting,
7 ChartTooltip,
8 ChartTooltipContent,
9 ChartConfig,
10} from "@marzneshin/common/components";
11import { XAxis, BarChart, YAxis, CartesianGrid, Bar } from "recharts"
12import { format as formatByte } from '@chbphone55/pretty-bytes';
13import { useTotalTrafficQuery } from "./api";
14import { UsageGraphSkeleton } from "./components";
15import {
16 ChartDateInterval,
17 SelectDateView,
18 useTransformDateUsageData,
19 dateXAxisTicks,
20 useFromNowInterval
21} from "@marzneshin/libs/stats-charts";
22
23const chartConfig = {
24 traffic: {
25 label: "Traffic",
26 color: "hsl(var(--chart-1))",
27 },
28} satisfies ChartConfig
29
30export const TotalTrafficsWidget: FC = () => {
31 const { t } = useTranslation();
32 const [timeRange, setTimeRange] = useState("1d")
33 const { start, end } = useFromNowInterval(timeRange as ChartDateInterval);
34 const { data, isPending } = useTotalTrafficQuery({ start, end })
35 const chartData = useTransformDateUsageData(data.usages);
36 const [totalAmount, totalMetric] = formatByte(data.total);
37
38 return (
39 <Awaiting
40 Component={
41 <SectionWidget
42 title={
43 <div className="hstack justify-between w-full">
44 {t("page.home.total-traffics.title")}
45 </div>
46 }
47 description={t("page.home.total-traffics.desc")}
48 options={
49 <div className="vstack justify-end w-full">
50 <span className="text-lg leading-none sm:text-2xl w-full">
51 {totalAmount} {totalMetric}
52 </span>
53 <span className="text-sm flex justify-end text-muted-foreground w-full">
54 Total
55 </span>
56 </div>
57 }
58 footer={
59 <SelectDateView timeRange={timeRange} setTimeRange={setTimeRange} />
60 }
61 >
62 <ChartContainer
63 config={chartConfig}
64 className="aspect-auto h-[320px] w-full"
65 >
66 <BarChart
67 accessibilityLayer
68 data={chartData}
69 margin={{
70 left: 12,
71 top: 13,
72 right: 12,
73 }}
74 >
75 <CartesianGrid vertical={false} />
76 <YAxis
77 tickLine={false}
78 axisLine={false}
79 tickMargin={8}
80 tickFormatter={(value) => {
81 const [amount, metric] = formatByte(value)
82 return `${amount} ${metric}`
83 }}
84 />
85 <XAxis
86 dataKey="datetime"
87 tickLine={false}
88 axisLine={false}
89 tickMargin={8}
90 tickFormatter={(value) => dateXAxisTicks(value, timeRange as ChartDateInterval)}
91 />
92 <ChartTooltip
93 content={
94 <ChartTooltipContent
95 indicator='line'
96 labelFormatter={(value) => {
97 return new Date(value).toLocaleDateString("en-US", {
98 month: "short",
99 day: "numeric",
100 year: "numeric",
101 hour: "numeric"
102 })
103 }}
104 valueFormatter={(value) => {
105 const [amount, metric] = formatByte(value as number)
106 return `${amount} ${metric}`
107 }}
108 />
109 }
110 />
111 <Bar dataKey="traffic" fill={`var(--color-traffic)`} />
112 </BarChart>
113 </ChartContainer>
114 </SectionWidget>
115 }
116 Skeleton={<UsageGraphSkeleton />}
117 isFetching={isPending}
118 />
119 );
120};