Kurzanmeldungen
This commit is contained in:
@@ -0,0 +1,37 @@
|
||||
// Schrittreihenfolge der Kurzanmeldung. Zwei Schritte sind optional: die Teilnahmeoptionen entfallen, wenn für
|
||||
// die Veranstaltung keine definiert sind, die Zahlungsart entfällt bei Betrag 0 oder wenn es ohnehin nur eine
|
||||
// Zahlungsart gibt (die wird dann vorausgewählt).
|
||||
export const SHORT_STEP_PERSON = 1
|
||||
export const SHORT_STEP_PARTICIPATION_OPTIONS = 2
|
||||
export const SHORT_STEP_PHOTO = 3
|
||||
export const SHORT_STEP_PAYMENT = 4
|
||||
export const SHORT_STEP_SUMMARY = 5
|
||||
|
||||
export function stepVisible(event, step, amountValue) {
|
||||
if (step === SHORT_STEP_PARTICIPATION_OPTIONS) {
|
||||
return (event.participationOptions?.length ?? 0) > 0
|
||||
}
|
||||
|
||||
if (step === SHORT_STEP_PAYMENT) {
|
||||
const activeMethods = (event.paymentMethods ?? []).filter(m => m.active)
|
||||
return amountValue > 0 && activeMethods.length > 1
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
export function nextVisibleFrom(event, step, amountValue) {
|
||||
let candidate = step + 1
|
||||
while (candidate < SHORT_STEP_SUMMARY && !stepVisible(event, candidate, amountValue)) {
|
||||
candidate++
|
||||
}
|
||||
return candidate
|
||||
}
|
||||
|
||||
export function prevVisibleFrom(event, step, amountValue) {
|
||||
let candidate = step - 1
|
||||
while (candidate > SHORT_STEP_PERSON && !stepVisible(event, candidate, amountValue)) {
|
||||
candidate--
|
||||
}
|
||||
return candidate
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
import {computed, reactive, ref} from 'vue'
|
||||
import axios from 'axios'
|
||||
import {differenceInYears, parseISO} from 'date-fns'
|
||||
import {SHORT_STEP_PERSON, nextVisibleFrom, prevVisibleFrom} from './shortStepFlow.js'
|
||||
|
||||
export function useShortSignupForm(event, participantData) {
|
||||
const currentStep = ref(SHORT_STEP_PERSON)
|
||||
const submitting = ref(false)
|
||||
const summaryLoading = ref(false)
|
||||
const submitResult = ref(null) // null | { status: 'success'|'exists', participant: {} }
|
||||
const submitError = ref('')
|
||||
|
||||
// Gibt es nur eine Zahlungsart, wird sie vorausgewählt und der Zahlungsschritt entfällt.
|
||||
const activeMethods = (event.paymentMethods ?? []).filter(m => m.active)
|
||||
|
||||
// Bei genau einem teilnehmenden Stamm wird dieser gesetzt, ohne danach zu fragen; erst ab zwei
|
||||
// Stämmen erscheint das Auswahlfeld.
|
||||
const localGroups = event.contributingLocalGroups ?? []
|
||||
const needsLocalGroup = localGroups.length > 1
|
||||
|
||||
const formData = reactive({
|
||||
userId: participantData.id,
|
||||
vorname: participantData.firstname ?? '',
|
||||
nachname: participantData.lastname ?? '',
|
||||
pfadiname: participantData.nickname ?? '',
|
||||
geburtsdatum: participantData.birthday ?? '',
|
||||
email_1: participantData.email ?? '',
|
||||
telefon_1: participantData.phone ?? '',
|
||||
localGroup: localGroups.length === 1 ? localGroups[0].id : (participantData.localGroup ?? '-1'),
|
||||
ansprechpartner: '',
|
||||
email_2: '',
|
||||
allergien: participantData.allergies ?? '',
|
||||
intolerances: participantData.intolerances ?? '',
|
||||
first_aid: '-1',
|
||||
badeerlaubnis: '-1',
|
||||
// Antworten auf die event-spezifischen Teilnahmeoptionen: { [question.key]: option.value }
|
||||
participationOptions: {},
|
||||
foto: {socialmedia: false, print: false, webseite: false, partner: false, intern: false},
|
||||
paymentMethod: activeMethods.length === 1 ? activeMethods[0].slug : null,
|
||||
paymentOptions: {},
|
||||
summary_information_correct: false,
|
||||
summary_accept_terms: false,
|
||||
legal_accepted: false,
|
||||
payment: false,
|
||||
})
|
||||
|
||||
// Minderjährigkeit steuert Kontaktperson, Erweiterte Erste Hilfe und Badeerlaubnis. Die Grenze von 18 Jahren
|
||||
// entspricht Age::isfullAged() im Backend.
|
||||
const isMinor = computed(() => {
|
||||
if (!formData.geburtsdatum) {
|
||||
return false
|
||||
}
|
||||
return differenceInYears(new Date(), parseISO(formData.geburtsdatum)) < 18
|
||||
})
|
||||
|
||||
const summaryAmount = ref('')
|
||||
const summaryAmountValue = ref(0)
|
||||
const amountLoaded = ref(false)
|
||||
|
||||
// Der Betrag steht für die ganze Anmeldung fest (feste Teilnahmegruppe, fester Zeitraum) und wird deshalb
|
||||
// genau einmal geholt. Er entscheidet, ob der Zahlungsschritt überhaupt vorkommt.
|
||||
const ensureAmount = async () => {
|
||||
if (amountLoaded.value) {
|
||||
return
|
||||
}
|
||||
|
||||
summaryLoading.value = true
|
||||
try {
|
||||
const res = await axios.post('/api/v1/event/' + event.id + '/short-calculate-amount')
|
||||
summaryAmount.value = res.data.amount
|
||||
summaryAmountValue.value = Number(res.data.amountValue ?? 0)
|
||||
amountLoaded.value = true
|
||||
|
||||
if (summaryAmountValue.value <= 0) {
|
||||
formData.paymentMethod = null
|
||||
formData.paymentOptions = {}
|
||||
}
|
||||
} finally {
|
||||
summaryLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const goToStep = (step) => {
|
||||
currentStep.value = step
|
||||
}
|
||||
|
||||
const goNext = async () => {
|
||||
await ensureAmount()
|
||||
goToStep(nextVisibleFrom(event, currentStep.value, summaryAmountValue.value))
|
||||
}
|
||||
|
||||
const goBack = async () => {
|
||||
await ensureAmount()
|
||||
goToStep(prevVisibleFrom(event, currentStep.value, summaryAmountValue.value))
|
||||
}
|
||||
|
||||
const submit = async () => {
|
||||
if (!formData.summary_information_correct || !formData.summary_accept_terms || !formData.legal_accepted) {
|
||||
return
|
||||
}
|
||||
|
||||
submitting.value = true
|
||||
submitError.value = ''
|
||||
try {
|
||||
const res = await axios.post('/api/v1/event/' + event.id + '/short-signup', {
|
||||
firstname: formData.vorname,
|
||||
lastname: formData.nachname,
|
||||
nickname: formData.pfadiname || null,
|
||||
birthday: formData.geburtsdatum,
|
||||
email: formData.email_1,
|
||||
phone: formData.telefon_1,
|
||||
localGroup: formData.localGroup !== '-1' ? formData.localGroup : null,
|
||||
contactPerson: isMinor.value ? formData.ansprechpartner : null,
|
||||
contactEmail: isMinor.value ? formData.email_2 : null,
|
||||
allergies: formData.allergien || null,
|
||||
intolerances: formData.intolerances || null,
|
||||
firstAidPermission: isMinor.value && formData.first_aid !== '-1' ? formData.first_aid : null,
|
||||
swimmingPermission: isMinor.value && formData.badeerlaubnis !== '-1' ? formData.badeerlaubnis : null,
|
||||
foto: formData.foto,
|
||||
participationOptions: formData.participationOptions,
|
||||
paymentMethod: formData.paymentMethod,
|
||||
paymentOptions: formData.paymentOptions,
|
||||
})
|
||||
|
||||
submitResult.value = {
|
||||
status: res.data.status,
|
||||
participant: res.data.participant,
|
||||
}
|
||||
} catch (error) {
|
||||
submitError.value = error.response?.data?.message
|
||||
?? 'Die Anmeldung konnte nicht gespeichert werden. Bitte versuche es später erneut.'
|
||||
} finally {
|
||||
submitting.value = false
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
currentStep,
|
||||
goToStep,
|
||||
goNext,
|
||||
goBack,
|
||||
formData,
|
||||
isMinor,
|
||||
localGroups,
|
||||
needsLocalGroup,
|
||||
submit,
|
||||
submitting,
|
||||
submitResult,
|
||||
submitError,
|
||||
summaryLoading,
|
||||
summaryAmount,
|
||||
summaryAmountValue,
|
||||
ensureAmount,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user