add report page
This commit is contained in:
parent
c57e514613
commit
37c230575d
@ -10,6 +10,7 @@
|
||||
export let required = false;
|
||||
export let disabled = false;
|
||||
export let size: 'xs' | 'sm' | 'md' | 'lg' = 'md';
|
||||
export let pickyWidth = true;
|
||||
|
||||
export let inputElement: HTMLInputElement | undefined = undefined;
|
||||
|
||||
@ -60,7 +61,10 @@
|
||||
</span>
|
||||
</label>
|
||||
{/if}
|
||||
<div class="relative flex items-center" class:sm:max-w-[16rem]={type !== 'checkbox'}>
|
||||
<div
|
||||
class="relative flex items-center"
|
||||
class:sm:max-w-[16rem]={type !== 'checkbox' && pickyWidth}
|
||||
>
|
||||
<input
|
||||
class:checkbox={type === 'checkbox'}
|
||||
class:checkbox-xs={type === 'checkbox' && size === 'xs'}
|
||||
|
42
src/lib/components/Input/Textarea.svelte
Normal file
42
src/lib/components/Input/Textarea.svelte
Normal file
@ -0,0 +1,42 @@
|
||||
<script lang="ts">
|
||||
export let id: string | null = null;
|
||||
export let name: string | null = null;
|
||||
export let value: string | null = null;
|
||||
export let label: string | null = null;
|
||||
export let notice: string | null = null;
|
||||
export let required = false;
|
||||
export let disabled = false;
|
||||
export let size: 'xs' | 'sm' | 'md' | 'lg' = 'md';
|
||||
export let rows = 2;
|
||||
</script>
|
||||
|
||||
<div>
|
||||
{#if label}
|
||||
<label class="label" for={id}>
|
||||
<span class="label-text">
|
||||
{label}
|
||||
{#if required}
|
||||
<span class="text-red-700">*</span>
|
||||
{/if}
|
||||
</span>
|
||||
</label>
|
||||
{/if}
|
||||
<textarea
|
||||
class="textarea textarea-bordered w-full"
|
||||
class:textarea-xs={size === 'xs'}
|
||||
class:textarea-sm={size === 'sm'}
|
||||
class:textarea-md={size === 'md'}
|
||||
class:textarea-lg={size === 'lg'}
|
||||
{id}
|
||||
{name}
|
||||
{required}
|
||||
{disabled}
|
||||
{rows}
|
||||
bind:value
|
||||
/>
|
||||
{#if notice}
|
||||
<label class="label" for={id}>
|
||||
<span class="label-text-alt">{notice}</span>
|
||||
</label>
|
||||
{/if}
|
||||
</div>
|
@ -5,15 +5,17 @@ import * as bcrypt from 'bcrypt';
|
||||
import {
|
||||
BeforeCreate,
|
||||
BeforeUpdate,
|
||||
BelongsTo,
|
||||
Column,
|
||||
ForeignKey,
|
||||
Index,
|
||||
Model,
|
||||
Sequelize,
|
||||
Table,
|
||||
Unique
|
||||
Table
|
||||
} from 'sequelize-typescript';
|
||||
import { Permissions } from '$lib/permissions';
|
||||
|
||||
@Table({ modelName: 'user' })
|
||||
@Table({ modelName: 'user', underscored: true })
|
||||
export class User extends Model {
|
||||
@Column({ type: DataTypes.STRING, allowNull: false })
|
||||
declare firstname: string;
|
||||
@ -30,10 +32,37 @@ export class User extends Model {
|
||||
@Column({ type: DataTypes.STRING })
|
||||
declare password: string;
|
||||
@Column({ type: DataTypes.UUIDV4 })
|
||||
@Index
|
||||
declare uuid: string;
|
||||
}
|
||||
|
||||
@Table({ modelName: 'admin' })
|
||||
@Table({ modelName: 'report', underscored: true })
|
||||
export class Report extends Model {
|
||||
@Column({ type: DataTypes.STRING, allowNull: false, unique: true })
|
||||
@Index
|
||||
declare url_id: string;
|
||||
@Column({ type: DataTypes.STRING, allowNull: false })
|
||||
declare subject: string;
|
||||
@Column({ type: DataTypes.STRING })
|
||||
declare body: string;
|
||||
@Column({ type: DataTypes.BOOLEAN, allowNull: false })
|
||||
declare draft: boolean;
|
||||
@Column({ type: DataTypes.BOOLEAN, allowNull: false })
|
||||
declare completed: boolean;
|
||||
@Column({ type: DataTypes.INTEGER, allowNull: false })
|
||||
@ForeignKey(() => User)
|
||||
declare reporter_user_id: number;
|
||||
@Column({ type: DataTypes.INTEGER, allowNull: false })
|
||||
@ForeignKey(() => User)
|
||||
declare reported_user_id: number;
|
||||
|
||||
@BelongsTo(() => User)
|
||||
declare reporter: User;
|
||||
@BelongsTo(() => User)
|
||||
declare reported: User;
|
||||
}
|
||||
|
||||
@Table({ modelName: 'admin', underscored: true })
|
||||
export class Admin extends Model {
|
||||
@Column({ type: DataTypes.STRING, allowNull: false, unique: true })
|
||||
declare username: string;
|
||||
@ -68,5 +97,5 @@ export class Admin extends Model {
|
||||
export const sequelize = new Sequelize(building ? 'sqlite::memory:' : env.DATABASE_URI, {
|
||||
// only log sql queries in dev mode
|
||||
logging: dev ? console.log : false,
|
||||
models: [User, Admin]
|
||||
models: [User, Report, Admin]
|
||||
});
|
||||
|
@ -1,3 +1,3 @@
|
||||
<div class="flex justify-center items-center w-full">
|
||||
<div class="flex justify-center w-full">
|
||||
<slot />
|
||||
</div>
|
||||
|
5
src/routes/report/+page.svelte
Normal file
5
src/routes/report/+page.svelte
Normal file
@ -0,0 +1,5 @@
|
||||
<div class="h-screen flex justify-center items-center">
|
||||
<h1 class="text-4xl">
|
||||
Reports können nur ingame mittels des <code>/report</code> Befehls erstellt werden
|
||||
</h1>
|
||||
</div>
|
32
src/routes/report/+server.ts
Normal file
32
src/routes/report/+server.ts
Normal file
@ -0,0 +1,32 @@
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
import { Report, User } from '$lib/server/database';
|
||||
import * as crypto from 'crypto';
|
||||
|
||||
export const POST = (async ({ request, url }) => {
|
||||
const data: { reporter: string; reported: string; reason: string } = await request.json();
|
||||
|
||||
if (data.reporter == undefined || data.reported == undefined || data.reason == 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 } });
|
||||
|
||||
if (reporter == null || reported == null) return new Response(null, { status: 400 });
|
||||
|
||||
const report = await Report.create({
|
||||
subject: data.reason,
|
||||
body: null,
|
||||
draft: true,
|
||||
url_id: crypto.randomBytes(18).toString('hex'),
|
||||
completed: false,
|
||||
reporter_user_id: reporter.id,
|
||||
reported_user_id: reported.id
|
||||
});
|
||||
|
||||
return new Response(
|
||||
JSON.stringify({ url: `${url.toString().replace(/\/$/, '')}/${report.url_id}` }),
|
||||
{
|
||||
status: 201
|
||||
}
|
||||
);
|
||||
}) satisfies RequestHandler;
|
3
src/routes/report/[...url_id]/+layout.svelte
Normal file
3
src/routes/report/[...url_id]/+layout.svelte
Normal file
@ -0,0 +1,3 @@
|
||||
<div class="flex justify-center items-center w-full min-h-screen h-full">
|
||||
<slot />
|
||||
</div>
|
28
src/routes/report/[...url_id]/+page.server.ts
Normal file
28
src/routes/report/[...url_id]/+page.server.ts
Normal file
@ -0,0 +1,28 @@
|
||||
import type { PageServerLoad } from './$types';
|
||||
import { Report, User } from '$lib/server/database';
|
||||
import { redirect } from '@sveltejs/kit';
|
||||
import { env } from '$env/dynamic/public';
|
||||
|
||||
export const load: PageServerLoad = async ({ params }) => {
|
||||
const report = await Report.findOne({
|
||||
where: { url_id: params.url_id },
|
||||
include: [
|
||||
{ model: User, as: 'reporter' },
|
||||
{ model: User, as: 'reported' }
|
||||
]
|
||||
});
|
||||
|
||||
if (report == null) throw redirect(302, `${env.PUBLIC_BASE_PATH}/`);
|
||||
|
||||
return {
|
||||
draft: report.draft,
|
||||
completed: report.completed,
|
||||
reason: report.subject,
|
||||
reporter: {
|
||||
name: report.reporter.username
|
||||
},
|
||||
reported: {
|
||||
name: report.reported.username
|
||||
}
|
||||
};
|
||||
};
|
32
src/routes/report/[...url_id]/+page.svelte
Normal file
32
src/routes/report/[...url_id]/+page.svelte
Normal file
@ -0,0 +1,32 @@
|
||||
<script lang="ts">
|
||||
import { fly } from 'svelte/transition';
|
||||
import type { PageData } from './$types';
|
||||
import ReportDraft from './ReportDraft.svelte';
|
||||
import ReportCompleted from './ReportCompleted.svelte';
|
||||
import ReportSubmitted from './ReportSubmitted.svelte';
|
||||
|
||||
export let data: PageData;
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<!-- just in case... -->
|
||||
<meta name="robots" content="noindex" />
|
||||
</svelte:head>
|
||||
|
||||
<div class="absolute top-12 grid card w-11/12 xl:w-2/3 2xl:w-1/2 p-6 shadow-lg">
|
||||
{#if data.draft}
|
||||
<div class="col-[1] row-[1]" transition:fly={{ x: -200, duration: 300 }}>
|
||||
<ReportDraft
|
||||
reason={data.reason}
|
||||
reportedName={data.reported.name}
|
||||
on:submit={() => (data.draft = false)}
|
||||
/>
|
||||
</div>
|
||||
{:else if data.completed}
|
||||
<ReportCompleted />
|
||||
{:else}
|
||||
<div class="col-[1] row-[1]" transition:fly={{ x: 200, duration: 300 }}>
|
||||
<ReportSubmitted />
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
13
src/routes/report/[...url_id]/+server.ts
Normal file
13
src/routes/report/[...url_id]/+server.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
import { Report } from '$lib/server/database';
|
||||
|
||||
export const POST = (async ({ params }) => {
|
||||
const report = await Report.findOne({ where: { url_id: params.url_id } });
|
||||
|
||||
if (report == null) return new Response(null, { status: 400 });
|
||||
|
||||
report.draft = false;
|
||||
await report.save();
|
||||
|
||||
return new Response(null, { status: 200 });
|
||||
}) satisfies RequestHandler;
|
3
src/routes/report/[...url_id]/ReportCompleted.svelte
Normal file
3
src/routes/report/[...url_id]/ReportCompleted.svelte
Normal file
@ -0,0 +1,3 @@
|
||||
<div>
|
||||
<h2 class="text-xl text-center">Dieser Report wurde von einem Admin bearbeitet</h2>
|
||||
</div>
|
67
src/routes/report/[...url_id]/ReportDraft.svelte
Normal file
67
src/routes/report/[...url_id]/ReportDraft.svelte
Normal file
@ -0,0 +1,67 @@
|
||||
<script lang="ts">
|
||||
import Textarea from '$lib/components/Input/Textarea.svelte';
|
||||
import Input from '$lib/components/Input/Input.svelte';
|
||||
import { env } from '$env/dynamic/public';
|
||||
import { page } from '$app/stores';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
|
||||
export let reportedName: string;
|
||||
export let reason: string;
|
||||
|
||||
async function submitReport() {
|
||||
await fetch(`${env.PUBLIC_BASE_PATH}/report/${$page.params.url_id}`, {
|
||||
method: 'POST'
|
||||
});
|
||||
}
|
||||
|
||||
let dispatch = createEventDispatcher();
|
||||
|
||||
let submitModal: HTMLDialogElement;
|
||||
</script>
|
||||
|
||||
<div>
|
||||
<h2 class="text-3xl text-center">Report für <code>{reportedName}</code></h2>
|
||||
<form on:submit|preventDefault={() => submitModal.show()}>
|
||||
<div class="space-y-4 my-4">
|
||||
<div>
|
||||
<Input type="text" value={reason} required={true} pickyWidth={false}>
|
||||
<span slot="label">Report Grund</span>
|
||||
</Input>
|
||||
</div>
|
||||
<div>
|
||||
<Textarea required={true} rows={4} label="Details über den Report Grund" />
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<Input type="submit" value="Reporten" />
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<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>
|
||||
<div>
|
||||
<h3 class="font-roboto font-medium text-xl">Report abschicken?</h3>
|
||||
<div class="my-4">
|
||||
<p>
|
||||
Nach dem Abschicken des Reports wird sich ein Moderator schnellstmöglich darum kümmern.
|
||||
</p>
|
||||
</div>
|
||||
<div class="flex flex-row space-x-1">
|
||||
<Input
|
||||
type="submit"
|
||||
value="Abschicken"
|
||||
on:click={async () => {
|
||||
await submitReport();
|
||||
dispatch('submit');
|
||||
}}
|
||||
/>
|
||||
<Input type="submit" value="Abbrechen" />
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
<form method="dialog" class="modal-backdrop bg-[rgba(0,0,0,.3)]">
|
||||
<button>close</button>
|
||||
</form>
|
||||
</dialog>
|
6
src/routes/report/[...url_id]/ReportSubmitted.svelte
Normal file
6
src/routes/report/[...url_id]/ReportSubmitted.svelte
Normal file
@ -0,0 +1,6 @@
|
||||
<div>
|
||||
<h2 class="text-2xl text-center">Report abgeschickt</h2>
|
||||
<p class="mt-4">
|
||||
Dein Report wurde abgeschickt und wird so schnell wie möglich von einem Admin bearbeitet.
|
||||
</p>
|
||||
</div>
|
@ -11,7 +11,10 @@ const config = {
|
||||
// adapter-auto only supports some environments, see https://kit.svelte.dev/docs/adapter-auto for a list.
|
||||
// If your environment is not supported or you settled on a specific environment, switch out the adapter.
|
||||
// See https://kit.svelte.dev/docs/adapters for more information about adapters.
|
||||
adapter: adapter()
|
||||
adapter: adapter(),
|
||||
csrf: {
|
||||
checkOrigin: false
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -8,5 +8,9 @@ export default {
|
||||
roboto: ['Roboto']
|
||||
}
|
||||
},
|
||||
plugins: [require('daisyui')]
|
||||
plugins: [require('daisyui')],
|
||||
|
||||
daisyui: {
|
||||
logs: false
|
||||
}
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user