make reported user nullable
All checks were successful
delpoy / build-and-deploy (push) Successful in 44s
All checks were successful
delpoy / build-and-deploy (push) Successful in 44s
This commit is contained in:
parent
72eeb59230
commit
81d97380ca
@ -9,6 +9,7 @@
|
||||
export let placeholder: string | null = null;
|
||||
export let required = false;
|
||||
export let disabled = false;
|
||||
export let readonly = false;
|
||||
export let size: 'xs' | 'sm' | 'md' | 'lg' = 'md';
|
||||
export let pickyWidth = true;
|
||||
|
||||
@ -85,6 +86,7 @@
|
||||
{placeholder}
|
||||
{required}
|
||||
{disabled}
|
||||
{readonly}
|
||||
bind:this={inputElement}
|
||||
autocomplete="off"
|
||||
on:input={(e) => {
|
||||
|
@ -1,7 +1,11 @@
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
|
||||
export let id: string | null = null;
|
||||
export let value = '';
|
||||
export let inputValue = '';
|
||||
export let suggestionRequired = false;
|
||||
export let emptyAllowed = false;
|
||||
export let searchSuggestionFunc: (
|
||||
input: string
|
||||
) => Promise<{ name: string; value: string }[]> = () => Promise.resolve([]);
|
||||
@ -10,7 +14,8 @@
|
||||
export let label: string | null = null;
|
||||
export let required = false;
|
||||
|
||||
let inputValue: string;
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
let searchSuggestions: { name: string; value: string }[] = [];
|
||||
$: if (!suggestionRequired) value = inputValue;
|
||||
</script>
|
||||
@ -47,13 +52,18 @@
|
||||
value = searchSuggestion.value;
|
||||
searchSuggestions = [];
|
||||
e.target?.setCustomValidity('');
|
||||
dispatch('submit', { input: inputValue, value: value });
|
||||
} else if (inputValue === '' && emptyAllowed) {
|
||||
dispatch('submit', { input: '', value: '' });
|
||||
}
|
||||
});
|
||||
}}
|
||||
on:invalid={(e) => {
|
||||
if (invalidMessage != null) e.target?.setCustomValidity(invalidMessage);
|
||||
}}
|
||||
pattern={suggestionRequired ? `${value ? inputValue : 'a^'}` : null}
|
||||
pattern={suggestionRequired
|
||||
? `${value ? inputValue : 'a^' + (emptyAllowed ? '|$^' : '')}`
|
||||
: null}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -68,6 +78,7 @@
|
||||
inputValue = searchSuggestion.name;
|
||||
value = searchSuggestion.value;
|
||||
searchSuggestions = [];
|
||||
dispatch('submit', { input: inputValue, value: value });
|
||||
}}>{searchSuggestion.name}</button
|
||||
>
|
||||
</li>
|
||||
@ -76,6 +87,7 @@
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<!-- close the search suggestions box when clicking outside -->
|
||||
{#if inputValue && searchSuggestions.length !== 0}
|
||||
<button
|
||||
class="absolute top-0 left-0 z-10 w-full h-full cursor-default"
|
||||
|
@ -56,7 +56,7 @@ export class Report extends Model {
|
||||
@Column({ type: DataTypes.INTEGER, allowNull: false })
|
||||
@ForeignKey(() => User)
|
||||
declare reporter_id: number;
|
||||
@Column({ type: DataTypes.INTEGER, allowNull: false })
|
||||
@Column({ type: DataTypes.INTEGER })
|
||||
@ForeignKey(() => User)
|
||||
declare reported_id: number;
|
||||
@Column({ type: DataTypes.INTEGER })
|
||||
|
18
src/lib/utils.ts
Normal file
18
src/lib/utils.ts
Normal file
@ -0,0 +1,18 @@
|
||||
import { env } from '$env/dynamic/public';
|
||||
|
||||
export async function usernameSuggestions(
|
||||
username: string
|
||||
): Promise<{ name: string; value: string }[]> {
|
||||
const response = await fetch(`${env.PUBLIC_BASE_PATH}/admin/users`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
limit: 6,
|
||||
search: username,
|
||||
slim: true
|
||||
})
|
||||
});
|
||||
const json: { username: string; uuid: string }[] = await response.json();
|
||||
return json.map((v) => {
|
||||
return { name: v.username, value: v.uuid };
|
||||
});
|
||||
}
|
@ -13,6 +13,8 @@
|
||||
import NewReportModal from './NewReportModal.svelte';
|
||||
import { onDestroy, onMount } from 'svelte';
|
||||
import { goto } from '$app/navigation';
|
||||
import Search from '$lib/components/Input/Search.svelte';
|
||||
import { usernameSuggestions } from '$lib/utils';
|
||||
|
||||
export let data: PageData;
|
||||
|
||||
@ -83,7 +85,8 @@
|
||||
auditor: data.self?.id || -1,
|
||||
notice: activeReport.notice || '',
|
||||
statement: activeReport.statement || '',
|
||||
status: activeReport.status
|
||||
status: activeReport.status,
|
||||
reported: activeReport.reported?.uuid || null
|
||||
})
|
||||
});
|
||||
}
|
||||
@ -93,7 +96,7 @@
|
||||
</script>
|
||||
|
||||
<div class="h-screen flex flex-row">
|
||||
<div class="w-full flex flex-col">
|
||||
<div class="w-full flex flex-col overflow-scroll">
|
||||
<HeaderBar bind:reportFilter />
|
||||
<hr class="divider my-1 mx-8 border-none" />
|
||||
<table class="table table-fixed h-fit">
|
||||
@ -140,14 +143,17 @@
|
||||
</button>
|
||||
</td>
|
||||
<td>
|
||||
{report.reported.username}
|
||||
{report.reported?.username || ''}
|
||||
{#if report.reported?.id}
|
||||
<button
|
||||
class="pl-1"
|
||||
title="Nach Reportetem Spieler filtern"
|
||||
on:click|stopPropagation={() => (reportFilter.reported = report.reported.username)}
|
||||
on:click|stopPropagation={() =>
|
||||
(reportFilter.reported = report.reported.username)}
|
||||
>
|
||||
<IconOutline name="magnifying-glass-outline" width="14" height="14" />
|
||||
</button>
|
||||
{/if}
|
||||
</td>
|
||||
<td
|
||||
>{new Intl.DateTimeFormat('de-DE', {
|
||||
@ -185,7 +191,7 @@
|
||||
</div>
|
||||
{#if activeReport}
|
||||
<div
|
||||
class="relative flex flex-col w-2/5 bg-base-200/50 px-4 py-6 overflow-scroll"
|
||||
class="relative flex flex-col w-2/5 h-screen 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">
|
||||
@ -226,19 +232,26 @@
|
||||
}}>✕</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>
|
||||
<h3 class="font-roboto font-semibold text-2xl mb-2">Report</h3>
|
||||
<div class="w-full">
|
||||
<Input readonly={true} size="sm" value={activeReport.reporter.username} pickyWidth={false}>
|
||||
<span slot="label">Reporter</span>
|
||||
</Input>
|
||||
<Search
|
||||
size="sm"
|
||||
suggestionRequired={true}
|
||||
emptyAllowed={true}
|
||||
searchSuggestionFunc={usernameSuggestions}
|
||||
invalidMessage="Es können nur registrierte Spieler reportet werden"
|
||||
label="Reporteter User"
|
||||
inputValue={activeReport.reported?.username || ''}
|
||||
on:submit={(e) =>
|
||||
(activeReport.reported = {
|
||||
...activeReport.reported,
|
||||
username: e.detail.input,
|
||||
uuid: e.detail.value
|
||||
})}
|
||||
/>
|
||||
<Textarea readonly={true} rows={1} label="Report Grund" value={activeReport.subject} />
|
||||
<Textarea readonly={true} rows={4} label="Report Details" value={activeReport.body} />
|
||||
</div>
|
||||
@ -301,6 +314,11 @@
|
||||
value="Speichern"
|
||||
on:click={async () => {
|
||||
await updateActiveReport();
|
||||
if (activeReport.reported?.username && activeReport.reported?.id === undefined) {
|
||||
activeReport.reported.id = -1;
|
||||
} else {
|
||||
activeReport.reported = undefined;
|
||||
}
|
||||
currentPageReports = [...currentPageReports];
|
||||
if (activeReport.originalStatus !== 'reviewed' && activeReport.status === 'reviewed') {
|
||||
$reportCount -= 1;
|
||||
|
@ -92,6 +92,7 @@ export const PATCH = (async ({ request, cookies }) => {
|
||||
|
||||
const data: {
|
||||
id: number;
|
||||
reported: string | null;
|
||||
auditor: number;
|
||||
notice: string | null;
|
||||
statement: string | null;
|
||||
@ -102,9 +103,13 @@ export const PATCH = (async ({ request, cookies }) => {
|
||||
|
||||
const report = await Report.findOne({ where: { id: data.id } });
|
||||
const admin = await Admin.findOne({ where: { id: data.auditor } });
|
||||
if (report === null || (admin === null && data.auditor != -1))
|
||||
const reported = data.reported
|
||||
? await User.findOne({ where: { uuid: data.reported } })
|
||||
: undefined;
|
||||
if (report === null || (admin === null && data.auditor != -1) || reported === null)
|
||||
return new Response(null, { status: 400 });
|
||||
|
||||
report.reported_id = reported?.id || null;
|
||||
if (data.notice != null) report.notice = data.notice;
|
||||
if (data.statement != null) report.statement = data.statement;
|
||||
if (data.status != null) report.status = data.status;
|
||||
@ -115,7 +120,7 @@ export const PATCH = (async ({ request, cookies }) => {
|
||||
return new Response();
|
||||
}) satisfies RequestHandler;
|
||||
|
||||
export const PUT = (async ({ request, cookies, url }) => {
|
||||
export const PUT = (async ({ request, cookies }) => {
|
||||
if (getSession(cookies, { permissions: [Permissions.ReportWrite] }) == null) {
|
||||
return new Response(null, {
|
||||
status: 401
|
||||
@ -124,23 +129,18 @@ export const PUT = (async ({ request, cookies, url }) => {
|
||||
|
||||
const data: {
|
||||
reporter: string;
|
||||
reported: string;
|
||||
reported: string | null;
|
||||
reason: string;
|
||||
body: string | null;
|
||||
} = await request.json();
|
||||
|
||||
if (
|
||||
data.reporter == null ||
|
||||
data.reported == null ||
|
||||
data.reason == null ||
|
||||
data.body === undefined
|
||||
)
|
||||
if (data.reporter == null || data.reason == null || data.body === undefined)
|
||||
return new Response(null, { status: 400 });
|
||||
|
||||
const reporter = await User.findOne({ where: { uuid: data.reporter } });
|
||||
const reported = await User.findOne({ where: { uuid: data.reported } });
|
||||
const reported = data.reported ? await User.findOne({ where: { uuid: data.reported } }) : null;
|
||||
|
||||
if (reporter == null || reported == null) return new Response(null, { status: 400 });
|
||||
if (reporter == null) return new Response(null, { status: 400 });
|
||||
|
||||
const report = await Report.create({
|
||||
subject: data.reason,
|
||||
@ -150,10 +150,12 @@ export const PUT = (async ({ request, cookies, url }) => {
|
||||
url_hash: crypto.randomBytes(18).toString('hex'),
|
||||
completed: false,
|
||||
reporter_id: reporter.id,
|
||||
reported_id: reported.id
|
||||
reported_id: reported?.id || null
|
||||
});
|
||||
report.dataValues.reporter = await User.findOne({ where: { id: report.reporter_id } });
|
||||
report.dataValues.reported = await User.findOne({ where: { id: report.reported_id } });
|
||||
report.dataValues.reported = report.reported_id
|
||||
? await User.findOne({ where: { id: report.reported_id } })
|
||||
: null;
|
||||
report.dataValues.auditor = null;
|
||||
|
||||
return new Response(JSON.stringify(report), {
|
||||
|
@ -4,6 +4,7 @@
|
||||
import Textarea from '$lib/components/Input/Textarea.svelte';
|
||||
import Search from '$lib/components/Input/Search.svelte';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import { usernameSuggestions } from '$lib/utils';
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
@ -12,27 +13,12 @@
|
||||
let reason = '';
|
||||
let body = '';
|
||||
|
||||
async function usernameSuggestions(username: string): Promise<{ name: string; value: string }[]> {
|
||||
const response = await fetch(`${env.PUBLIC_BASE_PATH}/admin/users`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
limit: 6,
|
||||
search: username,
|
||||
slim: true
|
||||
})
|
||||
});
|
||||
const json: { username: string; uuid: string }[] = await response.json();
|
||||
return json.map((v) => {
|
||||
return { name: v.username, value: v.uuid };
|
||||
});
|
||||
}
|
||||
|
||||
async function newReport() {
|
||||
const response = await fetch(`${env.PUBLIC_BASE_PATH}/admin/reports`, {
|
||||
method: 'PUT',
|
||||
body: JSON.stringify({
|
||||
reporter: reporter,
|
||||
reported: reported,
|
||||
reported: reported || null,
|
||||
reason: reason,
|
||||
body: body || null
|
||||
})
|
||||
@ -66,10 +52,10 @@
|
||||
<Search
|
||||
size="sm"
|
||||
suggestionRequired={true}
|
||||
emptyAllowed={true}
|
||||
searchSuggestionFunc={usernameSuggestions}
|
||||
invalidMessage="Es können nur registrierte Spieler reportet werden"
|
||||
label="Reporteter User"
|
||||
required={true}
|
||||
bind:value={reported}
|
||||
/>
|
||||
</div>
|
||||
|
@ -8,15 +8,14 @@ export const POST = (async ({ request, url }) => {
|
||||
if (env.REPORT_SECRET && url.searchParams.get('secret') !== env.REPORT_SECRET)
|
||||
return new Response(null, { status: 401 });
|
||||
|
||||
const data: { reporter: string; reported: string; reason: string } = await request.json();
|
||||
const data: { reporter: string; reported: string | null; reason: string } = await request.json();
|
||||
|
||||
if (data.reporter == null || data.reported == null || data.reason == null)
|
||||
return new Response(null, { status: 400 });
|
||||
if (data.reporter == null || data.reason == null) return new Response(null, { status: 400 });
|
||||
|
||||
const reporter = await User.findOne({ where: { uuid: data.reporter } });
|
||||
const reported = await User.findOne({ where: { uuid: data.reported } });
|
||||
const reported = data.reported ? await User.findOne({ where: { uuid: data.reported } }) : null;
|
||||
|
||||
if (reporter == null || reported == null) return new Response(null, { status: 400 });
|
||||
if (reporter == null) return new Response(null, { status: 400 });
|
||||
|
||||
const report = await Report.create({
|
||||
subject: data.reason,
|
||||
@ -26,7 +25,7 @@ export const POST = (async ({ request, url }) => {
|
||||
url_hash: crypto.randomBytes(18).toString('hex'),
|
||||
completed: false,
|
||||
reporter_id: reporter.id,
|
||||
reported_id: reported.id
|
||||
reported_id: reported?.id || null
|
||||
});
|
||||
|
||||
return new Response(
|
||||
|
@ -22,7 +22,7 @@ export const load: PageServerLoad = async ({ params }) => {
|
||||
name: report.reporter.username
|
||||
},
|
||||
reported: {
|
||||
name: report.reported.username
|
||||
name: report.reported?.username || null
|
||||
}
|
||||
};
|
||||
};
|
||||
|
@ -18,6 +18,7 @@
|
||||
<div class="col-[1] row-[1]" transition:fly={{ x: -200, duration: 300 }}>
|
||||
<ReportDraft
|
||||
reason={data.reason}
|
||||
reporterName={data.reporter.name}
|
||||
reportedName={data.reported.name}
|
||||
on:submit={() => (data.draft = false)}
|
||||
/>
|
||||
|
@ -5,7 +5,8 @@
|
||||
import { page } from '$app/stores';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
|
||||
export let reportedName: string;
|
||||
export let reporterName: string;
|
||||
export let reportedName: string | null;
|
||||
export let reason: string;
|
||||
|
||||
let body: string;
|
||||
@ -25,7 +26,10 @@
|
||||
</script>
|
||||
|
||||
<div>
|
||||
<h2 class="text-3xl text-center">Report für <code>{reportedName}</code></h2>
|
||||
<h2 class="text-3xl text-center">
|
||||
Report von <span class="underline">{reporterName}</span> gegen
|
||||
<span class="underline">{reportedName || 'unbekannt'}</span>
|
||||
</h2>
|
||||
<form on:submit|preventDefault={() => submitModal.show()}>
|
||||
<div class="space-y-4 my-4">
|
||||
<div>
|
||||
|
Loading…
x
Reference in New Issue
Block a user