make admin page mobile friendly
All checks were successful
deploy / build-and-deploy (push) Successful in 23s

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Gemini 3 <google-gemini-noreply@google.com>
This commit is contained in:
2026-04-20 22:05:31 +02:00
parent 31ad92a6e2
commit fdc9b24800
20 changed files with 199 additions and 153 deletions

View File

@@ -28,8 +28,8 @@
data={admins}
count={true}
keys={[
{ key: 'username', label: 'Username', width: 30 },
{ key: 'permissions', label: 'Berechtigungen', width: 60, transform: permissionsBadge }
{ key: 'username', label: 'Username' },
{ key: 'permissions', label: 'Berechtigungen', transform: permissionsBadge }
]}
onEdit={(admin) => (editPopupAdmin = admin)}
onDelete={onAdminDelete}

View File

@@ -32,8 +32,8 @@
data={blockedUsers}
count={true}
keys={[
{ key: 'uuid', label: 'UUID', width: 20, sortable: true },
{ key: 'comment', label: 'Kommentar', width: 70 }
{ key: 'uuid', label: 'UUID', sortable: true },
{ key: 'comment', label: 'Kommentar' }
]}
onEdit={(blockedUser) => (blockedUserEditPopupBlockedUser = blockedUser)}
onDelete={onBlockedUserDelete}

View File

@@ -21,8 +21,8 @@
data={directInvitations}
count={true}
keys={[
{ key: 'url', label: 'Link', width: 50 },
{ key: 'user.username', label: 'Registrierter Nutzer', width: 40, sortable: true }
{ key: 'url', label: 'Link' },
{ key: 'user.username', label: 'Registrierter Nutzer', sortable: true }
]}
onDelete={onDirectInvitationDelete}
/>

View File

@@ -12,15 +12,18 @@
let { feedback }: Props = $props();
</script>
<div class="relative bg-base-200 rounded-lg w-[calc(100%-1rem)] m-2 flex px-6 py-4 gap-2" hidden={feedback === null}>
<div
class="relative bg-base-200 rounded-lg w-[calc(100%-1rem)] m-2 flex flex-col lg:flex-row px-4 lg:px-6 py-4 gap-4"
hidden={feedback === null}
>
<button class="btn btn-sm btn-circle btn-ghost absolute right-2 top-2" onclick={() => (feedback = null)}>✕</button>
<div class="w-96">
<div class="w-full lg:w-96">
<Input value={feedback?.event} label="Event" readonly />
<Input value={feedback?.title} label="Titel" readonly />
<Input value={feedback?.username} label="Nutzer" readonly />
</div>
<div class="divider divider-horizontal"></div>
<div class="divider lg:divider-horizontal"></div>
<div class="w-full">
<Textarea value={feedback?.content} label="Inhalt" rows={9} readonly dynamicWidth />
<Textarea value={feedback?.content} label="Inhalt" rows={6} readonly dynamicWidth />
</div>
</div>

View File

@@ -20,15 +20,15 @@
{dateFormat.format(new Date(value))}
{/snippet}
<div class="h-screen flex flex-col justify-between">
<div class="h-full flex flex-col justify-between">
<DataTable
data={feedbacks}
count={true}
keys={[
{ key: 'event', label: 'Event', width: 10, sortable: true },
{ key: 'username', label: 'Nutzer', width: 10, sortable: true },
{ key: 'lastChanged', label: 'Datum', width: 10, sortable: true, transform: date },
{ key: 'content', label: 'Inhalt', width: 10 }
{ key: 'event', label: 'Event', sortable: true },
{ key: 'username', label: 'Nutzer', sortable: true },
{ key: 'lastChanged', label: 'Datum', sortable: true, transform: date },
{ key: 'content', label: 'Inhalt' }
]}
onClick={(feedback) => (activeFeedback = feedback)}
/>

View File

@@ -89,7 +89,10 @@
}
</script>
<div class="relative bg-base-200 rounded-lg w-[calc(100%-1rem)] m-2 flex px-6 py-4 gap-2" hidden={report === null}>
<div
class="relative bg-base-200 rounded-lg w-[calc(100%-1rem)] m-2 flex flex-col lg:flex-row px-4 lg:px-6 py-4 gap-4"
hidden={report === null}
>
<div class="absolute right-2 top-2">
<div class="dropdown dropdown-end">
<div tabindex="0" role="button" class="btn btn-sm btn-circle btn-ghost">
@@ -102,35 +105,38 @@
</div>
<button class="btn btn-sm btn-circle btn-ghost" onclick={() => (report = null)}>✕</button>
</div>
<div class="w-[34rem]">
<div class="w-full lg:w-[34rem]">
<UserSearch value={report?.reporter.username} label="Report Ersteller" readonly mustMatch />
<UserSearch
value={report?.reported?.username}
label="Reporteter Spieler"
onSubmit={(user) => (reportedUser = user)}
/>
<Textarea bind:value={notice} label="Interne Notizen" rows={10} />
<Textarea bind:value={notice} label="Interne Notizen" rows={6} />
</div>
<div class="w-full">
<Input value={report?.reason} label="Grund" readonly dynamicWidth />
<Textarea value={report?.body} label="Inhalt" readonly dynamicWidth rows={9} />
<Textarea value={report?.body} label="Inhalt" readonly dynamicWidth rows={6} />
<fieldset class="fieldset">
<legend class="fieldset-legend">Anhänge</legend>
<div class="h-16.5 rounded border border-dashed flex">
<div class="min-h-16.5 rounded border border-dashed border-base-content/20 flex flex-wrap p-1">
{#each reportAttachments as reportAttachment (reportAttachment.hash)}
<div>
<!-- svelte-ignore a11y_no_static_element_interactions -->
<!-- svelte-ignore a11y_click_events_have_key_events -->
<div class="cursor-zoom-in" onclick={() => (previewReportAttachment = reportAttachment)}>
<div class="cursor-zoom-in p-1" onclick={() => (previewReportAttachment = reportAttachment)}>
{#if reportAttachment.type === 'image'}
<img
src={location.pathname + '/attachment/' + reportAttachment.hash}
alt={reportAttachment.hash}
class="w-16 h-16"
class="w-16 h-16 object-cover rounded"
/>
{:else if reportAttachment.type === 'video'}
<!-- svelte-ignore a11y_media_has_caption -->
<video src={location.pathname + '/attachment/' + reportAttachment.hash} class="w-16 h-16"></video>
<video
src={location.pathname + '/attachment/' + reportAttachment.hash}
class="w-16 h-16 object-cover rounded"
></video>
{/if}
</div>
</div>
@@ -138,9 +144,9 @@
</div>
</fieldset>
</div>
<div class="divider divider-horizontal"></div>
<div class="flex flex-col w-[42rem]">
<Textarea bind:value={statement} label="Öffentliche Report Antwort" dynamicWidth rows={7} />
<div class="divider lg:divider-horizontal"></div>
<div class="flex flex-col w-full lg:w-[42rem]">
<Textarea bind:value={statement} label="Öffentliche Report Antwort" dynamicWidth rows={6} />
<Select
bind:value={status}
values={{ open: 'In Bearbeitung', closed: 'Bearbeitet' }}
@@ -150,7 +156,7 @@
/>
<Select bind:value={strikeReason} values={strikeReasonValues} label="Vergehen" dynamicWidth></Select>
<div class="divider mt-0 mb-2"></div>
<button class="btn mt-auto" onclick={onSaveButtonClick}>Speichern</button>
<button class="btn btn-primary mt-auto" onclick={onSaveButtonClick}>Speichern</button>
</div>
</div>

View File

@@ -1,6 +1,6 @@
<script lang="ts">
import BottomBar from '@app/admin/reports/BottomBar.svelte';
import { onMount, untrack } from 'svelte';
import { onMount } from 'svelte';
import DataTable from '@components/admin/table/DataTable.svelte';
import { type StrikeReasons, getStrikeReasons, reports } from '@app/admin/reports/reports.ts';
@@ -49,16 +49,16 @@
{/if}
{/snippet}
<div class="h-screen flex flex-col justify-between">
<div class="h-full flex flex-col justify-between">
<DataTable
data={reports}
count={true}
keys={[
{ key: 'reason', label: 'Grund', width: 35 },
{ key: 'reporter.username', label: 'Report Ersteller', width: 15 },
{ key: 'reported.username', label: 'Reporteter Spieler', width: 15 },
{ key: 'createdAt', label: 'Datum', width: 15, sortable: true, transform: date },
{ key: 'status.status', label: 'Bearbeitungsstatus', width: 10, sortable: true, transform: status }
{ key: 'reason', label: 'Grund' },
{ key: 'reporter.username', label: 'Report Ersteller' },
{ key: 'reported.username', label: 'Reporteter Spieler' },
{ key: 'createdAt', label: 'Datum', sortable: true, transform: date },
{ key: 'status.status', label: 'Bearbeitungsstatus', sortable: true, transform: status }
]}
onClick={(report) => (activeReport = report)}
/>

View File

@@ -99,16 +99,16 @@
}
</script>
<div class="h-full flex flex-col items-center justify-between">
<div class="grid grid-cols-2 w-full">
<div class="min-h-full flex flex-col items-center justify-between">
<div class="grid grid-cols-1 lg:grid-cols-2 w-full px-4 lg:px-12 gap-8">
{#each settingsInput as setting (setting.name)}
<div class="mx-12">
<div class="divider">{setting.name}</div>
<div class="flex flex-col gap-5">
<div class="flex flex-col">
<div class="divider font-bold">{setting.name}</div>
<div class="flex flex-col gap-6">
{#each setting.entries as entry (entry.name)}
<label class="flex justify-between">
<span class="mt-[.125rem] text-sm w-1/2">{entry.name}</span>
<div class="w-1/2">
<label class="flex flex-col sm:flex-row justify-between gap-2">
<span class="text-sm font-medium sm:w-1/2">{entry.name}</span>
<div class="sm:w-1/2 flex sm:justify-end">
{#if entry.type === 'checkbox'}
<input
type="checkbox"
@@ -122,7 +122,7 @@
{:else if entry.type === 'text'}
<input
type="text"
class="input input-bordered"
class="input input-bordered w-full sm:max-w-xs"
onchange={(e) => {
entry.onChange(e.currentTarget.value);
changes = dynamicSettings.getChanges();
@@ -131,7 +131,7 @@
/>
{:else if entry.type === 'textarea'}
<textarea
class="textarea"
class="textarea textarea-bordered w-full sm:max-w-xs min-h-24"
value={entry.value}
onchange={(e) => {
entry.onChange(e.currentTarget.value);
@@ -146,9 +146,9 @@
</div>
{/each}
</div>
<div>
<div class="py-12">
<button
class="btn btn-success mt-auto mb-8"
class="btn btn-success btn-lg px-12"
class:btn-disabled={Object.keys(changes).length === 0}
onclick={onSaveSettingsClick}>Speichern</button
>

View File

@@ -32,9 +32,9 @@
data={strikeReasons}
count={true}
keys={[
{ key: 'name', label: 'Name', width: 20 },
{ key: 'weight', label: 'Gewichtung', width: 50, sortable: true },
{ key: 'id', label: 'Id', width: 20 }
{ key: 'name', label: 'Name' },
{ key: 'weight', label: 'Gewichtung', sortable: true },
{ key: 'id', label: 'Id' }
]}
onDelete={onBlockedUserDelete}
onEdit={(strikeReason) => (editPopupStrikeReason = strikeReason)}

View File

@@ -17,9 +17,9 @@
<fieldset class="fieldset border border-base-200 rounded-box px-4">
<legend class="fieldset-legend">Account UUID finder</legend>
<div>
<div class="flex gap-3">
<Input bind:value={username} />
<Select bind:value={edition} values={{ java: 'Java', bedrock: 'Bedrock' }} />
<div class="flex flex-col sm:flex-row gap-3">
<Input bind:value={username} dynamicWidth />
<Select bind:value={edition} values={{ java: 'Java', bedrock: 'Bedrock' }} dynamicWidth />
</div>
<div class="flex justify-center">
<button class="btn w-4/6" class:disabled={!username} onclick={onSubmit}>UUID finden</button>

View File

@@ -47,13 +47,13 @@
data={users}
count={true}
keys={[
{ key: 'firstname', label: 'Vorname', width: 15, sortable: true },
{ key: 'lastname', label: 'Nachname', width: 15, sortable: true },
{ key: 'birthday', label: 'Geburtstag', width: 5, sortable: true },
{ key: 'telephone', label: 'Telefon', width: 12, sortable: true },
{ key: 'username', label: 'Username', width: 15, sortable: true },
{ key: 'edition', label: 'Edition', width: 5, sortable: true, transform: edition },
{ key: 'uuid', label: 'UUID', width: 23 }
{ key: 'firstname', label: 'Vorname', sortable: true },
{ key: 'lastname', label: 'Nachname', sortable: true },
{ key: 'birthday', label: 'Geburtstag', sortable: true },
{ key: 'telephone', label: 'Telefon', sortable: true },
{ key: 'username', label: 'Username', sortable: true },
{ key: 'edition', label: 'Edition', sortable: true, transform: edition },
{ key: 'uuid', label: 'UUID' }
]}
extraActions={blockAction}
onEdit={(user) => (editPopupUser = user)}