update admin pagination
All checks were successful
delpoy / build-and-deploy (push) Successful in 1m16s
All checks were successful
delpoy / build-and-deploy (push) Successful in 1m16s
This commit is contained in:
parent
676bfc23d8
commit
bd33727aa6
@ -0,0 +1,51 @@
|
||||
<script lang="ts">
|
||||
import { onMount, tick } from 'svelte';
|
||||
|
||||
export let onUpdate: () => Promise<any> = Promise.resolve;
|
||||
|
||||
let bodyElem: HTMLTableSectionElement;
|
||||
|
||||
function intersectionViewer() {
|
||||
let updating = false;
|
||||
let intersectionElement =
|
||||
bodyElem.rows.item(bodyElem.rows.length - 5) || bodyElem.lastElementChild;
|
||||
|
||||
new IntersectionObserver(
|
||||
async (entries, observer) => {
|
||||
if (entries.filter((e) => e.isIntersecting).length === 0 || updating) return;
|
||||
|
||||
updating = true;
|
||||
|
||||
const rows = bodyElem.rows.length;
|
||||
|
||||
await onUpdate();
|
||||
await tick();
|
||||
observer.disconnect();
|
||||
|
||||
updating = false;
|
||||
|
||||
if (rows === bodyElem.rows.length) return;
|
||||
|
||||
intersectionViewer();
|
||||
},
|
||||
{ threshold: 1.0 }
|
||||
).observe(intersectionElement!);
|
||||
}
|
||||
|
||||
onMount(async () => {
|
||||
await onUpdate();
|
||||
await tick();
|
||||
|
||||
new MutationObserver((entries) => {
|
||||
if (entries.filter((e) => e.removedNodes.length > 0).length === 0) return;
|
||||
|
||||
intersectionViewer();
|
||||
}).observe(bodyElem, { childList: true });
|
||||
|
||||
intersectionViewer();
|
||||
});
|
||||
</script>
|
||||
|
||||
<tbody bind:this={bodyElem}>
|
||||
<slot />
|
||||
</tbody>
|
@ -16,30 +16,26 @@
|
||||
import { goto } from '$app/navigation';
|
||||
import Search from '$lib/components/Input/Search.svelte';
|
||||
import { usernameSuggestions } from '$lib/utils';
|
||||
import PaginationTableBody from '$lib/components/PaginationTable/PaginationTableBody.svelte';
|
||||
|
||||
export let data: PageData;
|
||||
|
||||
let currentPageReports: (typeof Report.prototype.dataValues)[] = [];
|
||||
let currentPageTotal = 0;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
let currentPageReportsRequest: Promise<any> = Promise.resolve();
|
||||
let reportsPerPage = 50;
|
||||
let reportPage = 0;
|
||||
let reports: (typeof Report.prototype.dataValues)[] = [];
|
||||
let reportsPerRequest = 25;
|
||||
let reportFilter = { draft: false, status: null, reporter: null, reported: null };
|
||||
let activeReport: typeof Report.prototype.dataValues | null = null;
|
||||
|
||||
async function fetchPageReports(
|
||||
page: number,
|
||||
filter: typeof reportFilter | { hash: string }
|
||||
): Promise<{ reports: typeof currentPageReports; count: number }> {
|
||||
async function fetchReports(
|
||||
filter?: typeof reportFilter | { hash: string }
|
||||
): Promise<{ reports: typeof reports; count: number }> {
|
||||
if (!browser) return { reports: [], count: 0 };
|
||||
|
||||
const response = await fetch(`${env.PUBLIC_BASE_PATH}/admin/reports`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
...filter,
|
||||
limit: reportsPerPage,
|
||||
from: reportsPerPage * page
|
||||
...(filter ?? reportFilter),
|
||||
limit: reportsPerRequest,
|
||||
from: reports.length
|
||||
})
|
||||
});
|
||||
|
||||
@ -51,20 +47,15 @@
|
||||
return await response.json();
|
||||
}
|
||||
|
||||
$: currentPageReportsRequest = fetchPageReports(reportPage, reportFilter).then((r) => {
|
||||
currentPageReports = r.reports;
|
||||
currentPageTotal = r.count;
|
||||
});
|
||||
|
||||
async function openHashReport() {
|
||||
if (!window.location.hash) return;
|
||||
|
||||
const requestedHash = window.location.hash.substring(1);
|
||||
let report = currentPageReports.find((r) => r.url_hash === requestedHash);
|
||||
let report = reports.find((r) => r.url_hash === requestedHash);
|
||||
if (!report) {
|
||||
const hashReport = (await fetchPageReports(0, { hash: requestedHash })).reports[0];
|
||||
const hashReport = (await fetchReports({ hash: requestedHash })).reports[0];
|
||||
if (hashReport) {
|
||||
currentPageReports = [hashReport, ...currentPageReports];
|
||||
reports = [hashReport, ...reports];
|
||||
report = hashReport;
|
||||
} else {
|
||||
await goto(window.location.href.split('#')[0], { replaceState: true });
|
||||
@ -75,10 +66,8 @@
|
||||
activeReport = report;
|
||||
activeReport.originalStatus = report;
|
||||
}
|
||||
onMount(async () => {
|
||||
await currentPageReportsRequest;
|
||||
await openHashReport();
|
||||
|
||||
onMount(async () => {
|
||||
if (browser) window.addEventListener('hashchange', openHashReport);
|
||||
});
|
||||
onDestroy(() => {
|
||||
@ -102,6 +91,8 @@
|
||||
|
||||
let saveActiveReportChangesModal: HTMLDialogElement;
|
||||
let newReportModal: HTMLDialogElement;
|
||||
|
||||
$: if (reportFilter) fetchReports().then((r) => (reports = r.reports));
|
||||
</script>
|
||||
|
||||
<div class="h-full flex flex-row">
|
||||
@ -127,8 +118,11 @@
|
||||
<th>Reportstatus</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{#each currentPageReports as report}
|
||||
<PaginationTableBody
|
||||
onUpdate={async () =>
|
||||
await fetchReports().then((res) => (reports = [...reports, ...res.reports]))}
|
||||
>
|
||||
{#each reports as report}
|
||||
<tr
|
||||
class="hover [&>*]:text-sm cursor-pointer"
|
||||
class:bg-base-200={activeReport?.url_hash === report.url_hash}
|
||||
@ -195,22 +189,8 @@
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</PaginationTableBody>
|
||||
</table>
|
||||
<div class="flex justify-center items-center mb-2 mt-4 w-full">
|
||||
<div class="join">
|
||||
<!-- eslint-disable-next-line @typescript-eslint/no-unused-vars -->
|
||||
{#each Array(currentPageReports.length === reportsPerPage || reportPage > 0 ? Math.ceil(currentPageTotal / reportsPerPage) || 1 : 1) as _, i}
|
||||
<button
|
||||
class="join-item btn"
|
||||
class:btn-active={i === reportPage}
|
||||
on:click={() => {
|
||||
reportPage = i;
|
||||
}}>{i + 1}</button
|
||||
>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{#if activeReport}
|
||||
<div
|
||||
@ -358,7 +338,7 @@
|
||||
} else {
|
||||
activeReport.reported = undefined;
|
||||
}
|
||||
currentPageReports = [...currentPageReports];
|
||||
reports = [...reports];
|
||||
if (activeReport.originalStatus !== 'reviewed' && activeReport.status === 'reviewed') {
|
||||
$reportCount -= 1;
|
||||
} else if (
|
||||
@ -381,8 +361,8 @@
|
||||
<NewReportModal
|
||||
on:submit={(e) => {
|
||||
if (!e.detail.draft) $reportCount += 1;
|
||||
currentPageReports = [e.detail, ...currentPageReports];
|
||||
activeReport = currentPageReports[0];
|
||||
reports = [e.detail, ...reports];
|
||||
activeReport = reports[0];
|
||||
newReportModal.close();
|
||||
}}
|
||||
/>
|
||||
|
@ -4,81 +4,42 @@
|
||||
import Input from '$lib/components/Input/Input.svelte';
|
||||
import Select from '$lib/components/Input/Select.svelte';
|
||||
import { env } from '$env/dynamic/public';
|
||||
import type { User } from '$lib/server/database';
|
||||
import type { Report, User } from '$lib/server/database';
|
||||
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';
|
||||
import NewUserModal from './NewUserModal.svelte';
|
||||
import PaginationTableBody from '$lib/components/PaginationTable/PaginationTableBody.svelte';
|
||||
|
||||
export let data: PageData;
|
||||
|
||||
let currentPageUsers: (typeof User.prototype.dataValues)[] = [];
|
||||
let currentPageUsersRequest: Promise<void> = new Promise((resolve) => resolve());
|
||||
let usersPerPage = 200;
|
||||
let userPage = 0;
|
||||
let userFilter = { name: null, playertype: null };
|
||||
let users: (typeof User.prototype.dataValues)[] = [];
|
||||
let usersPerRequest = 25;
|
||||
let userFilter: { [k: string]: any } = { name: null, playertype: null };
|
||||
|
||||
let userTableContainerElement: HTMLDivElement;
|
||||
let newUserModal: HTMLDialogElement;
|
||||
|
||||
function fetchPageUsers(page: number) {
|
||||
if (!browser) return;
|
||||
async function fetchUsers(): Promise<typeof users> {
|
||||
if (!browser) return [];
|
||||
|
||||
if (userTableContainerElement) userTableContainerElement.scrollTop = 0;
|
||||
|
||||
// eslint-disable-next-line no-async-promise-executor
|
||||
currentPageUsersRequest = new Promise(async (resolve, reject) => {
|
||||
const response = await fetch(`${env.PUBLIC_BASE_PATH}/admin/users`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ ...userFilter, limit: usersPerPage, from: usersPerPage * page })
|
||||
});
|
||||
if (response.ok) {
|
||||
currentPageUsers = await response.json();
|
||||
resolve();
|
||||
} else {
|
||||
reject(Error());
|
||||
}
|
||||
const response = await fetch(`${env.PUBLIC_BASE_PATH}/admin/users`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ ...userFilter, limit: usersPerRequest, from: users.length })
|
||||
});
|
||||
|
||||
return await response.json();
|
||||
}
|
||||
|
||||
$: fetchPageUsers(userPage);
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unused-vars
|
||||
function fetchFilterPageUsers(_: any) {
|
||||
userPage == 0 ? fetchPageUsers(0) : (userPage = 0);
|
||||
}
|
||||
$: fetchFilterPageUsers(userFilter);
|
||||
|
||||
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':
|
||||
return (a - b) * multiplyValue;
|
||||
case 'string':
|
||||
return a.localeCompare(b) * multiplyValue;
|
||||
default:
|
||||
return (a - b) * multiplyValue;
|
||||
}
|
||||
});
|
||||
|
||||
async function updateUser(user: typeof User.prototype.dataValues) {
|
||||
const response = await fetch(`${env.PUBLIC_BASE_PATH}/admin/users`, {
|
||||
await fetch(`${env.PUBLIC_BASE_PATH}/admin/users`, {
|
||||
method: 'PATCH',
|
||||
body: JSON.stringify(user)
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new Error();
|
||||
}
|
||||
}
|
||||
|
||||
async function deleteUser(id: number) {
|
||||
@ -89,15 +50,15 @@
|
||||
})
|
||||
});
|
||||
if (response.ok) {
|
||||
currentPageUsers.splice(
|
||||
currentPageUsers.findIndex((v) => v.id == id),
|
||||
users.splice(
|
||||
users.findIndex((v) => v.id == id),
|
||||
1
|
||||
);
|
||||
currentPageUsers = currentPageUsers;
|
||||
} else {
|
||||
throw new Error();
|
||||
users = users;
|
||||
}
|
||||
}
|
||||
|
||||
$: if (userFilter) fetchUsers().then((u) => (users = u));
|
||||
</script>
|
||||
|
||||
<div class="h-full flex flex-col overflow-hidden">
|
||||
@ -109,141 +70,120 @@
|
||||
<!-- prettier-ignore -->
|
||||
<SortableTr class="[&>th]:bg-base-100 [&>th]:z-[1] [&>th]:sticky [&>th]:top-0">
|
||||
<th />
|
||||
<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>
|
||||
<SortableTh on:sort={(e) => userFilter = {...userFilter, sort: {key: 'firstname', asc: e.detail.asc}}}>Vorname</SortableTh>
|
||||
<SortableTh on:sort={(e) => userFilter = {...userFilter, sort: {key: 'lastname', asc: e.detail.asc}}}>Nachname</SortableTh>
|
||||
<SortableTh on:sort={(e) => userFilter = {...userFilter, sort: {key: 'birthday', asc: e.detail.asc}}}>Geburtstag</SortableTh>
|
||||
<SortableTh on:sort={(e) => userFilter = {...userFilter, sort: {key: 'telephone', asc: e.detail.asc}}}>Telefon</SortableTh>
|
||||
<SortableTh on:sort={(e) => userFilter = {...userFilter, sort: {key: 'username', asc: e.detail.asc}}}>Username</SortableTh>
|
||||
<SortableTh on:sort={(e) => userFilter = {...userFilter, sort: {key: 'playertype', asc: e.detail.asc}}}>Minecraft Edition</SortableTh>
|
||||
<SortableTh on:sort={(e) => userFilter = {...userFilter, sort: {key: 'password', asc: e.detail.asc}}}>Passwort</SortableTh>
|
||||
<SortableTh on:sort={(e) => userFilter = {...userFilter, sort: {key: 'uuid', asc: e.detail.asc}}}>UUID</SortableTh>
|
||||
<th />
|
||||
</SortableTr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{#key currentPageUsersRequest}
|
||||
<!-- eslint-disable-next-line @typescript-eslint/no-unused-vars -->
|
||||
{#await currentPageUsersRequest then _}
|
||||
{#each currentPageUsers as user, i}
|
||||
<tr>
|
||||
<td>{i + 1 + userPage * usersPerPage}</td>
|
||||
<td>
|
||||
<Input type="text" bind:value={user.firstname} disabled={!user.edit} size="sm" />
|
||||
</td>
|
||||
<td>
|
||||
<Input type="text" bind:value={user.lastname} disabled={!user.edit} size="sm" />
|
||||
</td>
|
||||
<td>
|
||||
<Input
|
||||
type="date"
|
||||
value={new Date(user.birthday).toISOString().split('T')[0]}
|
||||
on:input={(e) => (user.birthday = e.detail.target.valueAsDate.toISOString())}
|
||||
disabled={!user.edit}
|
||||
size="sm"
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
<Input type="tel" bind:value={user.telephone} disabled={!user.edit} size="sm" />
|
||||
</td>
|
||||
<td>
|
||||
<Input type="text" bind:value={user.username} disabled={!user.edit} size="sm" />
|
||||
</td>
|
||||
<td>
|
||||
<Select id="edition" bind:value={user.playertype} disabled={!user.edit} size="sm">
|
||||
<option value="java">Java Edition</option>
|
||||
<option value="bedrock">Bedrock Edition</option>
|
||||
<option value="noauth">Java noauth</option>
|
||||
</Select>
|
||||
</td>
|
||||
<td>
|
||||
<Input type="text" bind:value={user.password} disabled={!user.edit} size="sm" />
|
||||
</td>
|
||||
<td>
|
||||
<Input
|
||||
id="uuid"
|
||||
type="text"
|
||||
bind:value={user.uuid}
|
||||
disabled={!user.edit}
|
||||
size="sm"
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
<div class="flex gap-1">
|
||||
{#if user.edit}
|
||||
<button
|
||||
class="btn btn-sm btn-square"
|
||||
on:click={async (e) => {
|
||||
await buttonTriggeredRequest(e, updateUser(user));
|
||||
user.edit = false;
|
||||
}}
|
||||
>
|
||||
<Check size="18" />
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-sm btn-square"
|
||||
on:click={() => {
|
||||
user.edit = false;
|
||||
user = user.before;
|
||||
}}
|
||||
>
|
||||
<NoSymbol size="18" />
|
||||
</button>
|
||||
{:else}
|
||||
<button
|
||||
class="btn btn-sm btn-square"
|
||||
on:click={() => {
|
||||
user.before = structuredClone(user);
|
||||
user.edit = true;
|
||||
}}
|
||||
>
|
||||
<PencilSquare size="18" />
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-sm btn-square"
|
||||
on:click={(e) => buttonTriggeredRequest(e, deleteUser(user.id))}
|
||||
>
|
||||
<Trash size="18" />
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{/each}
|
||||
{/await}
|
||||
<PaginationTableBody
|
||||
onUpdate={async () => {
|
||||
await fetchUsers().then((u) => (users = [...users, ...u]));
|
||||
}}
|
||||
>
|
||||
{#each users as user, i}
|
||||
<tr>
|
||||
<td colspan="100">
|
||||
<div class="flex justify-center items-center">
|
||||
<button class="btn btn-sm" on:click={() => newUserModal.show()}>
|
||||
<Plus />
|
||||
<span>Neuer Spieler</span>
|
||||
</button>
|
||||
<td>{i + 1}</td>
|
||||
<td>
|
||||
<Input type="text" bind:value={user.firstname} disabled={!user.edit} size="sm" />
|
||||
</td>
|
||||
<td>
|
||||
<Input type="text" bind:value={user.lastname} disabled={!user.edit} size="sm" />
|
||||
</td>
|
||||
<td>
|
||||
<Input
|
||||
type="date"
|
||||
value={new Date(user.birthday).toISOString().split('T')[0]}
|
||||
on:input={(e) => (user.birthday = e.detail.target.valueAsDate.toISOString())}
|
||||
disabled={!user.edit}
|
||||
size="sm"
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
<Input type="tel" bind:value={user.telephone} disabled={!user.edit} size="sm" />
|
||||
</td>
|
||||
<td>
|
||||
<Input type="text" bind:value={user.username} disabled={!user.edit} size="sm" />
|
||||
</td>
|
||||
<td>
|
||||
<Select id="edition" bind:value={user.playertype} disabled={!user.edit} size="sm">
|
||||
<option value="java">Java Edition</option>
|
||||
<option value="bedrock">Bedrock Edition</option>
|
||||
<option value="noauth">Java noauth</option>
|
||||
</Select>
|
||||
</td>
|
||||
<td>
|
||||
<Input type="text" bind:value={user.password} disabled={!user.edit} size="sm" />
|
||||
</td>
|
||||
<td>
|
||||
<Input id="uuid" type="text" bind:value={user.uuid} disabled={!user.edit} size="sm" />
|
||||
</td>
|
||||
<td>
|
||||
<div class="flex gap-1">
|
||||
{#if user.edit}
|
||||
<button
|
||||
class="btn btn-sm btn-square"
|
||||
on:click={async (e) => {
|
||||
await buttonTriggeredRequest(e, updateUser(user));
|
||||
user.edit = false;
|
||||
}}
|
||||
>
|
||||
<Check size="18" />
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-sm btn-square"
|
||||
on:click={() => {
|
||||
user.edit = false;
|
||||
user = user.before;
|
||||
}}
|
||||
>
|
||||
<NoSymbol size="18" />
|
||||
</button>
|
||||
{:else}
|
||||
<button
|
||||
class="btn btn-sm btn-square"
|
||||
on:click={() => {
|
||||
user.before = structuredClone(user);
|
||||
user.edit = true;
|
||||
}}
|
||||
>
|
||||
<PencilSquare size="18" />
|
||||
</button>
|
||||
<button
|
||||
class="btn btn-sm btn-square"
|
||||
on:click={(e) => buttonTriggeredRequest(e, deleteUser(user.id))}
|
||||
>
|
||||
<Trash size="18" />
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{/key}
|
||||
</tbody>
|
||||
</table>
|
||||
<div class="flex justify-center items-center mb-2 mt-4 w-full">
|
||||
<div class="join">
|
||||
<!-- eslint-disable-next-line @typescript-eslint/no-unused-vars -->
|
||||
{#each Array(currentPageUsers.length === usersPerPage || userPage > 0 ? Math.ceil(data.count / usersPerPage) || 1 : 1) as _, i}
|
||||
<button
|
||||
class="join-item btn"
|
||||
class:btn-active={i === userPage}
|
||||
on:click={() => {
|
||||
userPage = i;
|
||||
}}>{i + 1}</button
|
||||
>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
<tr>
|
||||
<td colspan="100">
|
||||
<div class="flex justify-center items-center">
|
||||
<button class="btn btn-sm" on:click={() => newUserModal.show()}>
|
||||
<Plus />
|
||||
<span>Neuer Spieler</span>
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</PaginationTableBody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<dialog class="modal" bind:this={newUserModal}>
|
||||
<NewUserModal
|
||||
on:submit={(e) => {
|
||||
currentPageUsers = [...currentPageUsers, e.detail];
|
||||
users = [...users, e.detail];
|
||||
newUserModal.close();
|
||||
}}
|
||||
/>
|
||||
|
@ -43,7 +43,8 @@ export const POST = (async ({ request, cookies }) => {
|
||||
where: usersFindOptions,
|
||||
attributes: data.slim ? ['username', 'uuid'] : undefined,
|
||||
offset: data.from || 0,
|
||||
limit: data.limit || 100
|
||||
limit: data.limit || 100,
|
||||
order: data.sort ? [[data.sort.key, data.sort.asc ? 'ASC' : 'DESC']] : undefined
|
||||
});
|
||||
|
||||
return new Response(JSON.stringify(users));
|
||||
|
@ -2,7 +2,7 @@
|
||||
import Select from '$lib/components/Input/Select.svelte';
|
||||
import Input from '$lib/components/Input/Input.svelte';
|
||||
|
||||
export let userFilter = {
|
||||
export let userFilter: { [k: string]: any } = {
|
||||
name: null,
|
||||
playertype: null
|
||||
};
|
||||
|
@ -8,7 +8,23 @@ export const UserListSchema = z.object({
|
||||
playertype: z.enum(['java', 'bedrock', 'noauth']).nullish(),
|
||||
|
||||
search: z.string().nullish(),
|
||||
slim: z.boolean().nullish()
|
||||
slim: z.boolean().nullish(),
|
||||
|
||||
sort: z
|
||||
.object({
|
||||
key: z.enum([
|
||||
'firstname',
|
||||
'lastname',
|
||||
'birthday',
|
||||
'telephone',
|
||||
'username',
|
||||
'playertype',
|
||||
'password',
|
||||
'uuid'
|
||||
]),
|
||||
asc: z.boolean()
|
||||
})
|
||||
.nullish()
|
||||
});
|
||||
|
||||
export const UserEditSchema = z.object({
|
||||
|
Loading…
x
Reference in New Issue
Block a user