add direct invitation link
All checks were successful
deploy / build-and-deploy (push) Successful in 21s

This commit is contained in:
2025-11-02 21:31:17 +01:00
parent 593e76dcb4
commit b2c24f394f
15 changed files with 365 additions and 39 deletions

View File

@@ -0,0 +1,16 @@
---
import { Session } from '@util/session.ts';
import { Permissions } from '@util/permissions.ts';
import { BASE_PATH } from 'astro:env/server';
import AdminLayout from '@layouts/admin/AdminLayout.astro';
import SidebarActions from '@app/admin/directInvitations/SidebarActions.svelte';
import DirectInvitations from '@app/admin/directInvitations/DirectInvitations.svelte';
const session = Session.sessionFromCookies(Astro.cookies, Permissions.Reports);
if (!session) return Astro.redirect(`${BASE_PATH}/admin`);
---
<AdminLayout title="Direkte Einladungen">
<SidebarActions slot="actions" client:load />
<DirectInvitations client:load />
</AdminLayout>

View File

@@ -1,244 +0,0 @@
---
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 { getSettings, SettingKey } from '@util/settings';
import { db } from '@db/database.ts';
import { DISCORD_LINK, PAYPAL_LINK, START_DATE, TEAMSPEAK_LINK } from 'astro:env/server';
const signupSetting = await getSettings(db, [
SettingKey.SignupEnabled,
SettingKey.SignupDisabledMessage,
SettingKey.SignupDisabledSubMessage
]);
const signupEnabled = signupSetting[SettingKey.SignupEnabled] ?? false;
const signupDisabledMessage = signupSetting[SettingKey.SignupDisabledMessage] ?? 'Anmeldung deaktiviert';
const signupDisabledSubMessage = signupSetting[SettingKey.SignupDisabledSubMessage] ?? '';
---
<WebsiteLayout title="Anmeldung" footer={false}>
<div
class="flex justify-center w-full min-h-screen bg-base-200"
class:list={[!signupEnabled ? '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">
<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 class="btn btn-neutral">Anmeldung absenden</button>
</form>
</div>
</div>
</WebsiteLayout>
<RulesPopup client:idle />
<Popup client:idle />
<RegisteredPopup
client:idle
discordLink={DISCORD_LINK}
paypalLink={PAYPAL_LINK}
teamspeakLink={TEAMSPEAK_LINK}
startDate={START_DATE}
/>
{
!signupEnabled && (
<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 pt-20 lg:pt-0 text-2xl sm:text-4xl">
<h1>{signupDisabledMessage}</h1>
<h3>{signupDisabledSubMessage}</h3>
</div>
)
}
<script>
import { actions } from 'astro:actions';
import { popupState } from '@components/popup/Popup';
import { rulesPopupState, rulesPopupRead } from '@app/website/signup/RulesPopup';
import { registeredPopupState } from '@app/website/signup/RegisteredPopup';
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;
// 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
});
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', (e) => {
e.preventDefault();
sendSignup();
});
}
const pathname = document.location.pathname;
document.addEventListener('astro:page-load', () => {
if (document.location.pathname !== pathname) return;
setupClientValidation();
setupForm();
});
</script>

View File

@@ -0,0 +1,18 @@
---
import { db } from '@db/database.ts';
import Signup from '@app/website/signup/Signup.astro';
const { hash } = Astro.params;
const directSignup = await db.getDirectSignupByHash({ hash: hash! });
if (!directSignup) return new Response(null, { status: 404 });
---
<Signup
signupHash={hash}
signupDisabled={directSignup.user
? {
message: 'Es hat sich bereits jemand mit diesem Einladungslink registriert'
}
: undefined}
/>

View File

@@ -0,0 +1,24 @@
---
import { getSettings, SettingKey } from '@util/settings.ts';
import { db } from '@db/database.ts';
import Signup from '@app/website/signup/Signup.astro';
const signupSetting = await getSettings(db, [
SettingKey.SignupEnabled,
SettingKey.SignupDisabledMessage,
SettingKey.SignupDisabledSubMessage
]);
const signupEnabled = signupSetting[SettingKey.SignupEnabled] ?? false;
const signupDisabledMessage = signupSetting[SettingKey.SignupDisabledMessage] ?? 'Anmeldung deaktiviert';
const signupDisabledSubMessage = signupSetting[SettingKey.SignupDisabledSubMessage] ?? '';
---
<Signup
signupDisabled={signupEnabled
? undefined
: {
message: signupDisabledMessage,
subMessage: signupDisabledSubMessage
}}
}
/>