Handling of event addons
This commit is contained in:
@@ -224,6 +224,9 @@ class DetailsController extends CommonController {
|
|||||||
case 'by-participation-option':
|
case 'by-participation-option':
|
||||||
$participants = $this->eventParticipants->groupByParticipationOption($event, $request);
|
$participants = $this->eventParticipants->groupByParticipationOption($event, $request);
|
||||||
break;
|
break;
|
||||||
|
case 'by-addon':
|
||||||
|
$participants = $this->eventParticipants->groupByAddon($event, $request);
|
||||||
|
break;
|
||||||
case 'signed-off':
|
case 'signed-off':
|
||||||
$participants = $this->eventParticipants->getSignedOffParticipants($event, $request);
|
$participants = $this->eventParticipants->getSignedOffParticipants($event, $request);
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import ShadowedBox from "../../../Views/Components/ShadowedBox.vue";
|
|||||||
import TabbedPage from "../../../Views/Components/TabbedPage.vue";
|
import TabbedPage from "../../../Views/Components/TabbedPage.vue";
|
||||||
import ParticipantsList from "./Partials/ParticipantsList.vue";
|
import ParticipantsList from "./Partials/ParticipantsList.vue";
|
||||||
import ParticipantsByOption from "./Partials/ParticipantsByOption.vue";
|
import ParticipantsByOption from "./Partials/ParticipantsByOption.vue";
|
||||||
|
import ParticipantsByAddon from "./Partials/ParticipantsByAddon.vue";
|
||||||
import Overview from "./Partials/Overview.vue";
|
import Overview from "./Partials/Overview.vue";
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
@@ -37,16 +38,16 @@ const tabs = [
|
|||||||
component: ParticipantsByOption,
|
component: ParticipantsByOption,
|
||||||
endpoint: "/api/v1/event/details/" + props.event.identifier + '/participants/by-participation-option',
|
endpoint: "/api/v1/event/details/" + props.event.identifier + '/participants/by-participation-option',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: 'Teilis mit Zusatzoptionen',
|
||||||
|
component: ParticipantsByAddon,
|
||||||
|
endpoint: "/api/v1/event/details/" + props.event.identifier + '/participants/by-addon',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: 'Abgemeldete Teilis',
|
title: 'Abgemeldete Teilis',
|
||||||
component: ParticipantsList,
|
component: ParticipantsList,
|
||||||
endpoint: "/api/v1/event/details/" + props.event.identifier + '/participants/signed-off',
|
endpoint: "/api/v1/event/details/" + props.event.identifier + '/participants/signed-off',
|
||||||
},
|
},
|
||||||
{
|
|
||||||
title: 'Zusätze',
|
|
||||||
component: ParticipantsList,
|
|
||||||
endpoint: "/api/v1/cost-unit/open/archived-cost-units",
|
|
||||||
},
|
|
||||||
]
|
]
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
|||||||
@@ -0,0 +1,67 @@
|
|||||||
|
<script setup>
|
||||||
|
import {computed, inject} from "vue";
|
||||||
|
import ParticipantsList from "./ParticipantsList.vue";
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
data: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({participants: {}, event: {}, listType: ''}),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Von TabbedPage bereitgestellt: erlaubt den Sprung zum Übersicht-Tab (Index 0), wo die
|
||||||
|
// Zusatzoptionen konfiguriert werden.
|
||||||
|
const selectTab = inject('selectTab', null);
|
||||||
|
|
||||||
|
const hasAddons = computed(() => (props.data?.event?.addons?.length ?? 0) > 0);
|
||||||
|
|
||||||
|
function goToOverview() {
|
||||||
|
if (selectTab) {
|
||||||
|
selectTab(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<ParticipantsList v-if="hasAddons" :data="data" :show-group-mail="false"/>
|
||||||
|
|
||||||
|
<div v-else class="no-addons">
|
||||||
|
<h3>Keine Zusatzoptionen angelegt</h3>
|
||||||
|
<p>
|
||||||
|
Für diese Veranstaltung sind noch keine Zusatzoptionen angelegt. Sobald du unter
|
||||||
|
<strong>Übersicht → Zusatzoptionen</strong> Zusätze definierst, werden die Teilnehmenden hier nach
|
||||||
|
ihren gebuchten Zusätzen gruppiert.
|
||||||
|
</p>
|
||||||
|
<button v-if="selectTab" type="button" class="button" @click="goToOverview">
|
||||||
|
Zu den Zusatzoptionen
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.no-addons {
|
||||||
|
max-width: 640px;
|
||||||
|
margin: 20px auto;
|
||||||
|
padding: 24px;
|
||||||
|
background: #f8fafc;
|
||||||
|
border: 1px solid #e5e7eb;
|
||||||
|
border-radius: 10px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-addons p {
|
||||||
|
color: #4b5563;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.button {
|
||||||
|
margin-top: 12px;
|
||||||
|
padding: 8px 18px;
|
||||||
|
background: #2563eb;
|
||||||
|
color: #fff;
|
||||||
|
border: none;
|
||||||
|
border-radius: 8px;
|
||||||
|
font-weight: 600;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -141,6 +141,44 @@ class EventParticipantRepository {
|
|||||||
return $groups;
|
return $groups;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gruppiert die (aktiven) Teilnehmenden nach ihren gebuchten Zusätzen (Event-Addons). Da Addons Ja/Nein
|
||||||
|
* sind, entsteht eine Gruppe je Zusatz (Überschrift = Addon-Titel) mit den Teilnehmenden, die ihn gebucht
|
||||||
|
* haben; ein Teilnehmer kann in mehreren Gruppen auftauchen. Nur nicht-leere Gruppen. Die Reihenfolge folgt
|
||||||
|
* der Event-Definition; nachträglich entfernte Addons werden über den Snapshot-Titel weiterhin abgedeckt.
|
||||||
|
*/
|
||||||
|
public function groupByAddon(Event $event, Request $request, ?string $filter = null): array
|
||||||
|
{
|
||||||
|
$allParticipants = $this->getForList($event, $request);
|
||||||
|
|
||||||
|
// Gruppen-Reihenfolge aus der Event-Definition vorbelegen (leere Gruppen werden am Ende verworfen).
|
||||||
|
$groups = [];
|
||||||
|
foreach ($event->addons ?? [] as $addon) {
|
||||||
|
$title = $addon['title'] ?? ($addon['key'] ?? null);
|
||||||
|
if ($title !== null) {
|
||||||
|
$groups[$title] = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($allParticipants as $participant) {
|
||||||
|
foreach ($participant['selectedAddons'] ?? [] as $selectedAddon) {
|
||||||
|
$title = $selectedAddon['title'] ?? ($selectedAddon['addonKey'] ?? null);
|
||||||
|
if ($title === null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$groups[$title][] = $participant;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$groups = array_filter($groups, fn($group) => count($group) > 0);
|
||||||
|
|
||||||
|
if ($filter !== null) {
|
||||||
|
return array_key_exists($filter, $groups) ? [$filter => $groups[$filter]] : [];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $groups;
|
||||||
|
}
|
||||||
|
|
||||||
public function getSignedOffParticipants(Event $event, Request $request, ?string $filter = null) : array {
|
public function getSignedOffParticipants(Event $event, Request $request, ?string $filter = null) : array {
|
||||||
$allParticipants = $this->getForList($event, $request, true);
|
$allParticipants = $this->getForList($event, $request, true);
|
||||||
$participants = [];
|
$participants = [];
|
||||||
|
|||||||
Reference in New Issue
Block a user