website/src/routes/admin/reports/+page.svelte

277 lines
8.4 KiB
Svelte

<script lang="ts">
import { fly } from 'svelte/transition';
import type { PageData } from './$types';
import type { Report } from '$lib/server/database';
import { browser } from '$app/environment';
import { env } from '$env/dynamic/public';
import Select from '$lib/components/Input/Select.svelte';
import Input from '$lib/components/Input/Input.svelte';
import Textarea from '$lib/components/Input/Textarea.svelte';
import { reportCount } from '$lib/stores';
import HeaderBar from './HeaderBar.svelte';
import { IconOutline } from 'svelte-heros-v2';
import NewReportModal from './NewReportModal.svelte';
export let data: PageData;
let currentPageReports: (typeof Report.prototype.dataValues)[] = [];
let reportsPerPage = 50;
let reportPage = 0;
let reportFilter = { draft: false, status: null, reporter: null, reported: null };
let activeReport: typeof Report.prototype.dataValues | null = null;
async function fetchPageReports(
page: number,
filter: any
): Promise<(typeof Report.prototype.dataValues)[]> {
if (!browser) return [];
const response = await fetch(`${env.PUBLIC_BASE_PATH}/admin/reports`, {
method: 'POST',
body: JSON.stringify({ ...filter, limit: reportsPerPage, from: reportPage * page })
});
return await response.json();
}
$: fetchPageReports(reportPage, reportFilter).then((r) => (currentPageReports = r));
async function updateActiveReport() {
await fetch(`${env.PUBLIC_BASE_PATH}/admin/reports`, {
method: 'PATCH',
body: JSON.stringify({
id: activeReport.id,
auditor: data.self?.id || -1,
notice: activeReport.notice || '',
statement: activeReport.statement || '',
status: activeReport.status
})
});
}
let saveActiveReportChangesModal: HTMLDialogElement;
let newReportModal: HTMLDialogElement;
</script>
<div class="h-screen flex flex-row">
<div class="w-full flex flex-col">
<HeaderBar bind:reportFilter />
<hr class="divider my-1 mx-8 border-none" />
<table class="table table-fixed h-fit">
<colgroup>
<col style="width: 20%" />
<col style="width: 15%" />
<col style="width: 15%" />
<col style="width: 20%" />
<col style="width: 15%" />
<col style="width: 15%" />
</colgroup>
<thead>
<tr>
<th>Grund</th>
<th>Ersteller</th>
<th>Reporteter User</th>
<th>Datum</th>
<th>Bearbeitungsstatus</th>
<th>Reportstatus</th>
</tr>
</thead>
<tbody>
{#each currentPageReports as report}
<tr
class="hover [&>*]:text-sm cursor-pointer"
class:bg-base-200={activeReport === report}
on:click={() => {
activeReport = report;
activeReport.originalStatus = report.status;
}}
>
<td title={report.subject}><div class="overflow-scroll">{report.subject}</div></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',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit'
}).format(new Date(report.createdAt))} Uhr</td
>
<td>
{report.status === 'none'
? 'Unbearbeitet'
: report.status === 'review'
? 'In Bearbeitung'
: report.status === 'reviewed'
? 'Bearbeitet'
: ''}
</td>
<td>{report.draft ? 'Entwurf' : 'Erstellt'}</td>
</tr>
{/each}
<tr>
<td colspan="100">
<div class="flex justify-center items-center">
<button class="btn btn-sm" on:click={() => newReportModal.show()}>
<IconOutline name="plus-outline" />
<span>Neuer Report</span>
</button>
</div>
</td>
</tr>
</tbody>
</table>
</div>
{#if activeReport}
<div
class="relative flex flex-col w-2/5 bg-base-200/50 px-4 py-6 overflow-scroll"
transition:fly={{ x: 200, duration: 200 }}
>
<div class="absolute right-2 top-2 flex justify-center">
<form class="dropdown dropdown-end">
<!-- svelte-ignore a11y-no-noninteractive-tabindex a11y-label-has-associated-control -->
<label tabindex="0" class="btn btn-sm btn-circle btn-ghost text-center">
<IconOutline name="share-outline" height="1rem" width="auto" />
</label>
<!-- svelte-ignore a11y-no-noninteractive-tabindex -->
<ul
tabindex="0"
class="dropdown-content z-[1] menu p-2 shadow bg-base-100 rounded-box w-max"
>
<li>
<button
on:click={() =>
navigator.clipboard.writeText(
`${window.location.protocol}//${window.location.host}${env.PUBLIC_BASE_PATH}/report/${activeReport.url_hash}`
)}>Öffentlichen Link kopieren</button
>
</li>
</ul>
</form>
<button class="btn btn-sm btn-circle btn-ghost" on:click={() => (activeReport = null)}
>✕</button
>
</div>
<h3 class="font-roboto font-semibold text-2xl">Report</h3>
<div class="break-words my-2">
<i class="font-medium">{activeReport.reporter.username}</i> hat
<i class="font-medium">{activeReport.reported.username}</i>
am {new Intl.DateTimeFormat('de-DE', {
year: 'numeric',
month: 'long',
day: '2-digit',
hour: '2-digit',
minute: '2-digit'
}).format(new Date(activeReport.createdAt))} Uhr reportet.
</div>
<div class="w-full">
<Textarea readonly={true} rows={1} label="Report Grund" value={activeReport.subject} />
<Textarea readonly={true} rows={4} label="Report Details" value={activeReport.body} />
</div>
<div class="divider mx-4" />
<div>
<div
class="w-full"
title={activeReport.status === 'none'
? 'Zum Bearbeiten den Bearbeitungsstatus ändern'
: ''}
>
<Textarea
label="Interne Notizen"
readonly={activeReport.status === 'none'}
rows={1}
bind:value={activeReport.notice}
/>
</div>
<div
class="w-full"
title={activeReport.status === 'none'
? 'Zum Bearbeiten den Bearbeitungsstatus ändern'
: ''}
>
<Textarea
label="(Öffentliche) Report Antwort"
readonly={activeReport.status === 'none'}
rows={3}
bind:value={activeReport.statement}
/>
</div>
<Select label="Bearbeitungsstatus" size="sm" bind:value={activeReport.status}>
<option
value="none"
disabled={activeReport.auditor != null || activeReport.notice || activeReport.statement}
>Unbearbeitet</option
>
<option value="review">In Bearbeitung</option>
<option value="reviewed">Bearbeitet</option>
</Select>
</div>
<div class="self-end mt-auto pt-6 w-full flex justify-center">
<Input
type="submit"
value="Speichern"
on:click={() => saveActiveReportChangesModal.show()}
/>
</div>
</div>
{/if}
</div>
<dialog class="modal" bind:this={saveActiveReportChangesModal}>
<form method="dialog" class="modal-box">
<button class="btn btn-sm btn-circle btn-ghost absolute right-2 top-2"></button>
<h3 class="font-roboto text-xl">Änderungen Speichern?</h3>
<div class="flex flex-row space-x-2 mt-6">
<Input
type="submit"
value="Speichern"
on:click={async () => {
await updateActiveReport();
currentPageReports = [...currentPageReports];
if (activeReport.originalStatus !== 'reviewed' && activeReport.status === 'reviewed') {
$reportCount -= 1;
} else if (
activeReport.originalStatus === 'reviewed' &&
activeReport.status !== 'reviewed'
) {
$reportCount += 1;
}
}}
/>
<Input type="submit" value="Abbrechen" />
</div>
</form>
<form method="dialog" class="modal-backdrop bg-[rgba(0,0,0,.3)]">
<button>close</button>
</form>
</dialog>
<dialog class="modal" bind:this={newReportModal}>
<NewReportModal
on:submit={(e) => {
if (!e.detail.draft) $reportCount += 1;
currentPageReports = [e.detail, ...currentPageReports];
activeReport = currentPageReports[0];
}}
/>
</dialog>