mirror of
https://github.com/DeveloLongScript/MHSF.git
synced 2026-05-15 10:28:02 -05:00
Compare commits
4 Commits
cef83f9f6d
...
d08eb0f5e7
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d08eb0f5e7 | ||
|
|
2a29c8935a | ||
|
|
270f6efaa6 | ||
|
|
fcb4221fca |
@ -42,6 +42,7 @@ We use [Atlas](https://www.mongodb.com/atlas) to host our MongoDB database, but
|
|||||||
```dotenv
|
```dotenv
|
||||||
MONGO_DB="mongodb+srv://..."
|
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)
|
## Smaller things (for production-ready servers)
|
||||||
|
|
||||||
|
|||||||
@ -11,7 +11,7 @@ The tech stack of MHSF is relatively modern to ensure MHSF keeps up with standar
|
|||||||
- **shadcn/ui** is the UI framework used to keep the whole website consistent.
|
- **shadcn/ui** is the UI framework used to keep the whole website consistent.
|
||||||
- **Contentlayer** manages all the pages used for documentation
|
- **Contentlayer** manages all the pages used for documentation
|
||||||
- **TailwindCSS** makes MHSF use (mostly) no CSS for better efficency
|
- **TailwindCSS** makes MHSF use (mostly) no CSS for better efficency
|
||||||
- **react-hot-toast** provides the Toast used for MHSF
|
- **Sonner** provides the Toast used for MHSF
|
||||||
|
|
||||||
## Back-end
|
## Back-end
|
||||||
- **Inngest** runs periodic tasks
|
- **Inngest** runs periodic tasks
|
||||||
|
|||||||
@ -14,4 +14,8 @@ If they match, you should see a button named Click to own. Press that button, an
|
|||||||
|
|
||||||
## I can't link my server, because my server doesn't have a author
|
## I can't link my server, because my server doesn't have a author
|
||||||
|
|
||||||
Your server must have an author in-order to be automagically linked, and if it doesn't have an author, that means you will have to manually link your server. To do that, make an issue on GitHub, showing that your server has no author, but needs to be linked. Show proof that you own the server, along with your account username, and your account will own the server you need.
|
Your server must have an author in-order to be automagically linked, and if it doesn't have an author, that means you will have to manually link your server. To do that, make an issue on GitHub, showing that your server has no author, but needs to be linked. Show proof that you own the server, along with your account username, and your account will own the server you need.
|
||||||
|
|
||||||
|
## There is an error while linking my server!
|
||||||
|
This most likely is because the Minehut API is blocking the server-side request to verify your the owner of that server, or your server [has no author](#i-cant-link-my-server-because-my-server-doesnt-have-a-author).
|
||||||
|
Try again in 30 minute intervals, or just make an issue on GitHub to link your server.
|
||||||
@ -33,6 +33,7 @@
|
|||||||
"@unocss/postcss": "^0.61.5",
|
"@unocss/postcss": "^0.61.5",
|
||||||
"@unocss/transformer-directives": "^0.61.5",
|
"@unocss/transformer-directives": "^0.61.5",
|
||||||
"@unocss/webpack": "^0.61.5",
|
"@unocss/webpack": "^0.61.5",
|
||||||
|
"ag-grid-react": "^33.0.3",
|
||||||
"contentlayer": "^0.3.4",
|
"contentlayer": "^0.3.4",
|
||||||
"cron": "^3.1.7",
|
"cron": "^3.1.7",
|
||||||
"discord.js": "^14.15.3",
|
"discord.js": "^14.15.3",
|
||||||
@ -93,7 +94,7 @@
|
|||||||
"@types/react-dom": "^18",
|
"@types/react-dom": "^18",
|
||||||
"@types/react-twemoji": "^0.4.3",
|
"@types/react-twemoji": "^0.4.3",
|
||||||
"@unocss/eslint-config": "^0.61.5",
|
"@unocss/eslint-config": "^0.61.5",
|
||||||
"@unocss/preset-uno": "^0.61.5",
|
"@unocss/preset-uno": "^0.65.3",
|
||||||
"@unocss/transformer-compile-class": "^0.61.5",
|
"@unocss/transformer-compile-class": "^0.61.5",
|
||||||
"@vercel/analytics": "^1.3.1",
|
"@vercel/analytics": "^1.3.1",
|
||||||
"@vercel/speed-insights": "^1.0.12",
|
"@vercel/speed-insights": "^1.0.12",
|
||||||
|
|||||||
51
src/app/(main)/server/[server]/layout.tsx
Normal file
51
src/app/(main)/server/[server]/layout.tsx
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
/*
|
||||||
|
* 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 { LoadingSpinner } from "@/components/ui/loading-spinner";
|
||||||
|
import { useServerExists } from "@/lib/hooks/use-server-exists";
|
||||||
|
import { notFound } from "next/navigation";
|
||||||
|
|
||||||
|
export default function ServerLayout({
|
||||||
|
params,
|
||||||
|
children,
|
||||||
|
}: {
|
||||||
|
params: { server: string };
|
||||||
|
children: React.ReactNode;
|
||||||
|
}) {
|
||||||
|
const { serverExists, loading } = useServerExists(params.server);
|
||||||
|
|
||||||
|
if (loading) return <LoadingSpinner />;
|
||||||
|
|
||||||
|
if (!serverExists) notFound();
|
||||||
|
|
||||||
|
return children;
|
||||||
|
}
|
||||||
@ -30,12 +30,14 @@
|
|||||||
|
|
||||||
import Banner from "@/components/Banner";
|
import Banner from "@/components/Banner";
|
||||||
import ColorProvider from "@/components/ColorProvider";
|
import ColorProvider from "@/components/ColorProvider";
|
||||||
import { NewChart } from "@/components/NewChart";
|
import { NewChart } from "@/components/charts/NewChart";
|
||||||
import ServerView from "@/components/ServerView";
|
import ServerView from "@/components/ServerView";
|
||||||
import TabServer from "@/components/misc/TabServer";
|
import TabServer from "@/components/misc/TabServer";
|
||||||
import { Separator } from "@/components/ui/separator";
|
|
||||||
import type { Metadata, ResolvingMetadata } from "next";
|
import type { Metadata, ResolvingMetadata } from "next";
|
||||||
import StickyTopbar from "@/components/misc/StickyTopbar";
|
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 = {
|
type Props = {
|
||||||
params: { server: string };
|
params: { server: string };
|
||||||
@ -101,17 +103,23 @@ export default function ServerPage({ params }: { params: { server: string } }) {
|
|||||||
<ColorProvider server={params.server}>
|
<ColorProvider server={params.server}>
|
||||||
<div className={"pt-[300px] xl:px-[100px]"}>
|
<div className={"pt-[300px] xl:px-[100px]"}>
|
||||||
<Banner server={params.server} />
|
<Banner server={params.server} />
|
||||||
<div className="pt-8 z-10 relative">
|
<div className="pt-8 z-1 relative">
|
||||||
<ServerView server={params.server} />
|
<ServerView server={params.server} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<StickyTopbar scrollElevation={100} className="pt-4">
|
<StickyTopbar scrollElevation={100} className="pt-4">
|
||||||
<TabServer server={params.server} tabDef="statistics" />
|
<TabServer server={params.server} tabDef="statistics" />
|
||||||
</StickyTopbar>
|
</StickyTopbar>
|
||||||
<Separator />
|
|
||||||
<br />
|
<br />
|
||||||
<div className="p-4 gap-4">
|
<div className="p-4 gap-4 relative z-1">
|
||||||
<NewChart server={params.server} />
|
<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>
|
||||||
</div>
|
</div>
|
||||||
</ColorProvider>
|
</ColorProvider>
|
||||||
|
|||||||
@ -87,6 +87,7 @@ export default function AfterServerView({ server }: { server: string }) {
|
|||||||
getCommunityServerFavorites(server).then((c) => {
|
getCommunityServerFavorites(server).then((c) => {
|
||||||
mhsf.setFavorites(c);
|
mhsf.setFavorites(c);
|
||||||
});
|
});
|
||||||
|
setView(description !== "" || discord !== "" ? "desc" : "extra");
|
||||||
}
|
}
|
||||||
fetch("https://api.minehut.com/server/" + server + "?byName=true").then(
|
fetch("https://api.minehut.com/server/" + server + "?byName=true").then(
|
||||||
(c) => c.json().then((n) => setServerObject(n.server))
|
(c) => c.json().then((n) => setServerObject(n.server))
|
||||||
@ -96,7 +97,7 @@ export default function AfterServerView({ server }: { server: string }) {
|
|||||||
});
|
});
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
});
|
});
|
||||||
}, []);
|
}, [description, discord]);
|
||||||
if (loading) return <></>;
|
if (loading) return <></>;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -216,20 +217,13 @@ export default function AfterServerView({ server }: { server: string }) {
|
|||||||
</Card>
|
</Card>
|
||||||
)}
|
)}
|
||||||
{discord != "" && view == "desc" && (
|
{discord != "" && view == "desc" && (
|
||||||
<Card>
|
<iframe
|
||||||
<CardHeader>
|
src={`https://discord.com/widget?id=${discord}&theme=${resolvedTheme}`}
|
||||||
<CardTitle>Discord Server</CardTitle>
|
height="500"
|
||||||
<CardDescription className="p-4 prose dark:prose-invert">
|
allowTransparency={true}
|
||||||
<iframe
|
className="rounded-lg max-md:w-full"
|
||||||
src={`https://discord.com/widget?id=${discord}&theme=${resolvedTheme}`}
|
sandbox="allow-popups allow-popups-to-escape-sandbox allow-same-origin allow-scripts"
|
||||||
height="500"
|
/>
|
||||||
allowTransparency={true}
|
|
||||||
className="rounded-lg max-sm:w-[100px] max-md:w-[250px]"
|
|
||||||
sandbox="allow-popups allow-popups-to-escape-sandbox allow-same-origin allow-scripts"
|
|
||||||
/>
|
|
||||||
</CardDescription>
|
|
||||||
</CardHeader>
|
|
||||||
</Card>
|
|
||||||
)}{" "}
|
)}{" "}
|
||||||
{view == "achievements" && (
|
{view == "achievements" && (
|
||||||
<div className="col-span-4">
|
<div className="col-span-4">
|
||||||
@ -452,7 +446,7 @@ export default function AfterServerView({ server }: { server: string }) {
|
|||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<th className="border p-2">Server ID</th>
|
<th className="border p-2">Server ID</th>
|
||||||
<td className="border p-2">
|
<td className="border p-2 break-all">
|
||||||
{serverObject?._id == undefined ? (
|
{serverObject?._id == undefined ? (
|
||||||
"? (unknown)"
|
"? (unknown)"
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
@ -41,62 +41,62 @@ import type { SVGProps } from "react";
|
|||||||
* @returns A JSX element representing the colorful branding icon.
|
* @returns A JSX element representing the colorful branding icon.
|
||||||
*/
|
*/
|
||||||
export function BrandingColorfulIcon(props: SVGProps<SVGSVGElement>) {
|
export function BrandingColorfulIcon(props: SVGProps<SVGSVGElement>) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
width="266"
|
width="266"
|
||||||
height="265"
|
height="265"
|
||||||
viewBox="0 0 266 265"
|
viewBox="0 0 266 265"
|
||||||
fill="none"
|
fill="none"
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
<rect
|
<rect
|
||||||
x="0.524048"
|
x="0.524048"
|
||||||
width="264.939"
|
width="264.939"
|
||||||
height="264.939"
|
height="264.939"
|
||||||
rx="66"
|
rx="66"
|
||||||
fill="url(#paint0_linear_1_19)"
|
fill="url(#paint0_linear_1_19)"
|
||||||
/>
|
/>
|
||||||
<path
|
<path
|
||||||
d="M104.513 123.27H94.8717C92.3148 123.27 89.8626 122.254 88.0546 120.446C86.2466 118.638 85.2309 116.186 85.2309 113.629V94.3476C85.2309 91.7907 86.2466 89.3385 88.0546 87.5305C89.8626 85.7225 92.3148 84.7068 94.8717 84.7068H171.998C174.555 84.7068 177.007 85.7225 178.815 87.5305C180.623 89.3385 181.639 91.7907 181.639 94.3476V113.629C181.639 116.186 180.623 118.638 178.815 120.446C177.007 122.254 174.555 123.27 171.998 123.27H162.357M104.513 142.552H94.8717C92.3148 142.552 89.8626 143.567 88.0546 145.376C86.2466 147.184 85.2309 149.636 85.2309 152.193V171.474C85.2309 174.031 86.2466 176.483 88.0546 178.291C89.8626 180.099 92.3148 181.115 94.8717 181.115H171.998C174.555 181.115 177.007 180.099 178.815 178.291C180.623 176.483 181.639 174.031 181.639 171.474V152.193C181.639 149.636 180.623 147.184 178.815 145.376C177.007 143.567 174.555 142.552 171.998 142.552H162.357M104.513 103.988H104.561M104.513 161.833H104.561M138.255 103.988L118.974 132.911H147.896L128.615 161.833"
|
d="M104.513 123.27H94.8717C92.3148 123.27 89.8626 122.254 88.0546 120.446C86.2466 118.638 85.2309 116.186 85.2309 113.629V94.3476C85.2309 91.7907 86.2466 89.3385 88.0546 87.5305C89.8626 85.7225 92.3148 84.7068 94.8717 84.7068H171.998C174.555 84.7068 177.007 85.7225 178.815 87.5305C180.623 89.3385 181.639 91.7907 181.639 94.3476V113.629C181.639 116.186 180.623 118.638 178.815 120.446C177.007 122.254 174.555 123.27 171.998 123.27H162.357M104.513 142.552H94.8717C92.3148 142.552 89.8626 143.567 88.0546 145.376C86.2466 147.184 85.2309 149.636 85.2309 152.193V171.474C85.2309 174.031 86.2466 176.483 88.0546 178.291C89.8626 180.099 92.3148 181.115 94.8717 181.115H171.998C174.555 181.115 177.007 180.099 178.815 178.291C180.623 176.483 181.639 174.031 181.639 171.474V152.193C181.639 149.636 180.623 147.184 178.815 145.376C177.007 143.567 174.555 142.552 171.998 142.552H162.357M104.513 103.988H104.561M104.513 161.833H104.561M138.255 103.988L118.974 132.911H147.896L128.615 161.833"
|
||||||
stroke="white"
|
stroke="white"
|
||||||
stroke-width="10"
|
stroke-width="10"
|
||||||
stroke-linecap="round"
|
stroke-linecap="round"
|
||||||
stroke-linejoin="round"
|
stroke-linejoin="round"
|
||||||
/>
|
/>
|
||||||
<circle
|
<circle
|
||||||
cx="132.993"
|
cx="132.993"
|
||||||
cy="132.469"
|
cy="132.469"
|
||||||
r="91.3779"
|
r="91.3779"
|
||||||
stroke="url(#paint1_linear_1_19)"
|
stroke="url(#paint1_linear_1_19)"
|
||||||
stroke-width="8"
|
stroke-width="8"
|
||||||
/>
|
/>
|
||||||
<defs>
|
<defs>
|
||||||
<linearGradient
|
<linearGradient
|
||||||
id="paint0_linear_1_19"
|
id="paint0_linear_1_19"
|
||||||
x1="107.824"
|
x1="107.824"
|
||||||
y1="54.754"
|
y1="54.754"
|
||||||
x2="230.579"
|
x2="230.579"
|
||||||
y2="225.198"
|
y2="225.198"
|
||||||
gradientUnits="userSpaceOnUse"
|
gradientUnits="userSpaceOnUse"
|
||||||
>
|
>
|
||||||
<stop stop-color="#007BFF" />
|
<stop stop-color="#007BFF" />
|
||||||
<stop offset="1" stop-color="#BF00FF" stop-opacity="0.5" />
|
<stop offset="1" stop-color="#BF00FF" stop-opacity="0.5" />
|
||||||
</linearGradient>
|
</linearGradient>
|
||||||
<linearGradient
|
<linearGradient
|
||||||
id="paint1_linear_1_19"
|
id="paint1_linear_1_19"
|
||||||
x1="132.993"
|
x1="132.993"
|
||||||
y1="37.0914"
|
y1="37.0914"
|
||||||
x2="132.993"
|
x2="132.993"
|
||||||
y2="227.847"
|
y2="227.847"
|
||||||
gradientUnits="userSpaceOnUse"
|
gradientUnits="userSpaceOnUse"
|
||||||
>
|
>
|
||||||
<stop stop-color="#EFEC32" />
|
<stop stop-color="#EFEC32" />
|
||||||
<stop offset="1" stop-color="#98FF60" />
|
<stop offset="1" stop-color="#98FF60" />
|
||||||
</linearGradient>
|
</linearGradient>
|
||||||
</defs>
|
</defs>
|
||||||
</svg>
|
</svg>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Returns the optional Pride icon
|
* Returns the optional Pride icon
|
||||||
@ -107,71 +107,71 @@ export function BrandingColorfulIcon(props: SVGProps<SVGSVGElement>) {
|
|||||||
* @returns A JSX element representing the branding icon.
|
* @returns A JSX element representing the branding icon.
|
||||||
*/
|
*/
|
||||||
export function BrandingPrideIcon(props: SVGProps<SVGSVGElement>) {
|
export function BrandingPrideIcon(props: SVGProps<SVGSVGElement>) {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
width="265"
|
width="265"
|
||||||
height="265"
|
height="265"
|
||||||
viewBox="0 0 265 265"
|
viewBox="0 0 265 265"
|
||||||
fill="none"
|
fill="none"
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
<rect
|
<rect
|
||||||
width="264.939"
|
width="264.939"
|
||||||
height="264.939"
|
height="264.939"
|
||||||
rx="66"
|
rx="66"
|
||||||
fill="url(#paint0_linear_1_30)"
|
fill="url(#paint0_linear_1_30)"
|
||||||
/>
|
/>
|
||||||
<path
|
<path
|
||||||
d="M103.988 123.27H94.3476C91.7907 123.27 89.3385 122.254 87.5305 120.446C85.7225 118.638 84.7068 116.186 84.7068 113.629V94.3476C84.7068 91.7907 85.7225 89.3385 87.5305 87.5305C89.3385 85.7225 91.7907 84.7068 94.3476 84.7068H171.474C174.031 84.7068 176.483 85.7225 178.291 87.5305C180.099 89.3385 181.115 91.7907 181.115 94.3476V113.629C181.115 116.186 180.099 118.638 178.291 120.446C176.483 122.254 174.031 123.27 171.474 123.27H161.833M103.988 142.552H94.3476C91.7907 142.552 89.3385 143.567 87.5305 145.376C85.7225 147.184 84.7068 149.636 84.7068 152.193V171.474C84.7068 174.031 85.7225 176.483 87.5305 178.291C89.3385 180.099 91.7907 181.115 94.3476 181.115H171.474C174.031 181.115 176.483 180.099 178.291 178.291C180.099 176.483 181.115 174.031 181.115 171.474V152.193C181.115 149.636 180.099 147.184 178.291 145.376C176.483 143.567 174.031 142.552 171.474 142.552H161.833M103.988 103.988H104.037M103.988 161.833H104.037M137.731 103.988L118.45 132.911H147.372L128.091 161.833"
|
d="M103.988 123.27H94.3476C91.7907 123.27 89.3385 122.254 87.5305 120.446C85.7225 118.638 84.7068 116.186 84.7068 113.629V94.3476C84.7068 91.7907 85.7225 89.3385 87.5305 87.5305C89.3385 85.7225 91.7907 84.7068 94.3476 84.7068H171.474C174.031 84.7068 176.483 85.7225 178.291 87.5305C180.099 89.3385 181.115 91.7907 181.115 94.3476V113.629C181.115 116.186 180.099 118.638 178.291 120.446C176.483 122.254 174.031 123.27 171.474 123.27H161.833M103.988 142.552H94.3476C91.7907 142.552 89.3385 143.567 87.5305 145.376C85.7225 147.184 84.7068 149.636 84.7068 152.193V171.474C84.7068 174.031 85.7225 176.483 87.5305 178.291C89.3385 180.099 91.7907 181.115 94.3476 181.115H171.474C174.031 181.115 176.483 180.099 178.291 178.291C180.099 176.483 181.115 174.031 181.115 171.474V152.193C181.115 149.636 180.099 147.184 178.291 145.376C176.483 143.567 174.031 142.552 171.474 142.552H161.833M103.988 103.988H104.037M103.988 161.833H104.037M137.731 103.988L118.45 132.911H147.372L128.091 161.833"
|
||||||
stroke="white"
|
stroke="white"
|
||||||
stroke-width="10"
|
stroke-width="10"
|
||||||
stroke-linecap="round"
|
stroke-linecap="round"
|
||||||
stroke-linejoin="round"
|
stroke-linejoin="round"
|
||||||
/>
|
/>
|
||||||
<circle
|
<circle
|
||||||
cx="132.469"
|
cx="132.469"
|
||||||
cy="132.469"
|
cy="132.469"
|
||||||
r="91.3779"
|
r="91.3779"
|
||||||
stroke="url(#paint1_linear_1_30)"
|
stroke="url(#paint1_linear_1_30)"
|
||||||
stroke-width="8"
|
stroke-width="8"
|
||||||
/>
|
/>
|
||||||
<defs>
|
<defs>
|
||||||
<linearGradient
|
<linearGradient
|
||||||
id="paint0_linear_1_30"
|
id="paint0_linear_1_30"
|
||||||
x1="51.6631"
|
x1="51.6631"
|
||||||
y1="26.9354"
|
y1="26.9354"
|
||||||
x2="222.549"
|
x2="222.549"
|
||||||
y2="213.717"
|
y2="213.717"
|
||||||
gradientUnits="userSpaceOnUse"
|
gradientUnits="userSpaceOnUse"
|
||||||
>
|
>
|
||||||
<stop stop-color="#FF0000" />
|
<stop stop-color="#FF0000" />
|
||||||
<stop offset="0.110405" stop-color="#FF6200" />
|
<stop offset="0.110405" stop-color="#FF6200" />
|
||||||
<stop offset="0.225785" stop-color="#FFAE00" />
|
<stop offset="0.225785" stop-color="#FFAE00" />
|
||||||
<stop offset="0.326294" stop-color="#FFD500" />
|
<stop offset="0.326294" stop-color="#FFD500" />
|
||||||
<stop offset="0.422381" stop-color="#99EA00" />
|
<stop offset="0.422381" stop-color="#99EA00" />
|
||||||
<stop offset="0.498373" stop-color="#4DF457" />
|
<stop offset="0.498373" stop-color="#4DF457" />
|
||||||
<stop offset="0.593491" stop-color="#26D3AB" />
|
<stop offset="0.593491" stop-color="#26D3AB" />
|
||||||
<stop offset="0.699814" stop-color="#13A9D5" />
|
<stop offset="0.699814" stop-color="#13A9D5" />
|
||||||
<stop offset="0.805673" stop-color="#A200FF" />
|
<stop offset="0.805673" stop-color="#A200FF" />
|
||||||
<stop offset="0.884464" stop-color="#C62AEB" />
|
<stop offset="0.884464" stop-color="#C62AEB" />
|
||||||
<stop offset="0.957056" stop-color="white" />
|
<stop offset="0.957056" stop-color="white" />
|
||||||
<stop offset="0.997383" />
|
<stop offset="0.997383" />
|
||||||
</linearGradient>
|
</linearGradient>
|
||||||
<linearGradient
|
<linearGradient
|
||||||
id="paint1_linear_1_30"
|
id="paint1_linear_1_30"
|
||||||
x1="132.469"
|
x1="132.469"
|
||||||
y1="37.0914"
|
y1="37.0914"
|
||||||
x2="132.469"
|
x2="132.469"
|
||||||
y2="227.847"
|
y2="227.847"
|
||||||
gradientUnits="userSpaceOnUse"
|
gradientUnits="userSpaceOnUse"
|
||||||
>
|
>
|
||||||
<stop stop-color="#EFEC32" />
|
<stop stop-color="#EFEC32" />
|
||||||
<stop offset="1" stop-color="#98FF60" />
|
<stop offset="1" stop-color="#98FF60" />
|
||||||
</linearGradient>
|
</linearGradient>
|
||||||
</defs>
|
</defs>
|
||||||
</svg>
|
</svg>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -187,136 +187,171 @@ export function BrandingPrideIcon(props: SVGProps<SVGSVGElement>) {
|
|||||||
* @returns A JSX element representing the branding icon.
|
* @returns A JSX element representing the branding icon.
|
||||||
*/
|
*/
|
||||||
export function BrandingGenericIcon(props: SVGProps<SVGSVGElement>) {
|
export function BrandingGenericIcon(props: SVGProps<SVGSVGElement>) {
|
||||||
const { resolvedTheme } = useTheme();
|
const { resolvedTheme } = useTheme();
|
||||||
|
|
||||||
if (resolvedTheme == "dark") {
|
if (resolvedTheme == "dark") {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
width="265"
|
width="265"
|
||||||
height="266"
|
height="266"
|
||||||
viewBox="0 0 265 266"
|
viewBox="0 0 265 266"
|
||||||
fill="none"
|
fill="none"
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
<rect
|
<rect
|
||||||
x="0.0612793"
|
x="0.0612793"
|
||||||
y="0.86145"
|
y="0.86145"
|
||||||
width="264.939"
|
width="264.939"
|
||||||
height="264.939"
|
height="264.939"
|
||||||
rx="66"
|
rx="66"
|
||||||
fill="url(#paint0_linear_1_20)"
|
fill="url(#paint0_linear_1_20)"
|
||||||
/>
|
/>
|
||||||
<path
|
<path
|
||||||
d="M104.05 124.132H94.4089C91.852 124.132 89.3998 123.116 87.5918 121.308C85.7838 119.5 84.7681 117.048 84.7681 114.491V95.2091C84.7681 92.6522 85.7838 90.2 87.5918 88.392C89.3998 86.584 91.852 85.5683 94.4089 85.5683H171.536C174.092 85.5683 176.545 86.584 178.353 88.392C180.161 90.2 181.176 92.6522 181.176 95.2091V114.491C181.176 117.048 180.161 119.5 178.353 121.308C176.545 123.116 174.092 124.132 171.536 124.132H161.895M104.05 143.413H94.4089C91.852 143.413 89.3998 144.429 87.5918 146.237C85.7838 148.045 84.7681 150.497 84.7681 153.054V172.336C84.7681 174.893 85.7838 177.345 87.5918 179.153C89.3998 180.961 91.852 181.977 94.4089 181.977H171.536C174.092 181.977 176.545 180.961 178.353 179.153C180.161 177.345 181.176 174.893 181.176 172.336V153.054C181.176 150.497 180.161 148.045 178.353 146.237C176.545 144.429 174.092 143.413 171.536 143.413H161.895M104.05 104.85H104.098M104.05 162.695H104.098M137.793 104.85L118.511 133.772H147.433L128.152 162.695"
|
d="M104.05 124.132H94.4089C91.852 124.132 89.3998 123.116 87.5918 121.308C85.7838 119.5 84.7681 117.048 84.7681 114.491V95.2091C84.7681 92.6522 85.7838 90.2 87.5918 88.392C89.3998 86.584 91.852 85.5683 94.4089 85.5683H171.536C174.092 85.5683 176.545 86.584 178.353 88.392C180.161 90.2 181.176 92.6522 181.176 95.2091V114.491C181.176 117.048 180.161 119.5 178.353 121.308C176.545 123.116 174.092 124.132 171.536 124.132H161.895M104.05 143.413H94.4089C91.852 143.413 89.3998 144.429 87.5918 146.237C85.7838 148.045 84.7681 150.497 84.7681 153.054V172.336C84.7681 174.893 85.7838 177.345 87.5918 179.153C89.3998 180.961 91.852 181.977 94.4089 181.977H171.536C174.092 181.977 176.545 180.961 178.353 179.153C180.161 177.345 181.176 174.893 181.176 172.336V153.054C181.176 150.497 180.161 148.045 178.353 146.237C176.545 144.429 174.092 143.413 171.536 143.413H161.895M104.05 104.85H104.098M104.05 162.695H104.098M137.793 104.85L118.511 133.772H147.433L128.152 162.695"
|
||||||
stroke="white"
|
stroke="white"
|
||||||
stroke-width="10"
|
stroke-width="10"
|
||||||
stroke-linecap="round"
|
stroke-linecap="round"
|
||||||
stroke-linejoin="round"
|
stroke-linejoin="round"
|
||||||
/>
|
/>
|
||||||
<circle
|
<circle
|
||||||
cx="132.531"
|
cx="132.531"
|
||||||
cy="133.331"
|
cy="133.331"
|
||||||
r="91.3779"
|
r="91.3779"
|
||||||
stroke="url(#paint1_linear_1_20)"
|
stroke="url(#paint1_linear_1_20)"
|
||||||
stroke-width="8"
|
stroke-width="8"
|
||||||
/>
|
/>
|
||||||
<defs>
|
<defs>
|
||||||
<linearGradient
|
<linearGradient
|
||||||
id="paint0_linear_1_20"
|
id="paint0_linear_1_20"
|
||||||
x1="107.361"
|
x1="107.361"
|
||||||
y1="55.6155"
|
y1="55.6155"
|
||||||
x2="230.116"
|
x2="230.116"
|
||||||
y2="226.059"
|
y2="226.059"
|
||||||
gradientUnits="userSpaceOnUse"
|
gradientUnits="userSpaceOnUse"
|
||||||
>
|
>
|
||||||
<stop />
|
<stop />
|
||||||
</linearGradient>
|
</linearGradient>
|
||||||
<linearGradient
|
<linearGradient
|
||||||
id="paint1_linear_1_20"
|
id="paint1_linear_1_20"
|
||||||
x1="132.531"
|
x1="132.531"
|
||||||
y1="37.9529"
|
y1="37.9529"
|
||||||
x2="132.531"
|
x2="132.531"
|
||||||
y2="228.709"
|
y2="228.709"
|
||||||
gradientUnits="userSpaceOnUse"
|
gradientUnits="userSpaceOnUse"
|
||||||
>
|
>
|
||||||
<stop stop-color="#EFEC32" />
|
<stop stop-color="#EFEC32" />
|
||||||
<stop offset="1" stop-color="#98FF60" />
|
<stop offset="1" stop-color="#98FF60" />
|
||||||
</linearGradient>
|
</linearGradient>
|
||||||
</defs>
|
</defs>
|
||||||
</svg>
|
</svg>
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return (
|
return (
|
||||||
<svg
|
<svg
|
||||||
width="265"
|
width="265"
|
||||||
height="265"
|
height="265"
|
||||||
viewBox="0 0 265 265"
|
viewBox="0 0 265 265"
|
||||||
fill="none"
|
fill="none"
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
<rect
|
<rect
|
||||||
x="0.0612793"
|
x="0.0612793"
|
||||||
width="264.939"
|
width="264.939"
|
||||||
height="264.939"
|
height="264.939"
|
||||||
rx="66"
|
rx="66"
|
||||||
fill="url(#paint0_linear_1_25)"
|
fill="url(#paint0_linear_1_25)"
|
||||||
/>
|
/>
|
||||||
<path
|
<path
|
||||||
d="M104.05 123.27H94.4089C91.852 123.27 89.3998 122.254 87.5918 120.446C85.7838 118.638 84.7681 116.186 84.7681 113.629V94.3476C84.7681 91.7907 85.7838 89.3385 87.5918 87.5305C89.3998 85.7225 91.852 84.7068 94.4089 84.7068H171.536C174.092 84.7068 176.545 85.7225 178.353 87.5305C180.161 89.3385 181.176 91.7907 181.176 94.3476V113.629C181.176 116.186 180.161 118.638 178.353 120.446C176.545 122.254 174.092 123.27 171.536 123.27H161.895M104.05 142.552H94.4089C91.852 142.552 89.3998 143.567 87.5918 145.376C85.7838 147.184 84.7681 149.636 84.7681 152.193V171.474C84.7681 174.031 85.7838 176.483 87.5918 178.291C89.3998 180.099 91.852 181.115 94.4089 181.115H171.536C174.092 181.115 176.545 180.099 178.353 178.291C180.161 176.483 181.176 174.031 181.176 171.474V152.193C181.176 149.636 180.161 147.184 178.353 145.376C176.545 143.567 174.092 142.552 171.536 142.552H161.895M104.05 103.988H104.098M104.05 161.833H104.098M137.793 103.988L118.511 132.911H147.433L128.152 161.833"
|
d="M104.05 123.27H94.4089C91.852 123.27 89.3998 122.254 87.5918 120.446C85.7838 118.638 84.7681 116.186 84.7681 113.629V94.3476C84.7681 91.7907 85.7838 89.3385 87.5918 87.5305C89.3998 85.7225 91.852 84.7068 94.4089 84.7068H171.536C174.092 84.7068 176.545 85.7225 178.353 87.5305C180.161 89.3385 181.176 91.7907 181.176 94.3476V113.629C181.176 116.186 180.161 118.638 178.353 120.446C176.545 122.254 174.092 123.27 171.536 123.27H161.895M104.05 142.552H94.4089C91.852 142.552 89.3998 143.567 87.5918 145.376C85.7838 147.184 84.7681 149.636 84.7681 152.193V171.474C84.7681 174.031 85.7838 176.483 87.5918 178.291C89.3998 180.099 91.852 181.115 94.4089 181.115H171.536C174.092 181.115 176.545 180.099 178.353 178.291C180.161 176.483 181.176 174.031 181.176 171.474V152.193C181.176 149.636 180.161 147.184 178.353 145.376C176.545 143.567 174.092 142.552 171.536 142.552H161.895M104.05 103.988H104.098M104.05 161.833H104.098M137.793 103.988L118.511 132.911H147.433L128.152 161.833"
|
||||||
stroke="black"
|
stroke="black"
|
||||||
stroke-width="10"
|
stroke-width="10"
|
||||||
stroke-linecap="round"
|
stroke-linecap="round"
|
||||||
stroke-linejoin="round"
|
stroke-linejoin="round"
|
||||||
/>
|
/>
|
||||||
<circle
|
<circle
|
||||||
cx="132.531"
|
cx="132.531"
|
||||||
cy="132.469"
|
cy="132.469"
|
||||||
r="91.3779"
|
r="91.3779"
|
||||||
stroke="url(#paint1_linear_1_25)"
|
stroke="url(#paint1_linear_1_25)"
|
||||||
stroke-width="8"
|
stroke-width="8"
|
||||||
/>
|
/>
|
||||||
<defs>
|
<defs>
|
||||||
<linearGradient
|
<linearGradient
|
||||||
id="paint0_linear_1_25"
|
id="paint0_linear_1_25"
|
||||||
x1="107.361"
|
x1="107.361"
|
||||||
y1="54.754"
|
y1="54.754"
|
||||||
x2="230.116"
|
x2="230.116"
|
||||||
y2="225.198"
|
y2="225.198"
|
||||||
gradientUnits="userSpaceOnUse"
|
gradientUnits="userSpaceOnUse"
|
||||||
>
|
>
|
||||||
<stop stop-color="white" />
|
<stop stop-color="white" />
|
||||||
</linearGradient>
|
</linearGradient>
|
||||||
<linearGradient
|
<linearGradient
|
||||||
id="paint1_linear_1_25"
|
id="paint1_linear_1_25"
|
||||||
x1="132.531"
|
x1="132.531"
|
||||||
y1="37.0914"
|
y1="37.0914"
|
||||||
x2="132.531"
|
x2="132.531"
|
||||||
y2="227.847"
|
y2="227.847"
|
||||||
gradientUnits="userSpaceOnUse"
|
gradientUnits="userSpaceOnUse"
|
||||||
>
|
>
|
||||||
<stop stop-color="#EFEC32" />
|
<stop stop-color="#EFEC32" />
|
||||||
<stop offset="1" stop-color="#98FF60" />
|
<stop offset="1" stop-color="#98FF60" />
|
||||||
</linearGradient>
|
</linearGradient>
|
||||||
</defs>
|
</defs>
|
||||||
</svg>
|
</svg>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function BadgeOfAffiliation(props: SVGProps<SVGSVGElement>) {
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
width="81"
|
||||||
|
height="81"
|
||||||
|
viewBox="0 0 81 81"
|
||||||
|
fill="none"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<rect width="81" height="81" rx="34" fill="url(#paint0_linear_1_12)" />
|
||||||
|
<path
|
||||||
|
d="M29.5 36.5H26C25.0717 36.5 24.1815 36.1313 23.5251 35.4749C22.8687 34.8185 22.5 33.9283 22.5 33V26C22.5 25.0717 22.8687 24.1815 23.5251 23.5251C24.1815 22.8687 25.0717 22.5 26 22.5H54C54.9283 22.5 55.8185 22.8687 56.4749 23.5251C57.1313 24.1815 57.5 25.0717 57.5 26V33C57.5 33.9283 57.1313 34.8185 56.4749 35.4749C55.8185 36.1313 54.9283 36.5 54 36.5H50.5M29.5 43.5H26C25.0717 43.5 24.1815 43.8687 23.5251 44.5251C22.8687 45.1815 22.5 46.0717 22.5 47V54C22.5 54.9283 22.8687 55.8185 23.5251 56.4749C24.1815 57.1313 25.0717 57.5 26 57.5H54C54.9283 57.5 55.8185 57.1313 56.4749 56.4749C57.1313 55.8185 57.5 54.9283 57.5 54V47C57.5 46.0717 57.1313 45.1815 56.4749 44.5251C55.8185 43.8687 54.9283 43.5 54 43.5H50.5M29.5 29.5H29.5175M29.5 50.5H29.5175M41.75 29.5L34.75 40H45.25L38.25 50.5"
|
||||||
|
stroke="white"
|
||||||
|
stroke-width="3"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
/>
|
||||||
|
<defs>
|
||||||
|
<linearGradient
|
||||||
|
id="paint0_linear_1_12"
|
||||||
|
x1="40.5"
|
||||||
|
y1="0"
|
||||||
|
x2="40.5"
|
||||||
|
y2="81"
|
||||||
|
gradientUnits="userSpaceOnUse"
|
||||||
|
>
|
||||||
|
<stop stop-color="#37B14F" />
|
||||||
|
<stop offset="1" stop-color="#3D4B17" />
|
||||||
|
</linearGradient>
|
||||||
|
</defs>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export const Discord = (props: SVGProps<SVGSVGElement>) => (
|
export const Discord = (props: SVGProps<SVGSVGElement>) => (
|
||||||
<svg
|
<svg
|
||||||
viewBox="0 0 256 199"
|
viewBox="0 0 256 199"
|
||||||
width="1em"
|
width="1em"
|
||||||
height="1em"
|
height="1em"
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
preserveAspectRatio="xMidYMid"
|
preserveAspectRatio="xMidYMid"
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
<path
|
<path
|
||||||
d="M216.856 16.597A208.502 208.502 0 0 0 164.042 0c-2.275 4.113-4.933 9.645-6.766 14.046-19.692-2.961-39.203-2.961-58.533 0-1.832-4.4-4.55-9.933-6.846-14.046a207.809 207.809 0 0 0-52.855 16.638C5.618 67.147-3.443 116.4 1.087 164.956c22.169 16.555 43.653 26.612 64.775 33.193A161.094 161.094 0 0 0 79.735 175.3a136.413 136.413 0 0 1-21.846-10.632 108.636 108.636 0 0 0 5.356-4.237c42.122 19.702 87.89 19.702 129.51 0a131.66 131.66 0 0 0 5.355 4.237 136.07 136.07 0 0 1-21.886 10.653c4.006 8.02 8.638 15.67 13.873 22.848 21.142-6.58 42.646-16.637 64.815-33.213 5.316-56.288-9.08-105.09-38.056-148.36ZM85.474 135.095c-12.645 0-23.015-11.805-23.015-26.18s10.149-26.2 23.015-26.2c12.867 0 23.236 11.804 23.015 26.2.02 14.375-10.148 26.18-23.015 26.18Zm85.051 0c-12.645 0-23.014-11.805-23.014-26.18s10.148-26.2 23.014-26.2c12.867 0 23.236 11.804 23.015 26.2 0 14.375-10.148 26.18-23.015 26.18Z"
|
d="M216.856 16.597A208.502 208.502 0 0 0 164.042 0c-2.275 4.113-4.933 9.645-6.766 14.046-19.692-2.961-39.203-2.961-58.533 0-1.832-4.4-4.55-9.933-6.846-14.046a207.809 207.809 0 0 0-52.855 16.638C5.618 67.147-3.443 116.4 1.087 164.956c22.169 16.555 43.653 26.612 64.775 33.193A161.094 161.094 0 0 0 79.735 175.3a136.413 136.413 0 0 1-21.846-10.632 108.636 108.636 0 0 0 5.356-4.237c42.122 19.702 87.89 19.702 129.51 0a131.66 131.66 0 0 0 5.355 4.237 136.07 136.07 0 0 1-21.886 10.653c4.006 8.02 8.638 15.67 13.873 22.848 21.142-6.58 42.646-16.637 64.815-33.213 5.316-56.288-9.08-105.09-38.056-148.36ZM85.474 135.095c-12.645 0-23.015-11.805-23.015-26.18s10.149-26.2 23.015-26.2c12.867 0 23.236 11.804 23.015 26.2.02 14.375-10.148 26.18-23.015 26.18Zm85.051 0c-12.645 0-23.014-11.805-23.014-26.18s10.148-26.2 23.014-26.2c12.867 0 23.236 11.804 23.015 26.2 0 14.375-10.148 26.18-23.015 26.18Z"
|
||||||
fill="#5865F2"
|
fill="#5865F2"
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
);
|
);
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@ -58,8 +58,9 @@ import IconDisplay from "./IconDisplay";
|
|||||||
import { useClerk, useUser } from "@clerk/nextjs";
|
import { useClerk, useUser } from "@clerk/nextjs";
|
||||||
import { LoaderIcon } from "react-hot-toast";
|
import { LoaderIcon } from "react-hot-toast";
|
||||||
import { Separator } from "./ui/separator";
|
import { Separator } from "./ui/separator";
|
||||||
import { convert } from "@/components/NewChart";
|
import { convert } from "@/components/charts/NewChart";
|
||||||
import { LoadingSpinner } from "./ui/loading-spinner";
|
import { LoadingSpinner } from "./ui/loading-spinner";
|
||||||
|
import { BadgeOfAffiliation } from "./Icon";
|
||||||
|
|
||||||
export default function ServerView(props: { server: string }) {
|
export default function ServerView(props: { server: string }) {
|
||||||
const [single, setSingle] = useState(new ServerSingle(props.server));
|
const [single, setSingle] = useState(new ServerSingle(props.server));
|
||||||
@ -120,12 +121,13 @@ export default function ServerView(props: { server: string }) {
|
|||||||
<>
|
<>
|
||||||
{single.grabOnline() == undefined && !single.grabOffline()?.online && (
|
{single.grabOnline() == undefined && !single.grabOffline()?.online && (
|
||||||
<div className="grid pl-4 pr-4">
|
<div className="grid pl-4 pr-4">
|
||||||
|
<X />
|
||||||
<div
|
<div
|
||||||
className=" rounded p-2"
|
className=" rounded p-2"
|
||||||
style={{ backgroundColor: "rgba(244, 63, 94, .16)" }}
|
style={{ backgroundColor: "rgba(244, 63, 94, .16)" }}
|
||||||
>
|
>
|
||||||
<strong className="flex items-center">
|
<strong className="flex items-center">
|
||||||
This server is offline <X />
|
This server is offline
|
||||||
</strong>
|
</strong>
|
||||||
<p>
|
<p>
|
||||||
This means that the server can{"'"}t loading some resources, like
|
This means that the server can{"'"}t loading some resources, like
|
||||||
@ -169,6 +171,22 @@ export default function ServerView(props: { server: string }) {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{props.server}
|
{props.server}
|
||||||
|
{props.server === "CoreBoxx" && (
|
||||||
|
<Tooltip>
|
||||||
|
<TooltipTrigger>
|
||||||
|
<BadgeOfAffiliation className="size-8 pl-2" />
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent className="font-normal tracking-normal flex items-center">
|
||||||
|
|
||||||
|
<BadgeOfAffiliation className="size-8 pr-2" />
|
||||||
|
<span>
|
||||||
|
CoreBoxx is an official affiliate of MHSF. This server was
|
||||||
|
<br /> found to be a high-quality server and should be a
|
||||||
|
good join for any player!
|
||||||
|
</span>
|
||||||
|
</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
)}
|
||||||
</CardTitle>
|
</CardTitle>
|
||||||
<CardDescription>
|
<CardDescription>
|
||||||
{/* 1704088800000 is the Unix time (in milliseconds) of (GMT) Monday, January 1, 2024 6:00:00 AM */}
|
{/* 1704088800000 is the Unix time (in milliseconds) of (GMT) Monday, January 1, 2024 6:00:00 AM */}
|
||||||
|
|||||||
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>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -31,7 +31,14 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { CartesianGrid, Line, LineChart, XAxis, YAxis } from "recharts";
|
import {
|
||||||
|
CartesianGrid,
|
||||||
|
Line,
|
||||||
|
LineChart,
|
||||||
|
XAxis,
|
||||||
|
YAxis,
|
||||||
|
ReferenceLine,
|
||||||
|
} from "recharts";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Card,
|
Card,
|
||||||
@ -49,8 +56,16 @@ import {
|
|||||||
import { useEffectOnce } from "@/lib/useEffectOnce";
|
import { useEffectOnce } from "@/lib/useEffectOnce";
|
||||||
import { ServerResponse } from "@/lib/types/mh-server";
|
import { ServerResponse } from "@/lib/types/mh-server";
|
||||||
import { getCommunityServerFavorites, getShortTermData } from "@/lib/api";
|
import { getCommunityServerFavorites, getShortTermData } from "@/lib/api";
|
||||||
import { Skeleton } from "./ui/skeleton";
|
import { Skeleton } from "../ui/skeleton";
|
||||||
import FadeIn from "react-fade-in/lib/FadeIn";
|
import FadeIn from "react-fade-in/lib/FadeIn";
|
||||||
|
import {
|
||||||
|
Select,
|
||||||
|
SelectContent,
|
||||||
|
SelectGroup,
|
||||||
|
SelectItem,
|
||||||
|
SelectTrigger,
|
||||||
|
SelectValue,
|
||||||
|
} from "@/components/ui/select";
|
||||||
|
|
||||||
const chartConfig = {
|
const chartConfig = {
|
||||||
player_count: {
|
player_count: {
|
||||||
@ -68,15 +83,20 @@ export function NewChart({ server }: { server: string }) {
|
|||||||
React.useState<keyof typeof chartConfig>("player_count");
|
React.useState<keyof typeof chartConfig>("player_count");
|
||||||
|
|
||||||
const [chartData, setChartData] = React.useState<any>([]);
|
const [chartData, setChartData] = React.useState<any>([]);
|
||||||
|
const [allData, setAllData] = React.useState<any>([]);
|
||||||
const [joins, setJoins] = React.useState<any>(0);
|
const [joins, setJoins] = React.useState<any>(0);
|
||||||
const [loading, setLoading] = React.useState(true);
|
const [loading, setLoading] = React.useState(true);
|
||||||
|
const [dataMax, setDataMax] = React.useState(0);
|
||||||
const [favorites, setFavorites] = React.useState<any>(0);
|
const [favorites, setFavorites] = React.useState<any>(0);
|
||||||
|
const [filter, setFilter] = React.useState("60");
|
||||||
|
|
||||||
const allNums = { player_count: joins, favorites };
|
const allNums = { player_count: joins, favorites };
|
||||||
useEffectOnce(() => {
|
useEffectOnce(() => {
|
||||||
getShortTermData(server, ["player_count", "favorites", "date"]).then(
|
getShortTermData(server, ["player_count", "favorites", "date"]).then(
|
||||||
(c) => {
|
(c) => {
|
||||||
setChartData(c);
|
setAllData(c.data);
|
||||||
|
setChartData(c.data.slice(-60));
|
||||||
|
setDataMax(c.dataMax);
|
||||||
getCommunityServerFavorites(server).then((b) => setFavorites(b));
|
getCommunityServerFavorites(server).then((b) => setFavorites(b));
|
||||||
fetch("https://api.minehut.com/server/" + server + "?byName=true").then(
|
fetch("https://api.minehut.com/server/" + server + "?byName=true").then(
|
||||||
(k) => {
|
(k) => {
|
||||||
@ -90,6 +110,14 @@ export function NewChart({ server }: { server: string }) {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (filter === "all") {
|
||||||
|
setChartData(allData);
|
||||||
|
} else {
|
||||||
|
setChartData(allData.slice(Number(filter) * -1));
|
||||||
|
}
|
||||||
|
}, [filter, allData]);
|
||||||
|
|
||||||
if (loading)
|
if (loading)
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@ -105,7 +133,29 @@ export function NewChart({ server }: { server: string }) {
|
|||||||
<CardTitle>
|
<CardTitle>
|
||||||
{chartConfig[activeChart].label} Chart for {server}
|
{chartConfig[activeChart].label} Chart for {server}
|
||||||
</CardTitle>
|
</CardTitle>
|
||||||
<CardDescription>Showing the past 30 entries.</CardDescription>
|
<CardDescription className="flex items-center">
|
||||||
|
Showing {filter !== "all" && "the last"}{" "}
|
||||||
|
<Select value={filter} onValueChange={setFilter}>
|
||||||
|
{" "}
|
||||||
|
<SelectTrigger className="max-w-[80px] mx-2">
|
||||||
|
<SelectValue placeholder="60" />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
<SelectGroup className="max-h-[200px]">
|
||||||
|
<SelectItem value="30">30</SelectItem>
|
||||||
|
<SelectItem value="60">60</SelectItem>
|
||||||
|
<SelectItem value="90">90</SelectItem>
|
||||||
|
<SelectItem value="120">120</SelectItem>
|
||||||
|
<SelectItem value="150">150</SelectItem>
|
||||||
|
<SelectItem value="180">180</SelectItem>
|
||||||
|
<SelectItem value="210">210</SelectItem>
|
||||||
|
<SelectItem value="240">240</SelectItem>
|
||||||
|
<SelectItem value="all">all</SelectItem>
|
||||||
|
</SelectGroup>
|
||||||
|
</SelectContent>
|
||||||
|
</Select>{" "}
|
||||||
|
{filter === "all" && "of the"} entries.
|
||||||
|
</CardDescription>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex">
|
<div className="flex">
|
||||||
{["player_count", "favorites"].map((key) => {
|
{["player_count", "favorites"].map((key) => {
|
||||||
@ -149,9 +199,7 @@ export function NewChart({ server }: { server: string }) {
|
|||||||
tickMargin={8}
|
tickMargin={8}
|
||||||
minTickGap={32}
|
minTickGap={32}
|
||||||
tickFormatter={(value) => {
|
tickFormatter={(value) => {
|
||||||
return new Date(value).toLocaleTimeString("en-US", {
|
return `${new Date(value).getMonth() + 1}/${new Date(value).getDate()}`;
|
||||||
timeStyle: "short",
|
|
||||||
});
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<YAxis
|
<YAxis
|
||||||
@ -166,6 +214,9 @@ export function NewChart({ server }: { server: string }) {
|
|||||||
: ` ${value == 1 ? "favorite" : "favrts."}`)
|
: ` ${value == 1 ? "favorite" : "favrts."}`)
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
|
domain={
|
||||||
|
activeChart === "player_count" ? [0, dataMax + 10] : undefined
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
<ChartTooltip
|
<ChartTooltip
|
||||||
content={
|
content={
|
||||||
@ -174,13 +225,25 @@ export function NewChart({ server }: { server: string }) {
|
|||||||
nameKey={activeChart}
|
nameKey={activeChart}
|
||||||
indicator="line"
|
indicator="line"
|
||||||
labelFormatter={(value) => {
|
labelFormatter={(value) => {
|
||||||
return new Date(value).toLocaleTimeString("en-US", {
|
return `${new Date(value).toLocaleDateString("en-US", {
|
||||||
|
day: "numeric",
|
||||||
|
month: "short",
|
||||||
|
})} ${new Date(value).toLocaleTimeString("en-US", {
|
||||||
timeStyle: "short",
|
timeStyle: "short",
|
||||||
});
|
})}`;
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
{activeChart === "player_count" && (
|
||||||
|
<ReferenceLine
|
||||||
|
y={dataMax}
|
||||||
|
stroke={`var(--color-${activeChart})`}
|
||||||
|
strokeWidth={2}
|
||||||
|
label="all-time max"
|
||||||
|
strokeDasharray="3 3"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
<Line
|
<Line
|
||||||
dataKey={activeChart}
|
dataKey={activeChart}
|
||||||
type="monotone"
|
type="monotone"
|
||||||
163
src/components/charts/RelativeChart.tsx
Normal file
163
src/components/charts/RelativeChart.tsx
Normal file
@ -0,0 +1,163 @@
|
|||||||
|
/*
|
||||||
|
* 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 { CartesianGrid, Line, LineChart, XAxis, YAxis } from "recharts";
|
||||||
|
|
||||||
|
import {
|
||||||
|
Card,
|
||||||
|
CardContent,
|
||||||
|
CardDescription,
|
||||||
|
CardHeader,
|
||||||
|
CardTitle,
|
||||||
|
} from "@/components/ui/card";
|
||||||
|
import {
|
||||||
|
ChartContainer,
|
||||||
|
ChartTooltip,
|
||||||
|
ChartTooltipContent,
|
||||||
|
} from "@/components/ui/chart";
|
||||||
|
import { Skeleton } from "../ui/skeleton";
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import { getRelativeServerData } from "@/lib/api";
|
||||||
|
import { Tooltip, TooltipContent, TooltipTrigger } from "../ui/tooltip";
|
||||||
|
import { Info } from "lucide-react";
|
||||||
|
|
||||||
|
export function RelativeChart({ server }: { server: string }) {
|
||||||
|
const [chartData, setChartData] = useState<
|
||||||
|
Array<{ relativePercentage: number; date: string }>
|
||||||
|
>([]);
|
||||||
|
const [loading, setLoading] = useState(true);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
getRelativeServerData(server).then((d) => {
|
||||||
|
const newv: Array<{ relativePercentage: number; date: string }> = [];
|
||||||
|
|
||||||
|
d.forEach((c) =>
|
||||||
|
newv.push({
|
||||||
|
relativePercentage: c.relativePrecentage * 100,
|
||||||
|
date: c.date,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
setChartData(newv);
|
||||||
|
setLoading(false);
|
||||||
|
});
|
||||||
|
}, [server]);
|
||||||
|
|
||||||
|
if (loading)
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Skeleton className="w-full h-[437px]" />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card>
|
||||||
|
<CardHeader className="w-full relative">
|
||||||
|
<CardTitle>Relative percentage of {server}</CardTitle>
|
||||||
|
<CardDescription className="flex items-center">
|
||||||
|
Shows the last 30 entries.{" "}
|
||||||
|
<Tooltip>
|
||||||
|
<TooltipTrigger>
|
||||||
|
<Info className="size-4 ml-2" />
|
||||||
|
</TooltipTrigger>
|
||||||
|
<TooltipContent>
|
||||||
|
This is the percentage of players on your server <br />
|
||||||
|
compared to the entire Minehut network. <br />
|
||||||
|
<code>Server players / Minehut players</code>
|
||||||
|
</TooltipContent>
|
||||||
|
</Tooltip>
|
||||||
|
</CardDescription>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<ChartContainer
|
||||||
|
config={{
|
||||||
|
relativePercentage: {
|
||||||
|
label: "Relative percentage",
|
||||||
|
color: "hsl(var(--chart-2))",
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
className="h-[250px] w-full aspect-auto"
|
||||||
|
>
|
||||||
|
<LineChart
|
||||||
|
accessibilityLayer
|
||||||
|
data={chartData}
|
||||||
|
margin={{
|
||||||
|
left: 12,
|
||||||
|
right: 12,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<CartesianGrid vertical={false} />
|
||||||
|
<XAxis
|
||||||
|
dataKey="date"
|
||||||
|
tickLine={false}
|
||||||
|
axisLine={false}
|
||||||
|
tickMargin={8}
|
||||||
|
tickFormatter={(value) =>
|
||||||
|
`${new Date(value).toLocaleTimeString("en-US", {
|
||||||
|
timeStyle: "short",
|
||||||
|
})}`
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<YAxis
|
||||||
|
dataKey="relativePercentage"
|
||||||
|
tickLine={false}
|
||||||
|
axisLine={false}
|
||||||
|
tickFormatter={(value) => `${value}%`}
|
||||||
|
/>
|
||||||
|
<ChartTooltip
|
||||||
|
content={
|
||||||
|
<ChartTooltipContent
|
||||||
|
className="w-[150px]"
|
||||||
|
nameKey="relativePercentage"
|
||||||
|
indicator="line"
|
||||||
|
labelFormatter={(value) => {
|
||||||
|
return `${new Date(value).toLocaleDateString("en-US", {
|
||||||
|
day: "numeric",
|
||||||
|
month: "short",
|
||||||
|
})} ${new Date(value).toLocaleTimeString("en-US", {
|
||||||
|
timeStyle: "short",
|
||||||
|
})}`;
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<Line
|
||||||
|
dataKey="relativePercentage"
|
||||||
|
type="natural"
|
||||||
|
stroke="var(--color-relativePercentage)"
|
||||||
|
strokeWidth={2}
|
||||||
|
dot={false}
|
||||||
|
/>
|
||||||
|
</LineChart>
|
||||||
|
</ChartContainer>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
94
src/components/misc/AffiliatePopup.tsx
Normal file
94
src/components/misc/AffiliatePopup.tsx
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
/*
|
||||||
|
* 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 {
|
||||||
|
DialogDescription,
|
||||||
|
DialogHeader,
|
||||||
|
DialogTitle,
|
||||||
|
DialogTrigger,
|
||||||
|
} from "@/components/ui/dialog";
|
||||||
|
import { useState } from "react";
|
||||||
|
import useClipboard from "@/lib/useClipboard";
|
||||||
|
import { Button } from "../ui/button";
|
||||||
|
import { Check } from "lucide-react";
|
||||||
|
|
||||||
|
export default function AffiliatePopup() {
|
||||||
|
const [textCopied, setTextCopied] = useState(false);
|
||||||
|
const clipboard = useClipboard();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle className="text-black dark:text-white">CoreBoxx</DialogTitle>
|
||||||
|
<DialogDescription className="text-black dark:text-white text-md">
|
||||||
|
We won't claim to be a "unique server" or any of that. But we do aim to
|
||||||
|
give you the highest quality BoxPvP! Here's what we have: <br /> - 50
|
||||||
|
progression mines over 11 worlds, plus many more for events and other
|
||||||
|
items <br /> - Balanced gear sets - just because someone is a bit ahead
|
||||||
|
of you doesn't mean you deserve to die in one shot <br /> - We're
|
||||||
|
non-P2W, and we mean it! All perks are for convenience or benefit
|
||||||
|
everyone online! <br /> - A rapidly-growing, tight-knit community with
|
||||||
|
events and giveaways <br /> - Quests and storyline in development, in
|
||||||
|
case mining gets boring <br />
|
||||||
|
<br />
|
||||||
|
<code className="border my-2 p-3 flex rounded">
|
||||||
|
CoreBoxx.minehut.gg{" "}
|
||||||
|
</code>
|
||||||
|
<div
|
||||||
|
className="border rounded p-2 mb-8 text-sm"
|
||||||
|
style={{ backgroundColor: "hsl(var(--background))" }}
|
||||||
|
>
|
||||||
|
CoreBoxx is an official affiliate of MHSF. This server was found to be
|
||||||
|
a high-quality server and should be a good join for any player!
|
||||||
|
</div>
|
||||||
|
</DialogDescription>
|
||||||
|
<span className="w-full grid grid-cols-3 gap-2 opacity-80 backdrop-blur">
|
||||||
|
<Button onClick={() => window.open("/server/CoreBoxx", "_blank")}>
|
||||||
|
Open Page
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
setTextCopied(true);
|
||||||
|
clipboard.writeText("CoreBoxx.mhsf.minehut.gg");
|
||||||
|
setTimeout(() => setTextCopied(false), 1000);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{textCopied ? (
|
||||||
|
<Check size={16} className="flex items-center" />
|
||||||
|
) : (
|
||||||
|
<p>Copy IP</p>
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
|
<DialogTrigger asChild>
|
||||||
|
<Button variant="outline">Close</Button>
|
||||||
|
</DialogTrigger>
|
||||||
|
</span>
|
||||||
|
</DialogHeader>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -52,7 +52,7 @@ export function MiniJoinsChart({ server }: { server: string }) {
|
|||||||
|
|
||||||
useEffectOnce(() => {
|
useEffectOnce(() => {
|
||||||
getShortTermData(server, ["player_count", "date"]).then((result) => {
|
getShortTermData(server, ["player_count", "date"]).then((result) => {
|
||||||
setChartData(result.slice(-20));
|
setChartData(result.data.slice(-20));
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -61,7 +61,7 @@ export default function StickyTopbar({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`transition-all duration-300 ${isSticky ? "fixed left-0 w-full backdrop-blur shadow-lg " + className : "block w-full bg-transparent"}`}
|
className={`transition-all duration-300 z-[9] ${isSticky ? "fixed left-0 w-full backdrop-blur shadow-lg " + className : "block w-full bg-transparent"}`}
|
||||||
style={{
|
style={{
|
||||||
top: isSticky ? `${bannerSize * 32 + 38}px` : undefined,
|
top: isSticky ? `${bannerSize * 32 + 38}px` : undefined,
|
||||||
}}
|
}}
|
||||||
|
|||||||
@ -29,7 +29,6 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import * as DialogPrimitive from "@radix-ui/react-dialog";
|
import * as DialogPrimitive from "@radix-ui/react-dialog";
|
||||||
import { X } from "lucide-react";
|
import { X } from "lucide-react";
|
||||||
@ -52,7 +51,7 @@ const DialogOverlay = React.forwardRef<
|
|||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 backdrop-blur",
|
"fixed inset-0 z-50 bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 backdrop-blur",
|
||||||
className,
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
@ -69,7 +68,7 @@ const DialogContent = React.forwardRef<
|
|||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
|
"fixed left-[50%] top-[50%] z-50 grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
|
||||||
className,
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
@ -90,7 +89,7 @@ const DialogHeader = ({
|
|||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex flex-col space-y-1.5 text-center sm:text-left",
|
"flex flex-col space-y-1.5 text-center sm:text-left",
|
||||||
className,
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
@ -104,7 +103,7 @@ const DialogFooter = ({
|
|||||||
<div
|
<div
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
|
"flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
|
||||||
className,
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
@ -119,7 +118,7 @@ const DialogTitle = React.forwardRef<
|
|||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"text-lg font-semibold leading-none tracking-tight",
|
"text-lg font-semibold leading-none tracking-tight",
|
||||||
className,
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@ -1,41 +1,104 @@
|
|||||||
|
import GradientBanner from "@/components/effects/gradient-banner";
|
||||||
|
import MainBanner from "@/components/feat/MainBanner";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import Link from "next/link";
|
||||||
|
import { useState, useEffect } from "react";
|
||||||
|
import { DialogContent, Dialog } from "@/components/ui/dialog";
|
||||||
|
import AffiliatePopup from "@/components/misc/AffiliatePopup";
|
||||||
|
import { Gradient } from "stripe-gradient";
|
||||||
|
|
||||||
export const defaultBanners: {
|
export const defaultBanners: {
|
||||||
bannerSpace: number;
|
bannerSpace: number;
|
||||||
bannerContent: React.ReactNode;
|
bannerContent: React.ReactNode;
|
||||||
}[] = [
|
}[] = [
|
||||||
// The sponsor banner ALWAYS has to be first.
|
// The affilation banner ALWAYS has to be first.
|
||||||
// {
|
{
|
||||||
// bannerSpace: 2,
|
bannerSpace: 2,
|
||||||
// bannerContent: (
|
bannerContent: (
|
||||||
// <MainBanner size={2} className="max-h-[4rem] border-0">
|
<>
|
||||||
// {" "}
|
<AffiliateBanner />
|
||||||
// <GradientBanner>
|
</>
|
||||||
// <strong>???</strong> — <i>an official affiliate of MHSF</i>{" "}
|
),
|
||||||
// <br />
|
},
|
||||||
// Lorem ipsum odor amet, consectetuer adipiscing elit. — check it out
|
|
||||||
// </GradientBanner>
|
|
||||||
// </MainBanner>
|
|
||||||
// ),
|
|
||||||
// },
|
|
||||||
];
|
];
|
||||||
|
|
||||||
|
function AffiliateBanner() {
|
||||||
|
const [isOpen, setOpen] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const gradient = new Gradient();
|
||||||
|
|
||||||
|
const initializeGradient = () => {
|
||||||
|
const canvasElement = document.getElementById(
|
||||||
|
"gradient-dialog"
|
||||||
|
) as HTMLCanvasElement;
|
||||||
|
if (canvasElement) gradient.initGradient("#gradient-dialog");
|
||||||
|
};
|
||||||
|
|
||||||
|
if (isOpen) {
|
||||||
|
const timeoutId = setTimeout(initializeGradient, 100); // Delay to ensure canvas is ready
|
||||||
|
return () => clearTimeout(timeoutId); // Cleanup timeout
|
||||||
|
}
|
||||||
|
}, [isOpen]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Dialog open={isOpen} onOpenChange={setOpen}>
|
||||||
|
<DialogContent>
|
||||||
|
<>
|
||||||
|
<canvas
|
||||||
|
id="gradient-dialog"
|
||||||
|
className="h-full absolute w-[512px] rounded blur-sm"
|
||||||
|
style={
|
||||||
|
{
|
||||||
|
"--gradient-color-1": "#6ec3f4",
|
||||||
|
"--gradient-color-2": "#3a3aff",
|
||||||
|
"--gradient-color-3": "#ff61ab",
|
||||||
|
"--gradient-color-4": "#E63946",
|
||||||
|
webKitMaskImage:
|
||||||
|
"linear-gradient(to top, black 0%, transparent 25%, transparent 80%, black 100%)",
|
||||||
|
maskImage:
|
||||||
|
"linear-gradient(to top, black 0%, transparent 25%, transparent 80%, black 100%)",
|
||||||
|
} as React.CSSProperties
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
<div className="relative z-10">
|
||||||
|
<AffiliatePopup />
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
<div onClick={() => setOpen(true)} className="cursor-pointer">
|
||||||
|
<MainBanner size={2} className="max-h-[4rem] border-0">
|
||||||
|
<GradientBanner>
|
||||||
|
<strong>CoreBoxx</strong> — <i>an official affiliate of MHSF</i>{" "}
|
||||||
|
<br />
|
||||||
|
Season 3 is out the doors for the best box server on Minehut
|
||||||
|
</GradientBanner>
|
||||||
|
</MainBanner>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export const bannerHooks: (() =>
|
export const bannerHooks: (() =>
|
||||||
| { bannerSpace: number; bannerContent: React.ReactNode }
|
| { bannerSpace: number; bannerContent: React.ReactNode }
|
||||||
| undefined)[] = [
|
| undefined)[] = [
|
||||||
() => {
|
() => {
|
||||||
// if (process.env.NEXT_PUBLIC_VERCEL_ENV !== "production")
|
if (process.env.NEXT_PUBLIC_VERCEL_ENV !== "production")
|
||||||
// return {
|
return {
|
||||||
// bannerSpace: 1,
|
bannerSpace: 1,
|
||||||
// bannerContent: (
|
bannerContent: (
|
||||||
// <MainBanner className="bg-orange-600">
|
<MainBanner className="bg-orange-600">
|
||||||
// Your not in production!{" "}
|
Your not in production!{" "}
|
||||||
// <Link href="https://mhsf.app">
|
<Link href="https://mhsf.app">
|
||||||
// <Button variant="link" className="dark:text-black">
|
<Button variant="link" className="dark:text-black">
|
||||||
// Go to production
|
Go to production
|
||||||
// </Button>
|
</Button>
|
||||||
// </Link>
|
</Link>
|
||||||
// </MainBanner>
|
</MainBanner>
|
||||||
// ),
|
),
|
||||||
// };
|
};
|
||||||
return undefined;
|
return undefined;
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
708
src/lib/api.ts
708
src/lib/api.ts
@ -38,452 +38,486 @@
|
|||||||
import { Achievement } from "./types/achievement";
|
import { Achievement } from "./types/achievement";
|
||||||
|
|
||||||
const connector = (
|
const connector = (
|
||||||
endpoint: string,
|
endpoint: string,
|
||||||
options: { version: number; starting?: string },
|
options: { version: number; starting?: string }
|
||||||
) =>
|
) =>
|
||||||
`${options.starting == undefined ? "/" : `${options.starting}/`}api/v${options.version}${endpoint}`;
|
`${options.starting == undefined ? "/" : `${options.starting}/`}api/v${options.version}${endpoint}`;
|
||||||
|
|
||||||
async function apiConstructor<K>(
|
async function apiConstructor<K>(
|
||||||
connector: string,
|
connector: string,
|
||||||
requestInit: RequestInit,
|
requestInit: RequestInit,
|
||||||
modifier: (data: any) => K,
|
modifier: (data: any) => K
|
||||||
): Promise<K> {
|
): Promise<K> {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(connector, requestInit);
|
const response = await fetch(connector, requestInit);
|
||||||
|
|
||||||
if (response.status >= 400) {
|
if (response.status >= 400) {
|
||||||
throw Error("Error while running API");
|
throw Error("Error while running API");
|
||||||
}
|
}
|
||||||
return modifier(await response.json());
|
return modifier(await response.json());
|
||||||
} catch {
|
} catch {
|
||||||
throw Error("Error while running API");
|
throw Error("Error while running API");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export async function getMOTDFromServer(
|
export async function getMOTDFromServer(
|
||||||
list: Array<{ server: string; motd: string }>,
|
list: Array<{ server: string; motd: string }>
|
||||||
): Promise<Array<{ server: string; motd: string }>> {
|
): Promise<Array<{ server: string; motd: string }>> {
|
||||||
const result = await fetch(
|
const result = await fetch(
|
||||||
process.env.NEXT_PUBLIC_ALTERNATE_MOTD_ENDPOINT ??
|
process.env.NEXT_PUBLIC_ALTERNATE_MOTD_ENDPOINT ??
|
||||||
connector("/motd", { version: 1 }),
|
connector("/motd", { version: 1 }),
|
||||||
{
|
{
|
||||||
body: JSON.stringify({ motd: list }),
|
body: JSON.stringify({ motd: list }),
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
},
|
},
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
let json = await result.json();
|
let json = await result.json();
|
||||||
return json.result;
|
return json.result;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getRelativeServerData(
|
||||||
|
server: string
|
||||||
|
): Promise<Array<{ date: string; relativePrecentage: number }>> {
|
||||||
|
const result = await fetch(
|
||||||
|
connector("/history/" + server + "/get-relative-data", { version: 0 })
|
||||||
|
);
|
||||||
|
|
||||||
|
const json = await result.json();
|
||||||
|
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(
|
export async function getCommunityServerFavorites(
|
||||||
server: string,
|
server: string
|
||||||
): Promise<number> {
|
): Promise<number> {
|
||||||
const result = await fetch(
|
const result = await fetch(
|
||||||
connector(`/favorites/${server}/community-favorites`, { version: 0 }),
|
connector(`/favorites/${server}/community-favorites`, { version: 0 }),
|
||||||
{
|
{
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
},
|
},
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
let json = await result.json();
|
let json = await result.json();
|
||||||
return json.result;
|
return json.result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** requires authentication */
|
/** requires authentication */
|
||||||
export async function favoriteServer(server: string) {
|
export async function favoriteServer(server: string) {
|
||||||
try {
|
try {
|
||||||
await fetch(
|
await fetch(
|
||||||
connector(`/favorites/${server}/favorite-server`, { version: 0 }),
|
connector(`/favorites/${server}/favorite-server`, { version: 0 }),
|
||||||
{
|
{
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
},
|
},
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
} catch {
|
} catch {
|
||||||
throw Error("Not authenticated with a user.");
|
throw Error("Not authenticated with a user.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** requires authentication */
|
/** requires authentication */
|
||||||
export async function isFavorited(server: string): Promise<boolean> {
|
export async function isFavorited(server: string): Promise<boolean> {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
connector(`/favorites/${server}/favorited`, { version: 0 }),
|
connector(`/favorites/${server}/favorited`, { version: 0 }),
|
||||||
{
|
{
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
},
|
},
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
return (await response.json()).result;
|
return (await response.json()).result;
|
||||||
} catch {
|
} catch {
|
||||||
throw Error("Not authenticated with a user.");
|
throw Error("Not authenticated with a user.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** requires authentication */
|
/** requires authentication */
|
||||||
export async function getAccountFavorites(): Promise<Array<string>> {
|
export async function getAccountFavorites(): Promise<Array<string>> {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
connector(`/favorites/account-favorites`, { version: 0 }),
|
connector(`/favorites/account-favorites`, { version: 0 }),
|
||||||
{
|
{
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
},
|
},
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
return (await response.json()).result;
|
return (await response.json()).result;
|
||||||
} catch {
|
} catch {
|
||||||
throw Error("Not authenticated with a user.");
|
throw Error("Not authenticated with a user.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* currently not used in frontend yet
|
* currently not used in frontend yet
|
||||||
*/
|
*/
|
||||||
export async function getHistoricalData(
|
export async function getHistoricalData(
|
||||||
server: string,
|
server: string,
|
||||||
scopes: Array<"player_count" | "favorites" | "server" | "time">,
|
scopes: Array<"player_count" | "favorites" | "server" | "time">
|
||||||
): Promise<
|
): Promise<
|
||||||
Array<{
|
Array<{
|
||||||
player_count?: number;
|
player_count?: number;
|
||||||
favorites?: number;
|
favorites?: number;
|
||||||
server?: string;
|
server?: string;
|
||||||
time?: number;
|
time?: number;
|
||||||
}>
|
}>
|
||||||
> {
|
> {
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
connector(`/history/${server}/get-historical-data`, { version: 0 }),
|
connector(`/history/${server}/get-historical-data`, { version: 0 }),
|
||||||
{
|
{
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: JSON.stringify({ scopes }),
|
body: JSON.stringify({ scopes }),
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
},
|
},
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
return (await response.json()).data;
|
return (await response.json()).data;
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getShortTermData(
|
export async function getShortTermData(
|
||||||
server: string,
|
server: string,
|
||||||
scopes: Array<"player_count" | "favorites" | "server" | "date">,
|
scopes: Array<"player_count" | "favorites" | "server" | "date">
|
||||||
): Promise<
|
): Promise<{
|
||||||
Array<{
|
data: Array<{
|
||||||
player_count?: number;
|
player_count?: number;
|
||||||
favorites?: number;
|
favorites?: number;
|
||||||
server?: string;
|
server?: string;
|
||||||
time?: number;
|
time?: number;
|
||||||
}>
|
}>;
|
||||||
> {
|
dataMax: number;
|
||||||
const response = await fetch(
|
}> {
|
||||||
connector(`/history/${server}/get-short-term-data`, { version: 0 }),
|
const response = await fetch(
|
||||||
{
|
connector(`/history/${server}/get-short-term-data`, { version: 0 }),
|
||||||
method: "POST",
|
{
|
||||||
body: JSON.stringify({ scopes }),
|
method: "POST",
|
||||||
headers: {
|
body: JSON.stringify({ scopes }),
|
||||||
"Content-Type": "application/json",
|
headers: {
|
||||||
},
|
"Content-Type": "application/json",
|
||||||
},
|
},
|
||||||
);
|
}
|
||||||
|
);
|
||||||
|
|
||||||
return (await response.json()).data;
|
return await response.json();
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getMetaShortTerm(
|
export async function getMetaShortTerm(
|
||||||
scopes: Array<"total_players" | "total_servers" | "date">,
|
scopes: Array<"total_players" | "total_servers" | "date">
|
||||||
): Promise<
|
): Promise<
|
||||||
Array<{
|
Array<{
|
||||||
total_players?: number;
|
total_players?: number;
|
||||||
total_servers?: number;
|
total_servers?: number;
|
||||||
unix?: number;
|
unix?: number;
|
||||||
}>
|
}>
|
||||||
> {
|
> {
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
connector(`/history/meta-short-term-data`, { version: 0 }),
|
connector(`/history/meta-short-term-data`, { version: 0 }),
|
||||||
{
|
{
|
||||||
method: "POST",
|
method: "POST",
|
||||||
body: JSON.stringify({ scopes }),
|
body: JSON.stringify({ scopes }),
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
},
|
},
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
return (await response.json()).data;
|
return (await response.json()).data;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** requires authentication */
|
/** requires authentication */
|
||||||
export async function linkMCAccount(code: string): Promise<string | undefined> {
|
export async function linkMCAccount(code: string): Promise<string | undefined> {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
connector(`/account-linking/claim-account-code`, { version: 0 }),
|
connector(`/account-linking/claim-account-code`, { version: 0 }),
|
||||||
{
|
{
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
},
|
},
|
||||||
body: JSON.stringify({ code }),
|
body: JSON.stringify({ code }),
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
if (response.status == 400) {
|
if (response.status == 400) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (await response.json()).player;
|
return (await response.json()).player;
|
||||||
} catch {
|
} catch {
|
||||||
throw Error("Incorrect code");
|
throw Error("Incorrect code");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** requires authentication */
|
/** requires authentication */
|
||||||
export async function unlinkMCAccount(): Promise<boolean> {
|
export async function unlinkMCAccount(): Promise<boolean> {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
connector(`/account-linking/unlink-account`, { version: 0 }),
|
connector(`/account-linking/unlink-account`, { version: 0 }),
|
||||||
{
|
{
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
},
|
},
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} catch {
|
} catch {
|
||||||
throw Error(
|
throw Error(
|
||||||
"Not authenticated with a user or user already linked account.",
|
"Not authenticated with a user or user already linked account."
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function serverOwned(server: string): Promise<boolean> {
|
export async function serverOwned(server: string): Promise<boolean> {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
connector(`/account-linking/is-owned`, { version: 0 }),
|
connector(`/account-linking/is-owned`, { version: 0 }),
|
||||||
{
|
{
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
},
|
},
|
||||||
body: JSON.stringify({ server }),
|
body: JSON.stringify({ server }),
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
return (await response.json()).owned;
|
return (await response.json()).owned;
|
||||||
} catch {
|
} catch {
|
||||||
throw Error("Error while running API");
|
throw Error("Error while running API");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** requires authentication */
|
/** requires authentication */
|
||||||
export async function userOwnedServer(server: string): Promise<boolean> {
|
export async function userOwnedServer(server: string): Promise<boolean> {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
connector(`/account-linking/owned-user`, { version: 0 }),
|
connector(`/account-linking/owned-user`, { version: 0 }),
|
||||||
{
|
{
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
},
|
},
|
||||||
body: JSON.stringify({ server }),
|
body: JSON.stringify({ server }),
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
return (await response.json()).result;
|
return (await response.json()).result;
|
||||||
} catch {
|
} catch {
|
||||||
throw Error("Error while running API");
|
throw Error("Error while running API");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** requires authentication */
|
/** requires authentication */
|
||||||
export async function ownServer(server: string): Promise<boolean> {
|
export async function ownServer(server: string): Promise<boolean> {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
connector(`/account-linking/own-server`, { version: 0 }),
|
connector(`/account-linking/own-server`, { version: 0 }),
|
||||||
{
|
{
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
},
|
},
|
||||||
body: JSON.stringify({ server }),
|
body: JSON.stringify({ server }),
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
if (response.status >= 400) {
|
if (response.status >= 400) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} catch {
|
} catch {
|
||||||
throw Error("Error while running API");
|
throw Error("Error while running API");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** requires authentication */
|
/** requires authentication */
|
||||||
export async function unownServer(server: string): Promise<boolean> {
|
export async function unownServer(server: string): Promise<boolean> {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
connector(`/account-linking/unown-server`, { version: 0 }),
|
connector(`/account-linking/unown-server`, { version: 0 }),
|
||||||
{
|
{
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
},
|
},
|
||||||
body: JSON.stringify({ server }),
|
body: JSON.stringify({ server }),
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
if (response.status == 400) {
|
if (response.status == 400) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} catch {
|
} catch {
|
||||||
throw Error("Error while running API");
|
throw Error("Error while running API");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** requires authentication */
|
/** requires authentication */
|
||||||
export async function setCustomization(
|
export async function setCustomization(
|
||||||
server: string,
|
server: string,
|
||||||
customization: any,
|
customization: any
|
||||||
): Promise<boolean | Error> {
|
): Promise<boolean | Error> {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
connector(`/customization/${server}/set`, { version: 0 }),
|
connector(`/customization/${server}/set`, { version: 0 }),
|
||||||
{
|
{
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
},
|
},
|
||||||
body: JSON.stringify({ customization }),
|
body: JSON.stringify({ customization }),
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
if (response.status == 400) {
|
if (response.status == 400) {
|
||||||
throw Error("Error while running API");
|
throw Error("Error while running API");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} catch {
|
} catch {
|
||||||
throw Error("Error while running API");
|
throw Error("Error while running API");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getCustomization(server: string): Promise<any> {
|
export async function getCustomization(server: string): Promise<any> {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
connector(`/customization/${server}/get`, { version: 0 }),
|
connector(`/customization/${server}/get`, { version: 0 }),
|
||||||
{
|
{
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
},
|
},
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
if (response.status == 400) {
|
if (response.status == 400) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (await response.json()).results;
|
return (await response.json()).results;
|
||||||
} catch {
|
} catch {
|
||||||
throw Error("Error while running API");
|
throw Error("Error while running API");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function sortedFavorites(): Promise<
|
export async function sortedFavorites(): Promise<
|
||||||
Array<{ server: string; favorites: number }> | boolean
|
Array<{ server: string; favorites: number }> | boolean
|
||||||
> {
|
> {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
connector(`/sorting/favorites`, { version: 0 }),
|
connector(`/sorting/favorites`, { version: 0 }),
|
||||||
{
|
{
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
},
|
},
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
if (response.status == 400) {
|
if (response.status == 400) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (await response.json()).results;
|
return (await response.json()).results;
|
||||||
} catch {
|
} catch {
|
||||||
throw Error("Error while running API");
|
throw Error("Error while running API");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function reportServer(
|
export async function reportServer(
|
||||||
server: string,
|
server: string,
|
||||||
reason: string,
|
reason: string
|
||||||
): Promise<boolean> {
|
): Promise<boolean> {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(connector(`/report-server`, { version: 1 }), {
|
const response = await fetch(connector(`/report-server`, { version: 1 }), {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
},
|
},
|
||||||
body: JSON.stringify({ server, reason }),
|
body: JSON.stringify({ server, reason }),
|
||||||
});
|
});
|
||||||
|
|
||||||
if (response.status == 400) {
|
if (response.status == 400) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} catch {
|
} catch {
|
||||||
throw Error("Error while running API");
|
throw Error("Error while running API");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function setAccountSL(
|
export async function setAccountSL(
|
||||||
data: number | boolean | null,
|
data: number | boolean | null,
|
||||||
type: "srv" | "ipr" | "pad",
|
type: "srv" | "ipr" | "pad"
|
||||||
): Promise<boolean> {
|
): Promise<boolean> {
|
||||||
try {
|
try {
|
||||||
const response = await fetch(
|
const response = await fetch(
|
||||||
connector(`/account-sl/change`, { version: 0 }),
|
connector(`/account-sl/change`, { version: 0 }),
|
||||||
{
|
{
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json",
|
"Content-Type": "application/json",
|
||||||
},
|
},
|
||||||
body: JSON.stringify({ data, type }),
|
body: JSON.stringify({ data, type }),
|
||||||
},
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
if (response.status === 400) {
|
if (response.status === 400) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} catch {
|
} catch {
|
||||||
throw Error("Error while running API");
|
throw Error("Error while running API");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getAchievements = async (server: string) =>
|
export const getAchievements = async (server: string) =>
|
||||||
apiConstructor<{ _id: string; name: string; achievements: Achievement[] }[]>(
|
apiConstructor<{ _id: string; name: string; achievements: Achievement[] }[]>(
|
||||||
connector(`/achievements/${server}`, { version: 0 }),
|
connector(`/achievements/${server}`, { version: 0 }),
|
||||||
{},
|
{},
|
||||||
(data) => data.result,
|
(data) => data.result
|
||||||
);
|
);
|
||||||
|
|||||||
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,
|
||||||
|
};
|
||||||
|
}
|
||||||
@ -27,3 +27,24 @@
|
|||||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||||
* OTHER DEALINGS IN THE SOFTWARE.
|
* OTHER DEALINGS IN THE SOFTWARE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
|
export function useServerExists(server: string) {
|
||||||
|
const [serverExists, setServerExists] = useState(true);
|
||||||
|
const [loading, setLoading] = useState(true);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
fetch("https://api.minehut.com/server/" + server + "?byName=true")
|
||||||
|
.then((c) => c.json())
|
||||||
|
.then((d) => {
|
||||||
|
setServerExists(d.server != null);
|
||||||
|
setLoading(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
serverExists,
|
||||||
|
loading,
|
||||||
|
};
|
||||||
|
}
|
||||||
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,
|
||||||
|
};
|
||||||
|
}
|
||||||
@ -5,7 +5,7 @@ import MiniMessage from "minimessage-js";
|
|||||||
var numberOfItemsInView = 20;
|
var numberOfItemsInView = 20;
|
||||||
|
|
||||||
export default class ServersList {
|
export default class ServersList {
|
||||||
private servers: Array<OnlineServer> = [];
|
servers: Array<OnlineServer> = [];
|
||||||
currentServers: Array<OnlineServer> = [];
|
currentServers: Array<OnlineServer> = [];
|
||||||
private filters: Array<(server: OnlineServer) => Promise<boolean>> = [];
|
private filters: Array<(server: OnlineServer) => Promise<boolean>> = [];
|
||||||
extraData: any = { total_players: 0, total_servers: 0 };
|
extraData: any = { total_players: 0, total_servers: 0 };
|
||||||
|
|||||||
@ -50,7 +50,7 @@ export default async function handler(
|
|||||||
const client = new MongoClient(process.env.MONGO_DB as string);
|
const client = new MongoClient(process.env.MONGO_DB as string);
|
||||||
await client.connect();
|
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 collection = db.collection("auth_codes");
|
||||||
|
|
||||||
const entry = await collection.findOne({ code });
|
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);
|
const client = new MongoClient(process.env.MONGO_DB as string);
|
||||||
await client.connect();
|
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 collection = db.collection("owned-servers");
|
||||||
|
|
||||||
res.send({ owned: (await collection.findOne({ server })) != undefined });
|
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);
|
const client = new MongoClient(process.env.MONGO_DB as string);
|
||||||
await client.connect();
|
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 collection = db.collection("owned-servers");
|
||||||
|
|
||||||
if ((await collection.findOne({ server: server })) == undefined) {
|
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);
|
const client = new MongoClient(process.env.MONGO_DB as string);
|
||||||
await client.connect();
|
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 collection = db.collection("owned-servers");
|
||||||
|
|
||||||
res.send({
|
res.send({
|
||||||
|
|||||||
@ -44,7 +44,7 @@ export default async function handler(
|
|||||||
const client = new MongoClient(process.env.MONGO_DB as string);
|
const client = new MongoClient(process.env.MONGO_DB as string);
|
||||||
await client.connect();
|
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 users = db.collection("claimed-users");
|
||||||
const user = await (await clerkClient()).users.getUser(userId);
|
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);
|
const client = new MongoClient(process.env.MONGO_DB as string);
|
||||||
await client.connect();
|
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 collection = db.collection("owned-servers");
|
||||||
const customization = db.collection("customization");
|
const customization = db.collection("customization");
|
||||||
|
|
||||||
|
|||||||
@ -41,7 +41,7 @@ export default async function handler(
|
|||||||
const client = new MongoClient(process.env.MONGO_DB as string);
|
const client = new MongoClient(process.env.MONGO_DB as string);
|
||||||
await client.connect();
|
await client.connect();
|
||||||
|
|
||||||
const db = client.db("mhsf");
|
const db = client.db(process.env.CUSTOM_MONGO_DB ?? "mhsf");
|
||||||
const collection = db.collection("achievements");
|
const collection = db.collection("achievements");
|
||||||
|
|
||||||
res.send({ result: await collection.find({ name: server }).toArray() });
|
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);
|
const client = new MongoClient(process.env.MONGO_DB as string);
|
||||||
await client.connect();
|
await client.connect();
|
||||||
|
|
||||||
const db = client.db("mhsf");
|
const db = client.db(process.env.CUSTOM_MONGO_DB ?? "mhsf");
|
||||||
const collection = db.collection("customization");
|
const collection = db.collection("customization");
|
||||||
|
|
||||||
res.send({ results: await collection.findOne({ server }) });
|
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);
|
const client = new MongoClient(process.env.MONGO_DB as string);
|
||||||
await client.connect();
|
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 collection = db.collection("owned-servers");
|
||||||
const customizationColl = db.collection("customization");
|
const customizationColl = db.collection("customization");
|
||||||
if (!((await collection.findOne({ server, author: userId })) == undefined)) {
|
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);
|
const client = new MongoClient(process.env.MONGO_DB as string);
|
||||||
await client.connect();
|
await client.connect();
|
||||||
|
|
||||||
const db = client.db("mhsf");
|
const db = client.db(process.env.CUSTOM_MONGO_DB ?? "mhsf");
|
||||||
const collection = db.collection("customization");
|
const collection = db.collection("customization");
|
||||||
const results: { server: string; customization: any }[] = [];
|
const results: { server: string; customization: any }[] = [];
|
||||||
|
|
||||||
|
|||||||
@ -40,7 +40,7 @@ export default async function handler(
|
|||||||
|
|
||||||
await client.connect();
|
await client.connect();
|
||||||
|
|
||||||
const db = client.db("mhsf");
|
const db = client.db(process.env.CUSTOM_MONGO_DB ?? "mhsf");
|
||||||
const collection = db.collection("meta");
|
const collection = db.collection("meta");
|
||||||
const find = await collection.find({ server: server }).toArray();
|
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);
|
const client = new MongoClient(process.env.MONGO_DB as string);
|
||||||
await client.connect();
|
await client.connect();
|
||||||
|
|
||||||
const db = client.db("mhsf");
|
const db = client.db(process.env.CUSTOM_MONGO_DB ?? "mhsf");
|
||||||
const collection = db.collection("favorites");
|
const collection = db.collection("favorites");
|
||||||
const find = await collection.find({ user: userId }).toArray();
|
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);
|
const client = new MongoClient(process.env.MONGO_DB as string);
|
||||||
await client.connect();
|
await client.connect();
|
||||||
|
|
||||||
const db = client.db("mhsf");
|
const db = client.db(process.env.CUSTOM_MONGO_DB ?? "mhsf");
|
||||||
const collection = db.collection("favorites");
|
const collection = db.collection("favorites");
|
||||||
const find = await collection.find({ user: userId }).toArray();
|
const find = await collection.find({ user: userId }).toArray();
|
||||||
if (find.length == 0) res.send({ result: false });
|
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);
|
const client = new MongoClient(process.env.MONGO_DB as string);
|
||||||
await client.connect();
|
await client.connect();
|
||||||
|
|
||||||
const db = client.db("mhsf");
|
const db = client.db(process.env.CUSTOM_MONGO_DB ?? "mhsf");
|
||||||
const collection = db.collection("favorites");
|
const collection = db.collection("favorites");
|
||||||
const find = await collection.find({ user: userId }).toArray();
|
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)});
|
||||||
|
}
|
||||||
97
src/pages/api/v0/history/[server]/get-relative-data.ts
Normal file
97
src/pages/api/v0/history/[server]/get-relative-data.ts
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*//*
|
||||||
|
* MHSF, Minehut Server List
|
||||||
|
* All external content is rather licensed under the ECA Agreement
|
||||||
|
* located here: https://list.mlnehut.com/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 mh = client.db("mhsf").collection("mh");
|
||||||
|
const server = req.query.server as string;
|
||||||
|
|
||||||
|
const allData = await db.find({ server }).toArray();
|
||||||
|
const data: any[] = [];
|
||||||
|
if (server === "peww") console.log(allData.slice(-30));
|
||||||
|
|
||||||
|
for (const d of allData.slice(-30)) {
|
||||||
|
const dateOfEntry = new Date(d.date);
|
||||||
|
const result = await mh
|
||||||
|
.find({
|
||||||
|
date: {
|
||||||
|
$gte: new Date(dateOfEntry.getTime() - 1000 * 60 * 60),
|
||||||
|
$lt: new Date(dateOfEntry.getTime() + 1000 * 60 * 60),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.toArray();
|
||||||
|
|
||||||
|
if (result.length > 0) {
|
||||||
|
const resultedData = result[0];
|
||||||
|
data.push({
|
||||||
|
relativePrecentage: d.player_count / resultedData.total_players,
|
||||||
|
date: dateOfEntry,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
client.close();
|
||||||
|
res.send({ data });
|
||||||
|
}
|
||||||
@ -38,11 +38,16 @@ export default async function handler(
|
|||||||
const client = new MongoClient(process.env.MONGO_DB as string);
|
const client = new MongoClient(process.env.MONGO_DB as string);
|
||||||
const db = client.db("mhsf").collection("history");
|
const db = client.db("mhsf").collection("history");
|
||||||
const server = req.query.server as string;
|
const server = req.query.server as string;
|
||||||
|
let dataMax = 0;
|
||||||
const scopes: Array<string> = checkForInfoOrLeave(res, req.body.scopes);
|
const scopes: Array<string> = checkForInfoOrLeave(res, req.body.scopes);
|
||||||
|
|
||||||
const allData = await db.find({ server }).toArray();
|
const allData = await db.find({ server }).toArray();
|
||||||
const data: any[] = [];
|
const data: any[] = [];
|
||||||
|
|
||||||
|
dataMax = (
|
||||||
|
await db.find({ server }).sort({ player_count: -1 }).limit(1).toArray()
|
||||||
|
)[0].player_count;
|
||||||
|
|
||||||
allData.forEach((d) => {
|
allData.forEach((d) => {
|
||||||
const result: any = {};
|
const result: any = {};
|
||||||
scopes.forEach((b) => {
|
scopes.forEach((b) => {
|
||||||
@ -52,7 +57,7 @@ export default async function handler(
|
|||||||
});
|
});
|
||||||
|
|
||||||
client.close();
|
client.close();
|
||||||
res.send({ data });
|
res.send({ data, dataMax });
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkForInfoOrLeave(res: NextApiResponse, info: any) {
|
function checkForInfoOrLeave(res: NextApiResponse, info: any) {
|
||||||
|
|||||||
@ -38,7 +38,7 @@ export default async function handler(
|
|||||||
const client = new MongoClient(process.env.MONGO_DB as string);
|
const client = new MongoClient(process.env.MONGO_DB as string);
|
||||||
await client.connect();
|
await client.connect();
|
||||||
|
|
||||||
const db = client.db("mhsf");
|
const db = client.db(process.env.CUSTOM_MONGO_DB ?? "mhsf");
|
||||||
const collection = db.collection("meta");
|
const collection = db.collection("meta");
|
||||||
|
|
||||||
const all = await collection.find().toArray();
|
const all = await collection.find().toArray();
|
||||||
|
|||||||
96
yarn.lock
96
yarn.lock
@ -748,7 +748,7 @@
|
|||||||
resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz"
|
resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz"
|
||||||
integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==
|
integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==
|
||||||
|
|
||||||
"@jridgewell/sourcemap-codec@^1.4.15":
|
"@jridgewell/sourcemap-codec@^1.4.15", "@jridgewell/sourcemap-codec@^1.5.0":
|
||||||
version "1.5.0"
|
version "1.5.0"
|
||||||
resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz#3188bcb273a414b0d215fd22a58540b989b9409a"
|
resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz#3188bcb273a414b0d215fd22a58540b989b9409a"
|
||||||
integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==
|
integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==
|
||||||
@ -2494,6 +2494,11 @@
|
|||||||
resolved "https://registry.yarnpkg.com/@unocss/core/-/core-0.61.5.tgz#09f4da600f6f50dbb68a173f23566e9171d5cd3d"
|
resolved "https://registry.yarnpkg.com/@unocss/core/-/core-0.61.5.tgz#09f4da600f6f50dbb68a173f23566e9171d5cd3d"
|
||||||
integrity sha512-hB8zr2rnrCzz9x8ho2SAXQiYTEjwAPMiBzpaEe2C0+CFWeL1179h9508YVyZHHAzMyZILIG9YrVAWrrMdt2/Xg==
|
integrity sha512-hB8zr2rnrCzz9x8ho2SAXQiYTEjwAPMiBzpaEe2C0+CFWeL1179h9508YVyZHHAzMyZILIG9YrVAWrrMdt2/Xg==
|
||||||
|
|
||||||
|
"@unocss/core@0.65.3", "@unocss/core@^0.65.3":
|
||||||
|
version "0.65.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@unocss/core/-/core-0.65.3.tgz#c49c90e4be627cae1a29800e089588835f1f087a"
|
||||||
|
integrity sha512-xYkJ63lIadL6KqvGcaE2fFeLvo6rC1F+e+R9EFn0Aj0ArMRhiltZk8vvLFHP7iYjjdTdqDkAr/7IdrTosTo8Pg==
|
||||||
|
|
||||||
"@unocss/eslint-config@^0.61.5":
|
"@unocss/eslint-config@^0.61.5":
|
||||||
version "0.61.5"
|
version "0.61.5"
|
||||||
resolved "https://registry.yarnpkg.com/@unocss/eslint-config/-/eslint-config-0.61.5.tgz#01a8ebb4a626d22d6e29d9800c31d2f87bed2b6b"
|
resolved "https://registry.yarnpkg.com/@unocss/eslint-config/-/eslint-config-0.61.5.tgz#01a8ebb4a626d22d6e29d9800c31d2f87bed2b6b"
|
||||||
@ -2512,12 +2517,12 @@
|
|||||||
magic-string "^0.30.10"
|
magic-string "^0.30.10"
|
||||||
synckit "^0.9.1"
|
synckit "^0.9.1"
|
||||||
|
|
||||||
"@unocss/extractor-arbitrary-variants@0.61.5":
|
"@unocss/extractor-arbitrary-variants@0.65.3":
|
||||||
version "0.61.5"
|
version "0.65.3"
|
||||||
resolved "https://registry.yarnpkg.com/@unocss/extractor-arbitrary-variants/-/extractor-arbitrary-variants-0.61.5.tgz#478b15f0ae298b0f05d9978ef528b08723c22cb5"
|
resolved "https://registry.yarnpkg.com/@unocss/extractor-arbitrary-variants/-/extractor-arbitrary-variants-0.65.3.tgz#ed65b2aac189f9fcf083be6d8a0ec449cc9832ed"
|
||||||
integrity sha512-UB1EweAaJrUxv+h3n5FqoizKHrnUgUzkdmOdJTfV6xvow90ITqbUoza+L6iVMNfcrcXTx8QpDnWh6rhLRyKY+g==
|
integrity sha512-ZVGCjOZuU8daGxY7MUJQrI7aVKzZi1llRk53QgEUTU1q60X/fi8M2+A9mwEgG9MBVHBdsuvxqZ9Dp79IktSyLw==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@unocss/core" "0.61.5"
|
"@unocss/core" "0.65.3"
|
||||||
|
|
||||||
"@unocss/postcss@^0.61.5":
|
"@unocss/postcss@^0.61.5":
|
||||||
version "0.61.5"
|
version "0.61.5"
|
||||||
@ -2532,33 +2537,33 @@
|
|||||||
magic-string "^0.30.10"
|
magic-string "^0.30.10"
|
||||||
postcss "^8.4.39"
|
postcss "^8.4.39"
|
||||||
|
|
||||||
"@unocss/preset-mini@0.61.5":
|
"@unocss/preset-mini@0.65.3":
|
||||||
version "0.61.5"
|
version "0.65.3"
|
||||||
resolved "https://registry.yarnpkg.com/@unocss/preset-mini/-/preset-mini-0.61.5.tgz#60dc1aa2c05e415b6e9860493fb92a20acce62da"
|
resolved "https://registry.yarnpkg.com/@unocss/preset-mini/-/preset-mini-0.65.3.tgz#e72c6918af329875dbd102534ebfe31f9b465a23"
|
||||||
integrity sha512-gVm7Z9X0krx8CK/+pKAqcVmpqzRk1+SH7bfgRxKtKhyFSxJlwpjNp1rKm3gCT0F1Tlp3d8aufYRksaXGZhs8Ow==
|
integrity sha512-HG7mRfq0S2VKkw40duumoyIYaMBQGW1Uxb+Kw8HLGvoamnDmOZKb+TOXxys17Z5Z0vloi2CN1qqyJhYC0G6MSg==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@unocss/core" "0.61.5"
|
"@unocss/core" "0.65.3"
|
||||||
"@unocss/extractor-arbitrary-variants" "0.61.5"
|
"@unocss/extractor-arbitrary-variants" "0.65.3"
|
||||||
"@unocss/rule-utils" "0.61.5"
|
"@unocss/rule-utils" "0.65.3"
|
||||||
|
|
||||||
"@unocss/preset-uno@^0.61.5":
|
"@unocss/preset-uno@^0.65.3":
|
||||||
version "0.61.5"
|
version "0.65.3"
|
||||||
resolved "https://registry.yarnpkg.com/@unocss/preset-uno/-/preset-uno-0.61.5.tgz#80c85edaf4ed364c91df3400dae5abfe3976f21e"
|
resolved "https://registry.yarnpkg.com/@unocss/preset-uno/-/preset-uno-0.65.3.tgz#89634394fefa602aec950d1d4e43286e7c3afc09"
|
||||||
integrity sha512-CflB0l9CeZx+b/Q8mA4Ow4d63Caf+vFJ+1EGA06jG9qYjPLy76Rkci//0m9cEtO+vPnYtgLc7HZAZv0X6wh4Tg==
|
integrity sha512-1O9qVAG/W7t4X9VExuUPGGy+4n8yxfpuQ3NeFgXlEkT1Mi3cokS0Eb0quvttgLGbjQ2waoS4MWbGyMmDGHWnYQ==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@unocss/core" "0.61.5"
|
"@unocss/core" "0.65.3"
|
||||||
"@unocss/preset-mini" "0.61.5"
|
"@unocss/preset-mini" "0.65.3"
|
||||||
"@unocss/preset-wind" "0.61.5"
|
"@unocss/preset-wind" "0.65.3"
|
||||||
"@unocss/rule-utils" "0.61.5"
|
"@unocss/rule-utils" "0.65.3"
|
||||||
|
|
||||||
"@unocss/preset-wind@0.61.5":
|
"@unocss/preset-wind@0.65.3":
|
||||||
version "0.61.5"
|
version "0.65.3"
|
||||||
resolved "https://registry.yarnpkg.com/@unocss/preset-wind/-/preset-wind-0.61.5.tgz#049f4cf3d15be5d5bf1bb3c2108cff22a69d0884"
|
resolved "https://registry.yarnpkg.com/@unocss/preset-wind/-/preset-wind-0.65.3.tgz#0ea857ee19b7a4ca3654bb3fed57c3e1c28ec401"
|
||||||
integrity sha512-n4uepxv3gVoVQb0tv7iV8M4W0CgwLw0QaMX+3ECYzFLMynjCkZmFDtdQAX720yTvLZxwCxEZfQCgydOSt0qjZA==
|
integrity sha512-esptoeJEN1QZEXwMIU3OXumSi3TEbIXZg1SuuUYqOWXzldxANsfXSMdHtsiXUSMNwNsfmQl4XfBlGNYYK/7eyg==
|
||||||
dependencies:
|
dependencies:
|
||||||
"@unocss/core" "0.61.5"
|
"@unocss/core" "0.65.3"
|
||||||
"@unocss/preset-mini" "0.61.5"
|
"@unocss/preset-mini" "0.65.3"
|
||||||
"@unocss/rule-utils" "0.61.5"
|
"@unocss/rule-utils" "0.65.3"
|
||||||
|
|
||||||
"@unocss/rule-utils@0.61.5":
|
"@unocss/rule-utils@0.61.5":
|
||||||
version "0.61.5"
|
version "0.61.5"
|
||||||
@ -2568,6 +2573,14 @@
|
|||||||
"@unocss/core" "^0.61.5"
|
"@unocss/core" "^0.61.5"
|
||||||
magic-string "^0.30.10"
|
magic-string "^0.30.10"
|
||||||
|
|
||||||
|
"@unocss/rule-utils@0.65.3":
|
||||||
|
version "0.65.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/@unocss/rule-utils/-/rule-utils-0.65.3.tgz#dd00ca5469a693d3c4b3554d38e9c4a195ea577d"
|
||||||
|
integrity sha512-jndyth0X11FbvIDForYq90b+N5xsR31FRsmvp7AC7dcW71clemUEDHCwqzSJn8cVFwahgvlwWbEoYHPEgQrtIQ==
|
||||||
|
dependencies:
|
||||||
|
"@unocss/core" "^0.65.3"
|
||||||
|
magic-string "^0.30.17"
|
||||||
|
|
||||||
"@unocss/transformer-compile-class@^0.61.5":
|
"@unocss/transformer-compile-class@^0.61.5":
|
||||||
version "0.61.5"
|
version "0.61.5"
|
||||||
resolved "https://registry.yarnpkg.com/@unocss/transformer-compile-class/-/transformer-compile-class-0.61.5.tgz#be992b1a9e2300314618b1866854f1f6ea95419c"
|
resolved "https://registry.yarnpkg.com/@unocss/transformer-compile-class/-/transformer-compile-class-0.61.5.tgz#be992b1a9e2300314618b1866854f1f6ea95419c"
|
||||||
@ -2639,6 +2652,26 @@ acorn@^8.9.0:
|
|||||||
resolved "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz"
|
resolved "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz"
|
||||||
integrity sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==
|
integrity sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==
|
||||||
|
|
||||||
|
ag-charts-types@11.0.3:
|
||||||
|
version "11.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/ag-charts-types/-/ag-charts-types-11.0.3.tgz#a4aa4825b298014c1323a1e5832dec0ef4e5e135"
|
||||||
|
integrity sha512-q7O2viQXPyO014QVH7KjFCUmQ/NvMBx9ReZtramQ44u+/aAa0ttLi/coK+V0Hse4/MG1eB/2XhgymdooQ0Kalg==
|
||||||
|
|
||||||
|
ag-grid-community@33.0.3:
|
||||||
|
version "33.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/ag-grid-community/-/ag-grid-community-33.0.3.tgz#51511d5b048eedf24b3de596967e74a3272bfe88"
|
||||||
|
integrity sha512-HZeVmVieZ5Gm9j09Itecqm+OIX8X6cU4TJToUbsXv3DxdO9SK5/s8aAJAwBHtNRXOhHrhqQYwrY2yc3OtzWwcQ==
|
||||||
|
dependencies:
|
||||||
|
ag-charts-types "11.0.3"
|
||||||
|
|
||||||
|
ag-grid-react@^33.0.3:
|
||||||
|
version "33.0.3"
|
||||||
|
resolved "https://registry.yarnpkg.com/ag-grid-react/-/ag-grid-react-33.0.3.tgz#24f0c65ba48363e53db915aab13bb537b8e35025"
|
||||||
|
integrity sha512-6IcraSVqsUG/hzTeZ0D0dtddAcZKcWdN75ek/O+lCA6r22abJPe33nHBYVezkTV8k6D3JhA24mlTwduzn0GZLQ==
|
||||||
|
dependencies:
|
||||||
|
ag-grid-community "33.0.3"
|
||||||
|
prop-types "^15.8.1"
|
||||||
|
|
||||||
ajv@^6.12.4:
|
ajv@^6.12.4:
|
||||||
version "6.12.6"
|
version "6.12.6"
|
||||||
resolved "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz"
|
resolved "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz"
|
||||||
@ -5312,6 +5345,13 @@ magic-string@^0.30.10:
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@jridgewell/sourcemap-codec" "^1.4.15"
|
"@jridgewell/sourcemap-codec" "^1.4.15"
|
||||||
|
|
||||||
|
magic-string@^0.30.17:
|
||||||
|
version "0.30.17"
|
||||||
|
resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.30.17.tgz#450a449673d2460e5bbcfba9a61916a1714c7453"
|
||||||
|
integrity sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==
|
||||||
|
dependencies:
|
||||||
|
"@jridgewell/sourcemap-codec" "^1.5.0"
|
||||||
|
|
||||||
mangle-css-class-webpack-plugin@^5.1.0:
|
mangle-css-class-webpack-plugin@^5.1.0:
|
||||||
version "5.1.0"
|
version "5.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/mangle-css-class-webpack-plugin/-/mangle-css-class-webpack-plugin-5.1.0.tgz#42008a8fbe0257f491968796320eb94b17a36321"
|
resolved "https://registry.yarnpkg.com/mangle-css-class-webpack-plugin/-/mangle-css-class-webpack-plugin-5.1.0.tgz#42008a8fbe0257f491968796320eb94b17a36321"
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user