Display CMS Optimierungen 29-05-2026

- Mediathek: Video-Vorschaubilder statt Icons (FFmpeg-Thumbnails + Backfill-Command), Kategorie "Sonstiges"
- B2in Media-Picker zeigt alle Medientypen, Typ wird automatisch erkannt; Thumbnail-Preview vor allen Medien-URL-Feldern
- B2in Marke/Footer: Footer ein/aus, Logo+Claim frei positionierbar (Ecken) mit Constraints, separate Anzeige-Schalter
- Angebote-Modul dynamisch: kein Slide-Typ mehr, einheitliches Detail-Layout mit ein-/ausblendbaren Bloecken, Logo/Brand pro Slide, Streichpreis-Option
- Player: leere Module stoppen Endlosschleife, dynamische Layout-Anpassung bei verstecktem Footer/Header
- Fix: Script-Ladereihenfolge (Livewire vor Flux), entfernte stale public/flux/flux.js, Modal-Crash beim Aktualisieren behoben

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Kevin Adametz 2026-05-29 15:57:33 +00:00
parent 9262132325
commit 6c6d683b9a
42 changed files with 2267 additions and 13905 deletions

View file

@ -16,40 +16,43 @@
</flux:button>
</div>
@if($videos->isEmpty())
@if ($videos->isEmpty())
<div class="text-center py-12 text-zinc-500 dark:text-zinc-400">
<flux:icon.film class="w-16 h-16 mx-auto mb-4 opacity-50" />
<p>{{ __('Noch keine Videos vorhanden.') }}</p>
</div>
@else
<div class="space-y-3">
@foreach($videos as $index => $item)
@foreach ($videos as $index => $item)
<div wire:key="item-{{ $item->id }}"
class="flex items-center gap-4 p-4 bg-zinc-50 dark:bg-zinc-800 rounded-lg border border-zinc-200 dark:border-zinc-700 transition">
class="flex items-center gap-4 p-4 bg-zinc-50 dark:bg-zinc-800 rounded-lg border border-zinc-200 dark:border-zinc-700 transition">
<div class="flex flex-col gap-1">
@if($index > 0)
<flux:button wire:click="moveItem({{ $item->id }}, 'up')" size="xs" variant="ghost" icon="chevron-up" class="text-zinc-400 hover:text-zinc-600"></flux:button>
@if ($index > 0)
<flux:button wire:click="moveItem({{ $item->id }}, 'up')" size="xs" variant="ghost"
icon="chevron-up" class="text-zinc-400 hover:text-zinc-600"></flux:button>
@endif
@if($index < count($videos) - 1)
<flux:button wire:click="moveItem({{ $item->id }}, 'down')" size="xs" variant="ghost" icon="chevron-down" class="text-zinc-400 hover:text-zinc-600"></flux:button>
@if ($index < count($videos) - 1)
<flux:button wire:click="moveItem({{ $item->id }}, 'down')" size="xs"
variant="ghost" icon="chevron-down" class="text-zinc-400 hover:text-zinc-600">
</flux:button>
@endif
</div>
<div class="flex h-16 w-12 shrink-0 items-center justify-center rounded-lg bg-black text-[10px] font-semibold uppercase text-white">
Video
</div>
<x-media-thumb :url="$item->content['filename'] ?? ''" size="h-16 w-12" />
<div class="flex-1 min-w-0">
<div class="flex items-center gap-3 mb-1">
<flux:badge :color="$item->is_active ? 'green' : 'zinc'" size="sm">
{{ $item->is_active ? __('Aktiv') : __('Inaktiv') }}
</flux:badge>
<span class="font-semibold text-sm">{{ $item->content['title'] ?? $item->content['filename'] ?? '' }}</span>
<span
class="font-semibold text-sm">{{ $item->content['title'] ?? ($item->content['filename'] ?? '') }}</span>
</div>
@php
$videoSource = $item->content['filename'] ?? '';
$isMediaLibrarySource = str_starts_with($videoSource, '/storage/') || str_starts_with($videoSource, 'http');
$isMediaLibrarySource =
str_starts_with($videoSource, '/storage/') || str_starts_with($videoSource, 'http');
@endphp
<div class="flex flex-wrap items-center gap-2 text-xs text-zinc-600 dark:text-zinc-400">
<flux:badge size="sm" :color="$isMediaLibrarySource ? 'sky' : 'zinc'">
@ -61,9 +64,13 @@
</div>
<div class="flex items-center gap-2">
<flux:button wire:click="toggleItemStatus({{ $item->id }})" size="sm" variant="ghost" :icon="$item->is_active ? 'eye-slash' : 'eye'"></flux:button>
<flux:button wire:click="openItemModal({{ $item->id }})" size="sm" variant="ghost" icon="pencil"></flux:button>
<flux:button wire:click="deleteItem({{ $item->id }})" wire:confirm="Möchten Sie diesen Eintrag wirklich löschen?" size="sm" variant="ghost" icon="trash" class="text-red-600 hover:text-red-700"></flux:button>
<flux:button wire:click="toggleItemStatus({{ $item->id }})" size="sm" variant="ghost"
:icon="$item->is_active ? 'eye-slash' : 'eye'"></flux:button>
<flux:button wire:click="openItemModal({{ $item->id }})" size="sm" variant="ghost"
icon="pencil"></flux:button>
<flux:button wire:click="deleteItem({{ $item->id }})"
wire:confirm="Möchten Sie diesen Eintrag wirklich löschen?" size="sm" variant="ghost"
icon="trash" class="text-red-600 hover:text-red-700"></flux:button>
</div>
</div>
@endforeach
@ -76,34 +83,40 @@
<div class="flex items-center justify-between mb-6">
<div>
<flux:heading size="lg">{{ __('Footer-Inhalte') }}</flux:heading>
<flux:subheading>{{ __('Inhalte werden im Footer rotiert') }}</flux:subheading>
<flux:subheading>{{ __('Inhalte werden im Footer rotiert / ohne Inhalte bleibt der untere Teil frei.') }}
</flux:subheading>
</div>
<flux:button wire:click="openItemModal(null, 'footer')" icon="plus">
{{ __('Inhalt hinzufügen') }}
</flux:button>
</div>
@if($footers->isEmpty())
@if ($footers->isEmpty())
<div class="text-center py-12 text-zinc-500 dark:text-zinc-400">
<flux:icon.document-text class="w-16 h-16 mx-auto mb-4 opacity-50" />
<p>{{ __('Noch keine Footer-Inhalte vorhanden.') }}</p>
</div>
@else
<div class="space-y-3">
@foreach($footers as $index => $item)
@foreach ($footers as $index => $item)
<div wire:key="item-{{ $item->id }}"
class="flex items-center gap-4 p-4 bg-zinc-50 dark:bg-zinc-800 rounded-lg border border-zinc-200 dark:border-zinc-700 transition">
class="flex items-center gap-4 p-4 bg-zinc-50 dark:bg-zinc-800 rounded-lg border border-zinc-200 dark:border-zinc-700 transition">
<div class="flex flex-col gap-1">
@if($index > 0)
<flux:button wire:click="moveItem({{ $item->id }}, 'up')" size="xs" variant="ghost" icon="chevron-up" class="text-zinc-400 hover:text-zinc-600"></flux:button>
@if ($index > 0)
<flux:button wire:click="moveItem({{ $item->id }}, 'up')" size="xs"
variant="ghost" icon="chevron-up" class="text-zinc-400 hover:text-zinc-600">
</flux:button>
@endif
@if($index < count($footers) - 1)
<flux:button wire:click="moveItem({{ $item->id }}, 'down')" size="xs" variant="ghost" icon="chevron-down" class="text-zinc-400 hover:text-zinc-600"></flux:button>
@if ($index < count($footers) - 1)
<flux:button wire:click="moveItem({{ $item->id }}, 'down')" size="xs"
variant="ghost" icon="chevron-down" class="text-zinc-400 hover:text-zinc-600">
</flux:button>
@endif
</div>
<div class="flex h-16 w-12 shrink-0 flex-col justify-end rounded-lg bg-zinc-900 p-1 text-[8px] text-white">
<div
class="flex h-16 w-12 shrink-0 flex-col justify-end rounded-lg bg-zinc-900 p-1 text-[8px] text-white">
<div class="truncate text-zinc-400">{{ $item->content['headline'] ?? 'Footer' }}</div>
<div class="truncate font-semibold">{{ $item->content['subline'] ?? '' }}</div>
</div>
@ -117,16 +130,20 @@
</div>
<div class="text-xs text-zinc-600 dark:text-zinc-400">
{{ $item->content['subline'] ?? '' }}
@if(!empty($item->content['url']))
@if (!empty($item->content['url']))
<span class="ml-2">{{ Str::limit($item->content['url'], 40) }}</span>
@endif
</div>
</div>
<div class="flex items-center gap-2">
<flux:button wire:click="toggleItemStatus({{ $item->id }})" size="sm" variant="ghost" :icon="$item->is_active ? 'eye-slash' : 'eye'"></flux:button>
<flux:button wire:click="openItemModal({{ $item->id }})" size="sm" variant="ghost" icon="pencil"></flux:button>
<flux:button wire:click="deleteItem({{ $item->id }})" wire:confirm="Möchten Sie diesen Eintrag wirklich löschen?" size="sm" variant="ghost" icon="trash" class="text-red-600 hover:text-red-700"></flux:button>
<flux:button wire:click="toggleItemStatus({{ $item->id }})" size="sm" variant="ghost"
:icon="$item->is_active ? 'eye-slash' : 'eye'"></flux:button>
<flux:button wire:click="openItemModal({{ $item->id }})" size="sm" variant="ghost"
icon="pencil"></flux:button>
<flux:button wire:click="deleteItem({{ $item->id }})"
wire:confirm="Möchten Sie diesen Eintrag wirklich löschen?" size="sm" variant="ghost"
icon="trash" class="text-red-600 hover:text-red-700"></flux:button>
</div>
</div>
@endforeach