Fix für Short-Signups

This commit is contained in:
2026-09-10 22:27:57 +02:00
parent 4e981c140b
commit 328825293a
13 changed files with 242 additions and 57 deletions
@@ -1,36 +1,43 @@
// Schrittreihenfolge der Kurzanmeldung. Zwei Schritte sind optional: die Teilnahmeoptionen entfallen, wenn für
// Schrittreihenfolge der Kurzanmeldung. Drei Schritte sind optional: der Beitrag erscheint nur, wenn neben dem
// Standardbeitrag weitere Stufen (reduziert, Solidarität) hinterlegt sind, 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 const SHORT_STEP_FEE = 2
export const SHORT_STEP_PARTICIPATION_OPTIONS = 3
export const SHORT_STEP_PHOTO = 4
export const SHORT_STEP_PAYMENT = 5
export const SHORT_STEP_SUMMARY = 6
// `state` kommt mit den Beiträgen vom Server: { amountValue: Betrag der gewählten Stufe, feeOptionCount }.
export function stepVisible(event, step, state) {
if (step === SHORT_STEP_FEE) {
return state.feeOptionCount > 1
}
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 state.amountValue > 0 && activeMethods.length > 1
}
return true
}
export function nextVisibleFrom(event, step, amountValue) {
export function nextVisibleFrom(event, step, state) {
let candidate = step + 1
while (candidate < SHORT_STEP_SUMMARY && !stepVisible(event, candidate, amountValue)) {
while (candidate < SHORT_STEP_SUMMARY && !stepVisible(event, candidate, state)) {
candidate++
}
return candidate
}
export function prevVisibleFrom(event, step, amountValue) {
export function prevVisibleFrom(event, step, state) {
let candidate = step - 1
while (candidate > SHORT_STEP_PERSON && !stepVisible(event, candidate, amountValue)) {
while (candidate > SHORT_STEP_PERSON && !stepVisible(event, candidate, state)) {
candidate--
}
return candidate
@@ -1,4 +1,4 @@
import {computed, reactive, ref} from 'vue'
import {computed, reactive, ref, watch} from 'vue'
import axios from 'axios'
import {differenceInYears, parseISO} from 'date-fns'
import {SHORT_STEP_PERSON, nextVisibleFrom, prevVisibleFrom} from './shortStepFlow.js'
@@ -33,6 +33,8 @@ export function useShortSignupForm(event, participantData) {
intolerances: participantData.intolerances ?? '',
first_aid: '-1',
badeerlaubnis: '-1',
// Beitragsstufe; wählbar nur, wenn weitere Stufen hinterlegt sind.
feeType: 'standard',
// Antworten auf die event-spezifischen Teilnahmeoptionen: { [question.key]: option.value }
participationOptions: {},
foto: {socialmedia: false, print: false, webseite: false, partner: false, intern: false},
@@ -53,12 +55,18 @@ export function useShortSignupForm(event, participantData) {
return differenceInYears(new Date(), parseISO(formData.geburtsdatum)) < 18
})
const summaryAmount = ref('')
const summaryAmountValue = ref(0)
// Beitragsstufen mit fertig berechnetem Betrag: [{ value, label, amount, amountValue }]
const feeOptions = ref([])
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 selectedFee = computed(() =>
feeOptions.value.find(option => option.value === formData.feeType) ?? feeOptions.value[0] ?? null
)
const summaryAmount = computed(() => selectedFee.value?.amount ?? '')
const summaryAmountValue = computed(() => Number(selectedFee.value?.amountValue ?? 0))
// Teilnahmegruppe und Zeitraum stehen für die ganze Anmeldung fest, deshalb werden die Beträge aller Stufen
// genau einmal geholt. Die Wahl der Stufe entscheidet dann nur noch lokal über den Betrag.
const ensureAmount = async () => {
if (amountLoaded.value) {
return
@@ -67,31 +75,41 @@ export function useShortSignupForm(event, participantData) {
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)
feeOptions.value = res.data.feeTypes ?? []
amountLoaded.value = true
if (summaryAmountValue.value <= 0) {
formData.paymentMethod = null
formData.paymentOptions = {}
}
} finally {
summaryLoading.value = false
}
}
// Bei Betrag 0 gibt es nichts zu bezahlen. Wechselt die Stufe zurück auf einen Betrag, wird die einzige
// Zahlungsart wieder vorausgewählt -- sonst fragt sie niemand mehr ab.
watch(summaryAmountValue, (value) => {
if (value <= 0) {
formData.paymentMethod = null
formData.paymentOptions = {}
} else if (formData.paymentMethod === null && activeMethods.length === 1) {
formData.paymentMethod = activeMethods[0].slug
}
})
const flowState = computed(() => ({
amountValue: summaryAmountValue.value,
feeOptionCount: feeOptions.value.length,
}))
const goToStep = (step) => {
currentStep.value = step
}
const goNext = async () => {
await ensureAmount()
goToStep(nextVisibleFrom(event, currentStep.value, summaryAmountValue.value))
goToStep(nextVisibleFrom(event, currentStep.value, flowState.value))
}
const goBack = async () => {
await ensureAmount()
goToStep(prevVisibleFrom(event, currentStep.value, summaryAmountValue.value))
goToStep(prevVisibleFrom(event, currentStep.value, flowState.value))
}
const submit = async () => {
@@ -120,6 +138,7 @@ export function useShortSignupForm(event, participantData) {
participationOptions: formData.participationOptions,
paymentMethod: formData.paymentMethod,
paymentOptions: formData.paymentOptions,
feeType: formData.feeType,
})
submitResult.value = {
@@ -150,6 +169,9 @@ export function useShortSignupForm(event, participantData) {
summaryLoading,
summaryAmount,
summaryAmountValue,
feeOptions,
selectedFee,
flowState,
ensureAmount,
}
}