Files
Website/src/components/admin/pages/Perms.svelte

204 lines
8.4 KiB
Svelte

<!--
- This file is a part of the SteamWar software.
-
- Copyright (C) 2023 SteamWar.de-Serverteam
-
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU Affero General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU Affero General Public License for more details.
-
- You should have received a copy of the GNU Affero General Public License
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<script lang="ts">
import { run, preventDefault } from "svelte/legacy";
import { Button, Card, Checkbox, Input, Label, Navbar, NavBrand, Radio, Spinner } from "flowbite-svelte";
import { ArrowLeftOutline } from "flowbite-svelte-icons";
import { capitalize } from "../util.ts";
import { permsRepo } from "@repo/perms.ts";
import { me } from "@stores/me.ts";
import SWButton from "@components/styled/SWButton.svelte";
import SWModal from "@components/styled/SWModal.svelte";
import { userRepo } from "@repo/user.ts";
import { dataRepo } from "@repo/data.ts";
import type { Player } from "@type/data";
let search = $state("");
let playersList: Player[] = $state([]);
let debounceTimer: NodeJS.Timeout;
function fetchPlayers(searchTerm: string) {
clearTimeout(debounceTimer);
debounceTimer = setTimeout(async () => {
const res = await $dataRepo.queryPlayers(searchTerm || undefined, undefined, undefined, 100, 0, undefined, undefined);
playersList = res.players;
}, 300);
}
$effect(() => {
fetchPlayers(search);
});
let selectedPlayer: string | null = $state(null);
let selectedPlayerName: string = $state("");
let playerPerms = $state(loadPlayer(selectedPlayer));
let prefixEdit = $state("PREFIX_NONE");
let activePerms: string[] = $state([]);
let resetPasswordModal = $state(false);
let resetPassword = $state("");
let resetPasswordRepeat = $state("");
function loadPlayer(id: string | null) {
if (!id) {
return;
}
return $permsRepo.getPerms(id).then((value) => {
activePerms = value.perms;
prefixEdit = value.prefix.name;
return value;
});
}
function togglePerm(perm: string) {
return () => {
if (activePerms.includes(perm)) {
activePerms = activePerms.filter((value) => value !== perm);
} else {
activePerms = [...activePerms, perm];
}
};
}
function save() {
playerPerms!.then(async (perms) => {
if (perms.prefix.name != prefixEdit) {
await $permsRepo.setPrefix(selectedPlayer!, prefixEdit);
}
for (let value of activePerms) {
if (!perms.perms.includes(value)) {
await $permsRepo.addPerm(selectedPlayer!, value);
}
}
for (let value of perms.perms) {
if (!activePerms.includes(value)) {
await $permsRepo.removePerm(selectedPlayer!, value);
}
}
playerPerms = loadPlayer(selectedPlayer);
});
}
let permsFuture = $permsRepo.listPerms();
function resetPW() {
if (resetPassword === resetPasswordRepeat) {
$userRepo.setPassword(selectedPlayer!, resetPassword);
}
resetResetPassword();
}
function resetResetPassword() {
resetPassword = "";
resetPasswordRepeat = "";
resetPasswordModal = false;
}
run(() => {
playerPerms = loadPlayer(selectedPlayer);
});
</script>
<div class="flex flex-col h-screen overflow-hidden">
<Navbar>
{#snippet children({ hidden, toggle })}
<NavBrand href="#">
<ArrowLeftOutline></ArrowLeftOutline>
<span class="ml-4 self-center whitespace-nowrap text-xl font-semibold dark:text-white"> Permissions </span>
</NavBrand>
{/snippet}
</Navbar>
<div class="p-4 flex-1 overflow-hidden">
<div class="grid md:grid-cols-3 grid-cols-1 h-full gap-8">
<Card class="h-full flex flex-col overflow-hidden !max-w-full">
<div class="border-b border-b-gray-600 pb-2">
<Label for="user_search" class="mb-2">Search Users...</Label>
<Input type="text" id="user_search" placeholder="Name..." bind:value={search} />
</div>
{#if playersList.length < 100}
<ul class="flex-1 overflow-scroll">
{#each playersList as player (player.uuid)}
<li
class="p-4 transition-colors hover:bg-gray-700 cursor-pointer"
class:text-orange-500={player.uuid === selectedPlayer}
onclick={preventDefault(() => {
selectedPlayer = player.uuid;
selectedPlayerName = player.name;
})}
>
{player.name}
</li>
{/each}
</ul>
{/if}
</Card>
<Card class="!max-w-full" style="grid-column: 2/4">
{#if selectedPlayer}
<h1 class="text-3xl">{selectedPlayerName}</h1>
{#await permsFuture}
<Spinner></Spinner>
{:then perms}
{#await playerPerms}
<Spinner></Spinner>
{:then player}
<h1>Prefix</h1>
{#each Object.entries(perms.prefixes) as [key, prefix]}
<Radio name="prefix" bind:group={prefixEdit} value={prefix.name}>{capitalize(prefix.name.substring(7).toLowerCase())}</Radio>
{/each}
<h1>Permissions</h1>
{#each perms.perms as perm}
<Checkbox checked={activePerms.includes(perm)} onclick={togglePerm(perm)}>{capitalize(perm.toLowerCase())}</Checkbox>
{/each}
<div class="mt-4">
<Button disabled={prefixEdit === (player?.prefix.name ?? "") && activePerms === (player?.perms ?? [])} onclick={save}>Save</Button>
{#if $me != null && $me.perms.includes("ADMINISTRATION")}
<Button onclick={() => (resetPasswordModal = true)}>Reset Password</Button>
<SWModal bind:open={resetPasswordModal} title="Reset Password">
<Label for="new_password">New Password</Label>
<Input type="password" id="new_password" placeholder="New Password" bind:value={resetPassword} />
<Label for="repeat_password">Repeat Password</Label>
<Input type="password" id="repeat_password" placeholder="Repeat Password" bind:value={resetPasswordRepeat} />
{#snippet footer()}
<Button class="ml-auto mr-4" onclick={resetResetPassword}>Cancel</Button>
<Button disabled={resetPassword === "" || resetPassword !== resetPasswordRepeat} onclick={resetPW}>Reset Password</Button>
{/snippet}
</SWModal>
{/if}
</div>
{:catch error}
<p>{error.toString()}</p>
{/await}
{:catch error}
<p>{error.toString()}</p>
{/await}
{/if}
</Card>
</div>
</div>
</div>