mirror of
https://github.com/DeveloLongScript/MHSF.git
synced 2026-05-07 21:24:58 -05:00
fix: issue
This commit is contained in:
parent
fcb4221fca
commit
270f6efaa6
@ -42,6 +42,7 @@ We use [Atlas](https://www.mongodb.com/atlas) to host our MongoDB database, but
|
||||
```dotenv
|
||||
MONGO_DB="mongodb+srv://..."
|
||||
```
|
||||
You can also set `CUSTOM_MONGO_DB` to a database name that will apply to all operations except statistical operations.
|
||||
|
||||
## Smaller things (for production-ready servers)
|
||||
|
||||
|
||||
@ -36,6 +36,8 @@ import TabServer from "@/components/misc/TabServer";
|
||||
import type { Metadata, ResolvingMetadata } from "next";
|
||||
import StickyTopbar from "@/components/misc/StickyTopbar";
|
||||
import { RelativeChart } from "@/components/charts/RelativeChart";
|
||||
import { MonthlyChart } from "@/components/charts/MonthlyChart";
|
||||
import { DailyChart } from "@/components/charts/DailyChart";
|
||||
|
||||
type Props = {
|
||||
params: { server: string };
|
||||
@ -113,6 +115,11 @@ export default function ServerPage({ params }: { params: { server: string } }) {
|
||||
<NewChart server={params.server} />
|
||||
<br />
|
||||
<RelativeChart server={params.server} />
|
||||
<br />
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<MonthlyChart server={params.server} />
|
||||
<DailyChart server={params.server} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ColorProvider>
|
||||
|
||||
@ -117,6 +117,7 @@ import { LoadingSpinner } from "./ui/loading-spinner";
|
||||
import StickyTopbar from "./misc/StickyTopbar";
|
||||
import { HoverCard } from "@radix-ui/react-hover-card";
|
||||
import { HoverCardTrigger } from "./ui/hover-card";
|
||||
import { ExampleChart } from "./charts/ExampleChart";
|
||||
|
||||
// ag-grid
|
||||
ModuleRegistry.registerModules([AllCommunityModule]);
|
||||
@ -451,13 +452,22 @@ export default function ServerList() {
|
||||
and descriptions, making your server stand out with information
|
||||
that can be shown to players.
|
||||
</p>
|
||||
<BentoGrid className="max-h-[100px]">
|
||||
<BentoGrid className="max-h-[100px] mb-[300px]">
|
||||
{features.map((feature, idx) => (
|
||||
<BentoCard key={idx} {...feature} />
|
||||
))}
|
||||
</BentoGrid>
|
||||
|
||||
<Separator />
|
||||
|
||||
<br />
|
||||
<br />
|
||||
<h1 className="animate-fade-in -translate-y-4 text-balance bg-gradient-to-br from-black from-30% to-black/40 bg-clip-text pb-6 text-2xl font-semibold leading-none tracking-tighter text-transparent opacity-0 [--animation-delay:200ms] sm:text-2xl md:text-3xl lg:text-4xl dark:from-white dark:to-white/40">
|
||||
Monitor your success
|
||||
</h1>
|
||||
<p className="animate-fade-in mb-12 -translate-y-4 text-balance text-lg tracking-tight text-gray-400 opacity-0 [--animation-delay:400ms] md:text-xl">
|
||||
Ever wondered how a server was doing? MHSF constantly monitors servers
|
||||
and shows you statistics about how a server is doing at any point of time.
|
||||
</p>
|
||||
<ExampleChart />
|
||||
</div>
|
||||
)}
|
||||
<br />
|
||||
|
||||
@ -121,12 +121,13 @@ export default function ServerView(props: { server: string }) {
|
||||
<>
|
||||
{single.grabOnline() == undefined && !single.grabOffline()?.online && (
|
||||
<div className="grid pl-4 pr-4">
|
||||
<X />
|
||||
<div
|
||||
className=" rounded p-2"
|
||||
style={{ backgroundColor: "rgba(244, 63, 94, .16)" }}
|
||||
>
|
||||
<strong className="flex items-center">
|
||||
This server is offline <X />
|
||||
This server is offline
|
||||
</strong>
|
||||
<p>
|
||||
This means that the server can{"'"}t loading some resources, like
|
||||
|
||||
174
src/components/charts/DailyChart.tsx
Normal file
174
src/components/charts/DailyChart.tsx
Normal file
@ -0,0 +1,174 @@
|
||||
/*
|
||||
* MHSF, Minehut Server List
|
||||
* All external content is rather licensed under the ECA Agreement
|
||||
* located here: https://mhsf.app/docs/legal/external-content-agreement
|
||||
*
|
||||
* All code under MHSF is licensed under the MIT License
|
||||
* by open source contributors
|
||||
*
|
||||
* Copyright (c) 2024 dvelo
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
"use client";
|
||||
|
||||
import { TrendingDown, TrendingUp } from "lucide-react";
|
||||
import {
|
||||
Bar,
|
||||
BarChart,
|
||||
CartesianGrid,
|
||||
LabelList,
|
||||
Rectangle,
|
||||
XAxis,
|
||||
YAxis,
|
||||
} from "recharts";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardFooter,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "@/components/ui/card";
|
||||
import {
|
||||
ChartConfig,
|
||||
ChartContainer,
|
||||
ChartTooltip,
|
||||
ChartTooltipContent,
|
||||
} from "@/components/ui/chart";
|
||||
import { Skeleton } from "../ui/skeleton";
|
||||
import { getDailyData, getMonthlyData } from "@/lib/api";
|
||||
import { useTrend } from "@/lib/hooks/use-daily-trend";
|
||||
|
||||
const chartConfig = {
|
||||
result: {
|
||||
label: "Players",
|
||||
color: "hsl(var(--chart-3))",
|
||||
},
|
||||
} satisfies ChartConfig;
|
||||
|
||||
export function DailyChart({ server }: { server: string }) {
|
||||
const [chartData, setChartData] = useState<{ day: string; result: number }[]>(
|
||||
[],
|
||||
);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const { trend, percentage, success } = useTrend(chartData);
|
||||
|
||||
useEffect(() => {
|
||||
getDailyData(server).then((c) => {
|
||||
setChartData(c);
|
||||
setLoading(false);
|
||||
});
|
||||
}, [server]);
|
||||
|
||||
if (loading) return <Skeleton className="w-full h-[437px]" />;
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Average daily players</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<ChartContainer config={chartConfig} className="">
|
||||
<BarChart
|
||||
accessibilityLayer
|
||||
data={chartData}
|
||||
layout="vertical"
|
||||
margin={{
|
||||
left: -20,
|
||||
}}
|
||||
>
|
||||
<CartesianGrid horizontal={false} />
|
||||
<XAxis type="number" dataKey="result" hide />
|
||||
<YAxis
|
||||
dataKey="day"
|
||||
type="category"
|
||||
tickLine={false}
|
||||
tickMargin={10}
|
||||
axisLine={false}
|
||||
tickFormatter={(value) => value.slice(0, 3)}
|
||||
/>
|
||||
<ChartTooltip
|
||||
cursor={false}
|
||||
content={<ChartTooltipContent hideLabel />}
|
||||
/>
|
||||
<Bar
|
||||
dataKey="result"
|
||||
fill="var(--color-result)"
|
||||
radius={5}
|
||||
strokeWidth={2}
|
||||
activeIndex={chartData.findIndex(
|
||||
(c) =>
|
||||
c.day ===
|
||||
new Date().toLocaleDateString("en-US", { weekday: "long" }),
|
||||
)}
|
||||
activeBar={({ ...props }) => {
|
||||
return (
|
||||
<Rectangle
|
||||
{...props}
|
||||
fill="hsl(var(--chart-4))"
|
||||
stroke={props.payload.fill}
|
||||
strokeDasharray={4}
|
||||
strokeDashoffset={4}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
>
|
||||
<LabelList
|
||||
dataKey="result"
|
||||
position="insideLeft"
|
||||
offset={8}
|
||||
className="fill-[--color-label]"
|
||||
fontSize={12}
|
||||
/>
|
||||
</Bar>
|
||||
</BarChart>
|
||||
</ChartContainer>
|
||||
</CardContent>
|
||||
<CardFooter className="flex-col items-start gap-2 text-sm">
|
||||
{success ? (
|
||||
<div
|
||||
className={
|
||||
"flex gap-2 items-center font-medium leading-none " +
|
||||
(trend === "up" ? "text-green-400" : "text-red-400")
|
||||
}
|
||||
>
|
||||
Trending {trend} by {percentage}% today{" "}
|
||||
{trend === "up" ? (
|
||||
<TrendingUp className="h-4 w-4" />
|
||||
) : (
|
||||
<TrendingDown className="h-4 w-4" />
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<div className={"flex gap-2 items-center font-medium leading-none"}>
|
||||
Trending up by 0% today{" "}
|
||||
<span className="text-muted-foreground">(Insufficient data)</span>
|
||||
</div>
|
||||
)}
|
||||
<div className="leading-none text-muted-foreground">
|
||||
Showing an average of all data for {server}
|
||||
</div>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
191
src/components/charts/ExampleChart.tsx
Normal file
191
src/components/charts/ExampleChart.tsx
Normal file
@ -0,0 +1,191 @@
|
||||
/*
|
||||
* MHSF, Minehut Server List
|
||||
* All external content is rather licensed under the ECA Agreement
|
||||
* located here: https://mhsf.app/docs/legal/external-content-agreement
|
||||
*
|
||||
* All code under MHSF is licensed under the MIT License
|
||||
* by open source contributors
|
||||
*
|
||||
* Copyright (c) 2024 dvelo
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
"use client"
|
||||
|
||||
import * as React from "react"
|
||||
import { Area, AreaChart, CartesianGrid, XAxis } from "recharts"
|
||||
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "@/components/ui/card"
|
||||
import {
|
||||
ChartConfig,
|
||||
ChartContainer,
|
||||
ChartLegend,
|
||||
ChartLegendContent,
|
||||
ChartTooltip,
|
||||
ChartTooltipContent,
|
||||
} from "@/components/ui/chart"
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@/components/ui/select"
|
||||
const chartData = [
|
||||
{ date: "2024-04-01", player_count: 91 },
|
||||
{ date: "2024-04-02", player_count: 106 },
|
||||
{ date: "2024-04-03", player_count: 104 },
|
||||
{ date: "2024-04-04", player_count: 111 },
|
||||
{ date: "2024-04-05", player_count: 113 },
|
||||
{ date: "2024-04-06", player_count: 114 },
|
||||
{ date: "2024-04-07", player_count: 108 },
|
||||
{ date: "2024-04-08", player_count: 89 },
|
||||
{ date: "2024-04-09", player_count: 96 },
|
||||
{ date: "2024-04-10", player_count: 123 },
|
||||
{ date: "2024-04-11", player_count: 120 },
|
||||
{ date: "2024-04-12", player_count: 140 },
|
||||
{ date: "2024-04-13", player_count: 128 },
|
||||
{ date: "2024-04-14", player_count: 130 },
|
||||
{ date: "2024-04-15", player_count: 114 },
|
||||
{ date: "2024-04-16", player_count: 98 },
|
||||
{ date: "2024-04-17", player_count: 102 },
|
||||
{ date: "2024-04-18", player_count: 103 },
|
||||
{ date: "2024-04-19", player_count: 102 },
|
||||
{ date: "2024-04-20", player_count: 112},
|
||||
{ date: "2024-04-21", player_count: 117 },
|
||||
{ date: "2024-04-22", player_count: 119 },
|
||||
{ date: "2024-04-23", player_count: 129 },
|
||||
{ date: "2024-04-24", player_count: 121 },
|
||||
{ date: "2024-04-25", player_count: 126 },
|
||||
{ date: "2024-04-26", player_count: 98},
|
||||
{ date: "2024-04-27", player_count: 102 },
|
||||
{ date: "2024-04-28", player_count: 100 },
|
||||
{ date: "2024-04-29", player_count: 101 },
|
||||
{ date: "2024-04-30", player_count: 104 },
|
||||
{ date: "2024-05-01", player_count: 109 },
|
||||
{ date: "2024-05-02", player_count: 86 },
|
||||
{ date: "2024-05-03", player_count: 93 },
|
||||
{ date: "2024-05-04", player_count: 108 },
|
||||
{ date: "2024-05-05", player_count: 112 },
|
||||
{ date: "2024-05-06", player_count: 111 },
|
||||
{ date: "2024-05-07", player_count: 96 },
|
||||
{ date: "2024-05-08", player_count: 100 },
|
||||
{ date: "2024-05-09", player_count: 124 },
|
||||
{ date: "2024-05-10", player_count: 134 },
|
||||
{ date: "2024-05-11", player_count: 144 },
|
||||
{ date: "2024-05-12", player_count: 156 },
|
||||
{ date: "2024-05-13", player_count: 180 },
|
||||
{ date: "2024-05-14", player_count: 167 },
|
||||
{ date: "2024-05-15", player_count: 154 },
|
||||
{ date: "2024-05-16", player_count: 124 },
|
||||
{ date: "2024-05-17", player_count: 112 },
|
||||
{ date: "2024-05-18", player_count: 114 },
|
||||
{ date: "2024-05-19", player_count: 121 },
|
||||
{ date: "2024-05-20", player_count: 96 },
|
||||
{ date: "2024-05-21", player_count: 102 },
|
||||
{ date: "2024-05-22", player_count: 131 },
|
||||
]
|
||||
|
||||
const chartConfig = {
|
||||
player_count: {
|
||||
label: "Players",
|
||||
color: "hsl(var(--chart-1))",
|
||||
}
|
||||
} satisfies ChartConfig
|
||||
|
||||
export function ExampleChart() {
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader className="flex items-center gap-2 space-y-0 border-b py-5 sm:flex-row">
|
||||
<div className="grid flex-1 gap-1 text-center sm:text-left">
|
||||
<CardTitle className="text-sm">Player count over 3 months</CardTitle>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent className="px-2 pt-4 sm:px-6 sm:pt-6">
|
||||
<ChartContainer
|
||||
config={chartConfig}
|
||||
className="aspect-auto h-[250px] w-full"
|
||||
>
|
||||
<AreaChart data={chartData}>
|
||||
<defs>
|
||||
<linearGradient id="fillPlayers" x1="0" y1="0" x2="0" y2="1">
|
||||
<stop
|
||||
offset="5%"
|
||||
stopColor="var(--color-player_count)"
|
||||
stopOpacity={0.8}
|
||||
/>
|
||||
<stop
|
||||
offset="95%"
|
||||
stopColor="var(--color-player_count)"
|
||||
stopOpacity={0.1}
|
||||
/>
|
||||
</linearGradient>
|
||||
</defs>
|
||||
<CartesianGrid vertical={false} />
|
||||
<XAxis
|
||||
dataKey="date"
|
||||
tickLine={false}
|
||||
axisLine={false}
|
||||
tickMargin={8}
|
||||
minTickGap={32}
|
||||
tickFormatter={(value) => {
|
||||
const date = new Date(value)
|
||||
return date.toLocaleDateString("en-US", {
|
||||
month: "short",
|
||||
day: "numeric",
|
||||
})
|
||||
}}
|
||||
/>
|
||||
<ChartTooltip
|
||||
cursor={false}
|
||||
content={
|
||||
<ChartTooltipContent
|
||||
labelFormatter={(value) => {
|
||||
return new Date(value).toLocaleDateString("en-US", {
|
||||
month: "short",
|
||||
day: "numeric",
|
||||
})
|
||||
}}
|
||||
indicator="dot"
|
||||
/>
|
||||
}
|
||||
/>
|
||||
<Area
|
||||
dataKey="player_count"
|
||||
type="natural"
|
||||
fill="url(#fillPlayers)"
|
||||
stroke="var(--color-player_count)"
|
||||
stackId="a"
|
||||
/>
|
||||
<ChartLegend content={<ChartLegendContent />} />
|
||||
</AreaChart>
|
||||
</ChartContainer>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)
|
||||
}
|
||||
160
src/components/charts/MonthlyChart.tsx
Normal file
160
src/components/charts/MonthlyChart.tsx
Normal file
@ -0,0 +1,160 @@
|
||||
/*
|
||||
* MHSF, Minehut Server List
|
||||
* All external content is rather licensed under the ECA Agreement
|
||||
* located here: https://mhsf.app/docs/legal/external-content-agreement
|
||||
*
|
||||
* All code under MHSF is licensed under the MIT License
|
||||
* by open source contributors
|
||||
*
|
||||
* Copyright (c) 2024 dvelo
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
"use client";
|
||||
|
||||
import { TrendingDown, TrendingUp } from "lucide-react";
|
||||
import { Bar, BarChart, CartesianGrid, LabelList, Rectangle, XAxis, YAxis } from "recharts";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardFooter,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "@/components/ui/card";
|
||||
import {
|
||||
ChartConfig,
|
||||
ChartContainer,
|
||||
ChartTooltip,
|
||||
ChartTooltipContent,
|
||||
} from "@/components/ui/chart";
|
||||
import { Skeleton } from "../ui/skeleton";
|
||||
import { useTrend } from "@/lib/hooks/use-trend";
|
||||
import { getMonthlyData } from "@/lib/api";
|
||||
|
||||
const chartConfig = {
|
||||
result: {
|
||||
label: "Players",
|
||||
color: "hsl(var(--chart-3))",
|
||||
},
|
||||
} satisfies ChartConfig;
|
||||
|
||||
export function MonthlyChart({ server }: { server: string }) {
|
||||
const [chartData, setChartData] = useState<
|
||||
{ month: string; result: number }[]
|
||||
>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const { trend, percentage, success } = useTrend(chartData);
|
||||
|
||||
useEffect(() => {
|
||||
getMonthlyData(server).then((c) => {
|
||||
setChartData(c);
|
||||
setLoading(false);
|
||||
});
|
||||
}, [server]);
|
||||
|
||||
if (loading) return <Skeleton className="w-full h-[437px]" />;
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Average monthly players</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<ChartContainer config={chartConfig} className="">
|
||||
<BarChart
|
||||
accessibilityLayer
|
||||
data={chartData}
|
||||
layout="vertical"
|
||||
margin={{
|
||||
left: -20,
|
||||
}}
|
||||
>
|
||||
<CartesianGrid horizontal={false} />
|
||||
<XAxis type="number" dataKey="result" hide />
|
||||
<YAxis
|
||||
dataKey="month"
|
||||
type="category"
|
||||
tickLine={false}
|
||||
tickMargin={10}
|
||||
axisLine={false}
|
||||
tickFormatter={(value) => value.slice(0, 3)}
|
||||
/>
|
||||
<ChartTooltip
|
||||
cursor={false}
|
||||
content={<ChartTooltipContent hideLabel />}
|
||||
/>
|
||||
<Bar dataKey="result" fill="var(--color-result)" radius={5} activeIndex={chartData.findIndex(
|
||||
(c) =>
|
||||
c.month ===
|
||||
new Date().toLocaleDateString("en-US", { month: "long" }),
|
||||
)}
|
||||
activeBar={({ ...props }) => {
|
||||
return (
|
||||
<Rectangle
|
||||
{...props}
|
||||
fill="hsl(var(--chart-4))"
|
||||
stroke={props.payload.fill}
|
||||
strokeDasharray={4}
|
||||
strokeDashoffset={4}
|
||||
/>
|
||||
);
|
||||
}}>
|
||||
<LabelList
|
||||
dataKey="result"
|
||||
position="insideLeft"
|
||||
offset={8}
|
||||
className="fill-[--color-label]"
|
||||
fontSize={12}
|
||||
/>
|
||||
</Bar>
|
||||
</BarChart>
|
||||
</ChartContainer>
|
||||
</CardContent>
|
||||
<CardFooter className="flex-col items-start gap-2 text-sm">
|
||||
{success ? (
|
||||
<div
|
||||
className={
|
||||
"flex gap-2 items-center font-medium leading-none " +
|
||||
(trend === "up" ? "text-green-400" : "text-red-400")
|
||||
}
|
||||
>
|
||||
Trending {trend} by {percentage}% this month{" "}
|
||||
{trend === "up" ? (
|
||||
<TrendingUp className="h-4 w-4" />
|
||||
) : (
|
||||
<TrendingDown className="h-4 w-4" />
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<div className={"flex gap-2 items-center font-medium leading-none"}>
|
||||
Trending up by 0% this month{" "}
|
||||
<span className="text-muted-foreground">(Insufficient data)</span>
|
||||
</div>
|
||||
)}
|
||||
<div className="leading-none text-muted-foreground">
|
||||
Showing an average of all data for {server}
|
||||
</div>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
@ -134,7 +134,7 @@ export function NewChart({ server }: { server: string }) {
|
||||
{chartConfig[activeChart].label} Chart for {server}
|
||||
</CardTitle>
|
||||
<CardDescription className="flex items-center">
|
||||
Showing {filter !== "all" && <>the last</>}{" "}
|
||||
Showing {filter !== "all" && "the last"}{" "}
|
||||
<Select value={filter} onValueChange={setFilter}>
|
||||
{" "}
|
||||
<SelectTrigger className="max-w-[80px] mx-2">
|
||||
@ -154,7 +154,7 @@ export function NewChart({ server }: { server: string }) {
|
||||
</SelectGroup>
|
||||
</SelectContent>
|
||||
</Select>{" "}
|
||||
{filter === "all" && <>of the</>} entries.
|
||||
{filter === "all" && "of the"} entries.
|
||||
</CardDescription>
|
||||
</div>
|
||||
<div className="flex">
|
||||
|
||||
@ -52,7 +52,7 @@ export function MiniJoinsChart({ server }: { server: string }) {
|
||||
|
||||
useEffectOnce(() => {
|
||||
getShortTermData(server, ["player_count", "date"]).then((result) => {
|
||||
setChartData(result.slice(-20));
|
||||
setChartData(result.data.slice(-20));
|
||||
setLoading(false);
|
||||
});
|
||||
});
|
||||
|
||||
@ -89,6 +89,28 @@ export async function getRelativeServerData(
|
||||
return json.data;
|
||||
}
|
||||
|
||||
export async function getMonthlyData(
|
||||
server: string
|
||||
): Promise<Array<{ month: string; result: number }>> {
|
||||
const result = await fetch(
|
||||
connector("/history/" + server + "/get-monthly-data", { version: 0 })
|
||||
);
|
||||
|
||||
const json = await result.json();
|
||||
return json.result;
|
||||
}
|
||||
|
||||
export async function getDailyData(
|
||||
server: string
|
||||
): Promise<Array<{ day: string; result: number }>> {
|
||||
const result = await fetch(
|
||||
connector("/history/" + server + "/get-daily-data", { version: 0 })
|
||||
);
|
||||
|
||||
const json = await result.json();
|
||||
return json.result;
|
||||
}
|
||||
|
||||
export async function getCommunityServerFavorites(
|
||||
server: string
|
||||
): Promise<number> {
|
||||
|
||||
80
src/lib/hooks/use-daily-trend.tsx
Normal file
80
src/lib/hooks/use-daily-trend.tsx
Normal file
@ -0,0 +1,80 @@
|
||||
/*
|
||||
* MHSF, Minehut Server List
|
||||
* All external content is rather licensed under the ECA Agreement
|
||||
* located here: https://mhsf.app/docs/legal/external-content-agreement
|
||||
*
|
||||
* All code under MHSF is licensed under the MIT License
|
||||
* by open source contributors
|
||||
*
|
||||
* Copyright (c) 2024 dvelo
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
export function useTrend(data: { day: string; result: number }[]) {
|
||||
const [success, setSuccess] = useState(true);
|
||||
const [trend, setTrend] = useState<"up" | "down">("up");
|
||||
const [percentage, setPercentage] = useState<number>(0);
|
||||
|
||||
useEffect(() => {
|
||||
const today = new Date();
|
||||
const previousDay = new Date();
|
||||
previousDay.setDate(previousDay.getDate() - 1);
|
||||
|
||||
const previousDayData = data.find(
|
||||
(x) => x.day === previousDay.toLocaleString('en-us', { weekday: 'long' }),
|
||||
);
|
||||
const todayData = data.find(
|
||||
(x) => x.day === today.toLocaleString('en-us', { weekday: 'long' }),
|
||||
);
|
||||
|
||||
if (previousDayData === undefined || todayData === undefined) {
|
||||
setSuccess(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (previousDayData.result === 0) {
|
||||
setSuccess(false);
|
||||
return;
|
||||
}
|
||||
|
||||
setSuccess(true);
|
||||
setTrend(previousDayData.result < todayData.result ? "up" : "down");
|
||||
setPercentage(
|
||||
Math.abs(
|
||||
Number(
|
||||
(
|
||||
((todayData.result - previousDayData.result) /
|
||||
previousDayData.result) *
|
||||
100
|
||||
).toFixed(1),
|
||||
),
|
||||
),
|
||||
);
|
||||
}, [data]);
|
||||
|
||||
return {
|
||||
success,
|
||||
trend,
|
||||
percentage,
|
||||
};
|
||||
}
|
||||
95
src/lib/hooks/use-trend.tsx
Normal file
95
src/lib/hooks/use-trend.tsx
Normal file
@ -0,0 +1,95 @@
|
||||
/*
|
||||
* MHSF, Minehut Server List
|
||||
* All external content is rather licensed under the ECA Agreement
|
||||
* located here: https://mhsf.app/docs/legal/external-content-agreement
|
||||
*
|
||||
* All code under MHSF is licensed under the MIT License
|
||||
* by open source contributors
|
||||
*
|
||||
* Copyright (c) 2024 dvelo
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
const months = [
|
||||
"January",
|
||||
"February",
|
||||
"March",
|
||||
"April",
|
||||
"May",
|
||||
"June",
|
||||
"July",
|
||||
"August",
|
||||
"September",
|
||||
"October",
|
||||
"November",
|
||||
"December",
|
||||
];
|
||||
|
||||
export function useTrend(data: { month: string; result: number }[]) {
|
||||
const [success, setSuccess] = useState(true);
|
||||
const [trend, setTrend] = useState<"up" | "down">("up");
|
||||
const [percentage, setPercentage] = useState<number>(0);
|
||||
|
||||
useEffect(() => {
|
||||
const today = new Date();
|
||||
const previousMonth = new Date();
|
||||
previousMonth.setDate(0);
|
||||
|
||||
const previousMonthData = data.find(
|
||||
(x) => x.month === months[previousMonth.getMonth()],
|
||||
);
|
||||
const todayMonthData = data.find(
|
||||
(x) => x.month === months[today.getMonth()],
|
||||
);
|
||||
|
||||
if (previousMonthData === undefined || todayMonthData === undefined) {
|
||||
setSuccess(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (previousMonthData.result === 0) {
|
||||
setSuccess(false);
|
||||
return;
|
||||
}
|
||||
|
||||
setSuccess(true);
|
||||
setTrend(previousMonthData.result < todayMonthData.result ? "up" : "down");
|
||||
setPercentage(
|
||||
Math.abs(
|
||||
Number(
|
||||
(
|
||||
((todayMonthData.result - previousMonthData.result) /
|
||||
previousMonthData.result) *
|
||||
100
|
||||
).toFixed(1),
|
||||
),
|
||||
),
|
||||
);
|
||||
}, [data]);
|
||||
|
||||
return {
|
||||
success,
|
||||
trend,
|
||||
percentage,
|
||||
};
|
||||
}
|
||||
@ -50,7 +50,7 @@ export default async function handler(
|
||||
const client = new MongoClient(process.env.MONGO_DB as string);
|
||||
await client.connect();
|
||||
|
||||
const db = client.db("mhsf");
|
||||
const db = client.db(process.env.CUSTOM_MONGO_DB ?? "mhsf");
|
||||
const collection = db.collection("auth_codes");
|
||||
|
||||
const entry = await collection.findOne({ code });
|
||||
|
||||
@ -45,7 +45,7 @@ export default async function handler(
|
||||
const client = new MongoClient(process.env.MONGO_DB as string);
|
||||
await client.connect();
|
||||
|
||||
const db = client.db("mhsf");
|
||||
const db = client.db(process.env.CUSTOM_MONGO_DB ?? "mhsf");
|
||||
const collection = db.collection("owned-servers");
|
||||
|
||||
res.send({ owned: (await collection.findOne({ server })) != undefined });
|
||||
|
||||
@ -57,7 +57,7 @@ export default async function handler(
|
||||
const client = new MongoClient(process.env.MONGO_DB as string);
|
||||
await client.connect();
|
||||
|
||||
const db = client.db("mhsf");
|
||||
const db = client.db(process.env.CUSTOM_MONGO_DB ?? "mhsf");
|
||||
const collection = db.collection("owned-servers");
|
||||
|
||||
if ((await collection.findOne({ server: server })) == undefined) {
|
||||
|
||||
@ -56,7 +56,7 @@ export default async function handler(
|
||||
const client = new MongoClient(process.env.MONGO_DB as string);
|
||||
await client.connect();
|
||||
|
||||
const db = client.db("mhsf");
|
||||
const db = client.db(process.env.CUSTOM_MONGO_DB ?? "mhsf");
|
||||
const collection = db.collection("owned-servers");
|
||||
|
||||
res.send({
|
||||
|
||||
@ -44,7 +44,7 @@ export default async function handler(
|
||||
const client = new MongoClient(process.env.MONGO_DB as string);
|
||||
await client.connect();
|
||||
|
||||
const db = client.db("mhsf");
|
||||
const db = client.db(process.env.CUSTOM_MONGO_DB ?? "mhsf");
|
||||
const users = db.collection("claimed-users");
|
||||
const user = await (await clerkClient()).users.getUser(userId);
|
||||
|
||||
|
||||
@ -56,7 +56,7 @@ export default async function handler(
|
||||
const client = new MongoClient(process.env.MONGO_DB as string);
|
||||
await client.connect();
|
||||
|
||||
const db = client.db("mhsf");
|
||||
const db = client.db(process.env.CUSTOM_MONGO_DB ?? "mhsf");
|
||||
const collection = db.collection("owned-servers");
|
||||
const customization = db.collection("customization");
|
||||
|
||||
|
||||
@ -41,7 +41,7 @@ export default async function handler(
|
||||
const client = new MongoClient(process.env.MONGO_DB as string);
|
||||
await client.connect();
|
||||
|
||||
const db = client.db("mhsf");
|
||||
const db = client.db(process.env.CUSTOM_MONGO_DB ?? "mhsf");
|
||||
const collection = db.collection("achievements");
|
||||
|
||||
res.send({ result: await collection.find({ name: server }).toArray() });
|
||||
|
||||
@ -39,7 +39,7 @@ export default async function handler(
|
||||
const client = new MongoClient(process.env.MONGO_DB as string);
|
||||
await client.connect();
|
||||
|
||||
const db = client.db("mhsf");
|
||||
const db = client.db(process.env.CUSTOM_MONGO_DB ?? "mhsf");
|
||||
const collection = db.collection("customization");
|
||||
|
||||
res.send({ results: await collection.findOne({ server }) });
|
||||
|
||||
@ -86,7 +86,7 @@ export default async function handler(
|
||||
const client = new MongoClient(process.env.MONGO_DB as string);
|
||||
await client.connect();
|
||||
|
||||
const db = client.db("mhsf");
|
||||
const db = client.db(process.env.CUSTOM_MONGO_DB ?? "mhsf");
|
||||
const collection = db.collection("owned-servers");
|
||||
const customizationColl = db.collection("customization");
|
||||
if (!((await collection.findOne({ server, author: userId })) == undefined)) {
|
||||
|
||||
@ -44,7 +44,7 @@ export default async function handler(
|
||||
const client = new MongoClient(process.env.MONGO_DB as string);
|
||||
await client.connect();
|
||||
|
||||
const db = client.db("mhsf");
|
||||
const db = client.db(process.env.CUSTOM_MONGO_DB ?? "mhsf");
|
||||
const collection = db.collection("customization");
|
||||
const results: { server: string; customization: any }[] = [];
|
||||
|
||||
|
||||
@ -40,7 +40,7 @@ export default async function handler(
|
||||
|
||||
await client.connect();
|
||||
|
||||
const db = client.db("mhsf");
|
||||
const db = client.db(process.env.CUSTOM_MONGO_DB ?? "mhsf");
|
||||
const collection = db.collection("meta");
|
||||
const find = await collection.find({ server: server }).toArray();
|
||||
|
||||
|
||||
@ -46,7 +46,7 @@ export default async function handler(
|
||||
const client = new MongoClient(process.env.MONGO_DB as string);
|
||||
await client.connect();
|
||||
|
||||
const db = client.db("mhsf");
|
||||
const db = client.db(process.env.CUSTOM_MONGO_DB ?? "mhsf");
|
||||
const collection = db.collection("favorites");
|
||||
const find = await collection.find({ user: userId }).toArray();
|
||||
|
||||
|
||||
@ -45,7 +45,7 @@ export default async function handler(
|
||||
const client = new MongoClient(process.env.MONGO_DB as string);
|
||||
await client.connect();
|
||||
|
||||
const db = client.db("mhsf");
|
||||
const db = client.db(process.env.CUSTOM_MONGO_DB ?? "mhsf");
|
||||
const collection = db.collection("favorites");
|
||||
const find = await collection.find({ user: userId }).toArray();
|
||||
if (find.length == 0) res.send({ result: false });
|
||||
|
||||
@ -44,7 +44,7 @@ export default async function handler(
|
||||
const client = new MongoClient(process.env.MONGO_DB as string);
|
||||
await client.connect();
|
||||
|
||||
const db = client.db("mhsf");
|
||||
const db = client.db(process.env.CUSTOM_MONGO_DB ?? "mhsf");
|
||||
const collection = db.collection("favorites");
|
||||
const find = await collection.find({ user: userId }).toArray();
|
||||
|
||||
|
||||
62
src/pages/api/v0/history/[server]/get-daily-data.ts
Normal file
62
src/pages/api/v0/history/[server]/get-daily-data.ts
Normal file
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* MHSF, Minehut Server List
|
||||
* All external content is rather licensed under the ECA Agreement
|
||||
* located here: https://mhsf.app/docs/legal/external-content-agreement
|
||||
*
|
||||
* All code under MHSF is licensed under the MIT License
|
||||
* by open source contributors
|
||||
*
|
||||
* Copyright (c) 2024 dvelo
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import { MongoClient } from "mongodb";
|
||||
import { NextApiRequest, NextApiResponse } from "next";
|
||||
|
||||
export default async function handler(
|
||||
req: NextApiRequest,
|
||||
res: NextApiResponse
|
||||
) {
|
||||
const client = new MongoClient(process.env.MONGO_DB as string);
|
||||
const db = client.db("mhsf").collection("history");
|
||||
const server = req.query.server as string;
|
||||
|
||||
const daysOfWeek = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday']
|
||||
const result = await Promise.all([1,2,3,4,5,6,7].map(async (c) => {
|
||||
const results = await db.find({
|
||||
$and: [
|
||||
{ server },
|
||||
{ $expr: { $eq: [{ $dayOfWeek: "$date" }, c] } }
|
||||
]
|
||||
}).toArray()
|
||||
|
||||
if (results.length !== 0) {
|
||||
const averageNums = (results as any as {player_count: number}[]).map((x: {player_count: number}) => x.player_count)
|
||||
const average = averageNums.reduce((sum, val) => sum + val, 0) / averageNums.length;
|
||||
|
||||
return { day: daysOfWeek[c - 1], result: Math.floor(average) };
|
||||
}
|
||||
return undefined;
|
||||
}));
|
||||
|
||||
client.close()
|
||||
res.send({result: result.filter((c) => c !== undefined)});
|
||||
}
|
||||
62
src/pages/api/v0/history/[server]/get-monthly-data.ts
Normal file
62
src/pages/api/v0/history/[server]/get-monthly-data.ts
Normal file
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* MHSF, Minehut Server List
|
||||
* All external content is rather licensed under the ECA Agreement
|
||||
* located here: https://mhsf.app/docs/legal/external-content-agreement
|
||||
*
|
||||
* All code under MHSF is licensed under the MIT License
|
||||
* by open source contributors
|
||||
*
|
||||
* Copyright (c) 2024 dvelo
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to
|
||||
* deal in the Software without restriction, including without limitation the
|
||||
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
||||
* sell copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import { MongoClient } from "mongodb";
|
||||
import { NextApiRequest, NextApiResponse } from "next";
|
||||
|
||||
export default async function handler(
|
||||
req: NextApiRequest,
|
||||
res: NextApiResponse
|
||||
) {
|
||||
const client = new MongoClient(process.env.MONGO_DB as string);
|
||||
const db = client.db("mhsf").collection("history");
|
||||
const server = req.query.server as string;
|
||||
|
||||
const months = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']
|
||||
const result = await Promise.all([1,2,3,4,5,6,7,8,9,10,11,12].map(async (c) => {
|
||||
const results = await db.find({
|
||||
$and: [
|
||||
{ server },
|
||||
{ date: { $gte: new Date(new Date().getFullYear(), c - 1, 1), $lt: new Date(new Date().getFullYear(), c, 1) } }
|
||||
]
|
||||
}).toArray()
|
||||
|
||||
if (results.length !== 0) {
|
||||
const averageNums = (results as any as {player_count: number}[]).map((x: {player_count: number}) => x.player_count)
|
||||
const average = averageNums.reduce((sum, val) => sum + val, 0) / averageNums.length;
|
||||
|
||||
return { month: months[c - 1], result: Math.floor(average) };
|
||||
}
|
||||
return undefined;
|
||||
}));
|
||||
|
||||
client.close()
|
||||
res.send({result: result.filter((c) => c !== undefined)});
|
||||
}
|
||||
@ -38,7 +38,7 @@ export default async function handler(
|
||||
const client = new MongoClient(process.env.MONGO_DB as string);
|
||||
await client.connect();
|
||||
|
||||
const db = client.db("mhsf");
|
||||
const db = client.db(process.env.CUSTOM_MONGO_DB ?? "mhsf");
|
||||
const collection = db.collection("meta");
|
||||
|
||||
const all = await collection.find().toArray();
|
||||
|
||||
Loading…
Reference in New Issue
Block a user