Files
website/src/routes/register/Register.svelte

338 lines
9.6 KiB
Svelte

<script lang="ts">
import Select from '$lib/components/Input/Select.svelte';
import Input from '$lib/components/Input/Input.svelte';
import { createEventDispatcher, onMount } from 'svelte';
import { env } from '$env/dynamic/public';
import { rulesShort } from '$lib/rules';
import { RegisterSchema } from './schema';
import { dev } from '$app/environment';
const dispatch = createEventDispatcher();
const modalTimeoutSeconds = dev ? 0 : 30;
// eslint-disable-next-line @typescript-eslint/no-empty-function
let checkInputs = () => {};
let playertype = 'java';
let firstnameInput: HTMLInputElement;
let lastnameInput: HTMLInputElement;
let birthdayInput: HTMLInputElement;
let phoneInput: HTMLInputElement;
let usernameInput: HTMLInputElement;
let privacyInput: HTMLInputElement;
let logsInput: HTMLInputElement;
let rulesInput: HTMLInputElement;
onMount(() => {
checkInputs = () => {
let allInputs = [
firstnameInput,
lastnameInput,
birthdayInput,
usernameInput,
privacyInput,
logsInput,
rulesInput
];
if (!allInputs.every((v) => v.value || v.checked)) {
inputsInvalidMessage = 'Bitte fülle alle erforderlichen Felder aus';
} else {
inputsInvalidMessage = null;
}
};
});
async function sendRegister() {
// eslint-disable-next-line no-async-promise-executor
registerRequest = new Promise<void>(async (resolve, reject) => {
const parseResult = RegisterSchema.safeParse(
Object.fromEntries(new FormData(document.forms[0]))
);
if (!parseResult.success) {
reject(Error(parseResult.error.issues.map((i) => i.message).join('\n\n')));
return;
}
const response = await fetch(`${env.PUBLIC_BASE_PATH}/register`, {
method: 'POST',
body: JSON.stringify(Object.fromEntries(new FormData(document.forms[0])))
});
if (response.ok) {
dispatch('submit', {
firstname: firstnameInput.value,
lastname: lastnameInput.value,
birthday: birthdayInput.valueAsDate,
phone: phoneInput.value,
username: usernameInput.value,
edition: playertype == 'java' ? 'Java (PC)' : 'Bedrock (Konsolen und Handys)'
});
resolve();
} else if (response.status < 500) {
reject(Error((await response.json()).message));
} else {
reject(Error(`${response.statusText} (${response.status})`));
}
}).catch((e) => {
errorMessage = (e as Error).message;
registerRequest = null;
});
}
let rulesAccepted = false;
let rulesModal: HTMLDialogElement;
let rulesModalSecondsOpened = 0;
let rulesModalTimer: number | NodeJS.Timeout | undefined = undefined;
let inputsInvalidMessage: string | null = 'Bitte fülle alle erforderlichen Felder aus';
let registerRequest: Promise<void> | null = null;
let errorMessage: string = '';
</script>
<h1 class="text-center text-3xl lg:text-5xl">Anmeldung</h1>
<form id="form" on:input={checkInputs} on:submit|preventDefault={sendRegister}>
<div class="divider">Persönliche Angaben</div>
<div class="mx-2 grid grid-cols-1 sm:grid-cols-2 gap-y-4">
<Input
id="firstname"
name="firstname"
type="text"
required={true}
bind:inputElement={firstnameInput}
>
<span slot="label">Vorname</span>
</Input>
<Input
id="lastname"
name="lastname"
type="text"
required={true}
bind:inputElement={lastnameInput}
>
<span slot="label">Nachname</span>
</Input>
<Input
id="birthday"
name="birthday"
type="date"
required={true}
bind:inputElement={birthdayInput}
>
<span slot="label">Geburtstag</span>
<span slot="notice">Die Angabe hat keine Auswirkungen auf das Spielgeschehen</span>
</Input>
<Input
id="telephone"
name="telephone"
type="tel"
bind:inputElement={phoneInput}
pattern={new RegExp(/^[+()\s/\d]+$/)}
>
<span slot="label">Telefonnummer</span>
<p slot="notice">
Diese nutzen wir, um Dich in der Whatsapp-Gruppe zuzuordnen und kontaktieren zu können.
<br />
<b>Die Angabe ist freiwillig, hilft den Administratoren jedoch sehr!</b>
</p>
</Input>
</div>
<div class="divider">Spiel</div>
<div class="mx-2 grid grid-cols-1 sm:grid-cols-2 gap-y-4">
<Input
id="username"
name="username"
type="text"
required={true}
bind:inputElement={usernameInput}
>
<span slot="label">Minecraft-Spielername</span>
</Input>
<Select
id="playertype"
name="playertype"
label="Edition"
bind:value={playertype}
required={true}
>
<option value="java">Java Edition (PC)</option>
<option value="bedrock">Bedrock Edition (Konsolen und Handys)</option>
</Select>
</div>
<div class="divider" />
<div class="mx-2 grid gap-y-3 mb-6">
<div class="flex gap-4">
<Input
id="privacy"
name="privacy"
type="checkbox"
required={true}
bind:inputElement={privacyInput}
/>
<label for="privacy">
<span>
Ich bin mit der Speicherung meiner in der Anmeldung angegebenen, persönlichen Daten
einverstanden. Siehe <a class="link" href="https://mhsl.eu/id.html" target="_blank"
>Datenschutz</a
>
</span>
<span class="text-red-700">*</span>
</label>
</div>
<div class="flex gap-4">
<Input id="logs" name="logs" type="checkbox" required={true} bind:inputElement={logsInput} />
<label for="logs">
<span>
Ich bin mit der Speicherung in Form von Logs aller meiner, beim Spielen anfallenden,
persönlichen Daten durch den Server einverstanden
</span>
<span class="text-red-700">*</span>
<br />
<p class="text-[.75rem]">
Dies betrifft jede Interaktion im Spiel und zugehörige Daten wie z.B. Chatnachrichten
welche vom Minecraft Client an den Server übermittelt werden
</p>
</label>
</div>
<div class="flex gap-4">
<Input
id="rules"
name="rules"
type="checkbox"
required={true}
on:input={(e) => {
if (!rulesAccepted) {
e.detail.target.checked = false;
rulesModal.show();
rulesModalTimer = setInterval(() => rulesModalSecondsOpened++, 1000);
}
}}
bind:inputElement={rulesInput}
/>
<label for="rules">
Ich bin mit den <button
class="link"
on:click|preventDefault={() => {
rulesModal.show();
rulesModalTimer = setInterval(() => rulesModalSecondsOpened++, 1000);
}}>Regeln</button
>
einverstanden und achte sie
<span class="text-red-700">*</span>
</label>
</div>
</div>
<div
class={inputsInvalidMessage !== null ? 'tooltip tooltip-top' : 'grid w-min'}
data-tip={inputsInvalidMessage}
>
<div class="row-[1] col-[1]">
<Input
id="submit"
type="submit"
value="Anmeldung absenden"
disabled={inputsInvalidMessage !== null || registerRequest !== null}
/>
</div>
{#key registerRequest}
{#if registerRequest}
{#await registerRequest}
<span
class="relative top-[calc(50%-12px)] left-[calc(50%-12px)] row-[1] col-[1] loading loading-ring"
/>
{/await}
{/if}
{/key}
</div>
</form>
<dialog
class="modal"
on:close={() => {
clearInterval(rulesModalTimer);
rulesModalTimer = undefined;
}}
bind:this={rulesModal}
>
<form method="dialog" class="modal-box flex max-w-[95%] md:max-w-[90%] lg:max-w-[75%]">
<button class="btn btn-sm btn-circle btn-ghost absolute right-2 top-2"></button>
<div class="overflow-auto mt-5">
<div class="mb-4">
<div class="collapse collapse-arrow">
<input type="checkbox" autocomplete="off" checked />
<div class="collapse-title">
<p>0. Vorwort</p>
</div>
<div class="collapse-content">
<p>{rulesShort.header}</p>
<p class="mt-1 text-[.75rem]">{rulesShort.footer}</p>
</div>
<span class="block w-full h-[1px] mx-auto mb-1 bg-gray-600" />
</div>
{#each rulesShort.sections as section, i}
<div class="collapse collapse-arrow">
<input type="checkbox" autocomplete="off" />
<div class="collapse-title">
<p>{i + 1}. {section.title}</p>
</div>
<div class="collapse-content">
<p>{section.content}</p>
</div>
</div>
<span class="block w-full h-[1px] mx-auto mb-1 bg-gray-600" />
{/each}
</div>
<!-- svelte-ignore a11y-no-static-element-interactions a11y-click-events-have-key-events -->
<div
class="relative w-min"
title={rulesModalSecondsOpened < modalTimeoutSeconds
? `Regeln können in ${Math.max(
modalTimeoutSeconds - rulesModalSecondsOpened,
0
)} Sekunden akzeptiert werden`
: ''}
on:click={() => {
if (rulesModalSecondsOpened < modalTimeoutSeconds) {
errorMessage =
'Bitte lies die Regeln aufmerksam durch. Du kannst erst in einigen Sekunden fortfahren.';
}
}}
>
<div class="absolute top-0 left-0 h-full w-full overflow-hidden rounded-lg">
<div
style="width: {Math.min((rulesModalSecondsOpened / modalTimeoutSeconds) * 100, 100)}%"
class="h-full bg-base-300"
/>
</div>
<Input
id="rules-accept"
type="submit"
value="Akzeptieren"
disabled={rulesModalSecondsOpened < modalTimeoutSeconds}
containerClass="bg-transparent z-[1] relative"
on:click={() => {
rulesAccepted = true;
rulesInput.checked = true;
checkInputs();
}}
/>
</div>
</div>
</form>
<form method="dialog" class="modal-backdrop bg-[rgba(0,0,0,.3)]">
<button>close</button>
</form>
</dialog>
{#if errorMessage}
<dialog class="modal" on:close={() => setTimeout(() => (errorMessage = ''), 200)} open>
<form method="dialog" class="modal-box z-50">
<button class="btn btn-sm btn-circle btn-ghost absolute right-2 top-2"></button>
<h3 class="font-bold text-2xl">Achtung</h3>
<p class="py-4 whitespace-pre-line">{errorMessage}</p>
</form>
<form method="dialog" class="modal-backdrop bg-[rgba(0,0,0,.2)]">
<button>close</button>
</form>
</dialog>
{/if}