diff --git a/gui/app/components/Sidebar.tsx b/gui/app/components/Sidebar.tsx index 534066c..f337545 100644 --- a/gui/app/components/Sidebar.tsx +++ b/gui/app/components/Sidebar.tsx @@ -1,21 +1,26 @@ +"use client"; + +import { PACKS_ENDPOINT } from "@/api/ENDPOINTS"; +import { Pack } from "@/types/pack"; import { Sidebar, SidebarItem, SidebarItemGroup, SidebarItems, } from "flowbite-react"; -import { -// HiArrowSmRight, - HiChartPie, - HiPlusCircle -// HiInbox, -// HiShoppingBag, -// HiTable, -// HiUser, -// HiViewBoards, -} from "react-icons/hi"; +import { useEffect, useState } from "react"; +import { HiChartPie, HiPlusCircle } from "react-icons/hi"; export const Menu = () => { + const [packsData, setPacksData] = useState([]); + useEffect(() => { + async function _getPacksData() { + const res = await fetch(PACKS_ENDPOINT("getPacks")); + setPacksData(await res.json()); + } + _getPacksData(); + }, []); + return ( @@ -23,32 +28,20 @@ export const Menu = () => { Dashboard + {packsData && + packsData.map((pack) => { + return ( + +

{pack.title}

+

+ by {pack.author} +

+
+ ); + })} New mod pack - {/* - Kanban - - - Inbox - - - Users - - - Products - - - Sign In - - - Sign Up - */}
diff --git a/gui/app/pack/new/page.tsx b/gui/app/pack/new/page.tsx index 3a741a9..4ed230c 100644 --- a/gui/app/pack/new/page.tsx +++ b/gui/app/pack/new/page.tsx @@ -82,7 +82,7 @@ export default function PackNew() { } toast.update(tid, {render: data.message, type: "success", isLoading: false}) - router.push(`/pack/${data.id}`) + router.push(`/pack/?id=${data.id}`) } _submit(); diff --git a/gui/app/pack/page.tsx b/gui/app/pack/page.tsx new file mode 100644 index 0000000..fa64bf0 --- /dev/null +++ b/gui/app/pack/page.tsx @@ -0,0 +1,119 @@ +"use client"; + +import { PACK_ENDPOINT, PACKS_ENDPOINT } from "@/api/ENDPOINTS"; +import { Pack } from "@/types/pack"; +import { Button, Card, Spinner } from "flowbite-react"; +import { useSearchParams } from "next/navigation"; +import { useRouter } from "next/navigation"; +import { useEffect, useState } from "react"; + +import { HiDownload, HiTrash } from "react-icons/hi"; + +export default function PackPage() { + const [packData, setPackData] = useState(null); + const [packDataLoading, setPackDataLoading] = useState(true); + const router = useRouter(); + const id = useSearchParams().get("id") || ""; + + useEffect(() => { + async function _getPacksData() { + const res = await fetch(PACK_ENDPOINT("getPack", id)); + if (!res.ok) router.push("/404"); + setPackData(await res.json()); + setPackDataLoading(false); + } + if (id) { + _getPacksData(); + } else { + router.push("/404"); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const handleImageUpdate = (e: any) => { + if (!packData || !window) return; + + const file = e.target.files[0]; + const fileReader = new FileReader(); + fileReader.onloadend = () => { + const content = fileReader.result; + fetch(`${PACK_ENDPOINT("editPackImage", packData._id)}`, { + method: "POST", + body: JSON.stringify({ + image: content, + mimetype: file.type, + }), + headers: { + "content-type": "application/json", + accept: "application/json", + }, + }).then(() => location.reload()); + e.target.value = ""; + }; + fileReader.readAsDataURL(file); + }; + + function deletePack() { + if (!packData || !window) return; + + if (window.confirm(`Delete pack ${packData.title}?`)) { + fetch(`${PACKS_ENDPOINT("deletePack", packData._id)}`); + const ur = new URL(window.location.href) + ur.searchParams.delete("id") + ur.pathname = "/" + window.location.href = ur.href + } + } + + return ( +
+ {packDataLoading && ( +
+ +
+ )} + {packData && ( +
+ +
+
+

{packData.version}

+

{packData.modloader}

+
+
+ + handleImageUpdate(e)} + id="pack-icon" + className="hidden" + name="image" + type="file" + /> +
+

{packData.title}

+

by {packData.author}

+
+
+
+ + +
+
+
+
+ )} +
+ ); +} diff --git a/gui/app/page.tsx b/gui/app/page.tsx index 4f114c9..efbff14 100644 --- a/gui/app/page.tsx +++ b/gui/app/page.tsx @@ -30,7 +30,7 @@ export default function Home() {
{packsData.map((pack) => { return ( - +
{/* eslint-disable-next-line @next/next/no-img-element */} diff --git a/src/api/pack.py b/src/api/pack.py index c77b0f5..4ac03a8 100644 --- a/src/api/pack.py +++ b/src/api/pack.py @@ -1,12 +1,25 @@ import os import re from . import apiPack -from flask import request, jsonify, send_file, redirect, url_for +from flask import request, jsonify, send_file, redirect, url_for, abort from config import PACKS_FOLDER, IMG_ALLOWED_MIME from PIL import Image from io import BytesIO import base64 +import json +@apiPack.route("/", methods=["GET"]) +def getPack(id): + if not os.path.exists(f"{PACKS_FOLDER}/{id}/packfile.json"): + return jsonify({"status": "error", "message": "not found"}), 404 + + pack = {} + with open(f"{PACKS_FOLDER}/{id}/packfile.json") as fp: + pack = json.load(fp) + pack["_id"] = id + fp.close() + + return jsonify(pack) @apiPack.route("//image", methods=["GET"]) def getPackImage(id): diff --git a/src/api/packs.py b/src/api/packs.py index 8d788b3..cc2f5df 100644 --- a/src/api/packs.py +++ b/src/api/packs.py @@ -3,6 +3,7 @@ from . import apiPacks from flask import request, jsonify from config import PACKS_FOLDER import json +import shutil @apiPacks.route("/all", methods=["GET"]) @@ -59,3 +60,14 @@ def createPack(): "id": title, } ) + + +@apiPacks.route("//delete", methods=["GET"]) +def deletePack(id): + shutil.rmtree(f"{PACKS_FOLDER}/{id}") + return jsonify( + { + "status": "ok", + "message": f"pack deleted", + } + )