Updates and more

This commit is contained in:
2023-11-12 22:43:42 +01:00
parent 7450ecdabb
commit 3889f28eb8
43 changed files with 5188 additions and 322 deletions

View File

@@ -0,0 +1,33 @@
<script lang="ts">
import {t} from "astro-i18n"
import {createEventDispatcher} from "svelte";
import {schemRepo} from "../repo/repo.ts";
import {Modal, Spinner} from "flowbite-svelte";
import {astroI18n} from "astro-i18n";
import moment from "moment/moment";
import {CheckSolid, XCircleOutline} from "flowbite-svelte-icons";
import SchematicInfoModal from "./SchematicInfoModal.svelte";
const dispatch = createEventDispatcher();
export let schematicId: number;
let schemInfo = getSchematicInfo(schematicId);
function getSchematicInfo(id: number) {
return $schemRepo.getSchematicInfo(id);
}
</script>
{#await schemInfo}
<Modal title="Loading" open on:close={() => dispatch("reset")}>
<Spinner />
</Modal>
{:then info}
<SchematicInfoModal {info} on:reset />
{:catch e}
<Modal title="Error" open on:close={() => dispatch("reset")}>
<p>{e.message}</p>
<button class="btn !ml-auto" slot="footer" on:click={() => dispatch("reset")}>Close</button>
</Modal>
{/await}

View File

@@ -0,0 +1,56 @@
<script lang="ts">
import {astroI18n, t} from "astro-i18n";
import moment from "moment";
import {CheckSolid, XCircleOutline} from "flowbite-svelte-icons";
import {Modal} from "flowbite-svelte";
import type {SchematicInfo} from "../types/schem.ts";
import {createEventDispatcher} from "svelte";
import {schemRepo} from "../repo/repo.ts";
const dispatch = createEventDispatcher();
export let info: SchematicInfo;
async function download() {
const code = await $schemRepo.createDownload(info.schem.id);
window.open(import.meta.env.PUBLIC_API_SERVER + "/download/" + code.code, "_blank")
dispatch("reset")
}
</script>
<Modal title={info.schem.name} autoclose open on:close={() => dispatch("reset")}>
<p>{t("dashboard.schematic.info.path", {path: info.path})}</p>
<p class="flex !mt-0">
{t("dashboard.schematic.info.replaceColor")}
{#if info.schem.replaceColor}
<CheckSolid class="text-green-500 ml-2" />
{:else}
<XCircleOutline class="text-red-500 ml-2" />
{/if}
</p>
<p class="flex !mt-0">
{t("dashboard.schematic.info.allowReplay")}
{#if info.schem.allowReplay}
<CheckSolid class="text-green-500 ml-2" />
{:else}
<XCircleOutline class="text-red-500 ml-2" />
{/if}
</p>
<p class="!mt-0">{t("dashboard.schematic.info.type", {type: info.schem.type ?? t("dashboard.schematic.dir")})}</p>
<p class="!mt-0">{t("dashboard.schematic.info.updated", {updated: new Intl.DateTimeFormat(astroI18n.locale, {
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
day: "2-digit",
month: "2-digit",
year: "numeric"
}).format(moment(info.schem.lastUpdate).utc(false).toDate())})}</p>
<p class="!mt-0">{t("dashboard.schematic.info.item", {item: info.schem.item ?? (info.schem.type == null ? "CHEST" : "CAULDRON_ITEM")})}</p>
{#if info.members.length !== 0}
<p class="!mt-0">{t("dashboard.schematic.info.members", {members: info.members.join(", ")})}</p>
{/if}
<svelte:fragment slot="footer">
<button class="btn !ml-auto" on:click={download}>{t("dashboard.schematic.info.btn.download")}</button>
<button class="btn" on:click={() => dispatch("reset")}>{t("dashboard.schematic.info.btn.close")}</button>
</svelte:fragment>
</Modal>

View File

@@ -0,0 +1,115 @@
<script lang="ts">
import {t} from "astro-i18n";
import {ChevronDoubleRightOutline, FolderOutline, HomeOutline, InfoCircleOutline} from "flowbite-svelte-icons";
import SchematicListTile from "./SchematicListTile.svelte";
import {Breadcrumb, BreadcrumbItem, Tooltip} from "flowbite-svelte";
import {createEventDispatcher} from "svelte";
import type {SchematicList} from "../types/schem.ts";
import SchematicInfo from "./SchematicInfo.svelte";
import UploadModal from "./UploadModal.svelte";
const dispatch = createEventDispatcher();
export let schematics: SchematicList;
let uploadOpen = false;
let infoModalId: number | null = null;
function schemListClick(isDir: boolean, id: number) {
if (isDir) {
return () => dispatch("to", {id})
} else {
return () => infoModalId = id
}
}
</script>
<div class="flex justify-between">
<Breadcrumb navClass="py-4">
<BreadcrumbItem home>
<svelte:fragment slot="icon">
<HomeOutline class="w-6 h-6 mx-2 dark:text-white" />
</svelte:fragment>
<span on:click={() => dispatch("reset")} class="hover:underline hover:cursor-pointer text-2xl">{t("dashboard.schematic.home")}</span>
</BreadcrumbItem>
{#each schematics.breadcrumbs as bread}
<BreadcrumbItem>
<svelte:fragment slot="icon">
<ChevronDoubleRightOutline class="w-4 h-4 mx-2 dark:text-white" />
</svelte:fragment>
<span on:click={() => dispatch("to", {id: bread.id})} class="hover:underline hover:cursor-pointer text-2xl">{bread.name}</span>
</BreadcrumbItem>
{/each}
</Breadcrumb>
<div class="flex flex-col justify-center">
<button class="btn" on:click={() => uploadOpen = true}>
{t("dashboard.schematic.upload")}
</button>
</div>
</div>
<table>
<tbody>
<tr class="!cursor-auto">
<th>{t("dashboard.schematic.head.type")}</th>
<th>{t("dashboard.schematic.head.name")}</th>
<th class="hidden sm:table-cell">{t("dashboard.schematic.head.owner")}</th>
<th class="hidden sm:table-cell"></th>
<th class="hidden md:table-cell">{t("dashboard.schematic.head.updated")}</th>
<th>
<InfoCircleOutline />
<Tooltip>
<span>{t("dashboard.schematic.head.replaceColor")}</span>
</Tooltip>
</th>
<th>
<InfoCircleOutline />
<Tooltip>
<span>{t("dashboard.schematic.head.allowReplay")}</span>
</Tooltip>
</th>
</tr>
{#if schematics.breadcrumbs.length !== 0}
<tr on:click|preventDefault={() => {
if (schematics.breadcrumbs.length === 1) {
dispatch("reset")
} else {
dispatch("to", {id: schematics.breadcrumbs[schematics.breadcrumbs.length - 2].id})
}
}}>
<th>
<FolderOutline />
</th>
<th>../</th>
<th class="hidden sm:table-cell"></th>
<th class="hidden sm:table-cell">{t("dashboard.schematic.dir")}</th>
<th class="hidden md:table-cell"></th>
<th></th>
<th></th>
</tr>
{/if}
{#each schematics.schematics as schem}
<SchematicListTile schem={schem} players={schematics.players} on:click={schemListClick(schem.type == null, schem.id)} />
{/each}
</tbody>
</table>
<UploadModal bind:open={uploadOpen} on:refresh />
{#if infoModalId !== null}
<SchematicInfo schematicId={infoModalId} on:reset={() => infoModalId = null} />
{/if}
<style lang="scss">
table {
@apply w-full;
}
tr {
@apply transition-colors cursor-pointer border-b
dark:hover:bg-gray-800 hover:bg-gray-300;
}
th {
@apply text-left py-4 md:px-2;
}
</style>

View File

@@ -0,0 +1,58 @@
<script lang="ts">
import {astroI18n, t} from "astro-i18n";
import moment from "moment/moment.js";
import {CheckSolid, FileOutline, FolderOutline, XCircleOutline} from "flowbite-svelte-icons";
import type {Schematic} from "../types/schem.ts";
import type {Player} from "../types/data.ts";
export let schem: Schematic;
export let players: Record<number, Player>;
</script>
<tr on:click|preventDefault>
<th>
{#if schem.type == null}
<FolderOutline />
{:else}
<FileOutline />
{/if}
</th>
<th>
{schem.name}{#if schem.type == null}/{/if}
</th>
<th class="hidden sm:table-cell">{players[schem.owner].name}</th>
<th class="hidden sm:table-cell">{schem.type ?? t("dashboard.schematic.dir")}</th>
<th class="hidden md:table-cell">{new Intl.DateTimeFormat(astroI18n.locale, {
hour: "2-digit",
minute: "2-digit",
second: "2-digit",
day: "2-digit",
month: "2-digit",
year: "numeric"
}).format(moment(schem.lastUpdate).utc(false).toDate())}</th>
<th>
{#if schem.replaceColor}
<CheckSolid class="text-green-500" />
{:else}
<XCircleOutline class="text-red-500" />
{/if}
</th>
<th>
{#if schem.allowReplay}
<CheckSolid class="text-green-500" />
{:else}
<XCircleOutline class="text-red-500" />
{/if}
</th>
</tr>
<style lang="scss">
tr {
@apply transition-colors cursor-pointer border-b
dark:hover:bg-gray-800 hover:bg-gray-300;
}
th {
@apply text-left py-4 md:px-2;
}
</style>

View File

@@ -0,0 +1,47 @@
<script lang="ts">
import {Modal} from "flowbite-svelte";
import {schemRepo} from "../repo/repo.js";
import {createEventDispatcher} from "svelte";
const dispatch = createEventDispatcher();
export let open = false;
async function upload() {
if (uploadFile == null) {
return
}
let file = uploadFile[0];
let name = file.name;
let type = name.split(".").pop();
if (type !== "schem" && type !== "schematic") {
return
}
let content = await file.arrayBuffer();
// @ts-ignore
let b64 = btoa(String.fromCharCode.apply(null, new Uint8Array(content)));
await $schemRepo.uploadSchematic(name, b64);
open = false;
uploadFile = null;
dispatch("reset")
}
let uploadFile: File[] | null = null;
</script>
<Modal title="Upload Schematic" bind:open autoclose outsideclose>
<form>
<input type="file" bind:files={uploadFile} />
</form>
<svelte:fragment slot="footer">
<button class="btn !ml-auto" on:click={upload}>Upload</button>
<button class="btn btn-gray" on:click={() => open = false}>Close</button>
</svelte:fragment>
</Modal>

View File

@@ -0,0 +1,41 @@
<script lang="ts">
import {t} from "astro-i18n";
import type {Player} from "../types/data.ts";
import {tokenStore} from "../repo/repo.ts";
import {l} from "../../util/util.ts";
export let user: Player;
function logout() {
tokenStore.set("")
window.location.href = l("/login")
}
</script>
<div class="flex mb-4 flex-col md:flex-row">
<div>
<div class="bg-zinc-50 border-gray-100 py-24 px-12 border-2 m-2 transition duration-300 ease-in-out rounded-xl shadow-lg hidden md:block
hover:scale-105 hover:shadow-2xl
dark:bg-neutral-900 dark:border-gray-800 dark:text-white">
<figure>
<figcaption class="text-center mb-4 text-2xl">{user.name}</figcaption>
<img src={`https://visage.surgeplay.com/bust/150/${user.uuid}`} class="transition duration-300 ease-in-out hover:scale-110 hover:drop-shadow-2xl" alt={user.name + "s bust"} width="150" height="150" />
</figure>
</div>
<div class="flex flex-wrap">
<button class="btn mt-2" on:click={logout}>{t("dashboard.buttons.logout")}</button>
{#if user.perms.includes("MODERATION")}
<a class="btn w-fit mt-2" href="/admin">{t("dashboard.buttons.admin")}</a>
{/if}
</div>
</div>
<div>
<h1 class="text-4xl font-bold">{t("dashboard.title", {name: user.name})}</h1>
<p>{t("dashboard.rank", {rank: t("home.prefix." + user.prefix)})}</p>
<p>{t("dashboard.permissions")}</p>
<ul>
{#each user.perms as permission}
<li class="list-disc ml-6">{permission}</li>
{/each}
</ul>
</div>
</div>