diff --git a/apps/www/src/app/(main)/page.tsx b/apps/www/src/app/(main)/servers/page.tsx similarity index 100% rename from apps/www/src/app/(main)/page.tsx rename to apps/www/src/app/(main)/servers/page.tsx diff --git a/apps/www/src/components/icon.tsx b/apps/www/src/components/feat/icons/branding-icons.tsx similarity index 84% rename from apps/www/src/components/icon.tsx rename to apps/www/src/components/feat/icons/branding-icons.tsx index 597911f..6de76fb 100644 --- a/apps/www/src/components/icon.tsx +++ b/apps/www/src/components/feat/icons/branding-icons.tsx @@ -246,63 +246,62 @@ export function BrandingGenericIcon(props: SVGProps) { ); - } else { - return ( - - - - - - - - - - - - - - - ); } + return ( + + + + + + + + + + + + + + + ); } export function BadgeOfAffiliation(props: SVGProps) { diff --git a/apps/www/src/components/feat/navbar/navbar.tsx b/apps/www/src/components/feat/navbar/navbar.tsx index 19f8c5a..69faa6d 100644 --- a/apps/www/src/components/feat/navbar/navbar.tsx +++ b/apps/www/src/components/feat/navbar/navbar.tsx @@ -1,5 +1,5 @@ "use client"; -import { BrandingGenericIcon } from "@/components/icon"; +import { BrandingGenericIcon } from "@/components/feat/icons/branding-icons"; import { version } from "@/config/version"; import { useScroll } from "@/lib/hooks/use-scroll"; import { cn } from "@/lib/utils"; diff --git a/apps/www/src/components/feat/server-list/server-list.tsx b/apps/www/src/components/feat/server-list/server-list.tsx index 79cdee3..ea6aec7 100644 --- a/apps/www/src/components/feat/server-list/server-list.tsx +++ b/apps/www/src/components/feat/server-list/server-list.tsx @@ -25,11 +25,11 @@ export function ServerList() { totalPlayers={playerCount} topServer={servers[0]} /> - +

Servers

-
+
{servers.map((c) => ( ))} diff --git a/apps/www/src/components/feat/server-list/statistics.tsx b/apps/www/src/components/feat/server-list/statistics.tsx index 481397b..500722e 100644 --- a/apps/www/src/components/feat/server-list/statistics.tsx +++ b/apps/www/src/components/feat/server-list/statistics.tsx @@ -1,5 +1,14 @@ +import { FormSpinner } from "@/components/ui/form-spinner"; import { Material } from "@/components/ui/material"; +import { + Tooltip, + TooltipContent, + TooltipTrigger, +} from "@/components/ui/tooltip"; import type { OnlineServer } from "@/lib/types/mh-server"; +import { InfoIcon } from "lucide-react"; +import { useEffect, useState } from "react"; +import { motion, AnimatePresence } from "framer-motion"; export function Statistics({ totalPlayers, @@ -10,15 +19,101 @@ export function Statistics({ totalServers: number; topServer: OnlineServer; }) { + const [averagesLoading, setAveragesLoading] = useState(true); + const [error, setError] = useState(false); + const [averages, setAverages] = useState< + | { + totalServerAverage: number; + totalPlayerAverage: number; + } + | undefined + >(undefined); + + useEffect(() => { + try { + (async () => { + const fetchRes = await fetch("/api/v0/history/meta-daily-avg"); + const fetchJson: { + totalServerAverage: number; + totalPlayerAverage: number; + } = await fetchRes.json(); + + setAveragesLoading(false); + setAverages(fetchJson); + })(); + } catch (e) { + console.log(e); + setError(true); + } + }, []); + return (
- Total Players
- {totalPlayers} + + Total Players + + + + + + For players, the first number represents the current amount of +
+ players, while the second one represents the average of the last +
+ 100 entries of players. +
+
+
+ + {totalPlayers} + + {!averagesLoading && !error && ( + + {Math.round(averages?.totalPlayerAverage as number)} + + )} + + {averagesLoading && } +
- Total Servers
- {totalServers} + + Total Servers + + + + + + For servers, the first number represents the current amount of +
+ servers, while the second one represents the average of the last +
+ 100 entries of servers. +
+
+
+ + {totalServers} + + {!averagesLoading && !error && ( + + {Math.round(averages?.totalServerAverage as number)} + + )} + + {averagesLoading && } +
Top Server
diff --git a/apps/www/src/pages/api/v0/history/meta-daily-avg.ts b/apps/www/src/pages/api/v0/history/meta-daily-avg.ts new file mode 100644 index 0000000..587c3da --- /dev/null +++ b/apps/www/src/pages/api/v0/history/meta-daily-avg.ts @@ -0,0 +1,54 @@ +/* + * 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) 2025 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, type WithId } from "mongodb"; +import type { 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("mh"); + const array = (await db.find().toArray()) as WithId<{ + total_servers: number; + total_players: number; + date?: Date; + }>[]; + + const totalServerAverage = + array.slice(0, 100).reduce((acc, curr) => acc + curr.total_servers, 0) / + array.slice(0, 100).length; + const totalPlayerAverage = + array.slice(0, 100).reduce((acc, curr) => acc + curr.total_players, 0) / + array.slice(0, 100).length; + + res.send({ totalServerAverage, totalPlayerAverage }); +}