From 9f5fe25653cadbf095202bf856f8c86c071d4f05 Mon Sep 17 00:00:00 2001
From: bytedream <bytedream@protonmail.com>
Date: Mon, 25 Dec 2023 16:01:27 +0100
Subject: [PATCH] add ability to set reported user after report creation (#17)

---
 src/lib/components/Input/Select.svelte        |  9 ++-
 .../report/[...url_hash]/+page.server.ts      |  5 +-
 src/routes/report/[...url_hash]/+page.svelte  |  3 +-
 src/routes/report/[...url_hash]/+server.ts    | 14 +++-
 .../report/[...url_hash]/ReportDraft.svelte   | 72 +++++++++++++++++--
 5 files changed, 92 insertions(+), 11 deletions(-)

diff --git a/src/lib/components/Input/Select.svelte b/src/lib/components/Input/Select.svelte
index f3a0ee9..8f3127a 100644
--- a/src/lib/components/Input/Select.svelte
+++ b/src/lib/components/Input/Select.svelte
@@ -1,5 +1,7 @@
 <script lang="ts">
 	// eslint-disable-next-line no-undef
+	import { createEventDispatcher } from 'svelte';
+
 	type T = $$Generic;
 
 	export let id: string | null = null;
@@ -10,6 +12,9 @@
 	export let required = false;
 	export let disabled = false;
 	export let size: 'xs' | 'sm' | 'md' | 'lg' = 'md';
+	export let pickyWidth = true;
+
+	let dispatch = createEventDispatcher();
 </script>
 
 <div>
@@ -24,7 +29,8 @@
 		</label>
 	{/if}
 	<select
-		class="select select-bordered w-[100%] sm:max-w-[16rem]"
+		class="select select-bordered w-[100%]"
+		class:sm:max-w-[16rem]={pickyWidth}
 		class:select-xs={size === 'xs'}
 		class:select-sm={size === 'sm'}
 		class:select-md={size === 'md'}
@@ -34,6 +40,7 @@
 		{required}
 		{disabled}
 		bind:value
+		on:change={(e) => dispatch('change', { value: value })}
 	>
 		<slot />
 	</select>
diff --git a/src/routes/report/[...url_hash]/+page.server.ts b/src/routes/report/[...url_hash]/+page.server.ts
index b7ed125..6ad33c4 100644
--- a/src/routes/report/[...url_hash]/+page.server.ts
+++ b/src/routes/report/[...url_hash]/+page.server.ts
@@ -23,6 +23,9 @@ export const load: PageServerLoad = async ({ params }) => {
 		},
 		reported: {
 			name: report.reported?.username || null
-		}
+		},
+		users: report.draft
+			? (await User.findAll({ attributes: ['username'] })).map((u) => u.username)
+			: null
 	};
 };
diff --git a/src/routes/report/[...url_hash]/+page.svelte b/src/routes/report/[...url_hash]/+page.svelte
index 20f4915..5e84214 100644
--- a/src/routes/report/[...url_hash]/+page.svelte
+++ b/src/routes/report/[...url_hash]/+page.svelte
@@ -19,7 +19,8 @@
 			<ReportDraft
 				reason={data.reason}
 				reporterName={data.reporter.name}
-				reportedName={data.reported.name}
+				reportedName={data.reported.name ?? undefined}
+				users={data.users ?? []}
 				on:submit={() => (data.draft = false)}
 			/>
 		</div>
diff --git a/src/routes/report/[...url_hash]/+server.ts b/src/routes/report/[...url_hash]/+server.ts
index 963031b..e56721c 100644
--- a/src/routes/report/[...url_hash]/+server.ts
+++ b/src/routes/report/[...url_hash]/+server.ts
@@ -1,15 +1,23 @@
 import type { RequestHandler } from '@sveltejs/kit';
-import { Report } from '$lib/server/database';
+import { Report, User } from '$lib/server/database';
 
 export const POST = (async ({ request, params }) => {
 	const report = await Report.findOne({ where: { url_hash: params.url_hash } });
 
 	if (report == null) return new Response(null, { status: 400 });
 
-	const data: { body: string } = await request.json();
+	const data: { reported: string | null; subject: string; body: string } = await request.json();
+
+	const user =
+		data.reported != null ? await User.findOne({ where: { username: data.reported } }) : undefined;
+	if (user === null) {
+		return new Response(null, { status: 400 });
+	}
 
 	report.draft = false;
-	report.body = data.body || '';
+	report.subject = data.subject ?? '';
+	report.body = data.body ?? '';
+	report.reported_id = user?.id;
 	await report.save();
 
 	return new Response(null, { status: 200 });
diff --git a/src/routes/report/[...url_hash]/ReportDraft.svelte b/src/routes/report/[...url_hash]/ReportDraft.svelte
index 77257d7..f3f52c4 100644
--- a/src/routes/report/[...url_hash]/ReportDraft.svelte
+++ b/src/routes/report/[...url_hash]/ReportDraft.svelte
@@ -4,34 +4,78 @@
 	import { env } from '$env/dynamic/public';
 	import { page } from '$app/stores';
 	import { createEventDispatcher } from 'svelte';
+	import Search from '$lib/components/Input/Search.svelte';
+	import Select from '$lib/components/Input/Select.svelte';
 
 	export let reporterName: string;
 	export let reportedName: string | null;
 	export let reason: string;
+	export let users: string[];
+
+	let oldReported = reportedName;
+	$: reportedName = oldReported;
 
 	let body: string;
+	let userErrorModal: HTMLDialogElement;
+	let submitModal: HTMLDialogElement;
+
+	let dispatch = createEventDispatcher();
 
 	async function submitReport() {
 		await fetch(`${env.PUBLIC_BASE_PATH}/report/${$page.params.url_hash}`, {
 			method: 'POST',
 			body: JSON.stringify({
+				reported: reportedName || null,
+				subject: reason,
 				body: body
 			})
 		});
 	}
 
-	let dispatch = createEventDispatcher();
-
-	let submitModal: HTMLDialogElement;
+	async function suggestNames(input: string): Promise<{ name: string; value: string }[]> {
+		let i = 0;
+		return users
+			.filter((v) => v.includes(input) && v != reporterName && i++ < 6)
+			.map((v) => {
+				return { name: v, value: v };
+			});
+	}
 </script>
 
 <div>
 	<h2 class="text-3xl text-center">
 		Report von <span class="underline">{reporterName}</span> gegen
-		<span class="underline">{reportedName || 'unbekannt'}</span>
+		<span class="underline">{(reportedName ?? 'unbekannt') || oldReported}</span>
 	</h2>
-	<form on:submit|preventDefault={() => submitModal.show()}>
+	<form
+		on:submit|preventDefault={() => {
+			if (reportedName != null && users.findIndex((u) => u === reportedName) === -1) {
+				userErrorModal.show();
+			} else {
+				submitModal.show();
+			}
+		}}
+	>
 		<div class="space-y-4 my-4">
+			<div class="flex justify-center gap-4">
+				<Select
+					value={+(reportedName === null)}
+					size="sm"
+					pickyWidth={false}
+					on:change={(e) => (reportedName = e.detail.value === 0 ? '' : null)}
+				>
+					<option value={0}>Ich möchte einen bestimmten Spieler reporten</option>
+					<option value={1}>Ich möchte einen unbekannten Spieler reporten</option>
+				</Select>
+				{#if reportedName !== null}
+					<Search
+						size="sm"
+						bind:inputValue={oldReported}
+						suggestionRequired={true}
+						searchSuggestionFunc={suggestNames}
+					/>
+				{/if}
+			</div>
 			<div>
 				<Input type="text" bind:value={reason} required={true} pickyWidth={false}>
 					<span slot="label">Report Grund</span>
@@ -52,6 +96,24 @@
 	</form>
 </div>
 
+<dialog class="modal" bind:this={userErrorModal}>
+	<form method="dialog" class="modal-box">
+		<button class="btn btn-sm btn-circle btn-ghost absolute right-2 top-2">✕</button>
+		<div>
+			<h3 class="font-roboto font-medium text-xl">Fehler</h3>
+			<div class="my-4">
+				<p>Der zu reportende Spieler existiert nicht</p>
+			</div>
+			<div class="flex flex-row space-x-1">
+				<Input type="submit" value="Schließen" />
+			</div>
+		</div>
+	</form>
+	<form method="dialog" class="modal-backdrop bg-[rgba(0,0,0,.3)]">
+		<button>close</button>
+	</form>
+</dialog>
+
 <dialog class="modal" bind:this={submitModal}>
 	<form method="dialog" class="modal-box">
 		<button class="btn btn-sm btn-circle btn-ghost absolute right-2 top-2">✕</button>