feat: progress (2/15)

This commit is contained in:
dvelo 2025-02-15 22:56:37 -06:00
parent 0f254e09f6
commit c16341a9d6
6 changed files with 211 additions and 63 deletions

@ -246,7 +246,7 @@ export function BrandingGenericIcon(props: SVGProps<SVGSVGElement>) {
</defs>
</svg>
);
} else {
}
return (
<svg
width="265"
@ -303,7 +303,6 @@ export function BrandingGenericIcon(props: SVGProps<SVGSVGElement>) {
</svg>
);
}
}
export function BadgeOfAffiliation(props: SVGProps<SVGSVGElement>) {
return (

@ -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";

@ -25,11 +25,11 @@ export function ServerList() {
totalPlayers={playerCount}
topServer={servers[0]}
/>
<Separator className="my-3" />
<Separator className="my-6" />
<h1 className="scroll-m-20 text-2xl font-extrabold tracking-tight lg:text-4xl">
Servers
</h1>
<div className="grid grid-cols-4 gap-2 mt-3">
<div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-2 mt-3">
{servers.map((c) => (
<ServerCard server={c} key={c.name} />
))}

@ -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 (
<div className="grid grid-cols-3 gap-2">
<Material className="gap-2">
<strong>Total Players</strong> <br />
<span className="text-lg">{totalPlayers}</span>
<strong className="flex items-center gap-1">
Total Players
<Tooltip>
<TooltipTrigger>
<InfoIcon size={14} />
</TooltipTrigger>
<TooltipContent>
For players, the first number represents the current amount of
<br />
players, while the second one represents the average of the last
<br />
100 entries of players.
</TooltipContent>
</Tooltip>
</strong>
<span className="text-lg flex items-center gap-1">
{totalPlayers}
<AnimatePresence>
{!averagesLoading && !error && (
<motion.span
initial={{ opacity: 0, x: -10 }}
animate={{ opacity: 1, x: 0 }}
transition={{ duration: 0.15 }}
className="text-muted-foreground/80"
>
{Math.round(averages?.totalPlayerAverage as number)}
</motion.span>
)}
</AnimatePresence>
{averagesLoading && <FormSpinner />}
</span>
</Material>
<Material className="gap-2">
<strong>Total Servers</strong> <br />
<span className="text-lg">{totalServers}</span>
<strong className="flex items-center gap-1">
Total Servers
<Tooltip>
<TooltipTrigger>
<InfoIcon size={14} />
</TooltipTrigger>
<TooltipContent>
For servers, the first number represents the current amount of
<br />
servers, while the second one represents the average of the last
<br />
100 entries of servers.
</TooltipContent>
</Tooltip>
</strong>
<span className="text-lg flex items-center gap-1">
{totalServers}
<AnimatePresence>
{!averagesLoading && !error && (
<motion.span
initial={{ opacity: 0, x: -10 }}
animate={{ opacity: 1, x: 0 }}
transition={{ duration: 0.15 }}
className="text-muted-foreground/80"
>
{Math.round(averages?.totalServerAverage as number)}
</motion.span>
)}
</AnimatePresence>
{averagesLoading && <FormSpinner />}
</span>
</Material>
<Material className="gap-2">
<strong>Top Server</strong> <br />

@ -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 });
}