167 lines
6.3 KiB
Svelte
167 lines
6.3 KiB
Svelte
<script lang="ts">
|
|
import { editReport, getReportAttachments, type Report, type ReportStatus, type StrikeReasons } from './reports.ts';
|
|
import Input from '@components/input/Input.svelte';
|
|
import Textarea from '@components/input/Textarea.svelte';
|
|
import Select from '@components/input/Select.svelte';
|
|
import TeamSearch from '@components/admin/search/TeamSearch.svelte';
|
|
import { editReportStatus, getReportStatus } from '@app/admin/reports/reports.ts';
|
|
import { confirmPopupState } from '@components/popup/ConfirmPopup.ts';
|
|
import Icon from '@iconify/svelte';
|
|
|
|
// html bindings
|
|
let previewDialogElem: HTMLDialogElement;
|
|
|
|
// types
|
|
interface Props {
|
|
strikeReasons: StrikeReasons;
|
|
report: Report | null;
|
|
}
|
|
|
|
// inputs
|
|
let { strikeReasons, report }: Props = $props();
|
|
|
|
// states
|
|
let reportedTeam = $state<{ id: number; name: string } | null>(report?.reported ?? null);
|
|
|
|
let status = $state<'open' | 'closed' | null>(null);
|
|
let notice = $state<string | null>(null);
|
|
let statement = $state<string | null>(null);
|
|
let strikeReason = $state<string | null>(String(report?.strike?.strikeReasonId ?? null));
|
|
|
|
let reportAttachments = $state<{ type: 'image' | 'video'; hash: string }[]>([]);
|
|
let previewReportAttachment = $state<{ type: 'image' | 'video'; hash: string } | null>(null);
|
|
|
|
// consts
|
|
const strikeReasonValues = strikeReasons.reduce(
|
|
(prev, curr) => Object.assign(prev, { [curr.id]: `${curr.name} (${curr.weight})` }),
|
|
{ [null]: 'Kein Vergehen' }
|
|
);
|
|
|
|
// lifetime
|
|
$effect(() => {
|
|
if (!report) return;
|
|
|
|
getReportStatus(report).then((reportStatus) => {
|
|
if (!reportStatus) return;
|
|
|
|
status = reportStatus.status;
|
|
notice = reportStatus.notice;
|
|
statement = reportStatus.statement;
|
|
});
|
|
|
|
getReportAttachments(report).then((value) => {
|
|
if (value) reportAttachments = value;
|
|
});
|
|
});
|
|
|
|
$effect(() => {
|
|
if (previewReportAttachment) previewDialogElem.show();
|
|
});
|
|
|
|
// callbacks
|
|
async function onSaveButtonClick() {
|
|
$confirmPopupState = {
|
|
title: 'Änderungen speichern?',
|
|
message: 'Sollen die Änderungen am Report gespeichert werden?',
|
|
onConfirm: async () => {
|
|
if (reportedTeam?.id != report?.reported?.id) {
|
|
report!.reported = reportedTeam;
|
|
await editReport(report!);
|
|
}
|
|
await editReportStatus(report!, {
|
|
status: status,
|
|
notice: notice,
|
|
statement: statement,
|
|
strikeReasonId: Number(strikeReason)
|
|
} as ReportStatus);
|
|
}
|
|
};
|
|
}
|
|
|
|
function onCopyPublicLink(urlHash: string) {
|
|
navigator.clipboard.writeText(`${document.baseURI}report/${urlHash}`);
|
|
document.activeElement?.blur();
|
|
}
|
|
</script>
|
|
|
|
<div
|
|
class="absolute bottom-2 bg-base-200 rounded-lg w-[calc(100%-1rem)] mx-2 flex px-6 py-4 gap-2"
|
|
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"><Icon icon="heroicons:share" /></div>
|
|
<!-- svelte-ignore a11y_no_noninteractive_tabindex -->
|
|
<ul tabindex="0" class="menu dropdown-content bg-base-100 rounded-box z-1 p-2 shadow-sm w-max">
|
|
<li><button onclick={() => onCopyPublicLink(report?.urlHash)}>Öffentlichen Report Link kopieren</button></li>
|
|
</ul>
|
|
</div>
|
|
<button class="btn btn-sm btn-circle btn-ghost" onclick={() => (report = null)}>✕</button>
|
|
</div>
|
|
<div class="w-[34rem]">
|
|
<TeamSearch value={report?.reporter.name} label="Report Team" readonly mustMatch />
|
|
<TeamSearch value={report?.reported?.name} label="Reportetes Team" onSubmit={(team) => (reportedTeam = team)} />
|
|
<Textarea bind:value={notice} label="Interne Notizen" rows={10} />
|
|
</div>
|
|
<div class="w-full">
|
|
<Input value={report?.reason} label="Grund" readonly dynamicWidth />
|
|
<Textarea value={report?.body} label="Inhalt" readonly dynamicWidth rows={9} />
|
|
<fieldset class="fieldset">
|
|
<legend class="fieldset-legend">Anhänge</legend>
|
|
<div class="h-16.5 rounded border border-dashed flex">
|
|
{#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)}>
|
|
{#if reportAttachment.type === 'image'}
|
|
<img
|
|
src={location.pathname + '/attachment/' + reportAttachment.hash}
|
|
alt={reportAttachment.hash}
|
|
class="w-16 h-16"
|
|
/>
|
|
{:else if reportAttachment.type === 'video'}
|
|
<!-- svelte-ignore a11y_media_has_caption -->
|
|
<video src={location.pathname + '/attachment/' + reportAttachment.hash} class="w-16 h-16"></video>
|
|
{/if}
|
|
</div>
|
|
</div>
|
|
{/each}
|
|
</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} />
|
|
<Select
|
|
bind:value={status}
|
|
values={{ open: 'In Bearbeitung', closed: 'Bearbeitet' }}
|
|
defaultValue="Unbearbeitet"
|
|
label="Bearbeitungsstatus"
|
|
dynamicWidth
|
|
/>
|
|
<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>
|
|
</div>
|
|
</div>
|
|
|
|
<dialog
|
|
class="modal"
|
|
bind:this={previewDialogElem}
|
|
onclose={() => setTimeout(() => (previewReportAttachment = null), 300)}
|
|
>
|
|
<div class="modal-box">
|
|
{#if previewReportAttachment?.type === 'image'}
|
|
<img src={location.pathname + '/attachment/' + previewReportAttachment.hash} alt={previewReportAttachment.hash} />
|
|
{:else if previewReportAttachment?.type === 'video'}
|
|
<!-- svelte-ignore a11y_media_has_caption -->
|
|
<video src={location.pathname + '/attachment/' + previewReportAttachment.hash} controls></video>
|
|
{/if}
|
|
</div>
|
|
<form method="dialog" class="modal-backdrop bg-[rgba(0,0,0,.3)]">
|
|
<button class="absolute top-3 right-3 btn btn-circle">✕</button>
|
|
<button class="!cursor-default">close</button>
|
|
</form>
|
|
</dialog>
|