This commit is contained in:
1 parent
b32fc64de4
commit
2b983e5c6d
10 files changed
+450
-277
No files matched your search
@@ -0,0 +1,22 @@
|
||||
<script lang="ts">
|
||||
interface Props {
|
||||
serverIp: string;
|
||||
}
|
||||
|
||||
let { serverIp }: Props = $props();
|
||||
</script>
|
||||
|
||||
<div class="alert">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" class="h-6 w-6 shrink-0 stroke-current">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
|
||||
></path>
|
||||
</svg>
|
||||
<span>
|
||||
Die Anmeldung für Bedrock-Spieler ist über die Webseite nicht mehr möglich. Die Anmeldung erfolgt zukünftig direkt
|
||||
über den Minecraft-Server unter <b>{serverIp}</b>.
|
||||
</span>
|
||||
</div>
|
||||
@@ -0,0 +1,33 @@
|
||||
<script lang="ts">
|
||||
interface Props {
|
||||
onSelect: (edition: 'java' | 'bedrock') => void;
|
||||
selected?: 'java' | 'bedrock' | null;
|
||||
}
|
||||
|
||||
let { onSelect, selected = null }: Props = $props();
|
||||
</script>
|
||||
|
||||
<div>
|
||||
<div class="divider">Minecraft-Version</div>
|
||||
<p class="text-center mb-4">Bitte wähle zuerst deine Minecraft-Version aus.</p>
|
||||
<div class="mx-2 grid grid-cols-1 sm:grid-cols-2 gap-4 mb-6">
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-outline h-auto py-4 flex-col gap-1"
|
||||
class:btn-active={selected === 'java'}
|
||||
onclick={() => onSelect('java')}
|
||||
>
|
||||
<span class="text-lg">Java (PC)</span>
|
||||
<span class="text-xs font-normal">Anmeldung über die Webseite</span>
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
class="btn btn-outline h-auto py-4 flex-col gap-1"
|
||||
class:btn-active={selected === 'bedrock'}
|
||||
onclick={() => onSelect('bedrock')}
|
||||
>
|
||||
<span class="text-lg">Bedrock (Konsole und Handy)</span>
|
||||
<span class="text-xs font-normal">Anmeldung über den Minecraft-Server</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,265 @@
|
||||
<script lang="ts">
|
||||
import { actions } from 'astro:actions';
|
||||
import { onDestroy } from 'svelte';
|
||||
import Checkbox from '@components/input/Checkbox.svelte';
|
||||
import Input from '@components/input/Input.svelte';
|
||||
import Popup from '@components/popup/Popup.svelte';
|
||||
import { popupState } from '@components/popup/Popup.ts';
|
||||
import RegisteredPopup, { type RegistrationData } from './RegisteredPopup.svelte';
|
||||
import RulesPopup from './RulesPopup.svelte';
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
interface Props {
|
||||
signupHash?: string;
|
||||
serverIp: string;
|
||||
discordLink: string;
|
||||
paypalLink: string;
|
||||
teamspeakLink: string;
|
||||
startDate: string;
|
||||
onFinished: () => void;
|
||||
}
|
||||
|
||||
let { signupHash, serverIp, discordLink, paypalLink, teamspeakLink, startDate, onFinished }: Props = $props();
|
||||
|
||||
let rulesAccepted = $state(false);
|
||||
let rulesOpen = $state(false);
|
||||
let rulesRead = $state(false);
|
||||
let submitting = $state(false);
|
||||
let registration = $state<RegistrationData | null>(null);
|
||||
|
||||
let form: HTMLFormElement;
|
||||
|
||||
function formPopupOnReload(event: BeforeUnloadEvent) {
|
||||
if (
|
||||
form.firstname.value ||
|
||||
form.lastname.value ||
|
||||
form.birthday.value ||
|
||||
form.phone.value ||
|
||||
form.username.value ||
|
||||
form.privacy.checked ||
|
||||
form.logs.checked ||
|
||||
form.rules.checked
|
||||
) {
|
||||
event.preventDefault();
|
||||
event.returnValue = '';
|
||||
}
|
||||
}
|
||||
|
||||
let beforeUnloadAbortFn: (() => void) | null = null;
|
||||
onMount(() => {
|
||||
window.addEventListener('beforeunload', formPopupOnReload);
|
||||
beforeUnloadAbortFn = () => window.removeEventListener('beforeunload', formPopupOnReload);
|
||||
});
|
||||
|
||||
onDestroy(() => {
|
||||
beforeUnloadAbortFn?.();
|
||||
});
|
||||
|
||||
function resetForm() {
|
||||
form.reset();
|
||||
rulesAccepted = false;
|
||||
}
|
||||
|
||||
async function submit(event: SubmitEvent) {
|
||||
event.preventDefault();
|
||||
if (submitting) return;
|
||||
|
||||
submitting = true;
|
||||
|
||||
const { error } = await actions.signup.signup({
|
||||
firstname: form.firstname.value,
|
||||
lastname: form.lastname.value,
|
||||
birthday: form.birthday.value,
|
||||
phone: form.phone.value,
|
||||
username: form.username.value,
|
||||
edition: 'java',
|
||||
hash: signupHash
|
||||
});
|
||||
|
||||
submitting = false;
|
||||
|
||||
if (error) {
|
||||
popupState.set({
|
||||
type: 'error',
|
||||
title: 'Fehler',
|
||||
message: error.message
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
registration = {
|
||||
firstname: form.firstname.value,
|
||||
lastname: form.lastname.value,
|
||||
birthday: form.birthday.value,
|
||||
phone: form.phone.value,
|
||||
username: form.username.value,
|
||||
edition: 'Java'
|
||||
};
|
||||
|
||||
resetForm();
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="alert">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" class="h-6 w-6 shrink-0 stroke-current">
|
||||
<path
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
stroke-width="2"
|
||||
d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
|
||||
></path>
|
||||
</svg>
|
||||
<span>
|
||||
Du kannst dich auch direkt in Minecraft unter <b>{serverIp}</b> anmelden.
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<form
|
||||
bind:this={form}
|
||||
onsubmit={(e) => {
|
||||
submit(e);
|
||||
return false;
|
||||
}}
|
||||
autocomplete="off"
|
||||
method="get"
|
||||
>
|
||||
<div class="divider">Persönliche Angaben</div>
|
||||
<div class="mx-2 grid grid-cols-1 sm:grid-cols-2 gap-x-4">
|
||||
<Input
|
||||
id="firstname"
|
||||
type="text"
|
||||
label="Vorname"
|
||||
required
|
||||
validation={{
|
||||
pattern: '^\\p{L}{2,}',
|
||||
hint: 'Bitte gib Deinen vollständigen Vornamen an, dieser muss mindestens aus 2 Zeichen bestehen.'
|
||||
}}
|
||||
dynamicWidth
|
||||
/>
|
||||
<Input
|
||||
id="lastname"
|
||||
type="text"
|
||||
label="Nachname"
|
||||
required
|
||||
validation={{
|
||||
pattern: '^\\p{L}{2,}',
|
||||
hint: 'Bitte gib Deinen vollständigen Nachnamen an, dieser muss mindestens aus 2 Zeichen bestehen.'
|
||||
}}
|
||||
dynamicWidth
|
||||
/>
|
||||
<Input
|
||||
id="birthday"
|
||||
type="date"
|
||||
label="Geburtstag"
|
||||
required
|
||||
validation={{
|
||||
max: new Date(Date.now() - 1000 * 60 * 60 * 24 * 365 * 6).toLocaleDateString('sv-SE'),
|
||||
hint: 'Bitte gib Deinen vollständigen Geburtstag und die korrekte Jahreszahl an. Du musst mindestens 6 Jahre alt sein.'
|
||||
}}
|
||||
dynamicWidth
|
||||
>
|
||||
{#snippet notice()}
|
||||
<span>Die Angabe hat keine Auswirkungen auf das Spielgeschehen.</span>
|
||||
{/snippet}
|
||||
</Input>
|
||||
<Input
|
||||
id="phone"
|
||||
type="tel"
|
||||
label="Telefonnummer"
|
||||
validation={{
|
||||
pattern: '^[+\\(\\)\\s\\d]+$',
|
||||
hint: 'Bitte gib Deine vollständige Telefonnummer an, diese darf keine ungültigen Zeichen enthalten'
|
||||
}}
|
||||
dynamicWidth
|
||||
>
|
||||
{#snippet 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>
|
||||
{/snippet}
|
||||
</Input>
|
||||
</div>
|
||||
<div class="divider">Spiel</div>
|
||||
<div class="mx-2 grid grid-cols-1 gap-x-4">
|
||||
<Input id="username" type="text" label="Minecraft-Spielername" required dynamicWidth>
|
||||
{#snippet notice()}
|
||||
<span class="text-[.65rem]">
|
||||
Bitte gib deinen Minecraft-Java-Namen an. Dieser muss exakt mit deinem Ingame-Namen übereinstimmen.
|
||||
</span>
|
||||
{/snippet}
|
||||
</Input>
|
||||
</div>
|
||||
<div class="divider"></div>
|
||||
<div class="mx-2 grid gap-y-3 mb-6">
|
||||
<Checkbox id="privacy" required>
|
||||
{#snippet label()}
|
||||
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>
|
||||
{/snippet}
|
||||
</Checkbox>
|
||||
<Checkbox id="logs" required>
|
||||
{#snippet label()}
|
||||
Ich bin mit der Speicherung in Form von Logs aller meiner, beim Spielen anfallenden, persönlichen Daten durch
|
||||
den Server einverstanden
|
||||
{/snippet}
|
||||
{#snippet notice()}
|
||||
<span class="text-[.65rem]">
|
||||
Dies betrifft jede Interaktion im Spiel und zugehörige Daten wie z.B. Chatnachrichten welche vom Minecraft
|
||||
Client an den Server übermittelt werden
|
||||
</span>
|
||||
{/snippet}
|
||||
</Checkbox>
|
||||
<Checkbox
|
||||
id="rules"
|
||||
required
|
||||
bind:checked={rulesAccepted}
|
||||
onclick={(event) => {
|
||||
if (!rulesRead) {
|
||||
event.preventDefault();
|
||||
rulesOpen = true;
|
||||
}
|
||||
}}
|
||||
>
|
||||
{#snippet label()}
|
||||
Ich bin mit den
|
||||
<button
|
||||
class="link"
|
||||
onclick={(event) => {
|
||||
event.preventDefault();
|
||||
rulesOpen = true;
|
||||
}}
|
||||
>
|
||||
Regeln
|
||||
</button>
|
||||
einverstanden und achte sie
|
||||
{/snippet}
|
||||
</Checkbox>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-neutral" class:loading-overlay={submitting} disabled={submitting}>
|
||||
Anmeldung absenden
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<RulesPopup
|
||||
open={rulesOpen}
|
||||
onAccept={() => {
|
||||
rulesRead = true;
|
||||
rulesAccepted = true;
|
||||
}}
|
||||
onClose={() => (rulesOpen = false)}
|
||||
/>
|
||||
|
||||
<Popup />
|
||||
|
||||
<RegisteredPopup
|
||||
{registration}
|
||||
{discordLink}
|
||||
{paypalLink}
|
||||
{teamspeakLink}
|
||||
{startDate}
|
||||
newRegistrationAllowed={!signupHash}
|
||||
onClose={() => {
|
||||
registration = null;
|
||||
onFinished();
|
||||
}}
|
||||
/>
|
||||
@@ -1,26 +1,43 @@
|
||||
<script module lang="ts">
|
||||
export type RegistrationData = {
|
||||
firstname: string;
|
||||
lastname: string;
|
||||
birthday: string;
|
||||
phone: string;
|
||||
username: string;
|
||||
edition: string;
|
||||
};
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import { registeredPopupState } from '@app/website/signup/RegisteredPopup.ts';
|
||||
import Input from '@components/input/Input.svelte';
|
||||
import { onDestroy } from 'svelte';
|
||||
|
||||
interface Props {
|
||||
registration: RegistrationData | null;
|
||||
discordLink: string;
|
||||
paypalLink: string;
|
||||
teamspeakLink: string;
|
||||
startDate: string;
|
||||
newRegistrationAllowed: boolean;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
let { discordLink, paypalLink, teamspeakLink, startDate, newRegistrationAllowed }: Props = $props();
|
||||
let { registration, discordLink, paypalLink, teamspeakLink, startDate, newRegistrationAllowed, onClose }: Props =
|
||||
$props();
|
||||
|
||||
let skin: string | null = $state(null);
|
||||
|
||||
let modal: HTMLDialogElement;
|
||||
|
||||
const cancel = registeredPopupState.subscribe(async (value) => {
|
||||
if (!value) return;
|
||||
$effect(() => {
|
||||
if (!registration) return;
|
||||
|
||||
modal.show();
|
||||
if (!modal.open) modal.show();
|
||||
void loadSkin(registration.username);
|
||||
});
|
||||
|
||||
async function loadSkin(username: string) {
|
||||
skin = null;
|
||||
|
||||
const skinview3d = await import('skinview3d');
|
||||
const skinViewer = new skinview3d.SkinViewer({
|
||||
@@ -36,17 +53,19 @@
|
||||
skinViewer.camera.position.y = 22.0;
|
||||
skinViewer.camera.position.z = 42.0;
|
||||
|
||||
await skinViewer.loadSkin(`https://mc-heads.net/skin/${value.username}`);
|
||||
skinViewer.render();
|
||||
skin = skinViewer.canvas.toDataURL();
|
||||
|
||||
skinViewer.dispose();
|
||||
});
|
||||
|
||||
onDestroy(cancel);
|
||||
try {
|
||||
await skinViewer.loadSkin(`https://mc-heads.net/skin/${username}`);
|
||||
skinViewer.render();
|
||||
skin = skinViewer.canvas.toDataURL();
|
||||
} catch {
|
||||
// keep the loading spinner if the skin cannot be loaded
|
||||
} finally {
|
||||
skinViewer.dispose();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<dialog class="modal" bind:this={modal} onclose={() => ($registeredPopupState = null)}>
|
||||
<dialog class="modal" bind:this={modal} onclose={() => onClose()}>
|
||||
<form method="dialog" class="modal-box xl:w-5/12 max-w-10/12 z-10">
|
||||
<h1 class="text-center text-xl sm:text-3xl mb-8">Registrierung erfolgreich</h1>
|
||||
<p>
|
||||
@@ -73,12 +92,12 @@
|
||||
<div class="divider"></div>
|
||||
<div class="flex justify-around mt-2 mb-4">
|
||||
<div class="grid grid-cols-1 sm:grid-cols-2 w-full sm:w-fit gap-x-4 gap-y-2">
|
||||
<Input type="text" value={$registeredPopupState?.firstname} label="Vorname" disabled />
|
||||
<Input type="text" value={$registeredPopupState?.lastname} label="Nachname" disabled />
|
||||
<Input type="date" value={$registeredPopupState?.birthday} label="Geburtstag" disabled />
|
||||
<Input type="tel" value={$registeredPopupState?.phone} label="Telefonnummer" disabled />
|
||||
<Input type="text" value={$registeredPopupState?.username} label="Spielername" disabled />
|
||||
<Input type="text" value={$registeredPopupState?.edition} label="Edition" disabled />
|
||||
<Input type="text" value={registration?.firstname} label="Vorname" disabled />
|
||||
<Input type="text" value={registration?.lastname} label="Nachname" disabled />
|
||||
<Input type="date" value={registration?.birthday} label="Geburtstag" disabled />
|
||||
<Input type="tel" value={registration?.phone} label="Telefonnummer" disabled />
|
||||
<Input type="text" value={registration?.username} label="Spielername" disabled />
|
||||
<Input type="text" value={registration?.edition} label="Edition" disabled />
|
||||
</div>
|
||||
<div class="relative hidden md:flex justify-center w-[200px] my-4">
|
||||
{#if skin}
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
import { atom } from 'nanostores';
|
||||
|
||||
export const registeredPopupState = atom<{
|
||||
firstname: string;
|
||||
lastname: string;
|
||||
birthday: string;
|
||||
phone: string;
|
||||
username: string;
|
||||
edition: string;
|
||||
} | null>(null);
|
||||
@@ -1,35 +1,41 @@
|
||||
<script lang="ts">
|
||||
import { rulesPopupState, rulesPopupRead } from './RulesPopup.ts';
|
||||
import { rules } from '../../../rules.ts';
|
||||
import { popupState } from '@components/popup/Popup.ts';
|
||||
import { onDestroy } from 'svelte';
|
||||
import { rules } from '../../../rules.ts';
|
||||
|
||||
interface Props {
|
||||
open: boolean;
|
||||
onAccept: () => void;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
let { open, onAccept, onClose }: Props = $props();
|
||||
|
||||
const modalTimeoutSeconds = 30;
|
||||
|
||||
let modalElem: HTMLDialogElement;
|
||||
|
||||
let modalTimer = $state<ReturnType<typeof setInterval> | null>(null);
|
||||
let modalTimer: ReturnType<typeof setInterval> | null = null;
|
||||
let modalSecondsOpen = $state(import.meta.env.PROD ? 0 : modalTimeoutSeconds);
|
||||
|
||||
const cancel = rulesPopupState.subscribe((value) => {
|
||||
if (value == 'open') {
|
||||
modalElem.show();
|
||||
modalTimer = setInterval(() => modalSecondsOpen++, 1000);
|
||||
} else if (value == 'closed') {
|
||||
clearInterval(modalTimer!);
|
||||
$effect(() => {
|
||||
if (open) {
|
||||
if (!modalElem.open) modalElem.show();
|
||||
|
||||
if (modalTimer === null) {
|
||||
modalTimer = setInterval(() => modalSecondsOpen++, 1000);
|
||||
}
|
||||
} else if (modalTimer !== null) {
|
||||
clearInterval(modalTimer);
|
||||
modalTimer = null;
|
||||
}
|
||||
});
|
||||
|
||||
onDestroy(cancel);
|
||||
onDestroy(() => {
|
||||
if (modalTimer !== null) clearInterval(modalTimer);
|
||||
});
|
||||
</script>
|
||||
|
||||
<dialog
|
||||
class="modal"
|
||||
onclose={() => {
|
||||
if ($rulesPopupState !== 'accepted') $rulesPopupState = 'closed';
|
||||
}}
|
||||
bind:this={modalElem}
|
||||
>
|
||||
<dialog class="modal" onclose={() => onClose()} bind:this={modalElem}>
|
||||
<form method="dialog" class="modal-box flex flex-col max-h-[90%] 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">
|
||||
@@ -86,8 +92,7 @@
|
||||
};
|
||||
return;
|
||||
}
|
||||
$rulesPopupRead = true;
|
||||
$rulesPopupState = 'accepted';
|
||||
onAccept();
|
||||
}}>Akzeptieren</button
|
||||
>
|
||||
</div>
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
import { atom } from 'nanostores';
|
||||
|
||||
export const rulesPopupState = atom<'open' | 'closed' | 'accepted'>('closed');
|
||||
export const rulesPopupRead = atom(false);
|
||||
@@ -1,12 +1,7 @@
|
||||
---
|
||||
import WebsiteLayout from '@layouts/website/WebsiteLayout.astro';
|
||||
import Checkbox from '@components/input/Checkbox.svelte';
|
||||
import Input from '@components/input/Input.svelte';
|
||||
import Select from '@components/input/Select.svelte';
|
||||
import RulesPopup from '@app/website/signup/RulesPopup.svelte';
|
||||
import Popup from '@components/popup/Popup.svelte';
|
||||
import RegisteredPopup from '@app/website/signup/RegisteredPopup.svelte';
|
||||
import { DISCORD_LINK, PAYPAL_LINK, START_DATE, TEAMSPEAK_LINK } from 'astro:env/server';
|
||||
import SignupFlow from '@app/website/signup/SignupFlow.svelte';
|
||||
import { DISCORD_LINK, PAYPAL_LINK, SERVER_IP, START_DATE, TEAMSPEAK_LINK } from 'astro:env/server';
|
||||
|
||||
interface Props {
|
||||
signupDisabled?: {
|
||||
@@ -25,227 +20,22 @@ const { signupDisabled, signupHash } = Astro.props;
|
||||
class:list={[signupDisabled ? 'max-h-screen overflow-hidden' : undefined]}
|
||||
>
|
||||
<div class="relative grid card w-11/12 xl:w-2/3 2xl:w-1/2 p-6 my-12 bg-base-100 shadow-lg h-min">
|
||||
<h1 class="text-center text-3xl lg:text-5xl">Anmeldung</h1>
|
||||
<form id="signup" data-signup-hash={signupHash}>
|
||||
<div class="divider">Persönliche Angaben</div>
|
||||
<div class="mx-2 grid grid-cols-1 sm:grid-cols-2 gap-x-4">
|
||||
<Input
|
||||
id="firstname"
|
||||
type="text"
|
||||
label="Vorname"
|
||||
required
|
||||
validation={{
|
||||
pattern: '^\\p{L}{2,}',
|
||||
hint: 'Bitte gib Deinen vollständigen Vornamen an, dieser muss mindestens aus 2 Zeichen bestehen.'
|
||||
}}
|
||||
dynamicWidth
|
||||
/>
|
||||
<Input
|
||||
id="lastname"
|
||||
type="text"
|
||||
label="Nachname"
|
||||
required
|
||||
validation={{
|
||||
pattern: '^\\p{L}{2,}',
|
||||
hint: 'Bitte gib Deinen vollständigen Nachnamen an, dieser muss mindestens aus 2 Zeichen bestehen.'
|
||||
}}
|
||||
dynamicWidth
|
||||
/>
|
||||
<Input
|
||||
id="birthday"
|
||||
type="date"
|
||||
label="Geburtstag"
|
||||
required
|
||||
validation={{
|
||||
max: new Date(Date.now() - 1000 * 60 * 60 * 24 * 365 * 6).toLocaleDateString('sv-SE'),
|
||||
hint: 'Bitte gib Deinen vollständigen Geburtstag und die korrekte Jahreszahl an. Du musst mindestens 6 Jahre alt sein.'
|
||||
}}
|
||||
dynamicWidth
|
||||
>
|
||||
<span slot="notice">Die Angabe hat keine Auswirkungen auf das Spielgeschehen.</span>
|
||||
</Input>
|
||||
<Input
|
||||
id="phone"
|
||||
type="tel"
|
||||
label="Telefonnummer"
|
||||
validation={{
|
||||
pattern: '^[+\\(\\)\\s\\d]+$',
|
||||
hint: 'Bitte gib Deine vollständige Telefonnummer an, diese darf keine ungültigen Zeichen enthalten'
|
||||
}}
|
||||
dynamicWidth
|
||||
>
|
||||
<span 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>
|
||||
</span>
|
||||
</Input>
|
||||
</div>
|
||||
<div class="divider">Spiel</div>
|
||||
<div class="mx-2 grid grid-cols-1 sm:grid-cols-2 gap-x-4">
|
||||
<Input id="username" type="text" label="Minecraft-Spielername" required dynamicWidth>
|
||||
<span slot="notice" class="text-[.65rem]">
|
||||
Spielern der Bedrock-Edition wird empfohlen, ihren Microsoft-Account Namen einzugeben. Der
|
||||
Bedrock-Spielername kann nicht immer einem Minecraft Account zugeordnet werden.
|
||||
</span>
|
||||
</Input>
|
||||
<Select
|
||||
id="edition"
|
||||
values={{ java: 'Java (PC)', bedrock: 'Bedrock (Konsole und Handys)' }}
|
||||
label="Edition"
|
||||
required
|
||||
dynamicWidth
|
||||
/>
|
||||
</div>
|
||||
<div class="divider"></div>
|
||||
<div class="mx-2 grid gap-y-3 mb-6">
|
||||
<Checkbox id="checkbox" required>
|
||||
<span slot="label">
|
||||
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>
|
||||
</Checkbox>
|
||||
<Checkbox id="logs" required>
|
||||
<span slot="label">
|
||||
Ich bin mit der Speicherung in Form von Logs aller meiner, beim Spielen anfallenden, persönlichen Daten
|
||||
durch den Server einverstanden
|
||||
</span>
|
||||
<span slot="notice" class="text-[.65rem]">
|
||||
Dies betrifft jede Interaktion im Spiel und zugehörige Daten wie z.B. Chatnachrichten welche vom Minecraft
|
||||
Client an den Server übermittelt werden
|
||||
</span>
|
||||
</Checkbox>
|
||||
<Checkbox id="rules" required>
|
||||
<span slot="label">
|
||||
Ich bin mit den{' '}
|
||||
<a class="link" onclick="">
|
||||
Regeln
|
||||
</a>{' '}
|
||||
einverstanden und achte sie
|
||||
</span>
|
||||
</Checkbox>
|
||||
</div>
|
||||
<button id="send-btn" class="btn btn-neutral">
|
||||
Anmeldung absenden
|
||||
</button>
|
||||
</form>
|
||||
<SignupFlow
|
||||
client:load
|
||||
{signupHash}
|
||||
serverIp={SERVER_IP}
|
||||
discordLink={DISCORD_LINK}
|
||||
paypalLink={PAYPAL_LINK}
|
||||
teamspeakLink={TEAMSPEAK_LINK}
|
||||
startDate={START_DATE}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</WebsiteLayout>
|
||||
|
||||
<RulesPopup client:idle />
|
||||
|
||||
<Popup client:idle />
|
||||
|
||||
<RegisteredPopup
|
||||
client:idle
|
||||
discordLink={DISCORD_LINK}
|
||||
paypalLink={PAYPAL_LINK}
|
||||
teamspeakLink={TEAMSPEAK_LINK}
|
||||
startDate={START_DATE}
|
||||
newRegistrationAllowed={!signupHash}
|
||||
/>
|
||||
|
||||
{signupDisabled && (
|
||||
<div class="absolute top-0 left-0 w-full h-full bg-black/50 backdrop-blur-sm z-10 rounded-xl flex justify-center items-center flex-col gap-2 pt-20 lg:pt-0 px-5 lg:px-10 2xl:px-28">
|
||||
<h1 class="text-center text-2xl sm:text-4xl">{signupDisabled.message}</h1>
|
||||
<h3 class="text-xl sm:text-2xl">{signupDisabled.subMessage}</h3>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<script>
|
||||
import { actions } from 'astro:actions';
|
||||
import { popupState } from '@components/popup/Popup.ts';
|
||||
import { rulesPopupState, rulesPopupRead } from '@app/website/signup/RulesPopup.ts';
|
||||
import { registeredPopupState } from '@app/website/signup/RegisteredPopup.ts';
|
||||
|
||||
function setupClientValidation() {
|
||||
const rulesCheckbox = document.getElementById('rules') as HTMLInputElement;
|
||||
const rulesCheckboxRulesLink = rulesCheckbox.nextElementSibling!.querySelector('.link') as HTMLAnchorElement;
|
||||
|
||||
// add popup state subscriber to check when the accepted button is clicked
|
||||
rulesPopupState.subscribe((value) => {
|
||||
if (value == 'accepted') rulesCheckbox.checked = true;
|
||||
});
|
||||
|
||||
// add click handler to open rules popup to rules checkbox
|
||||
rulesCheckbox.addEventListener('click', (e) => {
|
||||
if (!rulesPopupRead.get()) {
|
||||
e.preventDefault();
|
||||
rulesPopupState.set('open');
|
||||
}
|
||||
});
|
||||
|
||||
// add click handler to open rules popup when clicking the rules link in the rules checkbox label
|
||||
rulesCheckboxRulesLink!.addEventListener('click', () => rulesPopupState.set('open'));
|
||||
}
|
||||
|
||||
function setupForm() {
|
||||
const form = document.getElementById('signup')! as HTMLFormElement;
|
||||
const sendBtn = document.getElementById('send-btn')! as HTMLButtonElement;
|
||||
|
||||
// reset form on site (re-)load
|
||||
form.reset();
|
||||
|
||||
const sendSignup = async () => {
|
||||
const { error } = await actions.signup.signup({
|
||||
firstname: form.firstname.value,
|
||||
lastname: form.lastname.value,
|
||||
birthday: form.birthday.value,
|
||||
phone: form.phone.value,
|
||||
username: form.username.value,
|
||||
edition: form.edition.value,
|
||||
hash: form.dataset.signupHash
|
||||
});
|
||||
|
||||
if (error) {
|
||||
if (error.code == 'CONFLICT' || error.code == 'FORBIDDEN') {
|
||||
popupState.set({
|
||||
type: 'error',
|
||||
title: 'Fehler',
|
||||
message: error.message
|
||||
});
|
||||
} else {
|
||||
popupState.set({
|
||||
type: 'error',
|
||||
title: 'Fehler',
|
||||
message: error.message
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
registeredPopupState.set({
|
||||
firstname: form.firstname.value,
|
||||
lastname: form.lastname.value,
|
||||
birthday: form.birthday.value,
|
||||
phone: form.phone.value,
|
||||
username: form.username.value,
|
||||
edition: form.edition.value[0].toUpperCase() + form.edition.value.slice(1)
|
||||
});
|
||||
|
||||
const cancel = registeredPopupState.subscribe((value) => {
|
||||
if (value) return;
|
||||
cancel();
|
||||
form.reset();
|
||||
});
|
||||
};
|
||||
|
||||
form.addEventListener('submit', async (e) => {
|
||||
e.preventDefault();
|
||||
sendBtn.classList.add('loading-overlay');
|
||||
await sendSignup();
|
||||
sendBtn.classList.remove('loading-overlay');
|
||||
});
|
||||
}
|
||||
|
||||
const pathname = document.location.pathname;
|
||||
document.addEventListener('astro:page-load', () => {
|
||||
if (document.location.pathname !== pathname) return;
|
||||
|
||||
setupClientValidation();
|
||||
setupForm();
|
||||
});
|
||||
</script>
|
||||
@@ -0,0 +1,51 @@
|
||||
<script lang="ts">
|
||||
import { tick } from 'svelte';
|
||||
import BedrockSignup from './BedrockSignup.svelte';
|
||||
import EditionSelection from './EditionSelection.svelte';
|
||||
import JavaSignup from './JavaSignup.svelte';
|
||||
|
||||
interface Props {
|
||||
signupHash?: string;
|
||||
serverIp: string;
|
||||
discordLink: string;
|
||||
paypalLink: string;
|
||||
teamspeakLink: string;
|
||||
startDate: string;
|
||||
}
|
||||
|
||||
let { signupHash, serverIp, discordLink, paypalLink, teamspeakLink, startDate }: Props = $props();
|
||||
|
||||
type Step = 'java' | 'bedrock' | null;
|
||||
|
||||
let step = $state<Step>(null);
|
||||
|
||||
async function selectEdition(edition: 'java' | 'bedrock') {
|
||||
step = edition;
|
||||
|
||||
if (edition === 'java') {
|
||||
await tick();
|
||||
document.getElementById('firstname')?.focus();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<div>
|
||||
<h1 class="text-center text-3xl lg:text-5xl">Anmeldung</h1>
|
||||
|
||||
<EditionSelection onSelect={selectEdition} selected={step} />
|
||||
|
||||
<div class:hidden={step !== 'java'}>
|
||||
<JavaSignup
|
||||
{signupHash}
|
||||
{serverIp}
|
||||
{discordLink}
|
||||
{paypalLink}
|
||||
{teamspeakLink}
|
||||
{startDate}
|
||||
onFinished={() => (step = null)}
|
||||
/>
|
||||
</div>
|
||||
<div class:hidden={step !== 'bedrock'}>
|
||||
<BedrockSignup {serverIp} />
|
||||
</div>
|
||||
</div>
|
||||
Reference in new issue
Block a user