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:
		| @@ -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({ | ||||
|   | ||||
		Reference in New Issue
	
	Block a user