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 })
|
||||
export class Feedback extends Model {
|
||||
@Column({ type: DataTypes.STRING, allowNull: false, unique: true })
|
||||
@Index
|
||||
declare url_hash: string;
|
||||
@Column({ type: DataTypes.STRING, allowNull: false })
|
||||
declare event: string;
|
||||
@Column({ type: DataTypes.STRING })
|
||||
declare content: string;
|
||||
@Column({ type: DataTypes.STRING, allowNull: false, unique: true })
|
||||
@Index
|
||||
declare url_hash: string;
|
||||
@Column({ type: DataTypes.INTEGER })
|
||||
@ForeignKey(() => User)
|
||||
declare user_id: number;
|
||||
|
@ -29,6 +29,12 @@
|
||||
href: `${env.PUBLIC_BASE_PATH}/faq`,
|
||||
active: false
|
||||
},
|
||||
{
|
||||
name: 'Feedback & Kontakt',
|
||||
sprite: `${env.PUBLIC_BASE_PATH}/img/menu-feedback.png`,
|
||||
href: `${env.PUBLIC_BASE_PATH}/feedback`,
|
||||
active: false
|
||||
},
|
||||
{
|
||||
name: 'Team',
|
||||
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,18 +13,16 @@
|
||||
<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>
|
||||
{#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}
|
||||
|
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