mirror of
https://github.com/DeveloLongScript/MHSF.git
synced 2026-05-07 16:54:59 -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>
|
</defs>
|
||||||
</svg>
|
</svg>
|
||||||
);
|
);
|
||||||
} else {
|
}
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
width="265"
|
width="265"
|
||||||
@ -303,7 +303,6 @@ export function BrandingGenericIcon(props: SVGProps<SVGSVGElement>) {
|
|||||||
</svg>
|
</svg>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
export function BadgeOfAffiliation(props: SVGProps<SVGSVGElement>) {
|
export function BadgeOfAffiliation(props: SVGProps<SVGSVGElement>) {
|
||||||
return (
|
return (
|
||||||
@ -1,5 +1,5 @@
|
|||||||
"use client";
|
"use client";
|
||||||
import { BrandingGenericIcon } from "@/components/icon";
|
import { BrandingGenericIcon } from "@/components/feat/icons/branding-icons";
|
||||||
import { version } from "@/config/version";
|
import { version } from "@/config/version";
|
||||||
import { useScroll } from "@/lib/hooks/use-scroll";
|
import { useScroll } from "@/lib/hooks/use-scroll";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
|
|||||||
@ -25,11 +25,11 @@ export function ServerList() {
|
|||||||
totalPlayers={playerCount}
|
totalPlayers={playerCount}
|
||||||
topServer={servers[0]}
|
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">
|
<h1 className="scroll-m-20 text-2xl font-extrabold tracking-tight lg:text-4xl">
|
||||||
Servers
|
Servers
|
||||||
</h1>
|
</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) => (
|
{servers.map((c) => (
|
||||||
<ServerCard server={c} key={c.name} />
|
<ServerCard server={c} key={c.name} />
|
||||||
))}
|
))}
|
||||||
|
|||||||
@ -1,5 +1,14 @@
|
|||||||
|
import { FormSpinner } from "@/components/ui/form-spinner";
|
||||||
import { Material } from "@/components/ui/material";
|
import { Material } from "@/components/ui/material";
|
||||||
|
import {
|
||||||
|
Tooltip,
|
||||||
|
TooltipContent,
|
||||||
|
TooltipTrigger,
|
||||||
|
} from "@/components/ui/tooltip";
|
||||||
import type { OnlineServer } from "@/lib/types/mh-server";
|
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({
|
export function Statistics({
|
||||||
totalPlayers,
|
totalPlayers,
|
||||||
@ -10,15 +19,101 @@ export function Statistics({
|
|||||||
totalServers: number;
|
totalServers: number;
|
||||||
topServer: OnlineServer;
|
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 (
|
return (
|
||||||
<div className="grid grid-cols-3 gap-2">
|
<div className="grid grid-cols-3 gap-2">
|
||||||
<Material className="gap-2">
|
<Material className="gap-2">
|
||||||
<strong>Total Players</strong> <br />
|
<strong className="flex items-center gap-1">
|
||||||
<span className="text-lg">{totalPlayers}</span>
|
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>
|
||||||
<Material className="gap-2">
|
<Material className="gap-2">
|
||||||
<strong>Total Servers</strong> <br />
|
<strong className="flex items-center gap-1">
|
||||||
<span className="text-lg">{totalServers}</span>
|
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>
|
||||||
<Material className="gap-2">
|
<Material className="gap-2">
|
||||||
<strong>Top Server</strong> <br />
|
<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