add feedback endpoint (#28) and some other stuff
All checks were successful
delpoy / build-and-deploy (push) Successful in 1m11s
All checks were successful
delpoy / build-and-deploy (push) Successful in 1m11s
This commit is contained in:
7
src/routes/feedback/+page.ts
Normal file
7
src/routes/feedback/+page.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import type { PageLoad } from './$types';
|
||||
import { redirect } from '@sveltejs/kit';
|
||||
import { env } from '$env/dynamic/public';
|
||||
|
||||
export const load: PageLoad = async () => {
|
||||
throw redirect(302, `${env.PUBLIC_BASE_PATH}/`);
|
||||
};
|
||||
3
src/routes/feedback/[...url_hash]/+layout.svelte
Normal file
3
src/routes/feedback/[...url_hash]/+layout.svelte
Normal file
@@ -0,0 +1,3 @@
|
||||
<div class="flex justify-center items-center w-full min-h-screen h-full">
|
||||
<slot />
|
||||
</div>
|
||||
18
src/routes/feedback/[...url_hash]/+page.server.ts
Normal file
18
src/routes/feedback/[...url_hash]/+page.server.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import type { PageServerLoad } from './$types';
|
||||
import { Feedback } from '$lib/server/database';
|
||||
import { redirect } from '@sveltejs/kit';
|
||||
import { env } from '$env/dynamic/public';
|
||||
|
||||
export const load: PageServerLoad = async ({ params }) => {
|
||||
const feedback = await Feedback.findOne({
|
||||
where: { url_hash: params.url_hash }
|
||||
});
|
||||
|
||||
if (!feedback) throw redirect(302, `${env.PUBLIC_BASE_PATH}/`);
|
||||
|
||||
return {
|
||||
draft: feedback.content === null,
|
||||
event: feedback.event,
|
||||
anonymous: feedback.user_id === null
|
||||
};
|
||||
};
|
||||
30
src/routes/feedback/[...url_hash]/+page.svelte
Normal file
30
src/routes/feedback/[...url_hash]/+page.svelte
Normal file
@@ -0,0 +1,30 @@
|
||||
<script lang="ts">
|
||||
import { fly } from 'svelte/transition';
|
||||
import type { PageData } from './$types';
|
||||
import FeedbackDraft from './FeedbackDraft.svelte';
|
||||
import FeedbackSent from './FeedbackSent.svelte';
|
||||
|
||||
export let data: PageData;
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>Feedback</title>
|
||||
<!-- 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 }}>
|
||||
<FeedbackDraft
|
||||
event={data.event}
|
||||
anonymous={data.anonymous}
|
||||
on:submit={() => (data.draft = false)}
|
||||
/>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="col-[1] row-[1]" transition:fly={{ x: 200, duration: 300 }}>
|
||||
<FeedbackSent />
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
24
src/routes/feedback/[...url_hash]/+server.ts
Normal file
24
src/routes/feedback/[...url_hash]/+server.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import type { RequestHandler } from '@sveltejs/kit';
|
||||
import { Feedback } from '$lib/server/database';
|
||||
import { FeedbackSubmitSchema } from './schema';
|
||||
|
||||
export const POST = (async ({ request, params }) => {
|
||||
const feedback = await Feedback.findOne({ where: { url_hash: params.url_hash } });
|
||||
|
||||
if (feedback == null) return new Response(null, { status: 400 });
|
||||
|
||||
const parseResult = await FeedbackSubmitSchema.safeParseAsync(await request.json());
|
||||
if (!parseResult.success) {
|
||||
return new Response(null, { status: 400 });
|
||||
}
|
||||
const data = parseResult.data;
|
||||
|
||||
feedback.content = data.content;
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
if (data.anonymous) feedback.user_id = null;
|
||||
|
||||
await feedback.save();
|
||||
|
||||
return new Response(null, { status: 200 });
|
||||
}) satisfies RequestHandler;
|
||||
78
src/routes/feedback/[...url_hash]/FeedbackDraft.svelte
Normal file
78
src/routes/feedback/[...url_hash]/FeedbackDraft.svelte
Normal file
@@ -0,0 +1,78 @@
|
||||
<script lang="ts">
|
||||
import Input from '$lib/components/Input/Input.svelte';
|
||||
import Textarea from '$lib/components/Input/Textarea.svelte';
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import { env } from '$env/dynamic/public';
|
||||
import { page } from '$app/stores';
|
||||
|
||||
export let event: string;
|
||||
export let anonymous: boolean;
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
let content = '';
|
||||
let sendAnonymous = true;
|
||||
|
||||
let submitModal: HTMLDialogElement;
|
||||
|
||||
async function submitFeedback() {
|
||||
await fetch(`${env.PUBLIC_BASE_PATH}/feedback/${$page.params.url_hash}`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
content: content,
|
||||
anonymous: sendAnonymous
|
||||
})
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<div>
|
||||
<h2 class="text-3xl text-center">Feedback</h2>
|
||||
<form on:submit|preventDefault={() => submitModal.show()}>
|
||||
<div class="space-y-4 my-4">
|
||||
<Input size="sm" pickyWidth={false} disabled value={event}>
|
||||
<span slot="label">Event</span>
|
||||
</Input>
|
||||
<Textarea required={true} rows={4} label="Feedback" bind:value={content} />
|
||||
<div>
|
||||
<Input type="submit" disabled={content === ''} value="Feedback senden" />
|
||||
</div>
|
||||
</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">Feedback abschicken?</h3>
|
||||
<div class="my-4">
|
||||
<p>Nach dem Abschicken des Feedbacks lässt es sich nicht mehr bearbeiten.</p>
|
||||
{#if !anonymous}
|
||||
<div class="flex items-center gap-2 mt-2">
|
||||
<Input type="checkbox" id="anonymous" size="xs" checked />
|
||||
<label
|
||||
for="anonymous"
|
||||
title="Dein Spielername wird nach dem Abschicken nicht mit dem Feedback zusammen gespeichert"
|
||||
>Anonym senden</label
|
||||
>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
<div class="flex flex-row space-x-1">
|
||||
<Input
|
||||
type="submit"
|
||||
value="Abschicken"
|
||||
on:click={async () => {
|
||||
await submitFeedback();
|
||||
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>
|
||||
4
src/routes/feedback/[...url_hash]/FeedbackSent.svelte
Normal file
4
src/routes/feedback/[...url_hash]/FeedbackSent.svelte
Normal file
@@ -0,0 +1,4 @@
|
||||
<div>
|
||||
<h2 class="text-2xl text-center">Feedback abgeschickt</h2>
|
||||
<p class="mt-4">Das Feedback wurde abgeschickt.</p>
|
||||
</div>
|
||||
6
src/routes/feedback/[...url_hash]/schema.ts
Normal file
6
src/routes/feedback/[...url_hash]/schema.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
export const FeedbackSubmitSchema = z.object({
|
||||
content: z.string(),
|
||||
anonymous: z.boolean()
|
||||
});
|
||||
Reference in New Issue
Block a user