gitscope
← marzneshin / v0.7.0 / dashboard/src/modules/users/components/widgets/user-nodes-usage-widget/index.tsx
https://dev.kronoz.win/r/marzneshin.git
dashboard/src/modules/users/components/widgets/user-nodes-usage-widget/index.tsx · TypeScript
Blame Raw
  1import { FC } from 'react';
  2import { useTranslation } from 'react-i18next';
  3import {
  4    SectionWidget,
  5    ChartLegendContent,
  6    ChartContainer,
  7    Awaiting,
  8    ChartLegend,
  9    ChartTooltip,
 10    ChartTooltipContent,
 11} from "@marzneshin/common/components";
 12import { Area, AreaChart, CartesianGrid, YAxis, XAxis } from "recharts"
 13import { useState } from "react";
 14import { UserNodesUsageWidgetProps } from "./types";
 15import { dateXAxisTicks, useFromNowInterval, SelectDateView, ChartDateInterval } from "@marzneshin/libs/stats-charts";
 16import { useChartConfig, useTransformData } from "./hooks";
 17import { format as formatByte } from '@chbphone55/pretty-bytes';
 18import { useUserNodeUsagesQuery } from "@marzneshin/modules/users";
 19import { UsageGraphSkeleton } from "./skeleton"
 20
 21export const UserNodesUsageWidget: FC<UserNodesUsageWidgetProps> = ({
 22    user,
 23}) => {
 24    const { t } = useTranslation();
 25    const [timeRange, setTimeRange] = useState("1d")
 26    const { start, end } = useFromNowInterval(timeRange as ChartDateInterval);
 27    const { data, isPending } = useUserNodeUsagesQuery({ username: user.username, start, end })
 28    const chartData = useTransformData(data);
 29    const config = useChartConfig(data);
 30    const [totalAmount, totalMetric] = formatByte(data.total);
 31
 32    return (
 33        <Awaiting
 34            Component={
 35                <SectionWidget
 36                    title={<div className="hstack justify-between w-full">{t("page.users.settings.nodes-usage.title")}</div>}
 37                    description={t("page.users.settings.nodes-usage.desc")}
 38                    options={
 39                        <div className="vstack justify-end w-full">
 40                            <span className="text-lg leading-none sm:text-2xl w-full">
 41                                {totalAmount} {totalMetric}
 42                            </span>
 43                            <span className="text-sm flex justify-end  text-muted-foreground w-full">
 44                                Total
 45                            </span>
 46                        </div>
 47                    }
 48                    footer={
 49                        <SelectDateView timeRange={timeRange} setTimeRange={setTimeRange} />
 50                    }
 51                >
 52                    <ChartContainer
 53                        className="aspect-auto h-[320px] w-full"
 54                        config={config}>
 55                        <AreaChart
 56                            accessibilityLayer
 57                            data={chartData}
 58                            margin={{
 59                                left: 13,
 60                                top: 13,
 61                                right: 12,
 62                            }}
 63                        >
 64                            <CartesianGrid vertical={false} />
 65                            <YAxis
 66                                tickLine={false}
 67                                axisLine={false}
 68                                tickMargin={8}
 69                                tickFormatter={(value) => {
 70                                    const [amount, metric] = formatByte(value)
 71                                    return `${amount} ${metric}`
 72                                }}
 73                            />
 74                            <XAxis
 75                                dataKey="datetime"
 76                                tickLine={false}
 77                                axisLine={false}
 78                                tickMargin={8}
 79                                tickFormatter={(value) => dateXAxisTicks(value, timeRange as ChartDateInterval)}
 80                            />
 81                            <ChartTooltip
 82                                cursor={false}
 83                                content={
 84                                    <ChartTooltipContent
 85                                        indicator='line'
 86                                        labelFormatter={(value) => {
 87                                            return new Date(value).toLocaleDateString("en-US", {
 88                                                month: "short",
 89                                                day: "numeric",
 90                                                year: "numeric",
 91                                                hour: "numeric"
 92                                            })
 93                                        }}
 94                                        valueFormatter={(value) => {
 95                                            const [amount, metric] = formatByte(value as number)
 96                                            return `${amount} ${metric}`
 97                                        }}
 98                                    />
 99                                }
100                            />
101                            <defs>
102                                {data.node_usages.map(node =>
103                                    <linearGradient key={node.node_name} id={node.node_name} x1="0" y1="0" x2="0" y2="1">
104                                        <stop
105                                            offset="5%"
106                                            stopColor={config[node.node_name].color}
107                                            stopOpacity={0.1}
108                                        />
109                                        <stop
110                                            offset="95%"
111                                            stopColor={config[node.node_name].color}
112                                            stopOpacity={0.8}
113                                        />
114                                    </linearGradient>
115                                )}
116                            </defs>
117                            {data.node_usages.map(node =>
118                                <Area
119                                    dataKey={node.node_name}
120                                    key={node.node_name}
121                                    type="natural"
122                                    fill={config[node.node_name].color}
123                                    fillOpacity={0.4}
124                                    stackId={node.node_id}
125                                    stroke={config[node.node_name].color}
126                                />
127                            )}
128                            <ChartLegend content={<ChartLegendContent />} />
129                        </AreaChart>
130                    </ChartContainer>
131                </SectionWidget>
132            }
133            Skeleton={<UsageGraphSkeleton />}
134            isFetching={isPending}
135        />
136    );
137};