Files
mareike/app/Domains/Event/Views/Partials/SignUpForm/steps/StepPaymentMethod.vue
T

224 lines
5.8 KiB
Vue

<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>