namespace tracking and creation
This commit is contained in:
parent
0941690f91
commit
0517e7c2e2
@ -120,6 +120,33 @@ router.route("/api/pack")
|
|||||||
return new Response(JSON.stringify({ packName }), { status: 200 });
|
return new Response(JSON.stringify({ packName }), { status: 200 });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
router.route("/api/pack/namespaces")
|
||||||
|
.get(async () => {
|
||||||
|
using store = new BearMetalStore();
|
||||||
|
|
||||||
|
const namespaces = Array.from(Deno.readDirSync(store.get("packlocation")))
|
||||||
|
.filter((dir) => dir.isDirectory).map((dir) => dir.name);
|
||||||
|
return new Response(JSON.stringify(namespaces), { status: 200 });
|
||||||
|
})
|
||||||
|
.post(async (req) => {
|
||||||
|
using store = new BearMetalStore();
|
||||||
|
|
||||||
|
const namespace = await req.text();
|
||||||
|
if (!namespace) {
|
||||||
|
return new Response("Namespace is required", { status: 400 });
|
||||||
|
}
|
||||||
|
|
||||||
|
const namespaceRx = /^[a-zA-Z0-9_\-]+$/;
|
||||||
|
if (!namespaceRx.test(namespace)) {
|
||||||
|
return new Response(
|
||||||
|
"Namespace must only contain letters, numbers, underscores, and dashes",
|
||||||
|
{ status: 400 },
|
||||||
|
);
|
||||||
|
}
|
||||||
|
await ensureDir(store.get("packlocation") + "/" + namespace);
|
||||||
|
return new Response(null, { status: 200 });
|
||||||
|
});
|
||||||
|
|
||||||
router.route("/api/packs")
|
router.route("/api/packs")
|
||||||
.get(async () => {
|
.get(async () => {
|
||||||
using store = new BearMetalStore();
|
using store = new BearMetalStore();
|
||||||
|
@ -7,7 +7,7 @@ export function App() {
|
|||||||
<BrowserRouter>
|
<BrowserRouter>
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route path="/" Component={Home} />
|
<Route path="/" Component={Home} />
|
||||||
<Route path="/editor" Component={Editor} />
|
<Route path="/editor/*" Component={Editor} />
|
||||||
</Routes>
|
</Routes>
|
||||||
</BrowserRouter>
|
</BrowserRouter>
|
||||||
);
|
);
|
||||||
|
3
src/atoms/namespace.ts
Normal file
3
src/atoms/namespace.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
import { atom } from "jotai";
|
||||||
|
|
||||||
|
export const namespaceAtom = atom<string>("");
|
62
src/components/editor/namespaceModal.tsx
Normal file
62
src/components/editor/namespaceModal.tsx
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
import { useState } from "preact/hooks";
|
||||||
|
import { fetchJson } from "../../util/fetchJson.ts";
|
||||||
|
import { Loader } from "../loader.tsx";
|
||||||
|
import { Modal } from "../modal.tsx";
|
||||||
|
import useSwr from "swr";
|
||||||
|
|
||||||
|
export const NamespaceModal = ({ close }: { close: () => void }) => {
|
||||||
|
const { data: namespaces, isLoading } = useSwr<string[]>(
|
||||||
|
"/api/pack/namespaces",
|
||||||
|
fetchJson,
|
||||||
|
);
|
||||||
|
const [namespace, setNamespace] = useState("");
|
||||||
|
const [invalid, setInvalid] = useState("");
|
||||||
|
|
||||||
|
const createNamespace = async (ns?: string) => {
|
||||||
|
const res = await fetch("/api/pack/namespaces", {
|
||||||
|
method: "POST",
|
||||||
|
body: ns ?? namespace,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (res.status === 200) {
|
||||||
|
return close();
|
||||||
|
}
|
||||||
|
|
||||||
|
setInvalid(await res.text());
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal>
|
||||||
|
{isLoading
|
||||||
|
? <Loader msg="Checking existing namespaces..." />
|
||||||
|
: (
|
||||||
|
<div class="flex flex-col gap-2">
|
||||||
|
<p>Create a new namespace</p>
|
||||||
|
{!namespaces?.includes("minecraft") &&
|
||||||
|
(
|
||||||
|
<button
|
||||||
|
class="w-full"
|
||||||
|
onClick={() => createNamespace("minecraft")}
|
||||||
|
>
|
||||||
|
Create default minecraft namespace
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
<form
|
||||||
|
onSubmit={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
createNamespace();
|
||||||
|
}}
|
||||||
|
class="flex gap-2"
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
value={namespace}
|
||||||
|
onInput={(e) => setNamespace((e.target as any).value)}
|
||||||
|
/>
|
||||||
|
<button type="submit">Create</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
};
|
3
src/components/editor/selector.tsx
Normal file
3
src/components/editor/selector.tsx
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export const Selector = () => {
|
||||||
|
return <div>Selector</div>;
|
||||||
|
};
|
@ -1,4 +1,4 @@
|
|||||||
import { classList } from "../classes.ts";
|
import { classList } from "../util/classes.ts";
|
||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
msg?: string;
|
msg?: string;
|
||||||
|
2
src/util/fetchJson.ts
Normal file
2
src/util/fetchJson.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export const fetchJson = (url: string, init?: RequestInit) =>
|
||||||
|
fetch(url, init).then((res) => res.json());
|
@ -3,11 +3,23 @@ import { Modal } from "../components/modal.tsx";
|
|||||||
import { LabelledHr } from "../components/labelledHr.tsx";
|
import { LabelledHr } from "../components/labelledHr.tsx";
|
||||||
import { PacksList } from "../components/packsList.tsx";
|
import { PacksList } from "../components/packsList.tsx";
|
||||||
import { PackInfo } from "../components/packInfo.tsx";
|
import { PackInfo } from "../components/packInfo.tsx";
|
||||||
|
import { useAtom } from "jotai";
|
||||||
|
import { namespaceAtom } from "../atoms/namespace.ts";
|
||||||
|
import { Route, Routes } from "react-router-dom";
|
||||||
|
import { Selector } from "../components/editor/selector.tsx";
|
||||||
|
import { NamespaceModal } from "../components/editor/namespaceModal.tsx";
|
||||||
|
|
||||||
export const Editor = () => {
|
export const Editor = () => {
|
||||||
const [packName, setPackName] = useState("");
|
const [packName, setPackName] = useState("");
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
const [namespaces, setNamespaces] = useState<string[]>([]);
|
const [namespaces, setNamespaces] = useState<string[]>([]);
|
||||||
|
const [namespace, setNamespace] = useAtom(namespaceAtom);
|
||||||
|
|
||||||
|
const fetchNamespaces = async () => {
|
||||||
|
const res = await fetch("/api/pack/namespaces");
|
||||||
|
const json = await res.json();
|
||||||
|
setNamespaces(json);
|
||||||
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
document.title = "BearMetal Packer";
|
document.title = "BearMetal Packer";
|
||||||
@ -15,6 +27,7 @@ export const Editor = () => {
|
|||||||
setPackName(json.packName);
|
setPackName(json.packName);
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
});
|
});
|
||||||
|
fetchNamespaces();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const setPackNameThing = async (event: SubmitEvent) => {
|
const setPackNameThing = async (event: SubmitEvent) => {
|
||||||
@ -56,9 +69,11 @@ export const Editor = () => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const [showNamespaceModal, setShowNamespaceModal] = useState(false);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div class="flex h-full">
|
<div class="flex h-full">
|
||||||
<div class="w-1/4 p-4 bg-primary-600">
|
<div class="w-1/4 p-4 bg-mixed-400">
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
<h1 class="text-2xl">BearMetalPacker</h1>
|
<h1 class="text-2xl">BearMetalPacker</h1>
|
||||||
<PackInfo packName={packName} />
|
<PackInfo packName={packName} />
|
||||||
@ -67,14 +82,39 @@ export const Editor = () => {
|
|||||||
<div>
|
<div>
|
||||||
<ul class="w-full">
|
<ul class="w-full">
|
||||||
{namespaces.map((namespace) => (
|
{namespaces.map((namespace) => (
|
||||||
<li class="cursor-pointer even:bg-black/5" // onClick={() => setNamespace(namespace)}
|
<li
|
||||||
|
class="text-lg cursor-pointer even:bg-black/5"
|
||||||
|
onClick={() => setNamespace(namespace)}
|
||||||
>
|
>
|
||||||
{namespace}
|
{namespace}
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
|
<li class="mt-4">
|
||||||
|
<button onClick={() => setShowNamespaceModal(true)}>
|
||||||
|
New Namespace
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
{showNamespaceModal && (
|
||||||
|
<NamespaceModal
|
||||||
|
close={() => {
|
||||||
|
setShowNamespaceModal(false);
|
||||||
|
fetchNamespaces();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
|
{!namespace ? <div>No namespace set</div> : (
|
||||||
|
<div>
|
||||||
|
<h3 class="text-lg">Namespace: {namespace}</h3>
|
||||||
|
<Routes>
|
||||||
|
<Route path="/editor" Component={Selector} />
|
||||||
|
</Routes>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user