From cd51eb3f973424c774745bb58d73f91209be8a3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20G=C3=BCnther?= Date: Wed, 12 Aug 2026 17:08:21 +0200 Subject: [PATCH] Handling of event addons --- .../Event/Controllers/DetailsController.php | 3 + app/Domains/Event/Views/Details.vue | 11 +-- .../Views/Partials/ParticipantsByAddon.vue | 67 +++++++++++++++++++ .../EventParticipantRepository.php | 38 +++++++++++ 4 files changed, 114 insertions(+), 5 deletions(-) create mode 100644 app/Domains/Event/Views/Partials/ParticipantsByAddon.vue diff --git a/app/Domains/Event/Controllers/DetailsController.php b/app/Domains/Event/Controllers/DetailsController.php index a197c4e..fc3f735 100644 --- a/app/Domains/Event/Controllers/DetailsController.php +++ b/app/Domains/Event/Controllers/DetailsController.php @@ -224,6 +224,9 @@ class DetailsController extends CommonController { case 'by-participation-option': $participants = $this->eventParticipants->groupByParticipationOption($event, $request); break; + case 'by-addon': + $participants = $this->eventParticipants->groupByAddon($event, $request); + break; case 'signed-off': $participants = $this->eventParticipants->getSignedOffParticipants($event, $request); break; diff --git a/app/Domains/Event/Views/Details.vue b/app/Domains/Event/Views/Details.vue index fc57a06..6476ad8 100644 --- a/app/Domains/Event/Views/Details.vue +++ b/app/Domains/Event/Views/Details.vue @@ -5,6 +5,7 @@ import ShadowedBox from "../../../Views/Components/ShadowedBox.vue"; import TabbedPage from "../../../Views/Components/TabbedPage.vue"; import ParticipantsList from "./Partials/ParticipantsList.vue"; import ParticipantsByOption from "./Partials/ParticipantsByOption.vue"; +import ParticipantsByAddon from "./Partials/ParticipantsByAddon.vue"; import Overview from "./Partials/Overview.vue"; const props = defineProps({ @@ -37,16 +38,16 @@ const tabs = [ component: ParticipantsByOption, 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', component: ParticipantsList, 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(() => { diff --git a/app/Domains/Event/Views/Partials/ParticipantsByAddon.vue b/app/Domains/Event/Views/Partials/ParticipantsByAddon.vue new file mode 100644 index 0000000..7753814 --- /dev/null +++ b/app/Domains/Event/Views/Partials/ParticipantsByAddon.vue @@ -0,0 +1,67 @@ + + + + + diff --git a/app/Repositories/EventParticipantRepository.php b/app/Repositories/EventParticipantRepository.php index ea184ab..efba1e6 100644 --- a/app/Repositories/EventParticipantRepository.php +++ b/app/Repositories/EventParticipantRepository.php @@ -141,6 +141,44 @@ class EventParticipantRepository { 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 { $allParticipants = $this->getForList($event, $request, true); $participants = [];