From 6d83b5f38c274247b87f41da3af893b3b0153dae Mon Sep 17 00:00:00 2001 From: Thorsten Bus Date: Mon, 4 May 2026 00:37:05 +0200 Subject: [PATCH] feat(service-edit): macro icon + Anpassen/Standard flow on service edit page --- app/Http/Controllers/ServiceController.php | 31 ++++ .../js/Components/ServicePartMacroPanel.vue | 145 ++++++++++++++++++ resources/js/Pages/Services/Edit.vue | 70 ++++++++- 3 files changed, 244 insertions(+), 2 deletions(-) create mode 100644 resources/js/Components/ServicePartMacroPanel.vue diff --git a/app/Http/Controllers/ServiceController.php b/app/Http/Controllers/ServiceController.php index c99af6c..24951c7 100644 --- a/app/Http/Controllers/ServiceController.php +++ b/app/Http/Controllers/ServiceController.php @@ -4,10 +4,12 @@ use App\Models\Service; use App\Models\ServiceAgendaItem; +use App\Models\ServiceMacroOverride; use App\Models\Setting; use App\Models\Slide; use App\Models\Song; use App\Services\AgendaMatcherService; +use App\Services\MacroResolutionService; use App\Services\ProBundleExportService; use Illuminate\Http\JsonResponse; use Illuminate\Http\RedirectResponse; @@ -225,6 +227,34 @@ public function edit(Service $service): Response return $arr; }, $filteredItems); + // Macro resolution per part type (for icons + Anpassen/Standard panel) + $resolver = app(MacroResolutionService::class); + $macros_per_part = []; + foreach (['information', 'moderation', 'sermon', 'song', 'agenda_item'] as $partType) { + $assignments = $resolver->resolveAssignmentsForPart($service, $partType); + $isOverridden = ServiceMacroOverride::where('service_id', $service->id) + ->where('part_type', $partType) + ->exists(); + $hasWarning = $assignments->contains( + fn ($a) => $a->macro?->isHidden() || ($a->position === 'by_label' && $a->label?->isHidden()) + ); + $macros_per_part[$partType] = [ + 'count' => $assignments->count(), + 'is_overridden' => $isOverridden, + 'has_warning' => $hasWarning, + 'assignments' => $assignments->map(fn ($a) => [ + 'id' => $a->id, + 'macro_id' => $a->macro_id, + 'macro_name' => $a->macro?->name, + 'macro_color' => $a->macro?->color, + 'macro_hidden' => $a->macro?->isHidden(), + 'position' => $a->position, + 'label_id' => $a->label_id, + 'label_name' => $a->label?->name, + ])->values()->all(), + ]; + } + return Inertia::render('Services/Edit', [ 'service' => [ 'id' => $service->id, @@ -292,6 +322,7 @@ public function edit(Service $service): Response 'title' => $nextService->title, 'date' => $nextService->date?->toDateString(), ] : null, + 'macros_per_part' => $macros_per_part, ]); } diff --git a/resources/js/Components/ServicePartMacroPanel.vue b/resources/js/Components/ServicePartMacroPanel.vue new file mode 100644 index 0000000..003a462 --- /dev/null +++ b/resources/js/Components/ServicePartMacroPanel.vue @@ -0,0 +1,145 @@ + + + diff --git a/resources/js/Pages/Services/Edit.vue b/resources/js/Pages/Services/Edit.vue index 82e046c..c489535 100644 --- a/resources/js/Pages/Services/Edit.vue +++ b/resources/js/Pages/Services/Edit.vue @@ -6,6 +6,8 @@ import InformationBlock from '@/Components/Blocks/InformationBlock.vue' import AgendaItemRow from '@/Components/AgendaItemRow.vue' import SongAgendaItem from '@/Components/SongAgendaItem.vue' import ArrangementDialog from '@/Components/ArrangementDialog.vue' +import MacroIcon from '@/Components/MacroIcon.vue' +import ServicePartMacroPanel from '@/Components/ServicePartMacroPanel.vue' const props = defineProps({ service: { @@ -40,8 +42,30 @@ const props = defineProps({ type: Object, default: () => ({}), }, + macros_per_part: { + type: Object, + default: () => ({}), + }, }) +const openMacroPanel = ref(null) + +const macroPartLabels = { + information: 'Informationen', + moderation: 'Moderation', + sermon: 'Predigt', + song: 'Lieder', + agenda_item: 'Ablaufpunkte', +} + +function toggleMacroPanel(partType) { + openMacroPanel.value = openMacroPanel.value === partType ? null : partType +} + +function macroPartData(partType) { + return props.macros_per_part?.[partType] ?? { count: 0, is_overridden: false, has_warning: false, assignments: [] } +} + const formattedDate = computed(() => { if (!props.service.date) return '' return new Date(props.service.date).toLocaleDateString('de-DE', { @@ -358,7 +382,31 @@ async function downloadService() {
-

Ablauf

+
+

Ablauf

+
+ +
+
-
+

Information

Info-Folien für alle kommenden Services

+
+ + +