GUI for Participant management

This commit is contained in:
2026-04-07 22:27:47 +02:00
parent 653e85b781
commit e6bd8c684d
20 changed files with 965 additions and 153 deletions

View File

@@ -3,7 +3,7 @@ import {reactive, inject, onMounted} from 'vue';
import AppLayout from "../../../../resources/js/layouts/AppLayout.vue";
import ShadowedBox from "../../../Views/Components/ShadowedBox.vue";
import TabbedPage from "../../../Views/Components/TabbedPage.vue";
import ListCostUnits from "../../CostUnit/Views/Partials/ListCostUnits.vue";
import ParticipantsList from "./Partials/ParticipantsList.vue";
import Overview from "./Partials/Overview.vue";
const props = defineProps({
@@ -18,27 +18,27 @@ const tabs = [
},
{
title: 'Alle Teilnehmendenden',
component: ListCostUnits,
endpoint: "/api/v1/cost-unit/open/current-running-jobs",
component: ParticipantsList,
endpoint: "/api/v1/event/details/" + props.event.identifier + '/participants/all',
},
{
title: 'Teilis nach Stamm',
component: ListCostUnits,
endpoint: "/api/v1/cost-unit/open/closed-cost-units",
component: ParticipantsList,
endpoint: "/api/v1/event/details/" + props.event.identifier + '/participants/by-local-group',
},
{
title: 'Teilis nach Teili-Gruppe',
component: ListCostUnits,
endpoint: "/api/v1/cost-unit/open/archived-cost-units",
component: ParticipantsList,
endpoint: "/api/v1/event/details/" + props.event.identifier + '/participants/by-participation-group',
},
{
title: 'Abgemeldete Teilis',
component: ListCostUnits,
endpoint: "/api/v1/cost-unit/open/archived-cost-units",
component: ParticipantsList,
endpoint: "/api/v1/event/details/" + props.event.identifier + '/participants/signed-off',
},
{
title: 'Zusätze',
component: ListCostUnits,
component: ParticipantsList,
endpoint: "/api/v1/cost-unit/open/archived-cost-units",
},
]

View File

@@ -1,5 +1,5 @@
<script setup>
import {onMounted, reactive, ref} from "vue";
import {onMounted, reactive, ref} from "vue";
import ParticipationFees from "./ParticipationFees.vue";
import ParticipationSummary from "./ParticipationSummary.vue";
import CommonSettings from "./CommonSettings.vue";

View File

@@ -1,31 +1,116 @@
<script setup>
import { reactive, watch } from "vue";
import AmountInput from "../../../../Views/Components/AmountInput.vue";
const props = defineProps({
editMode: Boolean,
participant: Object,
})
const props = defineProps({
editMode: Boolean,
participant: Object,
event: Object,
});
const emit = defineEmits(['closeParticipantDetails', 'markCocExisting','paymentComplete', 'cancelParticipation'])
console.log(props.participant)
function markCocExisting(participant) {
emit('markCocExisting', participant)
close()
}
const emit = defineEmits([
'closeParticipantDetails',
'markCocExisting',
'paymentComplete',
'cancelParticipation',
'editParticipant',
'saveParticipant',
]);
function paymentComplete(participant) {
emit('paymentComplete', participant)
close()
}
const form = reactive({
firstname: '',
lastname: '',
nickname: '',
address_1: '',
address_2: '',
postcode: '',
city: '',
localgroup: '',
birthday: '',
email_1: '',
phone_1: '',
contact_person: '',
email_2: '',
phone_2: '',
arrival: '',
departure: '',
participationType: '',
eatingHabit: '',
allergies: '',
intolerances: '',
medications: '',
extendedFirstAid: '',
swimmingPermission: '',
tetanusVaccination: '',
notes: '',
amountPaid: '',
amountExpected: '',
cocStatus: '',
});
function close() {
emit('closeParticipantDetails')
watch(
() => props.participant,
(participant) => {
form.firstname = participant?.firstname ?? '';
form.lastname = participant?.lastname ?? '';
form.nickname = participant?.nickname ?? '';
form.address_1 = participant?.address_1 ?? '';
form.address_2 = participant?.address_2 ?? '';
form.postcode = participant?.postcode ?? '';
form.city = participant?.city ?? '';
form.localgroup = participant?.local_group ?? '';
form.birthday = participant?.birthdayDate ?? '';
form.email_1 = participant?.email_1 ?? '';
form.phone_1 = participant?.phone_1 ?? '';
form.contact_person = participant?.contact_person ?? '';
form.email_2 = participant?.email_2 ?? '';
form.phone_2 = participant?.phone_2 ?? '';
form.arrival = participant?.arrival ?? '';
form.departure = participant?.departure ?? '';
form.participationType = participant?.participation_type ?? '';
form.eatingHabit = participant?.eating_habit ?? '';
form.allergies = participant?.allergies ?? '';
form.intolerances = participant?.intolerances ?? '';
form.medications = participant?.medications ?? '';
form.extendedFirstAid = participant?.first_aid_permission ?? '';
form.swimmingPermission = participant?.swimming_permission ?? '';
form.tetanusVaccination = participant?.tetanus_vaccination ?? '';
form.notes = participant?.notes ?? '';
form.amountPaid = participant?.amountPaid.short ?? '';
form.amountExpected = participant?.amountExpected.short ?? '';
form.cocStatus = participant?.efz_status ?? '';
},
{ immediate: true }
);
}
function markCocExisting(participant) {
emit('markCocExisting', participant);
close();
}
function cancelParticipation(participant) {
emit('cancelParticipation', participant)
close()
}
function paymentComplete(participant) {
emit('paymentComplete', participant);
close();
}
function close() {
emit('closeParticipantDetails');
}
function cancelParticipation(participant) {
emit('cancelParticipation', participant);
close();
}
function enableEditMode() {
emit('editParticipant');
}
function saveParticipant() {
emit('saveParticipant', { ...form });
}
</script>
<template>
@@ -40,7 +125,11 @@
<th>Name</th>
<td>
<span v-if="!props.editMode">
{{props.participant.firstname}} {{props.participant.lastname}}
{{ props.participant.firstname }} {{ props.participant.lastname }}
</span>
<span v-else>
<input v-model="form.firstname" type="text" placeholder="Vorname" />
<input v-model="form.lastname" type="text" placeholder="Nachname" />
</span>
</td>
</tr>
@@ -48,7 +137,8 @@
<tr>
<th>Pfadiname</th>
<td>
<span v-if="!props.editMode">{{props.participant.nickname}}</span>
<span v-if="!props.editMode">{{ props.participant.nickname }}</span>
<input v-else v-model="form.nickname" type="text" />
</td>
</tr>
@@ -56,25 +146,35 @@
<th>Anschrift</th>
<td>
<span v-if="!props.editMode">
{{props.participant.address_1}}<br />
{{props.participant.address_2}}<br />
{{props.participant.postcode}}
{{props.participant.city}}
{{ props.participant.address_1 }}<br />
{{ props.participant.address_2 }}<br />
{{ props.participant.postcode }}
{{ props.participant.city }}
</span>
<span v-else>
<input v-model="form.address_1" type="text" placeholder="Adresse 1" />
<input v-model="form.address_2" type="text" placeholder="Adresse 2" />
<input v-model="form.postcode" type="text" placeholder="PLZ" />
<input v-model="form.city" type="text" placeholder="Ort" />
</span>
</td>
</tr>
<tr>
<th>Stamm</th>
<td>
<span v-if="!props.editMode">{{props.participant.localgroup}}</span>
<span v-if="!props.editMode">{{ props.participant.localgroup }}</span>
<select v-else v-model="form.localgroup">
<option v-for="group in props.event.contributingLocalGroups" :key="group.id" :value="group.slug">{{ group.name }}</option>
</select>
</td>
</tr>
<tr>
<th>Geburtsdatum</th>
<td>
<span v-if="!props.editMode">
{{props.participant.birthday}}
</span>
<span v-if="!props.editMode">{{ props.participant.birthday }}</span>
<input v-else v-model="form.birthday" type="date" />
</td>
</tr>
</table>
@@ -86,35 +186,175 @@
<tr>
<th>E-Mail</th>
<td>
<span v-if="!props.editMode">{{props.participant.email_1}}</span>
<span v-if="!props.editMode">{{ props.participant.email_1 }}</span>
<input v-else v-model="form.email_1" type="email" />
</td>
</tr>
<tr>
<th>Telefon</th>
<td>
<span v-if="!props.editMode">{{props.participant.phone_1}}</span>
<span v-if="!props.editMode">{{ props.participant.phone_1 }}</span>
<input v-else v-model="form.phone_1" type="text" />
</td>
</tr>
<tr>
<th>Ansprechperson</th>
<td>
<span v-if="!props.editMode">{{props.participant.contact_person}}</span>
<span v-if="!props.editMode">{{ props.participant.contact_person }}</span>
<input v-else v-model="form.contact_person" type="text" />
</td>
</tr>
<tr>
<th>Ansprechperson E-Mail</th>
<td>
<span v-if="!props.editMode">{{props.participant.email_2}}</span>
<span v-if="!props.editMode">{{ props.participant.email_2 }}</span>
<input v-else v-model="form.email_2" type="email" />
</td>
</tr>
<tr>
<th>Ansprechperson Telefon</th>
<td>
<span v-if="!props.editMode">{{props.participant.phone_2}}</span>
<span v-if="!props.editMode">{{ props.participant.phone_2 }}</span>
<input v-else v-model="form.phone_2" type="text" />
</td>
</tr>
</table>
</div>
</div>
<div class="participationData">
<div>
<h3>Teilnahmedetails</h3>
<table>
<tr>
<th>Anreise</th>
<td>
<span v-if="!props.editMode">{{ props.participant.arrival }}</span>
<input v-else v-model="form.arrival" type="text" />
</td>
</tr>
<tr>
<th>Abreise</th>
<td>
<span v-if="!props.editMode">{{ props.participant.departure }}</span>
<input v-else v-model="form.departure" type="text" />
</td>
</tr>
<tr>
<th>Teilnahmegruppe</th>
<td>
<span v-if="!props.editMode">{{ props.participant.participationType }}</span>
<select v-else v-model="form.participationType">
<option
v-for="participationType in event.participationTypes"
:value="participationType.type.slug"
>
{{ participationType.type.name }}
</option>
</select>
</td>
</tr>
<tr>
<th>Ernährung</th>
<td>
<span v-if="!props.editMode">{{ props.participant.eatingHabit }}</span>
<select v-else v-model="form.eatingHabit">
<option
v-for="eatingHabit in event.eatingHabits"
:value="eatingHabit.slug"
>
{{ eatingHabit.name }}
</option>
</select>
</td>
</tr>
<tr>
<th>eFZ-Status</th>
<td>
<span v-if="!props.editMode">{{ props.participant.efzStatusReadable }}</span>
<select v-else v-model="form.cocStatus">
<option value="not_checked">Nicht geprüft</option>
<option value="not_required">Nicht erforderlich</option>
<option value="checked_valid">Geprüft und Vorhanden</option>
<option value="checked_invalid">Nicht vorhanden</option>
</select>
</td>
</tr>
<tr>
<th>Beitrag</th>
<td>
<span v-if="!props.editMode">{{ props.participant.amountPaid.readable }} / {{ props.participant.amountExpected.readable }}</span>
<span v-else>
<AmountInput v-model="form.amountPaid" style="width:74px" /> Euro
/
<AmountInput v-model="form.amountExpected" style="width: 74px" /> Euro
</span>
</td>
</tr>
</table>
</div>
<div>
<h3>Medizinische Informationen</h3>
<table>
<tr>
<th>Allergien</th>
<td>
<span v-if="!props.editMode">{{ props.participant.allergies }}</span>
<input type="text" v-else v-model="form.allergies" />
</td>
</tr>
<tr>
<th>Unverträglichkeiten</th>
<td>
<span v-if="!props.editMode">{{ props.participant.intolerances }}</span>
<input type="text" v-else v-model="form.intolerances" />
</td>
</tr>
<tr>
<th>Medikamente</th>
<td>
<span v-if="!props.editMode">{{ props.participant.medications }}</span>
<input type="text" v-else v-model="form.medications" />
</td>
</tr>
<tr>
<th>Erweiterte Erste Hilfe</th>
<td>
<span v-if="!props.editMode">{{ props.participant.extendedFirstAid }}</span>
<select v-else v-model="form.extendedFirstAid">
<option value="FIRST_AID_PERMISSION_ALLOWED">Erlaubt</option>
<option value="FIRST_AID_PERMISSION_DENIED">Verweigert</option>
</select>
</td>
</tr>
<tr>
<th>Badeerlaubnis</th>
<td>
<span v-if="!props.editMode">{{ props.participant.swimmingPermission }}</span>
<select v-else v-model="form.swimmingPermission">
<option value="SWIMMING_PERMISSION_ALLOWED">Vorhanden, kann schwimmen</option>
<option value="SWIMMING_PERMISSION_LIMITED">Vorhanden, kann nicht schwimmen</option>
<option value="SWIMMING_PERMISSION_DENIED">Verweigert</option>
</select>
</td>
</tr>
<tr>
<th>Letzte Tetanus-Impfung</th>
<td>
<span v-if="!props.editMode">{{ props.participant.tetanusVaccination }}</span>
<input v-else v-model="form.tetanusVaccination" type="date" />
</td>
</tr>
<tr>
<th>Anmerkungen</th>
<td>
<span v-if="!props.editMode">{{ props.participant.notes }}</span>
<textarea v-else v-model="form.notes"></textarea>
</td>
</tr>
</table>
@@ -122,104 +362,9 @@
</div>
</div>
<div class="participationData">
<div>
<h3>Teilnahmedetails</h3>
<table>
<tr>
<th>Anreise</th>
<td>
<span v-if="!props.editMode">{{props.participant.arrival}}</span>
</td>
</tr>
<tr>
<th>Abreise</th>
<td>
<span v-if="!props.editMode">{{props.participant.departure}}</span>
</td>
</tr>
<tr>
<th>Teilnahmegruppe</th>
<td>
<span v-if="!props.editMode">{{props.participant.participationType}}</span>
</td>
</tr>
<button v-if="!props.editMode" class="button" @click="enableEditMode">Bearbeiten</button>
<button v-else class="button" @click="saveParticipant">Speichern</button>
<tr>
<th>Ernährung</th>
<td>
<span v-if="!props.editMode">{{props.participant.eatingHabit}}</span>
</td>
</tr>
<tr>
<th>eFZ-Status</th>
<td>
<span v-if="!props.editMode">{{props.participant.efzStatusReadable}}</span>
</td>
</tr>
<tr>
<th>Beitrag</th>
<td>
<span v-if="!props.editMode">{{props.participant.amountPaid.readable}} / {{props.participant.amountExpected.readable}}</span>
</td>
</tr>
</table>
</div>
<div>
<h3>Medizinische Informationen</h3>
<table>
<tr>
<th>Allergien</th>
<td>
<span v-if="!props.editMode">{{props.participant.allergies}}</span>
</td>
</tr>
<tr>
<th>Unverträglichkeiten</th>
<td>
<span v-if="!props.editMode">{{props.participant.intolerances}}</span>
</td>
</tr>
<tr>
<th>Medikamente</th>
<td>
<span v-if="!props.editMode">{{props.participant.medications}}</span>
</td>
</tr>
<tr>
<th>Erweiterte Erste Hilfe</th>
<td>
<span v-if="!props.editMode">{{props.participant.extendedFirstAid}}</span>
</td>
</tr>
<tr>
<th>Badeerlaubnis</th>
<td>
<span v-if="!props.editMode">{{props.participant.swimmingPermission}}</span>
</td>
</tr>
<tr>
<th>Letzte Tetanus-Impfung</th>
<td>
<span v-if="!props.editMode">{{props.participant.tetanusVaccination}}</span>
</td>
</tr>
<tr>
<th>Anmerkungen</th>
<td>
<span v-if="!props.editMode">{{props.participant.notes}}</span>
</td>
</tr>
</table>
</div>
</div>
<button class="button">Bearbeiten</button>
<button class="button" @click="paymentComplete(props.participant)">Zahlung vollständig</button>
<button class="button" @click="markCocExisting(props.participant)">eFZ liegt vor</button>
<button class="button" @click="cancelParticipation(props.participant)">Abmelden</button>
@@ -231,6 +376,7 @@
.participationData {
display: flex;
margin-bottom: 20px;
gap: 20px;
}
.participationData div {
@@ -238,4 +384,19 @@
vertical-align: top;
}
input[type="text"],
input[type="email"],
input[type="date"],
textarea
{
width: 250px
}
textarea {
height: 100px;
}
select {
width: 262px;
}
</style>

View File

@@ -0,0 +1,432 @@
<script setup>
import { computed, reactive, ref } from "vue";
import Modal from "../../../../Views/Components/Modal.vue";
import ParticipantData from "./ParticipantData.vue";
import {toast} from "vue3-toastify";
import {useAjax} from "../../../../../resources/js/components/ajaxHandler.js";
import {format, getDay, getMonth, getYear} from "date-fns";
const props = defineProps({
data: {
type: Object,
default: () => ({
localGroups: {},
participants: {},
event: {},
}),
},
});
const today = format(new Date(), "yyyy-MM-dd");
const { request } = useAjax();
const searchTerms = reactive({});
const selectedStatuses = reactive({});
const event = computed(() => props.data?.event ?? {});
const participantGroups = computed(() => props.data?.participants ?? {});
const showParticipantDetails = ref(false);
const showParticipant = ref(null);
const editMode = ref(false);
const openCancelDialog = ref(false);
const openPartialPaymentDialogSwitch = ref(false);
defineEmits(['showParticipantDetails', 'markCocExisting', 'paymentComplete'])
function openParticipantDetails(input) {
showParticipantDetails.value = true;
showParticipant.value = input;
editMode.value = false;
}
async function saveParticipant(formData) {
if (!showParticipant.value?.identifier) {
return;
}
const data = await request('/api/v1/event/participant/' + showParticipant.value.identifier + '/update', {
method: "POST",
body: JSON.stringify(formData),
});
if (data.status === 'success') {
toast.success(data.message ?? 'Änderungen gespeichert');
editMode.value = false;
} else {
toast.error(data.message ?? 'Speichern fehlgeschlagen');
}
}
const getGroupEntries = computed(() => {
return Object.entries(participantGroups.value ?? {});
});
const getAgeCounts = (participants) => {
const buckets = {
'0-5': 0,
'6-11': 0,
'12-15': 0,
'16-17': 0,
'18-27': 0,
'27+': 0,
};
(participants ?? []).forEach((participant) => {
const age = Number(participant?.age);
if (!Number.isFinite(age)) {
return;
}
if (age >= 0 && age <= 5) {
buckets['0-5']++;
} else if (age <= 11) {
buckets['6-11']++;
} else if (age <= 15) {
buckets['12-15']++;
} else if (age <= 17) {
buckets['16-17']++;
} else if (age <= 27) {
buckets['18-27']++;
} else {
buckets['27+']++;
}
});
return buckets;
};
const getSearchText = (participant) => {
return [
participant?.firstname,
participant?.lastname,
participant?.nickname,
participant?.email_1,
participant?.email_2,
participant?.phone_1,
participant?.phone_2,
participant?.local_group_string,
participant?.participation_type_string,
participant?.efz_status_string,
participant?.amount_string,
]
.filter(Boolean)
.join(" ")
.toLowerCase();
};
const getFilteredParticipants = (groupKey, participants) => {
const searchTerm = (searchTerms[groupKey] ?? "").trim().toLowerCase();
return (participants ?? []).filter((participant) => {
const matchesSearch =
!searchTerm ||
getSearchText(participant).includes(searchTerm);
return matchesSearch;
});
};
const getRowClass = (participant) => {
if (participant?.unregistered_at) {
return "bg-gray-50 text-gray-500";
}
if (
Number(participant?.amount?.amount ?? participant?.amount ?? 0) !==
Number(participant?.amount_paid?.amount ?? participant?.amount_paid ?? 0)
) {
return "bg-red-50";
}
return "";
};
const ensureGroupState = (groupKey) => {
if (searchTerms[groupKey] === undefined) {
searchTerms[groupKey] = "";
}
if (selectedStatuses[groupKey] === undefined) {
selectedStatuses[groupKey] = "all";
}
};
async function paymentComplete(participant) {
const data = await request('/api/v1/event/participant/' + participant.identifier + '/payment-complete', {
method: "POST",
});
if (data.status === 'success') {
toast.success(data.message);
document.getElementById('participant-' + participant.identifier + '-payment').removeAttribute('class');
document.getElementById('participant-' + participant.identifier + '-paid').innerText = participant.amountExpected.readable;
document.getElementById('participant-' + participant.identifier + '-actions').style.display='none';
} else {
toast.error(data.message);
}
}
async function markCocExisting(participant) {
const data = await request('/api/v1/event/participant/' + participant.identifier + '/mark-coc-existing', {
method: "POST",
});
if (data.status === 'success') {
toast.success(data.message);
document.getElementById('participant-' + participant.identifier + '-coc-status').innerText = 'Gültig';
document.getElementById('participant-' + participant.identifier + '-coc-action').style.display='none';
document.getElementById('participant-' + participant.identifier + '-name').removeAttribute('class');
} else {
toast.error(data.message);
}
}
function openCancelParticipationDialog(participant) {
openCancelDialog.value = true;
showParticipant = participant;
}
async function execCancelParticipation() {
openCancelDialog.value = false;
toast.success('Abmeldung erfolgreich')
}
function openPartialPaymentDialog(participant) {
openPartialPaymentDialogSwitch.value = true;
showParticipant = participant;
}
async function execPartialPayment() {
openPartialPaymentDialogSwitch.value = false;
toast.success('Teilzahlung erfolgreich')
}
</script>
<template>
<h2>{{ event?.name ?? "Veranstaltung" }}</h2>
<div :key="groupKey">
<div>
<table style="width: 95%; margin: 20px auto; border-collapse: collapse;" v-for="[groupKey, participants] in getGroupEntries">
<thead>
<tr>
<th colspan="4" style="background: linear-gradient(to bottom, #fff, #f6f7f7); font-weight: bold">
{{ groupKey }} ({{ participants.length }} Personen)
</th>
</tr>
<tr style="background: linear-gradient(to bottom, #fff, #f6f7f7);">
<th>Name</th>
<th>Beitrag</th>
<th>E-Mail-Adresse</th>
<th>Telefon</th>
</tr>
</thead>
<tbody>
<template
v-for="participant in getFilteredParticipants(groupKey, participants)"
:key="participant.id"
>
<tr :class="getRowClass(participant)" :id="'participant-' + participant.identifier">
<td :id="'participant-' + participant.identifier +'-name'"
style="width: 300px;"
:class="participant.efz_status === 'checked_invalid' ? 'efz-invalid' :
participant.efz_status === 'not_checked' ? 'efz-not-checked' : ''">
<div v-html="participant.fullname" /><br />
Geburtsdatum: {{ participant.birthday }}<br />
Alter: {{ participant.age }} Jahre<br />
<span>eFZ-Status: <label :id="'participant-' + participant.identifier +'-coc-status'">{{ participant.efzStatusReadable }}</label></span> &nbsp;
<span :id="'participant-' + participant.identifier +'-coc-action'" v-if="participant.efz_status !== 'checked_valid' && participant.efz_status !== 'not_required'" class="link" style="color: #3cb62e; font-size: 11pt;" @click="markCocExisting(participant)">Vorhanden?</span>
</td>
<td :id="'participant-' + participant.identifier +'-payment'" :class="participant.amount_left_value != 0 && !unregistered_at ? 'not-paid' : ''" style="width: 275px; '">
Gezahlt: <label :id="'participant-' + participant.identifier + '-paid'">{{ participant?.amountPaid.readable }}</label> /<br />
Gesamt: {{ participant?.amountExpected.readable }}
<br /><br />
<span v-if="participant.amount_left_value != 0 && !unregistered_at" :id="'participant-' + participant.identifier + '-actions'">
<span class="link" style="font-size:10pt;" @click="paymentComplete(participant)">Zahlung buchen</span> &nbsp;
<span class="link" style="font-size:10pt;" @click="openPartialPaymentDialog(participant)">Teilzahlung buchen</span>
</span>
</td>
<td>
{{ participant?.email_1 ?? "-" }}
<div v-if="participant?.email_2 && participant.email_2 !== participant.email_1" class="text-xs text-gray-500">
{{ participant.email_2 }}
</div>
</td>
<td>
{{ participant?.phone_1 ?? "-" }}
<div v-if="participant?.phone_2 && participant.phone_2 !== participant.phone_1" class="text-xs text-gray-500">
{{ participant.phone_2 }}
</div>
</td>
</tr>
<tr class="participant-meta-row">
<td colspan="5" style="height: 15px !important; font-size: 9pt; background-color: #ffffff; border-top-style: none;">
{{ participant?.localgroup ?? "-" }} |
<strong> Anreise: </strong>{{ participant?.arrival ?? "-" }} |
<strong> Abreise: </strong>{{ participant?.departure ?? "-" }} |
<strong> Angemeldet am: </strong>{{ participant?.registerDate ?? "-" }} |
<span class="link" @click="openParticipantDetails(participant)">Details</span> |
<span class="link">E-Mail senden</span> |
<span @click="openCancelParticipationDialog(participant)" v-if="!unregistered_at" class="link" style="color: #da7070;">Abmelden</span>
<span v-else class="link" style="color: #3cb62e;">Wieder anmelden</span>
</td>
</tr>
</template>
<tr>
<td colspan="3" style="font-weight: normal;">
0 - 5 Jahre: <strong>{{ getAgeCounts(participants)['0-5'] ?? 0 }}</strong> |
6-11 Jahre: <strong>{{ getAgeCounts(participants)['6-11'] ?? 0 }}</strong> |
12-15 Jahre: <strong>{{ getAgeCounts(participants)['12-15'] ?? 0 }}</strong> |
16 - 17 Jahre: <strong>{{ getAgeCounts(participants)['16-17'] ?? 0 }}</strong> |
18 - 27 Jahre: <strong>{{ getAgeCounts(participants)['18-27'] ?? 0 }}</strong> |
27 Jahre und älter: <strong>{{ getAgeCounts(participants)['27+'] ?? 0 }}</strong>
</td>
<td>
E-Mail an Gruppe senden
</td>
</tr>
</tbody>
</table>
</div>
</div>
<Modal
:show="showParticipantDetails"
title="Anmeldedetails ansehen"
@close="showParticipantDetails = false;toast.success('HALLO');"
>
<ParticipantData
@cancelParticipation="openCancelParticipationDialog"
:participant="showParticipant"
:editMode="editMode"
:event="event"
@editParticipant="editMode = true"
@saveParticipant="saveParticipant"
@paymentComplete="paymentComplete"
@markCocExisting="markCocExisting"
@closeParticipantDetails="showParticipantDetails = false" />
</Modal>
<Modal
:show="openCancelDialog"
title="Anmeldung stornieren"
@close="openCancelDialog = false"
>
Datum der Abmeldung:
<input type="date" style="margin-top: 10px;" id="cancel_date" :value="today" />
<br /><br />
<button class="button" @click="execCancelParticipation()">Abmeldung durchführen</button>
</Modal>
<Modal
:show="openPartialPaymentDialogSwitch"
title="Teilbetragszahlung"
@close="openPartialPaymentDialogSwitch = false"
>
Gesamtbetrag der Zahlung:
<input type="text" style="margin-top: 10px; width: 150px !important;" id="partial_payment_amount" /> Euro
<br /><br />
<button class="button" @click="execPartialPayment()">Teilbetrag buchen</button>
</Modal>
</template>
<style scoped>
table {
margin-bottom: 60px !important;
}
tr {
vertical-align: top;
}
tr td {
height: 80px;
padding: 10px;
padding-top: 20px;
font-size: 11pt;
line-height: 1.5;
}
tr th {
height: 40px;
padding-left: 10px;
vertical-align: middle;
}
tr th:after {
content: "";
}
tr:nth-child(even) {
background-color: #f9fafb;
border-style: solid;
border-width: 0 1px;
border-color: #e5e7eb;
}
tr:nth-child(odd) {
background-color: #ffffff;
border-style: solid;
border-width: 5px 1px 0 1px;
border-color: #e5e7eb;
}
tr:first-child {
border-width: 1px 1px 0 1px;
}
tr:last-child {
border-width: 0 1px 1px 1px;
}
tr:last-child td {
background: linear-gradient(to bottom, #fff, #f6f7f7); font-weight: bold;
height: 30px;
}
.button {
display: block;
font-size: 10pt;
text-decoration: none;
}
.not-paid {
color: #ff0000; background-color: #ffe6e6;
}
.efz-invalid {
color: #ff0000; background-color: #ffe6e6;
}
.efz-not-checked {
color: #8D914BFF; background-color: #F4E99EFF;
}
</style>