Added listing of files and directories
This commit is contained in:
4
docker-dev.sh
Normal file
4
docker-dev.sh
Normal file
@@ -0,0 +1,4 @@
|
||||
|
||||
docker compose down &
|
||||
docker build -t jeopardy .
|
||||
docker compose up -d
|
||||
@@ -12,7 +12,8 @@
|
||||
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
||||
"format": "prettier --write .",
|
||||
"lint": "prettier --check . && eslint .",
|
||||
"docker-build": "docker build -t jeopardy ."
|
||||
"docker-build": "docker build -t jeopardy .",
|
||||
"docker-dev": "./docker-dev.sh"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/compat": "^1.2.5",
|
||||
|
||||
@@ -9,7 +9,10 @@
|
||||
cancelFn?: () => void;
|
||||
okFn: () => Promise<boolean>;
|
||||
oncloseFn?: () => void;
|
||||
okButtonText?: string;
|
||||
[key: string]: unknown;
|
||||
width?: string;
|
||||
height?: string;
|
||||
}
|
||||
|
||||
let {
|
||||
@@ -19,7 +22,10 @@
|
||||
cancelFn,
|
||||
okFn,
|
||||
oncloseFn,
|
||||
actionButtons
|
||||
actionButtons,
|
||||
okButtonText = "Ok",
|
||||
width,
|
||||
height
|
||||
}: Props = $props();
|
||||
|
||||
let dialog: HTMLDialogElement | undefined = $state(); // HTMLDialogElement
|
||||
@@ -40,10 +46,15 @@
|
||||
if (e.target === dialog) dialog.close();
|
||||
}}
|
||||
class="rounded-md"
|
||||
style:width
|
||||
style:height
|
||||
>
|
||||
<div class="flex flex-col gap-4 p-4">
|
||||
<div class="flex h-full flex-col gap-4 p-4">
|
||||
{@render header?.()}
|
||||
{@render children?.()}
|
||||
<div class="overflow-y-auto">
|
||||
{@render children?.()}
|
||||
</div>
|
||||
<div class="grow"></div>
|
||||
<!-- svelte-ignore a11y_autofocus -->
|
||||
<div class="flex justify-end gap-4">
|
||||
{@render actionButtons?.()}
|
||||
@@ -62,7 +73,7 @@
|
||||
dialog?.close();
|
||||
}
|
||||
}}
|
||||
class="btn min-w-[64px]">Ok</button
|
||||
class="btn min-w-[64px]">{okButtonText}</button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -2,15 +2,28 @@
|
||||
import { env } from "$env/dynamic/public";
|
||||
import axios from "axios";
|
||||
import Modal from "./Modal.svelte";
|
||||
import { url } from "./util";
|
||||
import { isDir, isRessource, type Directory, type Ressource } from "./Types";
|
||||
import { onMount } from "svelte";
|
||||
|
||||
interface Props {
|
||||
show: boolean;
|
||||
ok?: (res: Ressource) => void;
|
||||
}
|
||||
|
||||
let { show = $bindable(false) }: Props = $props();
|
||||
let { show = $bindable(false), ok = (res) => {} }: Props = $props();
|
||||
|
||||
let file: File | null = null;
|
||||
let path: string = "/";
|
||||
let path: string = $state("/");
|
||||
|
||||
let fetchingRessources = $state(false);
|
||||
let ressources: (Ressource | Directory)[] = $state([]);
|
||||
let selectedRessource: Ressource | undefined = $state();
|
||||
|
||||
let error = $state("");
|
||||
|
||||
let showNewDir = $state(false);
|
||||
let newDirName = $state("");
|
||||
|
||||
function handleFileChange(event: Event) {
|
||||
const target = event.target as HTMLInputElement | null;
|
||||
@@ -27,33 +40,182 @@
|
||||
formData.append("path", path);
|
||||
formData.append("file", file);
|
||||
|
||||
axios.post(
|
||||
`${env.PUBLIC_JEOPARDY_SERVER_PROTOCOL}://${env.PUBLIC_JEOPARDY_SERVER}/upload`,
|
||||
formData,
|
||||
{
|
||||
withCredentials: true,
|
||||
headers: {
|
||||
"Content-Type": "multipart/form-data"
|
||||
},
|
||||
onUploadProgress: (event) => {
|
||||
console.log(event);
|
||||
axios
|
||||
.post(
|
||||
`${env.PUBLIC_JEOPARDY_SERVER_PROTOCOL}://${env.PUBLIC_JEOPARDY_SERVER}/upload`,
|
||||
formData,
|
||||
{
|
||||
withCredentials: true,
|
||||
headers: {
|
||||
"Content-Type": "multipart/form-data"
|
||||
},
|
||||
onUploadProgress: (event) => {
|
||||
console.log(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
)
|
||||
.then((response) => {
|
||||
if (response.status === 200) {
|
||||
fetchDirectory();
|
||||
} else {
|
||||
alert("Failed with status: " + response.status);
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
alert(err);
|
||||
});
|
||||
}
|
||||
|
||||
async function fetchDirectory() {
|
||||
fetchingRessources = true;
|
||||
return axios
|
||||
.post(
|
||||
url("/directory"),
|
||||
{ path },
|
||||
{
|
||||
withCredentials: true
|
||||
}
|
||||
)
|
||||
.then((response) => {
|
||||
if (response.status === 200) {
|
||||
ressources = response.data;
|
||||
ressources.sort((a, b) => {
|
||||
if (isDir(a) && !isDir(b)) return -1;
|
||||
if (!isDir(a) && isDir(b)) return 1;
|
||||
return a.name.localeCompare(b.name);
|
||||
});
|
||||
if (path !== "/") {
|
||||
ressources.unshift({
|
||||
isDir: true,
|
||||
name: ".."
|
||||
});
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch((e) => {
|
||||
console.log(e);
|
||||
})
|
||||
.finally(() => {
|
||||
fetchingRessources = false;
|
||||
});
|
||||
}
|
||||
|
||||
async function addDirectoryOk(): Promise<boolean> {
|
||||
error = "";
|
||||
if (newDirName.length <= 0) {
|
||||
error = "Gib einen Namen für den Ordner ein";
|
||||
return false;
|
||||
}
|
||||
|
||||
return axios
|
||||
.put(url("/directory"), { name: newDirName, path }, { withCredentials: true })
|
||||
.then((response) => {
|
||||
if (response.status === 200) {
|
||||
fetchDirectory();
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
error = "Etwas ist schief gelaufen";
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
function addDirectoryCancel() {
|
||||
error = "";
|
||||
newDirName = "";
|
||||
}
|
||||
|
||||
function ressourceClicked(res: Ressource | Directory) {
|
||||
if (isRessource(res)) {
|
||||
selectedRessource = res;
|
||||
} else if (isDir(res)) {
|
||||
if (res.name === "..") {
|
||||
let breadcumbs = path.split("/");
|
||||
breadcumbs.pop();
|
||||
path = breadcumbs.join("/");
|
||||
if (path.length <= 0) path = "/";
|
||||
} else {
|
||||
if (!path.endsWith("/")) {
|
||||
path += "/";
|
||||
}
|
||||
path += res.name;
|
||||
}
|
||||
fetchDirectory();
|
||||
}
|
||||
}
|
||||
|
||||
function cancel() {
|
||||
selectedRessource = undefined;
|
||||
}
|
||||
|
||||
$effect(() => {
|
||||
if (show) {
|
||||
fetchDirectory();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<Modal
|
||||
bind:showModal={show}
|
||||
okFn={async () => {
|
||||
console.log("ok");
|
||||
if (selectedRessource) ok({ ...selectedRessource });
|
||||
return true;
|
||||
}}
|
||||
cancelFn={cancel}
|
||||
okButtonText={selectedRessource !== undefined ? selectedRessource.name : "Ok"}
|
||||
width="90%"
|
||||
height="90%"
|
||||
>
|
||||
{#snippet header()}
|
||||
<h2 class="text-3xl">Ressourcenmanager</h2>
|
||||
<h2 class="text-3xl">Ressourcenmanager - {path}</h2>
|
||||
{/snippet}
|
||||
<div>Ressourcenmanager</div>
|
||||
<div>
|
||||
{#if fetchingRessources}
|
||||
Loading...
|
||||
{:else}
|
||||
<div class="flex flex-col gap-2">
|
||||
{#each ressources as ressource}
|
||||
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
||||
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
||||
<div
|
||||
class="flex grow cursor-pointer items-center justify-between rounded-sm border-1 border-solid p-1 hover:bg-gray-200"
|
||||
onclick={() => ressourceClicked(ressource)}
|
||||
class:bg-green-200={isRessource(ressource) &&
|
||||
ressource._id === selectedRessource?._id}
|
||||
>
|
||||
<div>
|
||||
{#if isDir(ressource)}
|
||||
<i class="fa-solid fa-folder"></i>
|
||||
{ressource.name}
|
||||
{:else}
|
||||
{#if ressource.mimetype.includes("image")}
|
||||
<i class="fa-solid fa-image"></i>
|
||||
{:else if ressource.mimetype.includes("audio")}
|
||||
<i class="fa-solid fa-music"></i>
|
||||
{:else}
|
||||
<i class="fa-solid fa-file"></i>
|
||||
{/if}
|
||||
{ressource.name}
|
||||
{/if}
|
||||
</div>
|
||||
<!-- svelte-ignore a11y_consider_explicit_label -->
|
||||
<button
|
||||
type="button"
|
||||
class="btn border-red-600 text-red-600"
|
||||
onclick={(event) => {
|
||||
event.stopPropagation();
|
||||
console.log("delete", ressource.name);
|
||||
}}><i class="fa-solid fa-trash"></i></button
|
||||
>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
{#snippet actionButtons()}
|
||||
<form
|
||||
action={`${env.PUBLIC_JEOPARDY_SERVER_PROTOCOL}://${env.PUBLIC_JEOPARDY_SERVER}/upload`}
|
||||
@@ -64,5 +226,36 @@
|
||||
<input class="btn" type="file" name="file" onchange={handleFileChange} />
|
||||
<input type="submit" value="Hochladen" class="btn" />
|
||||
</form>
|
||||
<button class="btn" type="button" onclick={() => (showNewDir = true)}>Neuer Ordner</button>
|
||||
{/snippet}
|
||||
</Modal>
|
||||
|
||||
<Modal bind:showModal={showNewDir} okFn={addDirectoryOk} cancelFn={addDirectoryCancel}>
|
||||
{#snippet header()}
|
||||
<h2 class="text-3xl">Neuer Ordner</h2>
|
||||
{/snippet}
|
||||
<div>
|
||||
<label for="directory" class="">Name</label>
|
||||
<div>
|
||||
<input
|
||||
type="text"
|
||||
name="directory"
|
||||
id="directory"
|
||||
class="borders mt-2 mb-2 w-full"
|
||||
bind:value={newDirName}
|
||||
/>
|
||||
</div>
|
||||
{#if error.length > 0}
|
||||
<div class="text-red-700">{error}</div>
|
||||
{/if}
|
||||
</div>
|
||||
</Modal>
|
||||
|
||||
<style>
|
||||
.borders {
|
||||
border: 1px solid black;
|
||||
border-radius: 5px;
|
||||
display: flex;
|
||||
padding: 2px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1 +1,24 @@
|
||||
export type VisitedQuestions = number[][];
|
||||
|
||||
export type Directory = {
|
||||
name: string;
|
||||
isDir: true;
|
||||
};
|
||||
|
||||
export type Ressource = {
|
||||
_id: string;
|
||||
fullpath: string;
|
||||
path: string;
|
||||
user: string;
|
||||
mimetype: string;
|
||||
name: string;
|
||||
filename: string;
|
||||
};
|
||||
|
||||
export function isDir(dir: Directory | Ressource): dir is Directory {
|
||||
return (dir as Directory).isDir === true;
|
||||
}
|
||||
|
||||
export function isRessource(ressource: Ressource | Directory): ressource is Ressource {
|
||||
return (ressource as Directory).isDir === undefined;
|
||||
}
|
||||
|
||||
5
src/lib/util.ts
Normal file
5
src/lib/util.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
import { env } from "$env/dynamic/public";
|
||||
|
||||
export function url(path: string) {
|
||||
return `${env.PUBLIC_JEOPARDY_SERVER_PROTOCOL}://${env.PUBLIC_JEOPARDY_SERVER}${path.startsWith("/") ? "" : "/"}${path}`;
|
||||
}
|
||||
@@ -77,7 +77,12 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<RessourceManager bind:show={showRessourceManager}></RessourceManager>
|
||||
<RessourceManager
|
||||
bind:show={showRessourceManager}
|
||||
ok={(res) => {
|
||||
console.log(res);
|
||||
}}
|
||||
></RessourceManager>
|
||||
|
||||
<style>
|
||||
.profile {
|
||||
|
||||
Reference in New Issue
Block a user