From 8203b10b71e843319f104cd29c6bc253bb8a72b9 Mon Sep 17 00:00:00 2001
From: Liam
Date: Sun, 14 Apr 2024 19:55:07 +0100
Subject: [PATCH] add basic player page
---
next.config.mjs | 5 ++
package.json | 3 +
pnpm-lock.yaml | 113 ++++++++++++++++++++++++++++++-
src/app/layout.tsx | 5 +-
src/app/page.tsx | 13 ++--
src/app/player/page.tsx | 13 ++++
src/{lib => common}/utils.ts | 0
src/common/web-request.ts | 18 +++++
src/components/player-search.tsx | 77 +++++++++++++++++++++
src/components/ui/button.tsx | 47 +++++++++++++
src/components/ui/input.tsx | 22 ++++++
src/consts.ts | 1 +
12 files changed, 308 insertions(+), 9 deletions(-)
create mode 100644 src/app/player/page.tsx
rename src/{lib => common}/utils.ts (100%)
create mode 100644 src/common/web-request.ts
create mode 100644 src/components/player-search.tsx
create mode 100644 src/components/ui/button.tsx
create mode 100644 src/components/ui/input.tsx
create mode 100644 src/consts.ts
diff --git a/next.config.mjs b/next.config.mjs
index f020e7d..7d5a3e3 100644
--- a/next.config.mjs
+++ b/next.config.mjs
@@ -8,6 +8,11 @@ const nextConfig = {
hostname: "git.fascinated.cc",
pathname: "/**",
},
+ {
+ protocol: "https",
+ hostname: "api.mcutils.xyz",
+ pathname: "/**",
+ },
],
},
};
diff --git a/package.json b/package.json
index 699947e..2f5bdf5 100644
--- a/package.json
+++ b/package.json
@@ -9,6 +9,8 @@
"lint": "next lint"
},
"dependencies": {
+ "@radix-ui/react-slot": "^1.0.2",
+ "axios": "^1.6.8",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.0",
"lucide-react": "^0.368.0",
@@ -16,6 +18,7 @@
"next-themes": "^0.3.0",
"react": "^18",
"react-dom": "^18",
+ "react-toastify": "^10.0.5",
"tailwind-merge": "^2.2.2",
"tailwindcss-animate": "^1.0.7"
},
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 4eaa490..a6175bb 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -5,6 +5,12 @@ settings:
excludeLinksFromLockfile: false
dependencies:
+ '@radix-ui/react-slot':
+ specifier: ^1.0.2
+ version: 1.0.2(@types/react@18.2.78)(react@18.2.0)
+ axios:
+ specifier: ^1.6.8
+ version: 1.6.8
class-variance-authority:
specifier: ^0.7.0
version: 0.7.0
@@ -26,6 +32,9 @@ dependencies:
react-dom:
specifier: ^18
version: 18.2.0(react@18.2.0)
+ react-toastify:
+ specifier: ^10.0.5
+ version: 10.0.5(react-dom@18.2.0)(react@18.2.0)
tailwind-merge:
specifier: ^2.2.2
version: 2.2.2
@@ -284,6 +293,35 @@ packages:
requiresBuild: true
optional: true
+ /@radix-ui/react-compose-refs@1.0.1(@types/react@18.2.78)(react@18.2.0):
+ resolution: {integrity: sha512-fDSBgd44FKHa1FRMU59qBMPFcl2PZE+2nmqunj+BWFyYYjnhIDWL2ItDs3rrbJDQOtzt5nIebLCQc4QRfz6LJw==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ dependencies:
+ '@babel/runtime': 7.24.4
+ '@types/react': 18.2.78
+ react: 18.2.0
+ dev: false
+
+ /@radix-ui/react-slot@1.0.2(@types/react@18.2.78)(react@18.2.0):
+ resolution: {integrity: sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg==}
+ peerDependencies:
+ '@types/react': '*'
+ react: ^16.8 || ^17.0 || ^18.0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ dependencies:
+ '@babel/runtime': 7.24.4
+ '@radix-ui/react-compose-refs': 1.0.1(@types/react@18.2.78)(react@18.2.0)
+ '@types/react': 18.2.78
+ react: 18.2.0
+ dev: false
+
/@rushstack/eslint-patch@1.10.2:
resolution: {integrity: sha512-hw437iINopmQuxWPSUEvqE56NCPsiU8N4AYtfHmJFckclktzK9YQJieD3XkDCDH4OjL+C7zgPUh73R/nrcHrqw==}
dev: true
@@ -311,7 +349,6 @@ packages:
/@types/prop-types@15.7.12:
resolution: {integrity: sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==}
- dev: true
/@types/react-dom@18.2.25:
resolution: {integrity: sha512-o/V48vf4MQh7juIKZU2QGDfli6p1+OOi5oXx36Hffpc9adsHeXjVp8rHuPkjd8VT8sOJ2Zp05HR7CdpGTIUFUA==}
@@ -324,7 +361,6 @@ packages:
dependencies:
'@types/prop-types': 15.7.12
csstype: 3.1.3
- dev: true
/@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.4.5):
resolution: {integrity: sha512-5FKsVcHTk6TafQKQbuIVkXq58Fnbkd2wDL4LB7AURN7RUOu1utVP+G8+6u3ZhEroW3DF6hyo3ZEXxgKgp4KeCg==}
@@ -564,6 +600,10 @@ packages:
resolution: {integrity: sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==}
dev: true
+ /asynckit@0.4.0:
+ resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
+ dev: false
+
/available-typed-arrays@1.0.7:
resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==}
engines: {node: '>= 0.4'}
@@ -576,6 +616,16 @@ packages:
engines: {node: '>=4'}
dev: true
+ /axios@1.6.8:
+ resolution: {integrity: sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ==}
+ dependencies:
+ follow-redirects: 1.15.6
+ form-data: 4.0.0
+ proxy-from-env: 1.1.0
+ transitivePeerDependencies:
+ - debug
+ dev: false
+
/axobject-query@3.2.1:
resolution: {integrity: sha512-jsyHu61e6N4Vbz/v18DHwWYKK0bSWLqn47eeDSKPB7m8tqMHF9YJ+mhIk2lVteyZrY8tnSj/jHOv4YiTCuCJgg==}
dependencies:
@@ -689,6 +739,13 @@ packages:
/color-name@1.1.4:
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
+ /combined-stream@1.0.8:
+ resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
+ engines: {node: '>= 0.8'}
+ dependencies:
+ delayed-stream: 1.0.0
+ dev: false
+
/commander@4.1.1:
resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==}
engines: {node: '>= 6'}
@@ -712,7 +769,6 @@ packages:
/csstype@3.1.3:
resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
- dev: true
/damerau-levenshtein@1.0.8:
resolution: {integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==}
@@ -790,6 +846,11 @@ packages:
object-keys: 1.1.1
dev: true
+ /delayed-stream@1.0.0:
+ resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
+ engines: {node: '>=0.4.0'}
+ dev: false
+
/dequal@2.0.3:
resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==}
engines: {node: '>=6'}
@@ -1297,6 +1358,16 @@ packages:
resolution: {integrity: sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==}
dev: true
+ /follow-redirects@1.15.6:
+ resolution: {integrity: sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==}
+ engines: {node: '>=4.0'}
+ peerDependencies:
+ debug: '*'
+ peerDependenciesMeta:
+ debug:
+ optional: true
+ dev: false
+
/for-each@0.3.3:
resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==}
dependencies:
@@ -1310,6 +1381,15 @@ packages:
cross-spawn: 7.0.3
signal-exit: 4.1.0
+ /form-data@4.0.0:
+ resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==}
+ engines: {node: '>= 6'}
+ dependencies:
+ asynckit: 0.4.0
+ combined-stream: 1.0.8
+ mime-types: 2.1.35
+ dev: false
+
/fs.realpath@1.0.0:
resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==}
dev: true
@@ -1848,6 +1928,18 @@ packages:
braces: 3.0.2
picomatch: 2.3.1
+ /mime-db@1.52.0:
+ resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==}
+ engines: {node: '>= 0.6'}
+ dev: false
+
+ /mime-types@2.1.35:
+ resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==}
+ engines: {node: '>= 0.6'}
+ dependencies:
+ mime-db: 1.52.0
+ dev: false
+
/minimatch@3.1.2:
resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==}
dependencies:
@@ -2201,6 +2293,10 @@ packages:
react-is: 16.13.1
dev: true
+ /proxy-from-env@1.1.0:
+ resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==}
+ dev: false
+
/punycode@2.3.1:
resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
engines: {node: '>=6'}
@@ -2223,6 +2319,17 @@ packages:
resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==}
dev: true
+ /react-toastify@10.0.5(react-dom@18.2.0)(react@18.2.0):
+ resolution: {integrity: sha512-mNKt2jBXJg4O7pSdbNUfDdTsK9FIdikfsIE/yUCxbAEXl4HMyJaivrVFcn3Elvt5xvCQYhUZm+hqTIu1UXM3Pw==}
+ peerDependencies:
+ react: '>=18'
+ react-dom: '>=18'
+ dependencies:
+ clsx: 2.1.0
+ react: 18.2.0
+ react-dom: 18.2.0(react@18.2.0)
+ dev: false
+
/react@18.2.0:
resolution: {integrity: sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==}
engines: {node: '>=0.10.0'}
diff --git a/src/app/layout.tsx b/src/app/layout.tsx
index ac5cda5..8ee34e3 100644
--- a/src/app/layout.tsx
+++ b/src/app/layout.tsx
@@ -1,7 +1,9 @@
import Container from "@/components/container";
import ThemeProvider from "@/components/theme-provider";
-import { Inter } from "next/font/google";
+import { ToastContainer } from "react-toastify";
+import { Inter } from "next/font/google";
+import "react-toastify/dist/ReactToastify.css";
import "./globals.css";
const inter = Inter({ subsets: ["latin"] });
@@ -17,6 +19,7 @@ export default function RootLayout({
+
{children}
diff --git a/src/app/page.tsx b/src/app/page.tsx
index cfdd834..ae2e159 100644
--- a/src/app/page.tsx
+++ b/src/app/page.tsx
@@ -13,12 +13,15 @@ const buttons: Button[] = [
export default function Home() {
return (
-
Minecraft Utilities
-
- We provide a convenient API for Minecraft, simplifying the usage for players and developers.
-
+
Minecraft Utilities
+
+
Minecraft Utilities offers you many endpoints to get information about a minecraft server or a player.
+
+ You can use this information on your minecraft server or website. We offer you a simple and easy to use API.
+
+
-
+
{buttons.map((button, index) => {
return (
+
+
Search for a Player
+
You can enter a players uuid or username to get information about the player.
+
+
+
+ );
+}
diff --git a/src/lib/utils.ts b/src/common/utils.ts
similarity index 100%
rename from src/lib/utils.ts
rename to src/common/utils.ts
diff --git a/src/common/web-request.ts b/src/common/web-request.ts
new file mode 100644
index 0000000..6401f7f
--- /dev/null
+++ b/src/common/web-request.ts
@@ -0,0 +1,18 @@
+import { API_ENDPOINT } from "@/consts";
+import axios from "axios";
+
+export default class WebRequest {
+ private API_ENDPOINT: string = API_ENDPOINT;
+
+ /**
+ * Gets a response from the server
+ *
+ * @param endpoint the endpoint to send the request to
+ * @returns the response from the server
+ */
+ public static get(endpoint: string) {
+ return axios.get(API_ENDPOINT + endpoint, {
+ validateStatus: () => true, // Do not throw an error on non-200 status codes
+ });
+ }
+}
diff --git a/src/components/player-search.tsx b/src/components/player-search.tsx
new file mode 100644
index 0000000..fcba5c0
--- /dev/null
+++ b/src/components/player-search.tsx
@@ -0,0 +1,77 @@
+"use client";
+
+import WebRequest from "@/common/web-request";
+import Image from "next/image";
+import Link from "next/link";
+import { useState } from "react";
+import { toast } from "react-toastify";
+import { Button } from "./ui/button";
+import { Input } from "./ui/input";
+
+export default function PlayerSearch() {
+ const [playerId, setPlayerId] = useState
(null);
+ const [player, setPlayer] = useState(null);
+
+ const handleSearch = () => {
+ if (playerId === null || playerId.length <= 0) {
+ toast.error("Please enter a player ID");
+ return;
+ }
+ searchPlayer(playerId);
+ };
+
+ /**
+ * Searches for a player by their id
+ *
+ * @param playerId the player's id
+ */
+ const searchPlayer = async (playerId: string) => {
+ const response = await WebRequest.get("/player/" + playerId);
+ if (response.status !== 200) {
+ toast.error("Player not found");
+ return;
+ }
+ const { cache, player } = response.data;
+ setPlayer(player);
+ console.log(player);
+ };
+
+ return (
+
+
+ setPlayerId(e.target.value)} />
+
+
+
+ {player && (
+
+
+
+
+
+ Unique ID: {player.uniqueId}
+
+
+ Name: {player.username}
+
+
+
Skin Parts
+
+ {Object.keys(player.skin.parts).map((part: any, index: number) => {
+ return (
+
+
+ {part}
+
+
+ );
+ })}
+
+
+
+
+
+ )}
+
+ );
+}
diff --git a/src/components/ui/button.tsx b/src/components/ui/button.tsx
new file mode 100644
index 0000000..6df88c6
--- /dev/null
+++ b/src/components/ui/button.tsx
@@ -0,0 +1,47 @@
+import { Slot } from "@radix-ui/react-slot";
+import { cva, type VariantProps } from "class-variance-authority";
+import * as React from "react";
+
+import { cn } from "@/common/utils";
+
+const buttonVariants = cva(
+ "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
+ {
+ variants: {
+ variant: {
+ default: "bg-primary text-primary-foreground hover:bg-primary/90",
+ destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
+ outline: "border border-input bg-background hover:bg-accent hover:text-accent-foreground",
+ secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
+ ghost: "hover:bg-accent hover:text-accent-foreground",
+ link: "text-primary underline-offset-4 hover:underline",
+ },
+ size: {
+ default: "h-10 px-4 py-2",
+ sm: "h-9 rounded-md px-3",
+ lg: "h-11 rounded-md px-8",
+ icon: "h-10 w-10",
+ },
+ },
+ defaultVariants: {
+ variant: "default",
+ size: "default",
+ },
+ }
+);
+
+export interface ButtonProps
+ extends React.ButtonHTMLAttributes,
+ VariantProps {
+ asChild?: boolean;
+}
+
+const Button = React.forwardRef(
+ ({ className, variant, size, asChild = false, ...props }, ref) => {
+ const Comp = asChild ? Slot : "button";
+ return ;
+ }
+);
+Button.displayName = "Button";
+
+export { Button, buttonVariants };
diff --git a/src/components/ui/input.tsx b/src/components/ui/input.tsx
new file mode 100644
index 0000000..5141afd
--- /dev/null
+++ b/src/components/ui/input.tsx
@@ -0,0 +1,22 @@
+import * as React from "react";
+
+import { cn } from "@/common/utils";
+
+export interface InputProps extends React.InputHTMLAttributes {}
+
+const Input = React.forwardRef(({ className, type, ...props }, ref) => {
+ return (
+
+ );
+});
+Input.displayName = "Input";
+
+export { Input };
diff --git a/src/consts.ts b/src/consts.ts
new file mode 100644
index 0000000..81b4074
--- /dev/null
+++ b/src/consts.ts
@@ -0,0 +1 @@
+export const API_ENDPOINT = "https://api.mcutils.xyz";