Payment methode now selectable
This commit is contained in:
@@ -0,0 +1,223 @@
|
||||
<script setup>
|
||||
import {computed} from "vue";
|
||||
import PaymentMethodInputs from "../components/PaymentMethodInputs.vue";
|
||||
import Icon from "../../../../../../Views/Components/Icon.vue";
|
||||
|
||||
const props = defineProps({formData: Object, event: Object})
|
||||
const emit = defineEmits(['next', 'back'])
|
||||
|
||||
// Für die Aktion freigeschaltete Zahlungsmethoden.
|
||||
const methods = computed(() => (props.event.paymentMethods ?? []).filter(m => m.active))
|
||||
|
||||
const selectedMethod = computed(() =>
|
||||
methods.value.find(m => m.slug === props.formData.paymentMethod) ?? null
|
||||
)
|
||||
|
||||
const participantSchema = computed(() => selectedMethod.value?.participantOptionsSchema ?? [])
|
||||
|
||||
// Font-Awesome-Icon je Zahlungsart (Fallback: generische Karte).
|
||||
function iconFor(slug) {
|
||||
if (slug === 'PAYMENT_ACCOUNT_TRANSACTION') return 'building-columns'
|
||||
if (slug === 'PAYMENT_NOT_DEFINED') return 'money-bill-wave'
|
||||
return 'credit-card'
|
||||
}
|
||||
|
||||
function selectMethod(slug) {
|
||||
props.formData.paymentMethod = slug
|
||||
// Eingaben zurücksetzen -- pro Methode eigene Felder.
|
||||
props.formData.paymentOptions = {}
|
||||
}
|
||||
|
||||
// "Weiter" ist erst möglich, wenn eine Methode gewählt und alle Pflicht-Eingaben befüllt sind.
|
||||
const canProceed = computed(() => {
|
||||
if (!props.formData.paymentMethod) return false
|
||||
return participantSchema.value
|
||||
.filter(o => o.required)
|
||||
.every(o => {
|
||||
const value = props.formData.paymentOptions[o.name]
|
||||
return value !== undefined && value !== null && String(value).trim() !== ''
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<h3 style="margin: 0 0 6px 0; color: #111827;">Zahlungsweise</h3>
|
||||
<p style="margin: 0 0 24px 0; color: #6b7280; font-size: 0.95rem;">Bitte wähle, wie du deinen Teilnahmebeitrag
|
||||
begleichen möchtest.</p>
|
||||
|
||||
<div class="pay-card-row">
|
||||
<div
|
||||
v-for="method in methods"
|
||||
:key="method.slug"
|
||||
class="pay-card"
|
||||
:class="{ 'pay-card--active': formData.paymentMethod === method.slug }"
|
||||
role="radio"
|
||||
:aria-checked="formData.paymentMethod === method.slug"
|
||||
tabindex="0"
|
||||
@click="selectMethod(method.slug)"
|
||||
@keydown.enter.prevent="selectMethod(method.slug)"
|
||||
@keydown.space.prevent="selectMethod(method.slug)"
|
||||
>
|
||||
<div class="pay-card__badge">
|
||||
<Icon :name="iconFor(method.slug)"/>
|
||||
</div>
|
||||
<div class="pay-card__body">
|
||||
<h4 class="pay-card__title">{{ method.name }}</h4>
|
||||
<p v-if="method.description" class="pay-card__desc">{{ method.description }}</p>
|
||||
</div>
|
||||
<div class="pay-card__check" aria-hidden="true">✓</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="participantSchema.length > 0" class="pay-inputs">
|
||||
<PaymentMethodInputs :schema="participantSchema" v-model="formData.paymentOptions"/>
|
||||
</div>
|
||||
|
||||
<div class="btn-row">
|
||||
<button type="button" class="btn-secondary" @click="emit('back', 8)">← Zurück</button>
|
||||
<button type="button" class="btn-primary" :disabled="!canProceed" @click="emit('next', 10)">Weiter →
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.pay-card-row {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.pay-card {
|
||||
position: relative;
|
||||
flex: 1 1 280px;
|
||||
min-width: 0;
|
||||
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;
|
||||
}
|
||||
|
||||
.pay-card:hover {
|
||||
border-color: #2563eb;
|
||||
box-shadow: 0 4px 16px rgba(37, 99, 235, 0.12);
|
||||
transform: translateY(-2px);
|
||||
}
|
||||
|
||||
.pay-card--active {
|
||||
border-color: #2563eb;
|
||||
background: #eff6ff;
|
||||
box-shadow: 0 4px 16px rgba(37, 99, 235, 0.12);
|
||||
}
|
||||
|
||||
.pay-card__badge {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
margin-bottom: 14px;
|
||||
font-size: 1.6rem;
|
||||
color: #2563eb;
|
||||
background: #e0f2fe;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.pay-card--active .pay-card__badge {
|
||||
background: #dbeafe;
|
||||
}
|
||||
|
||||
.pay-card__body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.pay-card__title {
|
||||
margin: 0;
|
||||
font-size: 1.1rem;
|
||||
font-weight: 700;
|
||||
color: #111827;
|
||||
}
|
||||
|
||||
.pay-card__desc {
|
||||
margin: 0;
|
||||
font-size: 0.9rem;
|
||||
color: #374151;
|
||||
}
|
||||
|
||||
.pay-card__check {
|
||||
position: absolute;
|
||||
top: 12px;
|
||||
right: 12px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 50%;
|
||||
background: #2563eb;
|
||||
color: #fff;
|
||||
font-size: 0.8rem;
|
||||
font-weight: 700;
|
||||
opacity: 0;
|
||||
transform: scale(0.6);
|
||||
transition: opacity 0.12s, transform 0.12s;
|
||||
}
|
||||
|
||||
.pay-card--active .pay-card__check {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
|
||||
.pay-inputs {
|
||||
margin-top: 20px;
|
||||
padding: 16px;
|
||||
background: #f8fafc;
|
||||
border: 1px solid #e5e7eb;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
/* ─── Smartphone ─── */
|
||||
@media (max-width: 639px) {
|
||||
.pay-card-row {
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.pay-card {
|
||||
flex: 1 1 100%;
|
||||
flex-direction: row;
|
||||
text-align: left;
|
||||
align-items: center;
|
||||
gap: 14px;
|
||||
padding: 16px 14px;
|
||||
}
|
||||
|
||||
.pay-card__badge {
|
||||
flex: 0 0 52px;
|
||||
width: 52px;
|
||||
height: 52px;
|
||||
margin-bottom: 0;
|
||||
font-size: 1.6rem;
|
||||
}
|
||||
|
||||
.pay-card__body {
|
||||
flex: 1;
|
||||
align-items: flex-start;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.pay-card__title {
|
||||
font-size: 1rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user