428 lines
19 KiB
Vue
428 lines
19 KiB
Vue
<script setup>
|
|
import {onMounted, reactive, ref} from "vue";
|
|
import AmountInput from "../../../../Views/Components/AmountInput.vue";
|
|
import ErrorText from "../../../../Views/Components/ErrorText.vue";
|
|
import TextEditor from "../../../../Views/Components/TextEditor.vue";
|
|
import Modal from "../../../../Views/Components/Modal.vue";
|
|
import RichSelectBox from "../../../../Views/Components/RichSelectBox.vue";
|
|
import {PAYMENT_METHOD_ICONS} from "../../../../../resources/js/constants/paymentMethodIcons.js";
|
|
import {toast} from "vue3-toastify";
|
|
import {request} from "../../../../../resources/js/components/HttpClient.js";
|
|
|
|
const emit = defineEmits(['close'])
|
|
|
|
const props = defineProps({
|
|
event: Object,
|
|
})
|
|
|
|
// Verfügbare (aktive) Zahlungsarten des Tenants inkl. optionsSchema + Default-Config.
|
|
const availablePaymentMethods = ref([])
|
|
// Auswahl der Zahlungsart-Slugs für dieses Event (Default: leer bei neuen Events).
|
|
const paymentMethods = ref([])
|
|
// pro-Event-Config je Zahlungsmethode: { slug: { option: value } }
|
|
const paymentMethodConfigurations = reactive({})
|
|
|
|
// Optionen-Modal
|
|
const showOptionsModal = ref(false)
|
|
const optionsMethod = ref(null)
|
|
|
|
function openOptions(method) {
|
|
optionsMethod.value = method
|
|
showOptionsModal.value = true
|
|
}
|
|
|
|
onMounted(async () => {
|
|
const response = await fetch('/api/v1/core/retrieve-event-setting-data');
|
|
const data = await response.json();
|
|
availablePaymentMethods.value = data.paymentMethods ?? []
|
|
|
|
paymentMethods.value = props.event.paymentMethods?.map(t => t.slug) ?? []
|
|
|
|
// Pro-Event-Config vorbelegen: bereits gespeicherte Pivot-Config des Events hat Vorrang,
|
|
// sonst der Tenant-Standard (Snapshot-Quelle für neu zugewiesene Methoden).
|
|
const eventConfigBySlug = {}
|
|
for (const pm of props.event.paymentMethods ?? []) {
|
|
eventConfigBySlug[pm.slug] = pm.configuration ?? {}
|
|
}
|
|
for (const method of availablePaymentMethods.value) {
|
|
const source = eventConfigBySlug[method.slug] ?? method.configuration ?? {}
|
|
const config = {}
|
|
for (const option of method.optionsSchema ?? []) {
|
|
config[option.name] = source[option.name] ?? ''
|
|
}
|
|
paymentMethodConfigurations[method.slug] = config
|
|
}
|
|
})
|
|
|
|
const errors = reactive({})
|
|
const formData = reactive({
|
|
"pft_1_active": true,
|
|
"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_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_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_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,
|
|
'sibling_reduction': props.event.siblingReduction,
|
|
|
|
})
|
|
|
|
function validateInput() {
|
|
var noErrors = true;
|
|
|
|
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) {
|
|
errors.pft_2_description = 'Eine Beschreibung für diese Gruppe ist erforderlich';
|
|
noErrors = false;
|
|
}
|
|
|
|
if (formData.pft_3_description === '' && formData.pft_3_active) {
|
|
errors.pft_3_description = 'Eine Beschreibung für diese Gruppe ist erforderlich';
|
|
noErrors = false;
|
|
}
|
|
|
|
if (formData.pft_4_description === '' && formData.pft_4_active) {
|
|
errors.pft_4_description = 'Eine Beschreibung für diese Gruppe ist erforderlich';
|
|
noErrors = false;
|
|
}
|
|
|
|
return noErrors;
|
|
|
|
}
|
|
|
|
async function saveParticipationFees() {
|
|
if (!validateInput()) {
|
|
toast.error('Bitte prüfe alle Eingaben auf Fehler')
|
|
return;
|
|
}
|
|
|
|
if (paymentMethods.value.length === 0) {
|
|
toast.error('Mindestens eine Zahlungsmöglichkeit muss ausgewählt sein.')
|
|
return;
|
|
}
|
|
|
|
const paymentResponse = await request('/api/v1/event/details/' + props.event.id + '/payment-methods', {
|
|
method: "POST",
|
|
body: {
|
|
paymentMethods: paymentMethods.value,
|
|
paymentMethodConfigurations: paymentMethodConfigurations,
|
|
}
|
|
})
|
|
|
|
if (paymentResponse.status !== 'success') {
|
|
toast.error(paymentResponse.message ?? 'Die Zahlungsmöglichkeiten konnten nicht gespeichert werden.')
|
|
return;
|
|
}
|
|
|
|
const data = await request('/api/v1/event/details/' + props.event.id + '/participation-fees', {
|
|
method: "POST",
|
|
body: {
|
|
event_id: props.event.id,
|
|
pft_1_active: formData.pft_1_active,
|
|
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_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_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_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,
|
|
sibling_reduction: formData.sibling_reduction,
|
|
}
|
|
})
|
|
|
|
toast.success('Die Teilnahmekonditionen wurden gespeichert')
|
|
emit('close')
|
|
}
|
|
|
|
function recalculateMaxAmount(newValue) {
|
|
if (formData.maxAmount === 0) return;
|
|
|
|
var newAmount = parseFloat(newValue.replace(',', '.'));
|
|
if (props.event.payPerDay) {
|
|
newAmount = newAmount * props.event.duration;
|
|
}
|
|
|
|
var currentMaxAmount = formData.maxAmount.replace(',', '.');
|
|
|
|
if (newAmount > currentMaxAmount) {
|
|
formData.maxAmount = newAmount.toFixed(2).replace('.', ',');
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<table style="width: 100%;">
|
|
<tr>
|
|
<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>
|
|
Teilnehmende
|
|
</td>
|
|
<td>
|
|
<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 type="text" v-model="formData.pft_1_description" style="width: 300px;" />
|
|
<ErrorText :message="errors.pft_1_description" />
|
|
</td>
|
|
</tr>
|
|
|
|
<tr style="height: 65px; vertical-align: top;">
|
|
<td>
|
|
<input id="use_pft_2" type="checkbox" v-model="formData.pft_2_active" :checked="formData.pft_2_active" />
|
|
</td>
|
|
<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_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 type="text" v-model="formData.pft_2_description" style="width: 300px;" />
|
|
<ErrorText :message="errors.pft_2_description" />
|
|
</td>
|
|
</tr>
|
|
|
|
<tr style="height: 65px; vertical-align: top;">
|
|
<td>
|
|
<input id="use_pft_3" type="checkbox" v-model="formData.pft_3_active" :checked="formData.pft_3_active" />
|
|
</td>
|
|
<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_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 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;">
|
|
<td>
|
|
<input id="use_pft_4" type="checkbox" v-model="formData.pft_4_active" :checked="formData.pft_4_active" />
|
|
</td>
|
|
<td>
|
|
<label for="use_pft_4" style="cursor: default">
|
|
Sonstige
|
|
</label>
|
|
</td>
|
|
<td v-if="formData.pft_4_active">
|
|
<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" />
|
|
</td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td colspan="2">
|
|
Maximaler Beitrag für Veranstaltung:
|
|
</td>
|
|
<td colspan="2">
|
|
<AmountInput v-model="formData.maxAmount" class="width-small" /> Euro Gesamt
|
|
</td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td colspan="2">
|
|
<input type="checkbox" v-model="formData.sibling_reduction" id="sibling_reduction" :checked="formData.sibling_reduction" />
|
|
<label for="sibling_reduction">50% Preisnachlass für Geschwisterkinder gewähren</label>
|
|
</td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td colspan="4" style="padding-top: 30px;">
|
|
<h4>Zahlungsmöglichkeiten</h4>
|
|
<div v-for="paymentMethod in availablePaymentMethods" :key="paymentMethod.slug"
|
|
class="payment-method-row">
|
|
<input type="checkbox" :id="'paymentmethod' + paymentMethod.id" :value="paymentMethod.slug"
|
|
v-model="paymentMethods"/>
|
|
<label style="padding-left: 5px;" :for="'paymentmethod' + paymentMethod.id">{{
|
|
paymentMethod.name
|
|
}}</label>
|
|
<label
|
|
v-if="paymentMethods.includes(paymentMethod.slug) && (paymentMethod.optionsSchema?.length ?? 0) > 0"
|
|
class="link"
|
|
style="padding-left: 15px; font-size: 10pt;"
|
|
@click="openOptions(paymentMethod)"
|
|
>Optionen</label>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
|
|
<tr>
|
|
<td colspan="4" style="padding-top: 20px;">
|
|
<input type="button" value="Speichern" @click="saveParticipationFees" />
|
|
</td>
|
|
</tr>
|
|
</table>
|
|
|
|
<Modal
|
|
v-if="showOptionsModal"
|
|
:show="showOptionsModal"
|
|
:title="'Optionen: ' + (optionsMethod?.name ?? '')"
|
|
width="700px"
|
|
@close="showOptionsModal = false"
|
|
>
|
|
<div v-for="option in optionsMethod?.optionsSchema ?? []" :key="option.name" class="payment-method-config-row">
|
|
<label>{{ option.label }}<span v-if="option.required" class="required-marker">*</span></label>
|
|
<RichSelectBox v-if="option.type === 'icon'"
|
|
v-model="paymentMethodConfigurations[optionsMethod.slug][option.name]"
|
|
:options="PAYMENT_METHOD_ICONS"
|
|
placeholder="Symbol wählen…"/>
|
|
<TextEditor v-else-if="option.type === 'richtext'"
|
|
v-model="paymentMethodConfigurations[optionsMethod.slug][option.name]"/>
|
|
<input v-else type="text"
|
|
v-model="paymentMethodConfigurations[optionsMethod.slug][option.name]"
|
|
class="width-full"/>
|
|
<span v-if="option.hint" class="option-hint">{{ option.hint }}</span>
|
|
</div>
|
|
</Modal>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.payment-method-row {
|
|
margin-bottom: 6px;
|
|
}
|
|
|
|
.payment-method-config-row {
|
|
margin-bottom: 12px;
|
|
}
|
|
|
|
.payment-method-config-row label {
|
|
display: block;
|
|
font-size: 0.85rem;
|
|
color: #374151;
|
|
margin-bottom: 2px;
|
|
}
|
|
|
|
.required-marker {
|
|
color: #ef4444;
|
|
margin-left: 2px;
|
|
}
|
|
|
|
.option-hint {
|
|
display: block;
|
|
margin-top: 4px;
|
|
font-size: 0.8rem;
|
|
color: #6b7280;
|
|
}
|
|
</style>
|