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>{report.reporter.username}</td>
<td>{report.reported.username}</td>
<td class="flex">
{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
>{new Intl.DateTimeFormat('de-DE', {
year: 'numeric',

View File

@ -1,6 +1,6 @@
<script lang="ts">
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 Select from '$lib/components/Input/Select.svelte';
import { env } from '$env/dynamic/public';
@ -8,41 +8,11 @@
import { buttonTriggeredRequest } from '$lib/components/utils';
import { browser } from '$app/environment';
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;
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 currentPageUsersRequest: Promise<void> = new Promise((resolve) => resolve());
let usersPerPage = 50;
@ -70,12 +40,19 @@
});
}
async function sortUsers(key: string, reverse: boolean) {
const multiplyValue = reverse ? -1 : 1;
$: fetchPageUsers(userPage, userFilter);
currentPageUsers.sort((entryA, entryB) => {
const a = entryA[key];
const b = entryB[key];
let sortKey: string | null = null;
let sortAsc = false;
$: 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) {
case 'number':
@ -86,10 +63,6 @@
return (a - b) * multiplyValue;
}
});
currentPageUsers = currentPageUsers;
}
$: fetchPageUsers(userPage, userFilter);
async function updateUser(user: typeof User.prototype.dataValues) {
const response = await fetch(`${env.PUBLIC_BASE_PATH}/admin/users`, {
@ -126,29 +99,19 @@
<div class="h-full overflow-scroll" bind:this={userTableContainerElement}>
<table class="table table-auto">
<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 />
{#each headers as header}
<th>
<button
class="flex items-center"
on:click={() => {
sortUsers(header.key, (ascHeader = ascHeader == header ? null : header));
}}
>
{header.name}
<span class="ml-1">
<IconSolid
name={ascHeader === header ? 'chevron-up-solid' : 'chevron-down-solid'}
width="12"
height="12"
/>
</span>
</button>
</th>
{/each}
<SortableTh on:sort={(e) => { sortKey = 'firstname'; sortAsc = e.detail.asc }}>Vorname</SortableTh>
<SortableTh on:sort={(e) => { sortKey = 'lastname'; sortAsc = e.detail.asc }}>Nachname</SortableTh>
<SortableTh on:sort={(e) => { sortKey = 'birthday'; sortAsc = e.detail.asc }}>Geburtstag</SortableTh>
<SortableTh on:sort={(e) => { sortKey = 'telephone'; sortAsc = e.detail.asc }}>Telefon</SortableTh>
<SortableTh on:sort={(e) => { sortKey = 'username'; sortAsc = e.detail.asc }}>Username</SortableTh>
<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>
<th />
</tr>
</SortableTr>
</thead>
<tbody>
{#key currentPageUsersRequest}