Compare commits

..

2 Commits

Author SHA1 Message Date
56aa3c2673 add option to filter reporter or reported by clicking on the magnifying glass next to their names on admin report page
All checks were successful
delpoy / build-and-deploy (push) Successful in 49s
2023-09-30 15:39:44 +02:00
9be57c1004 add generic components for table sorting 2023-09-30 15:15:48 +02:00
4 changed files with 93 additions and 65 deletions

View File

@ -0,0 +1,35 @@
<script lang="ts">
import { createEventDispatcher, getContext, onDestroy } from 'svelte';
import type { Writable } from 'svelte/store';
import { IconSolid } from 'svelte-heros-v2';
let id = crypto.randomUUID();
let asc = false;
let { ascHeader } = getContext('sortableTr') as { ascHeader: Writable<null | string> };
ascHeader.subscribe((v) => {
if (v !== id) asc = false;
});
let dispatch = createEventDispatcher();
onDestroy(() => {
if ($ascHeader === id) $ascHeader = null;
});
</script>
<th>
<button
class="flex flex-center"
on:click={() => {
dispatch('sort', { asc: (asc = !asc) });
$ascHeader = id;
}}
>
<span class="mr-1"><slot /></span>
<IconSolid
name={$ascHeader === id && asc ? 'chevron-up-solid' : 'chevron-down-solid'}
width="12"
height="12"
/>
</button>
</th>

View File

@ -0,0 +1,12 @@
<script lang="ts">
import { setContext } from 'svelte';
import { writable } from 'svelte/store';
setContext('sortableTr', {
ascHeader: writable(null)
});
</script>
<tr {...$$restProps}>
<slot />
</tr>

View File

@ -87,8 +87,26 @@
}} }}
> >
<td title={report.subject}><div class="overflow-scroll">{report.subject}</div></td> <td title={report.subject}><div class="overflow-scroll">{report.subject}</div></td>
<td>{report.reporter.username}</td> <td class="flex">
<td>{report.reported.username}</td> {report.reporter.username}
<button
class="pl-1"
title="Nach Ersteller filtern"
on:click|stopPropagation={() => (reportFilter.reporter = report.reporter.username)}
>
<IconOutline name="magnifying-glass-outline" width="14" height="14" />
</button>
</td>
<td>
{report.reported.username}
<button
class="pl-1"
title="Nach Reportetem Spieler filtern"
on:click|stopPropagation={() => (reportFilter.reported = report.reported.username)}
>
<IconOutline name="magnifying-glass-outline" width="14" height="14" />
</button>
</td>
<td <td
>{new Intl.DateTimeFormat('de-DE', { >{new Intl.DateTimeFormat('de-DE', {
year: 'numeric', year: 'numeric',

View File

@ -1,6 +1,6 @@
<script lang="ts"> <script lang="ts">
import type { PageData } from './$types'; import type { PageData } from './$types';
import { IconOutline, IconSolid } from 'svelte-heros-v2'; import { IconOutline } from 'svelte-heros-v2';
import Input from '$lib/components/Input/Input.svelte'; import Input from '$lib/components/Input/Input.svelte';
import Select from '$lib/components/Input/Select.svelte'; import Select from '$lib/components/Input/Select.svelte';
import { env } from '$env/dynamic/public'; import { env } from '$env/dynamic/public';
@ -8,41 +8,11 @@
import { buttonTriggeredRequest } from '$lib/components/utils'; import { buttonTriggeredRequest } from '$lib/components/utils';
import { browser } from '$app/environment'; import { browser } from '$app/environment';
import HeaderBar from './HeaderBar.svelte'; import HeaderBar from './HeaderBar.svelte';
import SortableTr from '$lib/components/Table/SortableTr.svelte';
import SortableTh from '$lib/components/Table/SortableTh.svelte';
export let data: PageData; export let data: PageData;
let headers = [
{
name: 'Vorname',
key: 'firstname',
asc: false
},
{
name: 'Nachname',
key: 'lastname',
asc: false
},
{ name: 'Geburtstag', key: 'birthday', asc: false, sort: (a, b) => a.birthday - b.birthday },
{ name: 'Telefon', key: 'telephone', asc: false, sort: (a, b) => a.telephone - b.telephone },
{
name: 'Username',
key: 'username',
asc: false
},
{
name: 'Minecraft Edition',
key: 'playertype',
asc: false
},
{
name: 'Passwort',
key: 'password',
asc: false
},
{ name: 'UUID', key: 'uuid', asc: false }
];
let ascHeader: (typeof headers)[0] | null = null;
let currentPageUsers: (typeof User.prototype.dataValues)[] = []; let currentPageUsers: (typeof User.prototype.dataValues)[] = [];
let currentPageUsersRequest: Promise<void> = new Promise((resolve) => resolve()); let currentPageUsersRequest: Promise<void> = new Promise((resolve) => resolve());
let usersPerPage = 50; let usersPerPage = 50;
@ -70,12 +40,19 @@
}); });
} }
async function sortUsers(key: string, reverse: boolean) { $: fetchPageUsers(userPage, userFilter);
const multiplyValue = reverse ? -1 : 1;
currentPageUsers.sort((entryA, entryB) => { let sortKey: string | null = null;
const a = entryA[key]; let sortAsc = false;
const b = entryB[key]; $: if (sortKey != null)
currentPageUsers = currentPageUsers.sort((entryA, entryB) => {
const multiplyValue = sortAsc ? -1 : 1;
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
const a = entryA[sortKey];
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
const b = entryB[sortKey];
switch (typeof a) { switch (typeof a) {
case 'number': case 'number':
@ -86,10 +63,6 @@
return (a - b) * multiplyValue; return (a - b) * multiplyValue;
} }
}); });
currentPageUsers = currentPageUsers;
}
$: fetchPageUsers(userPage, userFilter);
async function updateUser(user: typeof User.prototype.dataValues) { async function updateUser(user: typeof User.prototype.dataValues) {
const response = await fetch(`${env.PUBLIC_BASE_PATH}/admin/users`, { const response = await fetch(`${env.PUBLIC_BASE_PATH}/admin/users`, {
@ -126,29 +99,19 @@
<div class="h-full overflow-scroll" bind:this={userTableContainerElement}> <div class="h-full overflow-scroll" bind:this={userTableContainerElement}>
<table class="table table-auto"> <table class="table table-auto">
<thead> <thead>
<tr class="[&>th]:bg-base-100 [&>th]:z-[1] [&>th]:sticky [&>th]:top-0"> <!-- prettier-ignore -->
<SortableTr class="[&>th]:bg-base-100 [&>th]:z-[1] [&>th]:sticky [&>th]:top-0">
<th /> <th />
{#each headers as header} <SortableTh on:sort={(e) => { sortKey = 'firstname'; sortAsc = e.detail.asc }}>Vorname</SortableTh>
<th> <SortableTh on:sort={(e) => { sortKey = 'lastname'; sortAsc = e.detail.asc }}>Nachname</SortableTh>
<button <SortableTh on:sort={(e) => { sortKey = 'birthday'; sortAsc = e.detail.asc }}>Geburtstag</SortableTh>
class="flex items-center" <SortableTh on:sort={(e) => { sortKey = 'telephone'; sortAsc = e.detail.asc }}>Telefon</SortableTh>
on:click={() => { <SortableTh on:sort={(e) => { sortKey = 'username'; sortAsc = e.detail.asc }}>Username</SortableTh>
sortUsers(header.key, (ascHeader = ascHeader == header ? null : header)); <SortableTh on:sort={(e) => { sortKey = 'playertype'; sortAsc = e.detail.asc }}>Minecraft Edition</SortableTh>
}} <SortableTh on:sort={(e) => { sortKey = 'password'; sortAsc = e.detail.asc }}>Passwort</SortableTh>
> <SortableTh on:sort={(e) => { sortKey = 'uuid'; sortAsc = e.detail.asc }}>UUID</SortableTh>
{header.name}
<span class="ml-1">
<IconSolid
name={ascHeader === header ? 'chevron-up-solid' : 'chevron-down-solid'}
width="12"
height="12"
/>
</span>
</button>
</th>
{/each}
<th /> <th />
</tr> </SortableTr>
</thead> </thead>
<tbody> <tbody>
{#key currentPageUsersRequest} {#key currentPageUsersRequest}