2025-03-15 16:58:24 -05:00
|
|
|
"use client";
|
2025-03-08 15:21:26 -06:00
|
|
|
import { Separator } from "@/components/ui/separator";
|
|
|
|
|
import type { ServerResponse } from "@/lib/types/mh-server";
|
2025-03-15 16:58:24 -05:00
|
|
|
import { Area, AreaChart, CartesianGrid, XAxis } from "recharts";
|
2025-03-08 15:21:26 -06:00
|
|
|
|
2025-03-15 16:58:24 -05:00
|
|
|
import {
|
2025-04-04 21:10:56 -05:00
|
|
|
type ChartConfig,
|
|
|
|
|
ChartContainer,
|
|
|
|
|
ChartTooltip,
|
|
|
|
|
ChartTooltipContent,
|
2025-03-15 16:58:24 -05:00
|
|
|
} from "@/components/ui/chart";
|
|
|
|
|
import type { useMHSFServer } from "@/lib/hooks/use-mhsf-server";
|
|
|
|
|
import { cn } from "@/lib/utils";
|
|
|
|
|
import { useQueryState } from "nuqs";
|
|
|
|
|
import { Badge } from "@/components/ui/badge";
|
|
|
|
|
import { convert } from "../util";
|
|
|
|
|
import { Material } from "@/components/ui/material";
|
2025-04-04 21:10:56 -05:00
|
|
|
import { Spinner } from "@/components/ui/spinner";
|
|
|
|
|
import { Placeholder } from "@/components/ui/placeholder";
|
|
|
|
|
import { CircleSlash } from "lucide-react";
|
2025-03-15 16:58:24 -05:00
|
|
|
|
|
|
|
|
export function StatisticsMainRow({
|
2025-04-04 21:10:56 -05:00
|
|
|
server,
|
|
|
|
|
mhsfData,
|
2025-03-15 16:58:24 -05:00
|
|
|
}: {
|
2025-04-04 21:10:56 -05:00
|
|
|
server: ServerResponse;
|
|
|
|
|
mhsfData: ReturnType<typeof useMHSFServer>;
|
2025-03-15 16:58:24 -05:00
|
|
|
}) {
|
2025-04-04 21:10:56 -05:00
|
|
|
const [statisticType, setStatisticType] = useQueryState("st", {
|
|
|
|
|
defaultValue: "playerCount",
|
|
|
|
|
});
|
2025-03-15 16:58:24 -05:00
|
|
|
|
2025-04-04 21:10:56 -05:00
|
|
|
return (
|
|
|
|
|
<Material
|
|
|
|
|
className="relative col-span-2 h-[250px] max-lg:mt-3"
|
|
|
|
|
padding="none"
|
|
|
|
|
>
|
|
|
|
|
<div className="p-4">
|
|
|
|
|
<span className="flex gap-4 mb-2">
|
|
|
|
|
<strong className="text-lg">Statistics</strong>
|
|
|
|
|
<button
|
|
|
|
|
type="button"
|
|
|
|
|
className={cn(
|
|
|
|
|
"text-sm cursor-pointer hover:bg-slate-100 dark:hover:bg-zinc-700/30 transition-all duration-75 disabled:opacity-50 disabled:pointer-events-none",
|
|
|
|
|
"rounded-xl px-2 flex items-center gap-2",
|
|
|
|
|
statisticType === "playerCount" &&
|
|
|
|
|
"bg-slate-100 dark:bg-zinc-700/30 font-medium",
|
|
|
|
|
)}
|
|
|
|
|
onClick={() => setStatisticType("playerCount")}
|
|
|
|
|
>
|
|
|
|
|
Player Count
|
|
|
|
|
<Badge className="px-1">
|
|
|
|
|
<code>{convert(server.joins)}</code>
|
|
|
|
|
</Badge>
|
|
|
|
|
</button>
|
|
|
|
|
<button
|
|
|
|
|
type="button"
|
|
|
|
|
className={cn(
|
|
|
|
|
"text-sm cursor-pointer hover:bg-slate-100 dark:hover:bg-zinc-700/30 transition-all duration-75 disabled:opacity-50 disabled:pointer-events-none",
|
|
|
|
|
"rounded-xl px-2 flex items-center gap-2",
|
|
|
|
|
statisticType === "favorites" &&
|
|
|
|
|
"bg-slate-100 dark:bg-zinc-700/30 font-medium",
|
|
|
|
|
)}
|
|
|
|
|
onClick={() => setStatisticType("favorites")}
|
|
|
|
|
>
|
|
|
|
|
Favorites
|
|
|
|
|
<Badge className="px-1">
|
|
|
|
|
<code>
|
|
|
|
|
{convert(
|
|
|
|
|
mhsfData.server?.favoriteData.favoriteNumber as number,
|
|
|
|
|
)}
|
|
|
|
|
</code>
|
|
|
|
|
</Badge>
|
|
|
|
|
</button>
|
|
|
|
|
</span>
|
|
|
|
|
<Separator />
|
|
|
|
|
</div>
|
|
|
|
|
<div className="mt-2">
|
|
|
|
|
{!mhsfData.loading ? (
|
|
|
|
|
<>
|
|
|
|
|
{(statisticType === "playerCount"
|
|
|
|
|
? mhsfData.server?.playerData.historically
|
|
|
|
|
: mhsfData.server?.favoriteData.favoriteHistoricalData
|
|
|
|
|
)?.length !== 0 ? (
|
|
|
|
|
<StatisticsChart
|
|
|
|
|
data={
|
|
|
|
|
statisticType === "playerCount"
|
|
|
|
|
? mhsfData.server?.playerData.historically
|
|
|
|
|
: mhsfData.server?.favoriteData.favoriteHistoricalData
|
|
|
|
|
}
|
|
|
|
|
mainDataPoint={statisticType}
|
|
|
|
|
/>
|
|
|
|
|
) : (
|
|
|
|
|
<span className="w-full h-full items-center justify-center flex">
|
|
|
|
|
<Placeholder
|
|
|
|
|
icon={<CircleSlash />}
|
|
|
|
|
title="There is no data to be collected"
|
|
|
|
|
description="This server probably never had any data collected in your choosen timespan."
|
|
|
|
|
/>
|
|
|
|
|
</span>
|
|
|
|
|
)}
|
|
|
|
|
</>
|
|
|
|
|
) : (
|
|
|
|
|
<Spinner />
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
</Material>
|
|
|
|
|
);
|
2025-03-15 16:58:24 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const chartConfig = {
|
2025-04-04 21:10:56 -05:00
|
|
|
playerCount: {
|
|
|
|
|
label: "Joins",
|
|
|
|
|
color: "hsl(var(--chart-1))",
|
|
|
|
|
},
|
|
|
|
|
favorites: {
|
|
|
|
|
label: "Favorites",
|
|
|
|
|
color: "hsl(var(--chart-2))",
|
|
|
|
|
},
|
2025-03-15 16:58:24 -05:00
|
|
|
} satisfies ChartConfig;
|
|
|
|
|
|
|
|
|
|
export function StatisticsChart({
|
2025-04-04 21:10:56 -05:00
|
|
|
data,
|
|
|
|
|
mainDataPoint,
|
2025-03-15 16:58:24 -05:00
|
|
|
}: {
|
2025-04-04 21:10:56 -05:00
|
|
|
data: any;
|
|
|
|
|
mainDataPoint: string;
|
2025-03-15 16:58:24 -05:00
|
|
|
}) {
|
2025-04-04 21:10:56 -05:00
|
|
|
console.log(data);
|
|
|
|
|
return (
|
|
|
|
|
<ChartContainer config={chartConfig} className="max-h-[202px] min-w-full">
|
|
|
|
|
<AreaChart
|
|
|
|
|
accessibilityLayer
|
|
|
|
|
data={data.slice(data.length - 30, data.length)}
|
|
|
|
|
margin={{
|
|
|
|
|
top: 30,
|
|
|
|
|
}}
|
|
|
|
|
className="rounded-b-xl"
|
|
|
|
|
>
|
|
|
|
|
<CartesianGrid vertical={false} horizontal={false} />
|
|
|
|
|
<XAxis dataKey="date" tickLine={false} axisLine={false} tick={false} />
|
|
|
|
|
<ChartTooltip
|
|
|
|
|
content={
|
|
|
|
|
<ChartTooltipContent
|
|
|
|
|
className="w-[150px]"
|
|
|
|
|
nameKey={mainDataPoint}
|
|
|
|
|
indicator="line"
|
|
|
|
|
labelFormatter={(value) => {
|
|
|
|
|
return `${new Date(value).toLocaleDateString("en-US", {
|
|
|
|
|
day: "numeric",
|
|
|
|
|
month: "short",
|
|
|
|
|
})} ${new Date(value).toLocaleTimeString("en-US", {
|
|
|
|
|
timeStyle: "short",
|
|
|
|
|
})}`;
|
|
|
|
|
}}
|
|
|
|
|
/>
|
|
|
|
|
}
|
|
|
|
|
/>
|
|
|
|
|
<defs>
|
|
|
|
|
<linearGradient
|
|
|
|
|
id={`fill${mainDataPoint}`}
|
|
|
|
|
x1="0"
|
|
|
|
|
y1="0"
|
|
|
|
|
x2="0"
|
|
|
|
|
y2="1"
|
|
|
|
|
>
|
|
|
|
|
<stop
|
|
|
|
|
offset="25%"
|
|
|
|
|
stopColor={`var(--color-${mainDataPoint})`}
|
|
|
|
|
stopOpacity={0.8}
|
|
|
|
|
/>
|
|
|
|
|
<stop
|
|
|
|
|
offset="95%"
|
|
|
|
|
stopColor={`var(--color-${mainDataPoint})`}
|
|
|
|
|
stopOpacity={0.1}
|
|
|
|
|
/>
|
|
|
|
|
</linearGradient>
|
|
|
|
|
</defs>
|
|
|
|
|
<Area
|
|
|
|
|
dataKey={mainDataPoint}
|
|
|
|
|
type="natural"
|
|
|
|
|
fill={`url(#fill${mainDataPoint})`}
|
|
|
|
|
fillOpacity={0.4}
|
|
|
|
|
stroke={`var(--color-${mainDataPoint})`}
|
|
|
|
|
stackId="a"
|
|
|
|
|
/>
|
|
|
|
|
</AreaChart>
|
|
|
|
|
</ChartContainer>
|
|
|
|
|
);
|
2025-03-08 15:21:26 -06:00
|
|
|
}
|