Basic signup for events
This commit is contained in:
102
app/Domains/Event/Views/Partials/AvailableEvents.vue
Normal file
102
app/Domains/Event/Views/Partials/AvailableEvents.vue
Normal file
@@ -0,0 +1,102 @@
|
||||
<script setup>
|
||||
|
||||
import ShadowedBox from "../../../../Views/Components/ShadowedBox.vue";
|
||||
|
||||
const props = defineProps({
|
||||
events: Array,
|
||||
})
|
||||
|
||||
console.log(props.events)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div style="width: 95%; margin: 20px auto;">
|
||||
|
||||
<div v-if="props.events.length === 0" style="text-align: center; color: #6b7280; padding: 40px 0;">
|
||||
Aktuell sind keine Veranstaltungen verfügbar.
|
||||
</div>
|
||||
|
||||
<shadowed-box
|
||||
v-for="event in props.events"
|
||||
:key="event.id"
|
||||
style="padding: 24px; margin-bottom: 20px;"
|
||||
>
|
||||
<div style="display: flex; justify-content: space-between; align-items: flex-start; flex-wrap: wrap; gap: 12px;">
|
||||
<div>
|
||||
<h2 style="margin: 0 0 4px 0; font-size: 1.25rem;">{{ event.name }}</h2>
|
||||
<span style="color: #6b7280; font-size: 0.9rem;">{{ event.postalCode }} {{ event.location }}</span>
|
||||
</div>
|
||||
<span
|
||||
v-if="event.registrationAllowed"
|
||||
style="background: #d1fae5; color: #065f46; padding: 4px 12px; border-radius: 999px; font-size: 0.8rem; font-weight: 600; white-space: nowrap;"
|
||||
>
|
||||
Anmeldung offen
|
||||
</span>
|
||||
<span
|
||||
v-else
|
||||
style="background: #fee2e2; color: #991b1b; padding: 4px 12px; border-radius: 999px; font-size: 0.8rem; font-weight: 600; white-space: nowrap;"
|
||||
>
|
||||
Anmeldung geschlossen
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<hr style="margin: 16px 0; border: none; border-top: 1px solid #e5e7eb;" />
|
||||
|
||||
<table style="width: 100%; border-collapse: collapse;">
|
||||
<tr>
|
||||
<th style="text-align: left; padding: 6px 12px 6px 0; width: 220px; color: #374151; font-weight: 600;">Zeitraum</th>
|
||||
<td style="padding: 6px 0; color: #111827;">{{ event.eventBegin }} – {{ event.eventEnd }} ({{ event.duration }} Tage)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th style="text-align: left; padding: 6px 12px 6px 0; width: 220px; color: #374151; font-weight: 600;">Veranstaltungsort</th>
|
||||
<td style="padding: 6px 0; color: #111827;">{{ event.postalCode }} {{ event.location }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th style="text-align: left; padding: 6px 12px 6px 0; color: #374151; font-weight: 600;">Frühbuchen bis</th>
|
||||
<td style="padding: 6px 0; color: #111827;">{{ event.earlyBirdEnd.formatted }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th style="text-align: left; padding: 6px 12px 6px 0; color: #374151; font-weight: 600;">Anmeldeschluss</th>
|
||||
<td style="padding: 6px 0; color: #111827;">{{ event.registrationFinalEnd.formatted }}</td>
|
||||
</tr>
|
||||
<tr v-if="event.email">
|
||||
<th style="text-align: left; padding: 6px 12px 6px 0; color: #374151; font-weight: 600;">Kontakt</th>
|
||||
<td style="padding: 6px 0;">
|
||||
<a :href="'mailto:' + event.email" style="color: #2563eb;">{{ event.email }}</a>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<div style="margin-top: 20px; display: flex; justify-content: flex-end;">
|
||||
<a
|
||||
:href="'/event/' + event.id + '/signup'"
|
||||
style="
|
||||
display: inline-block;
|
||||
padding: 10px 24px;
|
||||
background-color: #2563eb;
|
||||
color: white;
|
||||
border-radius: 8px;
|
||||
text-decoration: none;
|
||||
font-weight: 600;
|
||||
font-size: 0.95rem;
|
||||
opacity: 1;
|
||||
transition: background-color 0.2s;
|
||||
"
|
||||
:style="{ opacity: event.registrationAllowed ? '1' : '0.5', pointerEvents: event.registrationAllowed ? 'auto' : 'none' }"
|
||||
>
|
||||
Zur Anmeldung →
|
||||
</a>
|
||||
</div>
|
||||
</shadowed-box>
|
||||
|
||||
</div><div style="width: 95%; margin: 20px auto;">
|
||||
|
||||
<div v-if="props.events.length === 0" style="text-align: center; color: #6b7280; padding: 40px 0;">
|
||||
Aktuell sind keine Veranstaltungen verfügbar.
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
||||
@@ -16,19 +16,27 @@
|
||||
const errors = reactive({})
|
||||
const formData = reactive({
|
||||
"pft_1_active": true,
|
||||
"pft_1_amount": props.event.participationFee_1.amount,
|
||||
"pft_1_amount_standard": props.event.participationFee_1.amount_standard_edit,
|
||||
"pft_1_amount_reduced": props.event.participationFee_1.amount_reduced_edit,
|
||||
"pft_1_amount_solidarity": props.event.participationFee_1.amount_solidarity_edit,
|
||||
"pft_1_description": props.event.participationFee_1.description,
|
||||
|
||||
"pft_2_active": props.event.participationFee_2.active,
|
||||
"pft_2_amount": props.event.participationFee_2.amount,
|
||||
"pft_2_amount_standard": props.event.participationFee_2.amount_standard_edit,
|
||||
"pft_2_amount_reduced": props.event.participationFee_2.amount_reduced_edit,
|
||||
"pft_2_amount_solidarity": props.event.participationFee_2.amount_solidarity_edit,
|
||||
"pft_2_description": props.event.participationFee_2.description,
|
||||
|
||||
"pft_3_active": props.event.participationFee_3.active,
|
||||
"pft_3_amount": props.event.participationFee_3.amount,
|
||||
"pft_3_amount_standard": props.event.participationFee_3.amount_standard_edit,
|
||||
"pft_3_amount_reduced": props.event.participationFee_3.amount_reduced_edit,
|
||||
"pft_3_amount_solidarity": props.event.participationFee_3.amount_solidarity_edit,
|
||||
"pft_3_description": props.event.participationFee_3.description,
|
||||
|
||||
"pft_4_active": props.event.participationFee_4.active,
|
||||
"pft_4_amount": props.event.participationFee_4.amount,
|
||||
"pft_4_amount_standard": props.event.participationFee_4.amount_standard_edit,
|
||||
"pft_4_amount_reduced": props.event.participationFee_4.amount_reduced_edit,
|
||||
"pft_4_amount_solidarity": props.event.participationFee_4.amount_solidarity_edit,
|
||||
"pft_4_description": props.event.participationFee_4.description,
|
||||
|
||||
'maxAmount': props.event.maxAmount,
|
||||
@@ -38,17 +46,17 @@
|
||||
function validateInput() {
|
||||
var noErrors = true;
|
||||
|
||||
if (formData.pft_1_description === '' && !props.event.solidarityPayment) {
|
||||
if (formData.pft_1_description === '') {
|
||||
errors.pft_1_description = 'Eine Beschreibung für diese Gruppe ist erforderlich';
|
||||
noErrors = false;
|
||||
}
|
||||
|
||||
if (formData.pft_2_description === '' && formData.pft_2_active && !props.event.solidarityPayment) {
|
||||
if (formData.pft_2_description === '' && formData.pft_2_active) {
|
||||
errors.pft_2_description = 'Eine Beschreibung für diese Gruppe ist erforderlich';
|
||||
noErrors = false;
|
||||
}
|
||||
|
||||
if (formData.pft_3_description === '' && formData.pft_3_active && !props.event.solidarityPayment) {
|
||||
if (formData.pft_3_description === '' && formData.pft_3_active) {
|
||||
errors.pft_3_description = 'Eine Beschreibung für diese Gruppe ist erforderlich';
|
||||
noErrors = false;
|
||||
}
|
||||
@@ -73,19 +81,27 @@
|
||||
body: {
|
||||
event_id: props.event.id,
|
||||
pft_1_active: formData.pft_1_active,
|
||||
pft_1_amount: formData.pft_1_amount,
|
||||
pft_1_amount_standard: formData.pft_1_amount_standard,
|
||||
pft_1_amount_reduced: formData.pft_1_amount_reduced,
|
||||
pft_1_amount_solidarity: formData.pft_1_amount_solidarity,
|
||||
pft_1_description: formData.pft_1_description,
|
||||
|
||||
pft_2_active: formData.pft_2_active,
|
||||
pft_2_amount: formData.pft_2_amount,
|
||||
pft_2_amount_standard: formData.pft_2_amount_standard,
|
||||
pft_2_amount_reduced: formData.pft_2_amount_reduced,
|
||||
pft_2_amount_solidarity: formData.pft_2_amount_solidarity,
|
||||
pft_2_description: formData.pft_2_description,
|
||||
|
||||
pft_3_active: formData.pft_3_active,
|
||||
pft_3_amount: formData.pft_3_amount,
|
||||
pft_3_amount_standard: formData.pft_3_amount_standard,
|
||||
pft_3_amount_reduced: formData.pft_3_amount_reduced,
|
||||
pft_3_amount_solidarity: formData.pft_3_amount_solidarity,
|
||||
pft_3_description: formData.pft_3_description,
|
||||
|
||||
pft_4_active: formData.pft_4_active,
|
||||
pft_4_amount: formData.pft_4_amount,
|
||||
pft_4_amount_standard: formData.pft_4_amount_standard,
|
||||
pft_4_amount_reduced: formData.pft_4_amount_reduced,
|
||||
pft_4_amount_solidarity: formData.pft_4_amount_solidarity,
|
||||
pft_4_description: formData.pft_4_description,
|
||||
|
||||
maxAmount: formData.maxAmount,
|
||||
@@ -114,29 +130,41 @@
|
||||
<template>
|
||||
<table style="width: 100%;">
|
||||
<tr>
|
||||
<td>Aktiv</td>
|
||||
<td>Preisgruppe</td>
|
||||
<td>Betrag</td>
|
||||
<td>Beschreibung</td>
|
||||
<td><h4>Aktiv</h4></td>
|
||||
<td><h4>Preisgruppe</h4></td>
|
||||
<td v-if="!props.event.solidarityPayment"><h4>Betrag</h4></td>
|
||||
<td v-else><h4>Regulärer Beitrag</h4></td>
|
||||
<td v-if="props.event.solidarityPayment"><h4>Reduzierter Beitrag</h4></td>
|
||||
<td v-if="props.event.solidarityPayment"><h4>Solidaritätsbeitrag</h4></td>
|
||||
<td><h4>Beschreibung</h4></td>
|
||||
</tr>
|
||||
<tr style="height: 65px; vertical-align: top">
|
||||
<td>
|
||||
<input type="checkbox" v-model="formData.participationFeeType_1" checked disabled/>
|
||||
</td>
|
||||
<td v-if="props.event.solidarityPayment">
|
||||
Regulärer Beitrag
|
||||
</td>
|
||||
<td v-else>
|
||||
<td>
|
||||
Teilnehmende
|
||||
</td>
|
||||
<td>
|
||||
<AmountInput v-model="formData.pft_1_amount" class="width-small" @blur="recalculateMaxAmount(formData.pft_1_amount)" />
|
||||
<label v-if="props.event.payPerDay"> Euro / Tag</label>
|
||||
<label v-else> Euro Gesamt</label>
|
||||
<AmountInput v-model="formData.pft_1_amount_standard" class="width-small" @blur="recalculateMaxAmount(formData.pft_1_amount_standard)" />
|
||||
<label style="font-size: 10pt;" v-if="props.event.payPerDay"> Euro / Tag</label>
|
||||
<label style="font-size: 10pt;" v-else> Euro Gesamt</label>
|
||||
</td>
|
||||
|
||||
<td v-if="props.event.solidarityPayment">
|
||||
<AmountInput v-model="formData.pft_1_amount_reduced" class="width-small" @blur="recalculateMaxAmount(formData.pft_1_amount_reduced)" />
|
||||
<label style="font-size: 10pt;" v-if="props.event.payPerDay"> Euro / Tag</label>
|
||||
<label style="font-size: 10pt;" v-else> Euro Gesamt</label>
|
||||
</td>
|
||||
|
||||
<td v-if="props.event.solidarityPayment">
|
||||
<AmountInput v-model="formData.pft_1_amount_solidarity" class="width-small" @blur="recalculateMaxAmount(formData.pft_1_amount_solidarity)" />
|
||||
<label style="font-size: 10pt;" v-if="props.event.payPerDay"> Euro / Tag</label>
|
||||
<label style="font-size: 10pt;" v-else> Euro Gesamt</label>
|
||||
</td>
|
||||
|
||||
<td>
|
||||
<input v-if="!props.event.solidarityPayment" type="text" v-model="formData.pft_1_description" style="width: 300px;" />
|
||||
<label v-else></label>
|
||||
<input type="text" v-model="formData.pft_1_description" style="width: 300px;" />
|
||||
<ErrorText :message="errors.pft_1_description" />
|
||||
</td>
|
||||
</tr>
|
||||
@@ -145,24 +173,32 @@
|
||||
<td>
|
||||
<input id="use_pft_2" type="checkbox" v-model="formData.pft_2_active" :checked="formData.pft_2_active" />
|
||||
</td>
|
||||
<td v-if="props.event.solidarityPayment">
|
||||
<label for="use_pft_2" style="cursor: default">
|
||||
Solidaritätsbeitrag
|
||||
</label>
|
||||
</td>
|
||||
<td v-else>
|
||||
<td>
|
||||
<label for="use_pft_2" style="cursor: default">
|
||||
Kernteam
|
||||
</label>
|
||||
</td>
|
||||
<td v-if="formData.pft_2_active">
|
||||
<AmountInput v-model="formData.pft_2_amount" class="width-small" @blur="recalculateMaxAmount(formData.pft_2_amount)" />
|
||||
<label v-if="props.event.payPerDay"> Euro / Tag</label>
|
||||
<label v-else> Euro Gesamt</label>
|
||||
<AmountInput v-model="formData.pft_2_amount_standard" class="width-small" @blur="recalculateMaxAmount(formData.pft_2_amount_standard)" />
|
||||
<label style="font-size: 10pt;" v-if="props.event.payPerDay"> Euro / Tag</label>
|
||||
<label style="font-size: 10pt;" v-else> Euro Gesamt</label>
|
||||
</td>
|
||||
|
||||
<td v-if="props.event.solidarityPayment && formData.pft_2_active">
|
||||
<AmountInput v-model="formData.pft_2_amount_reduced" class="width-small" @blur="recalculateMaxAmount(formData.pft_2_amount_reduced)" />
|
||||
<label style="font-size: 10pt;" v-if="props.event.payPerDay"> Euro / Tag</label>
|
||||
<label style="font-size: 10pt;" v-else> Euro Gesamt</label>
|
||||
</td>
|
||||
|
||||
<td v-if="props.event.solidarityPayment && formData.pft_2_active">
|
||||
<AmountInput v-model="formData.pft_2_amount_solidarity" class="width-small" @blur="recalculateMaxAmount(formData.pft_2_amount_solidarity)" />
|
||||
<label style="font-size: 10pt;" v-if="props.event.payPerDay"> Euro / Tag</label>
|
||||
<label style="font-size: 10pt;" v-else> Euro Gesamt</label>
|
||||
</td>
|
||||
|
||||
|
||||
<td v-if="formData.pft_2_active">
|
||||
<input v-if="!props.event.solidarityPayment" type="text" v-model="formData.pft_2_description" style="width: 300px;" />
|
||||
<label v-else></label>
|
||||
<input type="text" v-model="formData.pft_2_description" style="width: 300px;" />
|
||||
<ErrorText :message="errors.pft_2_description" />
|
||||
</td>
|
||||
</tr>
|
||||
@@ -171,29 +207,36 @@
|
||||
<td>
|
||||
<input id="use_pft_3" type="checkbox" v-model="formData.pft_3_active" :checked="formData.pft_3_active" />
|
||||
</td>
|
||||
<td v-if="props.event.solidarityPayment">
|
||||
<label for="use_pft_3" style="cursor: default">
|
||||
Reduzierter Beitrag
|
||||
</label>
|
||||
</td>
|
||||
<td v-else>
|
||||
<td>
|
||||
<label for="use_pft_3" style="cursor: default">
|
||||
Unterstützende
|
||||
</label>
|
||||
</td>
|
||||
<td v-if="formData.pft_3_active">
|
||||
<AmountInput v-model="formData.pft_3_amount" class="width-small" @blur="recalculateMaxAmount(formData.pft_3_amount)" />
|
||||
<label v-if="props.event.payPerDay"> Euro / Tag</label>
|
||||
<label v-else> Euro Gesamt</label>
|
||||
<AmountInput v-model="formData.pft_3_amount_standard" class="width-small" @blur="recalculateMaxAmount(formData.pft_3_amount_standard)" />
|
||||
<label style="font-size: 10pt;" v-if="props.event.payPerDay"> Euro / Tag</label>
|
||||
<label style="font-size: 10pt;" v-else> Euro Gesamt</label>
|
||||
</td>
|
||||
|
||||
<td v-if="props.event.solidarityPayment && formData.pft_3_active">
|
||||
<AmountInput v-model="formData.pft_3_amount_reduced" class="width-small" @blur="recalculateMaxAmount(formData.pft_3_amount_reduced)" />
|
||||
<label style="font-size: 10pt;" v-if="props.event.payPerDay"> Euro / Tag</label>
|
||||
<label style="font-size: 10pt;" v-else> Euro Gesamt</label>
|
||||
</td>
|
||||
|
||||
<td v-if="props.event.solidarityPayment && formData.pft_3_active">
|
||||
<AmountInput v-model="formData.pft_3_amount_solidarity" class="width-small" @blur="recalculateMaxAmount(formData.pft_3_amount_solidarity)" />
|
||||
<label style="font-size: 10pt;" v-if="props.event.payPerDay"> Euro / Tag</label>
|
||||
<label style="font-size: 10pt;" v-else> Euro Gesamt</label>
|
||||
</td>
|
||||
|
||||
<td v-if="formData.pft_3_active">
|
||||
<input v-if="!props.event.solidarityPayment" type="text" v-model="formData.pft_3_description" style="width: 300px;" />
|
||||
<label v-else>Nach Verfügbarkeit</label>
|
||||
<input type="text" v-model="formData.pft_3_description" style="width: 300px;" />
|
||||
<ErrorText :message="errors.pft_3_description" />
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr style="height: 65px; vertical-align: top;" v-if="!props.event.solidarityPayment">
|
||||
<tr style="height: 65px; vertical-align: top;">
|
||||
<td>
|
||||
<input id="use_pft_4" type="checkbox" v-model="formData.pft_4_active" :checked="formData.pft_4_active" />
|
||||
</td>
|
||||
@@ -203,10 +246,23 @@
|
||||
</label>
|
||||
</td>
|
||||
<td v-if="formData.pft_4_active">
|
||||
<AmountInput v-model="formData.pft_4_amount" class="width-small" @blur="recalculateMaxAmount(formData.pft_4_amount)" />
|
||||
<label v-if="props.event.payPerDay"> Euro / Tag</label>
|
||||
<label v-else> Euro Gesamt</label>
|
||||
<AmountInput v-model="formData.pft_4_amount_standard" class="width-small" @blur="recalculateMaxAmount(formData.pft_4_amount_standard)" />
|
||||
<label style="font-size: 10pt;" v-if="props.event.payPerDay"> Euro / Tag</label>
|
||||
<label style="font-size: 10pt;" v-else> Euro Gesamt</label>
|
||||
</td>
|
||||
|
||||
<td v-if="props.event.solidarityPayment && formData.pft_4_active">
|
||||
<AmountInput v-model="formData.pft_4_amount_reduced" class="width-small" @blur="recalculateMaxAmount(formData.pft_4_amount_reduced)" />
|
||||
<label style="font-size: 10pt;" v-if="props.event.payPerDay"> Euro / Tag</label>
|
||||
<label style="font-size: 10pt;" v-else> Euro Gesamt</label>
|
||||
</td>
|
||||
|
||||
<td v-if="props.event.solidarityPayment && formData.pft_4_active">
|
||||
<AmountInput v-model="formData.pft_4_amount_solidarity" class="width-small" @blur="recalculateMaxAmount(formData.pft_4_amount_solidarity)" />
|
||||
<label style="font-size: 10pt;" v-if="props.event.payPerDay"> Euro / Tag</label>
|
||||
<label style="font-size: 10pt;" v-else> Euro Gesamt</label>
|
||||
</td>
|
||||
|
||||
<td v-if="formData.pft_4_active">
|
||||
<input type="text" v-model="formData.pft_4_description" style="width: 300px;" />
|
||||
<ErrorText :message="errors.pft_4_description" />
|
||||
|
||||
@@ -43,7 +43,11 @@ const steps = [
|
||||
<template>
|
||||
<div>
|
||||
<!-- Nach Submit -->
|
||||
<SubmitSuccess v-if="submitResult?.type === 'success'" :data="submitResult.data" />
|
||||
<SubmitSuccess
|
||||
v-if="submitResult?.status === 'success'"
|
||||
:participant="submitResult?.participant"
|
||||
:event="event"
|
||||
/>
|
||||
<SubmitAlreadyExists v-else-if="submitResult?.type === 'exists'" :data="submitResult.data" />
|
||||
|
||||
<template v-else>
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
<script setup>
|
||||
defineProps({ data: Object })
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div style="padding: 20px 0;">
|
||||
<h3>{{ data.nicename }}</h3>
|
||||
<p>{{ data.text_1 }}</p>
|
||||
<p>{{ data.text_2 }}</p>
|
||||
<a :href="data.email_link" style="color: #2563eb;">{{ data.email_text }}</a>
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,60 @@
|
||||
<script setup>
|
||||
const props = defineProps({
|
||||
participant: Object,
|
||||
event: Object,
|
||||
})
|
||||
|
||||
console.log(props.event)
|
||||
console.log(props.participant)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div style="padding: 20px 0;">
|
||||
<h3>Hallo {{ props.participant.nicename }},</h3>
|
||||
<p>Vielen Dank für dein Interesse an der Veranstaltung {{event.name}}<br />Wir haben folgende Daten erhalten:</p>
|
||||
|
||||
<table class="form-table" style="margin-bottom: 20px;">
|
||||
<tr><td>Anreise:</td><td>{{ props.participant.arrival }}</td></tr>
|
||||
<tr><td>Abreise:</td><td>{{ props.participant.departure }}</td></tr>
|
||||
<tr><td>Teilnahmegruppe:</td><td>{{ props.participant.participation_group }}</td></tr>
|
||||
</table>
|
||||
|
||||
<div v-if="props.participant.efz_status === 'NOT_CHECKED'" style="font-weight: bold; color: #b45309; margin-bottom: 20px;">
|
||||
Dein erweitertes Führungszeugnis konnte nicht automatisch geprüft werden. Bitte kontaktiere die Aktionsleitung.
|
||||
</div>
|
||||
<div v-else-if="props.participant.efz_status === 'CHECKED_INVALID'" style="font-weight: bold; color: #dc2626; margin-bottom: 20px;">
|
||||
Du hast noch kein erweitertes Führungszeugnis hinterlegt. Bitte reiche es umgehend ein.
|
||||
</div>
|
||||
|
||||
<template v-if="props.participant.needs_payment">
|
||||
<table class="form-table" style="margin-bottom: 16px;">
|
||||
<tr>
|
||||
<td>Kontoinhaber:</td><td>{{ props.event.accountOwner }}</td>
|
||||
<td rowspan="4" style="vertical-align: top; padding-left: 20px;" v-if="props.participant.identifier !== ''">
|
||||
<img :src="'/print-girocode/' + props.participant.identifier" alt="GiroCode" style="max-width: 180px;" />
|
||||
<span style="width: 180px; text-align: center; display: block; font-size: 0.8rem; color: #6b7280; margin-top: 4px;">Giro-Code</span>
|
||||
</td>
|
||||
</tr>
|
||||
<tr><td>IBAN:</td><td>{{ props.event.accountIban }}</td></tr>
|
||||
<tr><td>Verwendungszweck:</td><td>{{ props.participant.payment_purpose }}</td></tr>
|
||||
<tr><td>Betrag:</td><td><strong>{{ props.participant.amount_left_string }}</strong></td></tr>
|
||||
</table>
|
||||
|
||||
<p>
|
||||
Bitte beachte, dass deine Anmeldung erst nach Zahlungseiongang vollständig ist.<br />
|
||||
Wenn dieser nicht bis zum {{ props.event.registrationFinalEnd.formatted }} erfolgt, kann deine Anmeldung storniert werden.<br /><br />
|
||||
Solltest du den Beitrag bis zu diesem Datum nicht oder nur teilweise überweisen können, kontaktiere bitte die Aktionsleitung, damit wir eine gemeinsame Lösiung finden können.
|
||||
</p>
|
||||
|
||||
|
||||
</template>
|
||||
<p v-else>
|
||||
Du musst keinen Beitrag überweisen. Deine Anmeldung ist bestätigt.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Du erhältst innerhalb von 2 Stunden eine E-Mail mit weiteren Informationen.<br />
|
||||
Kontakt: <a :href="'mailto:' + props.event.email" style="color: #2563eb;">{{ props.event.email }}</a>
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,98 @@
|
||||
import { ref, reactive, computed } from 'vue'
|
||||
import axios from 'axios'
|
||||
|
||||
export function useSignupForm(event, participantData) {
|
||||
const currentStep = ref(1)
|
||||
const submitting = ref(false)
|
||||
const summaryLoading = ref(false)
|
||||
const submitResult = ref(null) // null | { type: 'success'|'exists', data: {} }
|
||||
|
||||
const selectedAddons = reactive({})
|
||||
console.log(participantData)
|
||||
|
||||
const formData = reactive({
|
||||
eatingHabit: 'EATING_HABIT_VEGAN',
|
||||
userId: participantData.id,
|
||||
eventId: event.id,
|
||||
vorname: participantData.firstname ?? '',
|
||||
nachname: participantData.lastname ?? '',
|
||||
pfadiname: participantData.nickname ?? '',
|
||||
localGroup: participantData.localGroup ?? '-1',
|
||||
geburtsdatum: participantData.birthday ?? '',
|
||||
address1: participantData.address_1 ?? '',
|
||||
address2: participantData.address_2 ?? '',
|
||||
plz: participantData.postcode ?? '',
|
||||
ort: participantData.city ?? '',
|
||||
telefon_1: participantData.phone ?? '',
|
||||
email_1: participantData.email ?? '',
|
||||
participationType: '',
|
||||
ansprechpartner: '',
|
||||
telefon_2: '',
|
||||
email_2: '',
|
||||
badeerlaubnis: '-1',
|
||||
first_aid: '-1',
|
||||
participant_group: '',
|
||||
beitrag: 'regular',
|
||||
arrival: event.eventBeginInternal?.split('T')[0] ?? '',
|
||||
departure: event.eventEndInternal?.split('T')[0] ?? '',
|
||||
anreise_essen: '1',
|
||||
abreise_essen: '2',
|
||||
foto: { socialmedia: false, print: false, webseite: false, partner: false, intern: false },
|
||||
allergien: participantData.allergies ?? '',
|
||||
intolerances: participantData.intolerances ?? '',
|
||||
medikamente: participantData.medications ?? '',
|
||||
tetanusVaccination: participantData.tetanusVaccination ?? '',
|
||||
essgewohnheit: 'vegetarian',
|
||||
anmerkungen: '',
|
||||
summary_information_correct: false,
|
||||
summary_accept_terms: false,
|
||||
legal_accepted: false,
|
||||
payment: false,
|
||||
})
|
||||
|
||||
const summaryAmount = ref('')
|
||||
|
||||
const goToStep = async (step) => {
|
||||
if (step === 9) {
|
||||
summaryLoading.value = true
|
||||
summaryAmount.value = ''
|
||||
try {
|
||||
const res = await axios.post('/api/v1/event/' + event.id + '/calculate-amount', {
|
||||
arrival: formData.arrival,
|
||||
departure: formData.departure,
|
||||
event_id: event.id,
|
||||
participation_group: formData.participant_group,
|
||||
selected_amount: formData.beitrag,
|
||||
addons: selectedAddons,
|
||||
participationType: formData.participationType,
|
||||
beitrag: formData.beitrag,
|
||||
})
|
||||
summaryAmount.value = res.data.amount
|
||||
} finally {
|
||||
summaryLoading.value = false
|
||||
}
|
||||
}
|
||||
currentStep.value = step
|
||||
}
|
||||
|
||||
const submit = async () => {
|
||||
if (!formData.summary_information_correct || !formData.summary_accept_terms || !formData.legal_accepted || !formData.payment) {
|
||||
return
|
||||
}
|
||||
submitting.value = true
|
||||
try {
|
||||
const res = await axios.post('/api/v1/event/'+ event.id + '/signup', {
|
||||
addons: selectedAddons,
|
||||
registration_data: { ...formData },
|
||||
})
|
||||
submitResult.value = {
|
||||
status: res.data.status,
|
||||
participant: res.data.participant,
|
||||
}
|
||||
} finally {
|
||||
submitting.value = false
|
||||
}
|
||||
}
|
||||
|
||||
return { currentStep, goToStep, formData, selectedAddons, submit, submitting, submitResult, summaryLoading, summaryAmount }
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
<script setup>
|
||||
const props = defineProps({ formData: Object, event: Object, selectedAddons: Object })
|
||||
const emit = defineEmits(['next', 'back'])
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<!-- Solidarbeitrag-Auswahl -->
|
||||
<div v-if="event.solidarityPayment" style="margin-bottom: 20px;">
|
||||
<h3>Beitrag</h3>
|
||||
<label v-if="event.participationFee_1?.active" style="display: block; margin-bottom: 8px;">
|
||||
<input type="radio" v-model="formData.beitrag" value="reduced" />
|
||||
{{ event.participationFee_1.name }} ({{ event.participationFee_1.amount }} €)
|
||||
</label>
|
||||
<label style="display: block; margin-bottom: 8px;">
|
||||
<input type="radio" v-model="formData.beitrag" value="regular" />
|
||||
{{ event.participationFee_2?.name ?? 'Regulärer Beitrag' }} ({{ event.participationFee_2?.amount }} €)
|
||||
</label>
|
||||
<label v-if="event.participationFee_3?.active" style="display: block; margin-bottom: 8px;">
|
||||
<input type="radio" v-model="formData.beitrag" value="social" />
|
||||
{{ event.participationFee_3.name }} ({{ event.participationFee_3.amount }} €)
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<!-- Addons -->
|
||||
<div v-if="event.addons?.length > 0">
|
||||
<h3>Zusatzoptionen</h3>
|
||||
<div v-for="addon in event.addons" :key="addon.id" style="margin-bottom: 16px; padding: 12px; background: #f8fafc; border-radius: 8px;">
|
||||
<label style="display: flex; gap: 12px; cursor: pointer;">
|
||||
<input type="checkbox" v-model="selectedAddons[addon.id]" style="margin-top: 4px;" />
|
||||
<span>
|
||||
<strong>{{ addon.name }}</strong>
|
||||
<span style="display: block; color: #6b7280; font-size: 0.875rem;">Betrag: {{ addon.amount }}</span>
|
||||
<span style="display: block; color: #374151; font-size: 0.875rem; margin-top: 4px;">{{ addon.description }}</span>
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="btn-row">
|
||||
<button type="button" class="btn-secondary" @click="emit('back', 5)">← Zurück</button>
|
||||
<button type="button" class="btn-primary" @click="emit('next', 7)">Weiter →</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
103
app/Domains/Event/Views/Partials/SignUpForm/steps/StepAge.vue
Normal file
103
app/Domains/Event/Views/Partials/SignUpForm/steps/StepAge.vue
Normal file
@@ -0,0 +1,103 @@
|
||||
|
||||
<script setup>
|
||||
defineProps({ event: Object })
|
||||
const emit = defineEmits(['next'])
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<h3 style="margin: 0 0 6px 0; color: #111827;">Wer nimmt teil?</h3>
|
||||
<p style="margin: 0 0 24px 0; color: #6b7280; font-size: 0.95rem;">Bitte wähle deine Altersgruppe aus.</p>
|
||||
|
||||
<div style="display: flex; gap: 20px; flex-wrap: wrap;">
|
||||
|
||||
<!-- Kind / Jugendliche:r -->
|
||||
<div class="age-card" @click="emit('next', 2)">
|
||||
<div class="age-card__badge">
|
||||
<img :src="'/images/children.png'" alt="Abzeichen Kind" class="age-card__img" onerror="this.style.display='none'" />
|
||||
<div class="age-card__badge-fallback">👦</div>
|
||||
</div>
|
||||
<div class="age-card__body">
|
||||
<h4 class="age-card__title">Mein Kind anmelden:</h4>
|
||||
<p class="age-card__desc">Mein Kind ist <strong>jünger als {{ event.alcoholicsAge }} Jahre.</strong></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Erwachsene:r -->
|
||||
<div class="age-card" @click="emit('next', 3)">
|
||||
<div class="age-card__badge">
|
||||
<img :src="'/images/adults.png'" alt="Abzeichen Erwachsene" class="age-card__img" onerror="this.style.display='none'" />
|
||||
<div class="age-card__badge-fallback">🧑</div>
|
||||
</div>
|
||||
<div class="age-card__body">
|
||||
<h4 class="age-card__title">Mich selbst anmelden</h4>
|
||||
<p class="age-card__desc">Ich bin <strong>{{ event.alcoholicsAge }} Jahre oder älter</strong>.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.age-card {
|
||||
flex: 1;
|
||||
min-width: 220px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
background: #f8fafc;
|
||||
border: 2px solid #e5e7eb;
|
||||
border-radius: 12px;
|
||||
padding: 28px 20px;
|
||||
cursor: pointer;
|
||||
transition: border-color 0.15s, box-shadow 0.15s, transform 0.1s;
|
||||
text-align: center;
|
||||
}
|
||||
.age-card:hover {
|
||||
border-color: #2563eb;
|
||||
box-shadow: 0 4px 16px rgba(37, 99, 235, 0.12);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.age-card__badge {
|
||||
position: relative;
|
||||
width: 350px;
|
||||
height: 200px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.age-card__img {
|
||||
width: 350px;
|
||||
height: 200px;
|
||||
object-fit: contain;
|
||||
}
|
||||
.age-card__badge-fallback {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 3rem;
|
||||
background: #e0f2fe;
|
||||
border-radius: 50%;
|
||||
}
|
||||
/* Fallback ausblenden wenn Bild geladen ist */
|
||||
.age-card__img:not([style*="display:none"]) + .age-card__badge-fallback {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.age-card__body { display: flex; flex-direction: column; align-items: center; gap: 6px; }
|
||||
.age-card__title { margin: 0; font-size: 1.1rem; font-weight: 700; color: #111827; }
|
||||
.age-card__desc { margin: 0; font-size: 0.9rem; color: #374151; }
|
||||
.age-card__hint { margin: 0; font-size: 0.8rem; color: #6b7280; }
|
||||
.age-card__cta {
|
||||
margin-top: 10px;
|
||||
display: inline-block;
|
||||
padding: 6px 18px;
|
||||
background: #2563eb;
|
||||
color: white;
|
||||
border-radius: 999px;
|
||||
font-size: 0.85rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,52 @@
|
||||
<script setup>
|
||||
const props = defineProps({ formData: Object, event: Object })
|
||||
const emit = defineEmits(['next', 'back'])
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<h3>Allergien & Ernährung</h3>
|
||||
<table class="form-table">
|
||||
<tr><td>Allergien:</td><td><input type="text" v-model="props.formData.allergien" /></td></tr>
|
||||
<tr>
|
||||
<td>
|
||||
Letzte Teranus-Impfung:
|
||||
<span style="display: block; font-size: 0.8rem; color: #6b7280; margin-top: 4px;">Lass das Feld frei, wenn die Information nicht vorliegt oder du diese nicht mitteilen willst</span>
|
||||
</td><td><input type="date" v-model="props.formData.tetanusVaccination" /></td></tr>
|
||||
|
||||
|
||||
|
||||
|
||||
<tr><td>Unverträglichkeiten:</td><td><input type="text" v-model="props.formData.intolerances" /></td></tr>
|
||||
<tr>
|
||||
<td>
|
||||
Medikamente:<br />
|
||||
<span style="display: block; font-size: 0.8rem; color: #6b7280; margin-top: 4px;">Bitte in ausreichender Menge mitbringen</span>
|
||||
</td>
|
||||
<td>
|
||||
<input type="text" v-model="props.formData.medikamente" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Ernährungsweise:</td>
|
||||
<td>
|
||||
<select v-model="props.formData.eatingHabit">
|
||||
<option
|
||||
v-for="eatingHabit in props.event.eatingHabits"
|
||||
:value="eatingHabit.data.slug">{{eatingHabit.data.name}}</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Anmerkungen:</td>
|
||||
<td><textarea rows="5" v-model="props.formData.anmerkungen" style="width: 100%;"></textarea></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2" class="btn-row">
|
||||
<button type="button" class="btn-secondary" @click="emit('back', 7)">← Zurück</button>
|
||||
<button type="button" class="btn-primary" @click="emit('next', 9)">Weiter →</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,81 @@
|
||||
<script setup>
|
||||
const props = defineProps({ formData: Object, event: Object })
|
||||
const emit = defineEmits(['next', 'back'])
|
||||
|
||||
const next = () => {
|
||||
const arrival = new Date(props.formData.arrival)
|
||||
arrival.setHours(0,0,0,0);
|
||||
const departure = new Date(props.formData.departure)
|
||||
const eventStart = new Date(props.event.eventBeginInternal)
|
||||
const eventEnd = new Date(props.event.eventEndInternal)
|
||||
|
||||
arrival.setHours(0,0,0,0);
|
||||
departure.setHours(0,0,0,0);
|
||||
eventStart.setHours(0,0,0,0);
|
||||
eventEnd.setHours(0,0,0,0);
|
||||
|
||||
|
||||
if (arrival < eventStart) {
|
||||
alert('Bitte gültige Anreise angeben innerhalb des Veranstaltungszeitraums wählen.')
|
||||
return
|
||||
}
|
||||
|
||||
if (arrival > eventEnd) {
|
||||
alert('Bitte gültige Abreise angeben innerhalb des Veranstaltungszeitraums wählen.')
|
||||
return
|
||||
}
|
||||
|
||||
if (departure < arrival) {
|
||||
alert('Abreise kann niht vor der Anreise liegen. Bitte korrigieren.')
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
|
||||
const hasAddons = (props.event.addons?.length > 0) || props.event.solidarityPayment
|
||||
emit('next', 5)
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
const back = () => emit('back', 3)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<h3>An- und Abreise</h3>
|
||||
<table class="form-table">
|
||||
<tr>
|
||||
<td>Anreise:</td>
|
||||
<td>
|
||||
<input type="date" v-model="formData.arrival" /><br />
|
||||
<select v-model="formData.anreise_essen" style="margin-top: 6px;">
|
||||
<option value="1">Vor dem Abendessen</option>
|
||||
<option value="2">Vor dem Mittagessen</option>
|
||||
<option value="3">Vor dem Frühstück</option>
|
||||
<option value="4">Keine Mahlzeit</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Abreise:</td>
|
||||
<td>
|
||||
<input type="date" v-model="formData.departure" /><br />
|
||||
<select v-model="formData.abreise_essen" style="margin-top: 6px;">
|
||||
<option value="1">Nach dem Frühstück</option>
|
||||
<option value="2">Nach dem Mittagessen</option>
|
||||
<option value="3">Nach dem Abendessen</option>
|
||||
<option value="4">Keine Mahlzeit</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2" class="btn-row">
|
||||
<button type="button" class="btn-secondary" @click="back">← Zurück</button>
|
||||
<button type="button" class="btn-primary" @click="next">Weiter →</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,125 @@
|
||||
<script setup>
|
||||
import ErrorText from "../../../../../../Views/Components/ErrorText.vue";
|
||||
import {reactive} from "vue";
|
||||
|
||||
const props = defineProps({ formData: Object, event: Object })
|
||||
const emit = defineEmits(['next', 'back'])
|
||||
|
||||
const errors = reactive({
|
||||
ansprechpartner: '',
|
||||
telefon_2: '',
|
||||
email_2: '',
|
||||
badeerlaubnis: '',
|
||||
first_aid: '',
|
||||
})
|
||||
|
||||
const next = () => {
|
||||
errors.ansprechpartner = ''
|
||||
errors.telefon_2 = ''
|
||||
errors.email_2 = ''
|
||||
errors.badeerlaubnis = ''
|
||||
errors.first_aid = ''
|
||||
|
||||
let hasError = false
|
||||
|
||||
if (!props.formData.ansprechpartner) {
|
||||
errors.ansprechpartner = 'Bitte eine Kontaktperson angeben.'
|
||||
hasError = true
|
||||
}
|
||||
|
||||
if (!props.formData.telefon_2) {
|
||||
errors.telefon_2 = 'Bitte eine Telefonnummer angeben.'
|
||||
hasError = true
|
||||
}
|
||||
|
||||
if (!props.formData.email_2) {
|
||||
errors.email_2 = 'Bitte eine E-Mail-Adresse angeben.'
|
||||
hasError = true
|
||||
}
|
||||
|
||||
if (props.formData.badeerlaubnis === '-1') {
|
||||
errors.badeerlaubnis = 'Bitte triff eine Entscheidung. Bist du dir unsicher, kontaktiere bitte die Aktionsleitung'
|
||||
hasError = true
|
||||
}
|
||||
|
||||
if (props.formData.first_aid === '-1') {
|
||||
errors.first_aid = 'Bitte triff eine Entscheidung. Bist du dir unsicher, kontaktiere bitte die Aktionsleitung.'
|
||||
hasError = true
|
||||
}
|
||||
|
||||
if (hasError) {
|
||||
return
|
||||
}
|
||||
|
||||
emit('next', 3)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<h3>Kontaktperson</h3>
|
||||
<table class="form-table">
|
||||
<tr>
|
||||
<td>Name (Nachname, Vorname):</td>
|
||||
<td>
|
||||
<input type="text" v-model="formData.ansprechpartner" />
|
||||
<ErrorText :message="errors.ansprechpartner" />
|
||||
</td>
|
||||
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Telefon:</td>
|
||||
<td>
|
||||
<input type="text" v-model="formData.telefon_2" />
|
||||
<ErrorText :message="errors.telefon_2" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>E-Mail:</td>
|
||||
<td>
|
||||
<input type="text" v-model="formData.email_2" />
|
||||
<ErrorText :message="errors.email_2" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Badeerlaubnis:</td>
|
||||
<td>
|
||||
<select v-model="formData.badeerlaubnis">
|
||||
<option value="-1">Bitte wählen</option>
|
||||
|
||||
<option
|
||||
v-for="swimmingPermission in props.event.swimmingPermissions"
|
||||
:value="swimmingPermission.slug">{{swimmingPermission.name}}</option>
|
||||
</select>
|
||||
<ErrorText :message="errors.badeerlaubnis" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Erweiterte Erste Hilfe erlaubt:*</td>
|
||||
<td>
|
||||
<select v-model="formData.first_aid">
|
||||
<option value="-1">Bitte wählen</option>
|
||||
|
||||
<option
|
||||
v-for="firstAidPermission in props.event.firstAidPermissions"
|
||||
:value="firstAidPermission.slug">{{firstAidPermission.name}}</option>
|
||||
</select><br />
|
||||
<span style="font-size: 0.8rem; color: #6b7280;">
|
||||
Nicht dringend-notwendige Erste-Hilfe-Maßnahmen, beinhaltet das Entfernen von Zecken und Splittern sowie das Kleben von Pflastern.
|
||||
</span>
|
||||
<ErrorText :message="errors.first_aid" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2" class="btn-row">
|
||||
<button type="button" class="btn-primary" @click="next">Weiter →</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,124 @@
|
||||
<script setup>
|
||||
import {reactive} from "vue";
|
||||
|
||||
const props = defineProps({ formData: Object, localGroups: Array })
|
||||
const emit = defineEmits(['next', 'back'])
|
||||
|
||||
const errors = reactive({
|
||||
vorname: '',
|
||||
nachname: '',
|
||||
geburtsdatum: '',
|
||||
localGroup: '',
|
||||
address1: '',
|
||||
plz: '',
|
||||
ort: '',
|
||||
telefon_1: '',
|
||||
email_1: '',
|
||||
})
|
||||
|
||||
const next = () => {
|
||||
errors.vorname = ''
|
||||
errors.nachname = ''
|
||||
errors.geburtsdatum = ''
|
||||
errors.localGroup = ''
|
||||
errors.address1 = ''
|
||||
errors.plz = ''
|
||||
errors.ort = ''
|
||||
errors.telefon_1 = ''
|
||||
errors.email_1 = ''
|
||||
|
||||
let hasError = false
|
||||
|
||||
if (!props.formData.vorname) {
|
||||
errors.vorname = 'Bitte den Vornamen angeben.'
|
||||
hasError = true
|
||||
}
|
||||
|
||||
if (!props.formData.nachname) {
|
||||
errors.nachname = 'Bitte den Nachnamen angeben.'
|
||||
hasError = true
|
||||
}
|
||||
|
||||
if (!props.formData.geburtsdatum) {
|
||||
errors.geburtsdatum = 'Bitte das Geburtsdatum angeben.'
|
||||
hasError = true
|
||||
}
|
||||
|
||||
if (props.formData.localGroup === '-1') {
|
||||
errors.localGroup = 'Bitte den Stamm auswählen.'
|
||||
hasError = true
|
||||
}
|
||||
|
||||
if (!props.formData.address1) {
|
||||
errors.address1 = 'Bitte die Adresse angeben.'
|
||||
hasError = true
|
||||
}
|
||||
|
||||
if (!props.formData.plz) {
|
||||
errors.plz = 'Bitte die Postleitzahl angeben.'
|
||||
hasError = true
|
||||
}
|
||||
|
||||
if (!props.formData.ort) {
|
||||
errors.ort = 'Bitte den Ort angeben.'
|
||||
hasError = true
|
||||
}
|
||||
|
||||
if (!props.formData.email_1) {
|
||||
errors.email_1 = 'Bitte eine E-Mail-Adresse angeben.'
|
||||
hasError = true
|
||||
}
|
||||
|
||||
if (hasError) {
|
||||
return
|
||||
}
|
||||
emit('next', 4)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<h3>Persönliche Daten</h3>
|
||||
<table class="form-table">
|
||||
<tr><td>Vorname:</td><td><input type="text" v-model="props.formData.vorname" /></td></tr>
|
||||
<tr><td>Nachname:</td><td><input type="text" v-model="props.formData.nachname" /></td></tr>
|
||||
<tr><td>Pfadiname:</td><td><input type="text" v-model="props.formData.pfadiname" /></td></tr>
|
||||
<tr>
|
||||
<td>Stamm:</td>
|
||||
<td>
|
||||
<select v-model="props.formData.localGroup">
|
||||
<option value="-1">Bitte wählen</option>
|
||||
<option v-for="lg in localGroups" :key="lg.id" :value="lg.id">{{ lg.name }}</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr><td>Geburtsdatum:</td><td><input type="date" v-model="props.formData.geburtsdatum" /></td></tr>
|
||||
<tr>
|
||||
<td>Adresse:</td>
|
||||
<td>
|
||||
<input type="text" v-model="props.formData.address1" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td>
|
||||
<td>
|
||||
<input type="text" v-model="props.formData.address2" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>PLZ, Ort:</td>
|
||||
<td>
|
||||
<input maxlength="5" type="text" v-model="props.formData.plz" style="width: 100px; margin-right: 8px;" />
|
||||
<input type="text" v-model="props.formData.ort" style="width: calc(100% - 110px);" />
|
||||
</td>
|
||||
</tr>
|
||||
<tr><td>Telefon:</td><td><input type="text" v-model="props.formData.telefon_1" /></td></tr>
|
||||
<tr><td>E-Mail:</td><td><input type="text" v-model="props.formData.email_1" /></td></tr>
|
||||
<tr>
|
||||
<td colspan="2" class="btn-row">
|
||||
<button type="button" class="btn-primary" @click="next">Weiter →</button>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,33 @@
|
||||
<script setup>
|
||||
const props = defineProps({ formData: Object, event: Object })
|
||||
const emit = defineEmits(['next', 'back'])
|
||||
|
||||
const acceptAll = () => {
|
||||
Object.keys(props.formData.foto).forEach(k => props.formData.foto[k] = true)
|
||||
emit('next', 8)
|
||||
}
|
||||
|
||||
const back = () => {
|
||||
const hasAddons = (props.event.addons?.length > 0) || props.event.solidarityPayment
|
||||
emit('back', hasAddons ? 6 : 5)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<h3>Fotoerlaubnis</h3>
|
||||
<div v-for="[key, label] in [['socialmedia','Social Media'],['print','Printmedien'],['webseite','Webseite'],['partner','Partnerorganisationen'],['intern','Interne Zwecke']]"
|
||||
:key="key"
|
||||
style="margin-bottom: 10px;">
|
||||
<label style="display: flex; gap: 10px; cursor: pointer;">
|
||||
<input type="checkbox" v-model="formData.foto[key]" />
|
||||
{{ label }}
|
||||
</label>
|
||||
</div>
|
||||
<div class="btn-row">
|
||||
<button type="button" class="btn-secondary" @click="back">← Zurück</button>
|
||||
<button type="button" class="btn-primary" style="background: #059669;" @click="acceptAll">Alle akzeptieren & weiter</button>
|
||||
<button type="button" class="btn-primary" @click="emit('next', 8)">Weiter →</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -0,0 +1,130 @@
|
||||
<script setup>
|
||||
import { watch } from "vue";
|
||||
|
||||
const props = defineProps({ formData: Object, event: Object })
|
||||
const emit = defineEmits(['next', 'back'])
|
||||
|
||||
watch(
|
||||
() => props.formData.participationType,
|
||||
(value) => {
|
||||
if (!value) {
|
||||
props.formData.beitrag = 'standard'
|
||||
return
|
||||
}
|
||||
|
||||
props.formData.beitrag = 'standard'
|
||||
}
|
||||
)
|
||||
|
||||
const nextStep = () => {
|
||||
const hasAddons = (props.event.addons?.length ?? 0) > 0
|
||||
emit('next', hasAddons ? 6 : 7)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<h3 v-if="event.solidarityPayment">Solidarbeitrag – Teilnahmegruppe</h3>
|
||||
<h3 v-else>Ich nehme teil als ...</h3>
|
||||
|
||||
<table style="width: 100%;">
|
||||
<tr
|
||||
v-for="participationType in props.event.participationTypes"
|
||||
:key="participationType.type.slug"
|
||||
style="vertical-align: top;"
|
||||
>
|
||||
<td style="width: 50px; padding-top: 6px;">
|
||||
<input
|
||||
:id="participationType.type.slug"
|
||||
v-model="props.formData.participationType"
|
||||
type="radio"
|
||||
:value="participationType.type.slug"
|
||||
/>
|
||||
</td>
|
||||
<td style="padding-bottom: 16px;">
|
||||
<label :for="participationType.type.slug" style="line-height: 1.5; font-weight: 600; cursor: pointer;">
|
||||
{{ participationType.type.name }}
|
||||
</label><br />
|
||||
|
||||
<label
|
||||
:for="participationType.type.slug"
|
||||
style="line-height: 1.5; padding-left: 15px; font-style: italic; color: #606060; cursor: pointer;"
|
||||
>
|
||||
{{ participationType.description }}
|
||||
</label>
|
||||
|
||||
<div
|
||||
v-if="props.formData.participationType === participationType.type.slug"
|
||||
style="margin-top: 10px; margin-left: 15px; padding: 12px 14px; background: #f8fafc; border-left: 3px solid #2563eb; border-radius: 6px;"
|
||||
>
|
||||
<template
|
||||
v-if="participationType.amount_reduced !== null || participationType.amount_solidarity !== null"
|
||||
>
|
||||
<div style="margin-bottom: 8px; font-size: 0.95rem; font-weight: 600; color: #374151;">
|
||||
Beitrag auswählen
|
||||
</div>
|
||||
|
||||
<label style="display: block; margin-bottom: 8px; cursor: pointer;">
|
||||
<input type="radio" v-model="props.formData.beitrag" value="standard" />
|
||||
Standardbeitrag
|
||||
<span style="color: #606060;">
|
||||
({{ participationType.amount_standard.readable }}
|
||||
<template v-if="props.event.payPerDay">/ Tag</template>
|
||||
<template v-else>Gesamt</template>)
|
||||
</span>
|
||||
</label>
|
||||
|
||||
<label
|
||||
v-if="participationType.amount_reduced !== null"
|
||||
style="display: block; margin-bottom: 8px; cursor: pointer;"
|
||||
>
|
||||
<input type="radio" v-model="props.formData.beitrag" value="reduced" />
|
||||
Reduzierter Beitrag
|
||||
<span style="color: #606060;">
|
||||
({{ participationType.amount_reduced.readable }}
|
||||
<template v-if="props.event.payPerDay">/ Tag</template>
|
||||
<template v-else>Gesamt</template>)
|
||||
</span>
|
||||
</label>
|
||||
|
||||
<label
|
||||
v-if="participationType.amount_solidarity !== null"
|
||||
style="display: block; margin-bottom: 0; cursor: pointer;"
|
||||
>
|
||||
<input type="radio" v-model="props.formData.beitrag" value="solidarity" />
|
||||
Solidaritätsbeitrag
|
||||
<span style="color: #606060;">
|
||||
({{ participationType.amount_solidarity.readable }}
|
||||
<template v-if="props.event.payPerDay">/ Tag</template>
|
||||
<template v-else>Gesamt</template>)
|
||||
</span>
|
||||
</label>
|
||||
</template>
|
||||
|
||||
<template v-else>
|
||||
<div style="font-size: 0.9rem; color: #606060;">
|
||||
Standardbeitrag:
|
||||
<strong>{{ participationType.amount_standard.readable }}</strong>
|
||||
<template v-if="props.event.payPerDay">/ Tag</template>
|
||||
<template v-else>Gesamt</template>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<div class="btn-row">
|
||||
<button type="button" class="btn-secondary" @click="emit('back', 3)">← Zurück</button>
|
||||
<button
|
||||
type="button"
|
||||
v-if="props.formData.participationType"
|
||||
class="btn-primary"
|
||||
@click="nextStep"
|
||||
>
|
||||
Weiter →
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
<script setup>
|
||||
import {format, parseISO} from "date-fns";
|
||||
|
||||
const props = defineProps({
|
||||
formData: Object,
|
||||
event: Object,
|
||||
summaryAmount: String,
|
||||
summaryLoading: Boolean,
|
||||
submitting: Boolean,
|
||||
})
|
||||
const emit = defineEmits(['back', 'submit'])
|
||||
|
||||
function formatDate(dateString) {
|
||||
if (!dateString) return ''
|
||||
return format(parseISO(dateString), 'dd.MM.yyyy')
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<h3>Zusammenfassung</h3>
|
||||
|
||||
<div v-if="summaryLoading" style="color: #6b7280; padding: 20px 0;">Wird geladen…</div>
|
||||
<div v-else>
|
||||
<table class="form-table" style="margin-bottom: 20px;">
|
||||
<tr><td>Veranstaltung:</td><td><strong>{{ event.name }}</strong></td></tr>
|
||||
<tr><td>Anreise:</td><td>{{ formatDate(formData.arrival) }}</td></tr>
|
||||
<tr><td>Abreise:</td><td>{{ formatDate(formData.departure) }}</td></tr>
|
||||
</table>
|
||||
|
||||
<div style="display: flex; flex-direction: column; gap: 10px; margin-bottom: 20px;">
|
||||
<label style="display: flex; gap: 10px; cursor: pointer;">
|
||||
<input type="checkbox" v-model="formData.summary_information_correct" />
|
||||
Ich bestätige, dass alle Angaben korrekt sind.
|
||||
</label>
|
||||
<label style="display: flex; gap: 10px; cursor: pointer;">
|
||||
<input type="checkbox" v-model="formData.summary_accept_terms" />
|
||||
Ich akzeptiere die Teilnahmebedingungen.
|
||||
</label>
|
||||
<label style="display: flex; gap: 10px; cursor: pointer;">
|
||||
<input type="checkbox" v-model="formData.legal_accepted" />
|
||||
Ich stimme der Datenschutzerklärung zu.
|
||||
</label>
|
||||
<label style="display: flex; gap: 10px; cursor: pointer;">
|
||||
<input type="checkbox" v-model="formData.payment" />
|
||||
Ich bestätige, den Betrag von <strong>{{ summaryAmount }}</strong> zu überweisen.
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="btn-row">
|
||||
<button type="button" class="btn-secondary" @click="emit('back', 8)">← Zurück</button>
|
||||
<button
|
||||
type="submit"
|
||||
class="btn-primary"
|
||||
:disabled="!formData.summary_information_correct || !formData.summary_accept_terms || !formData.legal_accepted || !formData.payment || submitting"
|
||||
style="background: #059669;"
|
||||
>
|
||||
{{ submitting ? 'Wird gesendet…' : 'Jetzt anmelden ✓' }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
Reference in New Issue
Block a user