mirror of
https://github.com/DeveloLongScript/MHSF.git
synced 2026-05-07 15:54:58 -05:00
feat: progress (2/15)
This commit is contained in:
parent
0f254e09f6
commit
c16341a9d6
@ -246,7 +246,7 @@ export function BrandingGenericIcon(props: SVGProps<SVGSVGElement>) {
|
||||
</defs>
|
||||
</svg>
|
||||
);
|
||||
} else {
|
||||
}
|
||||
return (
|
||||
<svg
|
||||
width="265"
|
||||
@ -302,7 +302,6 @@ export function BrandingGenericIcon(props: SVGProps<SVGSVGElement>) {
|
||||
</defs>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export function BadgeOfAffiliation(props: SVGProps<SVGSVGElement>) {
|
||||
@ -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 />
|
||||
|
||||
54
apps/www/src/pages/api/v0/history/meta-daily-avg.ts
Normal file
54
apps/www/src/pages/api/v0/history/meta-daily-avg.ts
Normal file
@ -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 });
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user