add public feedback/contact option
All checks were successful
delpoy / build-and-deploy (push) Successful in 1m5s
All checks were successful
delpoy / build-and-deploy (push) Successful in 1m5s
This commit is contained in:
parent
ceaf006dd5
commit
55798fd294
@ -99,13 +99,13 @@ export class StrikePunishment extends Model {
|
|||||||
|
|
||||||
@Table({ modelName: 'feedback', underscored: true })
|
@Table({ modelName: 'feedback', underscored: true })
|
||||||
export class Feedback extends Model {
|
export class Feedback extends Model {
|
||||||
@Column({ type: DataTypes.STRING, allowNull: false, unique: true })
|
|
||||||
@Index
|
|
||||||
declare url_hash: string;
|
|
||||||
@Column({ type: DataTypes.STRING, allowNull: false })
|
@Column({ type: DataTypes.STRING, allowNull: false })
|
||||||
declare event: string;
|
declare event: string;
|
||||||
@Column({ type: DataTypes.STRING })
|
@Column({ type: DataTypes.STRING })
|
||||||
declare content: string;
|
declare content: string;
|
||||||
|
@Column({ type: DataTypes.STRING, allowNull: false, unique: true })
|
||||||
|
@Index
|
||||||
|
declare url_hash: string;
|
||||||
@Column({ type: DataTypes.INTEGER })
|
@Column({ type: DataTypes.INTEGER })
|
||||||
@ForeignKey(() => User)
|
@ForeignKey(() => User)
|
||||||
declare user_id: number;
|
declare user_id: number;
|
||||||
|
@ -29,6 +29,12 @@
|
|||||||
href: `${env.PUBLIC_BASE_PATH}/faq`,
|
href: `${env.PUBLIC_BASE_PATH}/faq`,
|
||||||
active: false
|
active: false
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: 'Feedback & Kontakt',
|
||||||
|
sprite: `${env.PUBLIC_BASE_PATH}/img/menu-feedback.png`,
|
||||||
|
href: `${env.PUBLIC_BASE_PATH}/feedback`,
|
||||||
|
active: false
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'Team',
|
name: 'Team',
|
||||||
sprite: `${env.PUBLIC_BASE_PATH}/img/menu-team.png`,
|
sprite: `${env.PUBLIC_BASE_PATH}/img/menu-team.png`,
|
||||||
|
5
src/routes/feedback/+layout.svelte
Normal file
5
src/routes/feedback/+layout.svelte
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<div class="flex justify-center items-center w-full min-h-screen h-full">
|
||||||
|
<div class="absolute top-12 grid card w-11/12 xl:w-2/3 2xl:w-1/2 p-6 shadow-lg">
|
||||||
|
<slot />
|
||||||
|
</div>
|
||||||
|
</div>
|
122
src/routes/feedback/+page.svelte
Normal file
122
src/routes/feedback/+page.svelte
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import Input from '$lib/components/Input/Input.svelte';
|
||||||
|
import Textarea from '$lib/components/Input/Textarea.svelte';
|
||||||
|
import { env } from '$env/dynamic/public';
|
||||||
|
import Select from '$lib/components/Input/Select.svelte';
|
||||||
|
|
||||||
|
let content = '';
|
||||||
|
let type = 'feedback';
|
||||||
|
|
||||||
|
let submitModal: HTMLDialogElement;
|
||||||
|
let sentModal: HTMLDialogElement & { type?: string } = {};
|
||||||
|
|
||||||
|
async function submitFeedback() {
|
||||||
|
await fetch(`${env.PUBLIC_BASE_PATH}/feedback`, {
|
||||||
|
method: 'POST',
|
||||||
|
body: JSON.stringify({
|
||||||
|
content: content,
|
||||||
|
type: type
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<svelte:head>
|
||||||
|
<title>Feedback & Kontakt</title>
|
||||||
|
</svelte:head>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h2 class="text-3xl text-center">Feedback & Kontakt</h2>
|
||||||
|
<form on:submit|preventDefault={() => submitModal.show()}>
|
||||||
|
<div class="space-y-4 mt-6 mb-4">
|
||||||
|
<Select size="sm" bind:value={type}>
|
||||||
|
<option value="feedback">Feedback</option>
|
||||||
|
<option value="contact">Kontakt</option>
|
||||||
|
</Select>
|
||||||
|
<Textarea
|
||||||
|
required={true}
|
||||||
|
rows={4}
|
||||||
|
label={type === 'feedback' ? 'Feedback' : 'Anfrage'}
|
||||||
|
bind:value={content}
|
||||||
|
/>
|
||||||
|
<div>
|
||||||
|
<Input type="submit" disabled={type === '' || content === ''} value="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">
|
||||||
|
{type === 'feedback' ? 'Feedback' : 'Kontaktanfrage'} abschicken?
|
||||||
|
</h3>
|
||||||
|
<div class="my-4">
|
||||||
|
{#if type === 'feedback'}
|
||||||
|
<p>Nach dem Abschicken des Feedbacks lässt es sich nicht mehr bearbeiten.</p>
|
||||||
|
{:else}
|
||||||
|
<p>
|
||||||
|
Bitte hinterlege eine Rückmeldemöglichkeit in deiner Anfrage. Nachdem sie abgeschickt
|
||||||
|
wurde, kannst du die Nachricht nicht mehr bearbeiten.
|
||||||
|
</p>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
<div class="flex flex-row space-x-1">
|
||||||
|
<Input
|
||||||
|
type="submit"
|
||||||
|
value="Abschicken"
|
||||||
|
on:click={async () => {
|
||||||
|
await submitFeedback();
|
||||||
|
sentModal.type = type;
|
||||||
|
sentModal.show();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<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>
|
||||||
|
|
||||||
|
<dialog class="modal" bind:this={sentModal}>
|
||||||
|
<form
|
||||||
|
method="dialog"
|
||||||
|
class="modal-box"
|
||||||
|
on:submit={() => {
|
||||||
|
content = '';
|
||||||
|
type = 'feedback';
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<button class="btn btn-sm btn-circle btn-ghost absolute right-2 top-2">✕</button>
|
||||||
|
<div>
|
||||||
|
<h3 class="font-roboto font-medium text-xl">
|
||||||
|
{sentModal.type === 'feedback' ? 'Feedback' : 'Kontaktanfrage'} abgeschickt
|
||||||
|
</h3>
|
||||||
|
<div class="my-4">
|
||||||
|
{#if type === 'feedback'}
|
||||||
|
<p>Dein Feedback wurde abgeschickt.</p>
|
||||||
|
{:else}
|
||||||
|
<p>
|
||||||
|
Deine Kontaktanfrage wurde abgeschickt. Jemand aus dem Team wird sich nächstmöglich bei
|
||||||
|
Dir melden.
|
||||||
|
</p>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
<Input type="submit" value="Schließen" />
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<form
|
||||||
|
method="dialog"
|
||||||
|
class="modal-backdrop bg-[rgba(0,0,0,.3)]"
|
||||||
|
on:submit={() => {
|
||||||
|
content = '';
|
||||||
|
type = 'feedback';
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<button>close</button>
|
||||||
|
</form>
|
||||||
|
</dialog>
|
@ -1,7 +0,0 @@
|
|||||||
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}/`);
|
|
||||||
};
|
|
20
src/routes/feedback/+server.ts
Normal file
20
src/routes/feedback/+server.ts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import type { RequestHandler } from '@sveltejs/kit';
|
||||||
|
import { Feedback } from '$lib/server/database';
|
||||||
|
import { FeedbackSubmitSchema } from './schema';
|
||||||
|
import crypto from 'crypto';
|
||||||
|
|
||||||
|
export const POST = (async ({ request }) => {
|
||||||
|
const parseResult = await FeedbackSubmitSchema.safeParseAsync(await request.json());
|
||||||
|
if (!parseResult.success) {
|
||||||
|
return new Response(null, { status: 400 });
|
||||||
|
}
|
||||||
|
const data = parseResult.data;
|
||||||
|
|
||||||
|
await Feedback.create({
|
||||||
|
event: `website-${data.type}`,
|
||||||
|
content: data.content,
|
||||||
|
url_hash: crypto.randomBytes(18).toString('hex')
|
||||||
|
});
|
||||||
|
|
||||||
|
return new Response(null, { status: 200 });
|
||||||
|
}) satisfies RequestHandler;
|
@ -1,3 +0,0 @@
|
|||||||
<div class="flex justify-center items-center w-full min-h-screen h-full">
|
|
||||||
<slot />
|
|
||||||
</div>
|
|
@ -13,7 +13,6 @@
|
|||||||
<meta name="robots" content="noindex" />
|
<meta name="robots" content="noindex" />
|
||||||
</svelte:head>
|
</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}
|
{#if data.draft}
|
||||||
<div class="col-[1] row-[1]" transition:fly={{ x: -200, duration: 300 }}>
|
<div class="col-[1] row-[1]" transition:fly={{ x: -200, duration: 300 }}>
|
||||||
<FeedbackDraft
|
<FeedbackDraft
|
||||||
@ -27,4 +26,3 @@
|
|||||||
<FeedbackSent />
|
<FeedbackSent />
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
|
||||||
|
6
src/routes/feedback/schema.ts
Normal file
6
src/routes/feedback/schema.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import { z } from 'zod';
|
||||||
|
|
||||||
|
export const FeedbackSubmitSchema = z.object({
|
||||||
|
content: z.string(),
|
||||||
|
type: z.enum(['feedback', 'contact'])
|
||||||
|
});
|
BIN
static/img/menu-feedback.png
Normal file
BIN
static/img/menu-feedback.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 152 B |
Loading…
x
Reference in New Issue
Block a user