show report attachments in admin ui
All checks were successful
deploy / build-and-deploy (/testvaro, /opt/website-test, website-test) (push) Successful in 22s
deploy / build-and-deploy (/varo, /opt/website, website) (push) Successful in 15s

This commit is contained in:
2025-06-22 16:29:48 +02:00
parent 1a81b5fb06
commit 9a6e44b2d5
8 changed files with 133 additions and 10 deletions

View File

@ -1,5 +1,5 @@
<script lang="ts">
import { editReport, type Report, type ReportStatus, type StrikeReasons } from './reports.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';
@ -7,6 +7,9 @@
import { editReportStatus, getReportStatus } from '@app/admin/reports/reports.ts';
import { confirmPopupState } from '@components/popup/ConfirmPopup.ts';
// html bindings
let previewDialogElem: HTMLDialogElement;
// types
interface Props {
strikeReasons: StrikeReasons;
@ -18,11 +21,15 @@
// 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})` }),
@ -40,6 +47,14 @@
notice = reportStatus.notice;
statement = reportStatus.statement;
});
getReportAttachments(report).then((value) => {
if (value) reportAttachments = value;
});
});
$effect(() => {
if (previewReportAttachment) previewDialogElem.show();
});
// callbacks
@ -71,16 +86,38 @@
<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={8} />
<Textarea bind:value={notice} label="Interne Notizen" rows={10} />
</div>
<div class="divider divider-horizontal"></div>
<div class="w-full">
<Input value={report?.reason} label="Grund" readonly dynamicWidth />
<Textarea value={report?.body} label="Inhalt" readonly dynamicWidth rows={12} />
<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={5} />
<Textarea bind:value={statement} label="Öffentliche Report Antwort" dynamicWidth rows={7} />
<Select
bind:value={status}
values={{ open: 'In Bearbeitung', closed: 'Bearbeitet' }}
@ -93,3 +130,22 @@
<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>

View File

@ -84,6 +84,18 @@ export async function editReportStatus(report: Report, reportStatus: ReportStatu
}
}
export async function getReportAttachments(report: Report) {
const { data, error } = await actions.report.reportAttachments({
reportId: report.id
});
if (error) {
actionErrorPopup(error);
return;
}
return data.reportAttachments;
}
export async function getStrikeReasons() {
const { data, error } = await actions.report.strikeReasons();
if (error) {

View File

@ -3,7 +3,6 @@
import { allowedImageTypes, allowedVideoTypes } from '@util/media.ts';
// bindings
let containerElem: HTMLDivElement;
let hiddenFileInputElem: HTMLInputElement;
let previewDialogElem: HTMLDialogElement;
@ -123,7 +122,6 @@
<!-- svelte-ignore a11y_no_static_element_interactions -->
<!-- svelte-ignore a11y_click_events_have_key_events -->
<div
bind:this={containerElem}
class="h-12 rounded border border-dashed flex cursor-pointer"
class:h-26={uploadFiles.length > 0}
dropzone="copy"