b2in/resources/views/livewire/admin/cms/display-media-picker.blade.php
Kevin Adametz 6c6d683b9a 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>
2026-05-29 15:57:33 +00:00

154 lines
8.6 KiB
PHP

<div>
<div class="flex items-end gap-3">
<div class="flex-1">
@if ($selectedMedia)
@php
$selectedThumb = $selectedMedia->getThumbnailUrl()
?? ($selectedMedia->isImage() && $selectedMedia->isExternal() ? $selectedMedia->external_url : null);
$selectedVideoFrame = (! $selectedThumb && $selectedMedia->isVideo() && $selectedMedia->isUpload()) ? $selectedMedia->getUrl() : null;
@endphp
<div class="flex items-center gap-3 rounded-lg border border-zinc-200 p-2 dark:border-zinc-700">
@if ($selectedThumb)
<img src="{{ $selectedThumb }}"
alt="{{ $selectedMedia->filename }}"
class="h-16 w-16 rounded-md object-cover" />
@elseif ($selectedVideoFrame)
<video class="h-16 w-16 rounded-md object-cover" preload="metadata" muted playsinline>
<source src="{{ $selectedVideoFrame }}#t=1" type="{{ $selectedMedia->mime_type }}">
</video>
@elseif ($selectedMedia->isVideo())
<div class="flex h-16 w-16 items-center justify-center rounded-md bg-purple-50 dark:bg-purple-900/20">
<x-heroicon-o-film class="h-8 w-8 text-purple-500" />
</div>
@elseif ($selectedMedia->isExternal())
<div class="flex h-16 w-16 items-center justify-center rounded-md bg-blue-50 dark:bg-blue-900/20">
<x-heroicon-o-link class="h-8 w-8 text-blue-500" />
</div>
@else
<div class="flex h-16 w-16 items-center justify-center rounded-md bg-zinc-100 dark:bg-zinc-800">
<x-heroicon-o-photo class="h-8 w-8 text-zinc-400" />
</div>
@endif
<div class="min-w-0 flex-1">
<p class="truncate text-sm font-medium text-zinc-700 dark:text-zinc-300">
{{ $selectedMedia->getDisplayName() }}
</p>
<div class="flex items-center gap-2 text-xs text-zinc-400">
<span>{{ $selectedMedia->getHumanFileSize() }}</span>
@if ($selectedMedia->isExternal())
<flux:badge size="sm" color="blue">Extern</flux:badge>
@endif
<flux:badge size="sm" :color="$selectedMedia->isVideo() ? 'purple' : 'sky'">
{{ $selectedMedia->isVideo() ? 'Video' : 'Bild' }}
</flux:badge>
</div>
</div>
<flux:button size="xs" variant="ghost" icon="x-mark" wire:click="clearSelection" />
</div>
@else
<div class="rounded-lg border-2 border-dashed border-zinc-300 px-4 py-3 text-center text-sm text-zinc-400 dark:border-zinc-600">
Kein Medium ausgewählt
</div>
@endif
</div>
<flux:button size="sm" variant="ghost" icon="photo" wire:click="openPicker">
{{ $label }}
</flux:button>
</div>
<flux:modal wire:model="showModal" class="w-full max-w-4xl space-y-4 overflow-y-auto max-h-[85vh]">
<flux:heading size="lg">{{ $label }}</flux:heading>
<div class="flex flex-col gap-3">
<flux:input wire:model.live.debounce.300ms="search" placeholder="Suchen (Dateiname oder Titel)..." icon="magnifying-glass" size="sm" />
<flux:file-upload wire:model="quickUploads" multiple
accept="image/jpeg,image/png,image/gif,image/webp,image/svg+xml,video/mp4,video/webm,.jpg,.jpeg,.png,.gif,.webp,.svg,.mp4,.webm,.mov">
<flux:file-upload.dropzone
heading="Datei hochladen"
text="Bilder inkl. SVG und Videos bis 200 MB"
with-progress />
</flux:file-upload>
@if (isset($quickUploads) && count($quickUploads) > 0)
<div class="flex flex-wrap items-center gap-2">
@foreach ($quickUploads as $index => $upload)
<flux:file-item
:heading="$upload->getClientOriginalName()"
:image="(str_starts_with($upload->getMimeType() ?? '', 'image/') && $upload->isPreviewable())
? $upload->temporaryUrl()
: null"
:size="$upload->getSize()">
<x-slot name="actions">
<flux:file-item.remove wire:click="removeQuickUpload({{ $index }})" />
</x-slot>
</flux:file-item>
@endforeach
</div>
@endif
<flux:error name="quickUploads" />
</div>
<div class="grid grid-cols-3 gap-2 sm:grid-cols-4 md:grid-cols-6">
@forelse ($mediaItems as $item)
<div wire:key="dpick-{{ $item->id }}"
class="group cursor-pointer overflow-hidden rounded-lg border transition-all
{{ $value === $item->id ? 'border-blue-500 ring-2 ring-blue-200' : 'border-zinc-200 hover:border-blue-300 dark:border-zinc-700' }}"
wire:click="selectMedia({{ $item->id }})">
<div class="relative aspect-square bg-zinc-100 dark:bg-zinc-800">
@php
$pickThumb = $item->getThumbnailUrl()
?? ($item->isImage() && $item->isExternal() ? $item->external_url : null);
$pickVideoFrame = (! $pickThumb && $item->isVideo() && $item->isUpload()) ? $item->getUrl() : null;
@endphp
@if ($pickThumb)
<img src="{{ $pickThumb }}"
alt="{{ $item->filename }}" class="h-full w-full object-cover" loading="lazy" />
@elseif ($pickVideoFrame)
<video class="h-full w-full object-cover" preload="metadata" muted playsinline>
<source src="{{ $pickVideoFrame }}#t=1" type="{{ $item->mime_type }}">
</video>
@elseif ($item->isVideo())
<div class="flex h-full w-full items-center justify-center text-purple-500">
<x-heroicon-o-film class="h-10 w-10" />
</div>
@elseif ($item->isExternal() && $item->isImage())
<div class="flex h-full w-full items-center justify-center text-blue-500">
<x-heroicon-o-photo class="h-10 w-10" />
</div>
@else
<div class="flex h-full w-full items-center justify-center text-zinc-400">
<x-heroicon-o-link class="h-10 w-10" />
</div>
@endif
{{-- Badges --}}
<div class="absolute right-1 top-1 flex flex-col gap-1">
@if ($item->isExternal())
<flux:badge size="sm" color="blue" class="text-[10px]!">URL</flux:badge>
@endif
@if ($item->isVideo())
<flux:badge size="sm" color="purple" class="text-[10px]!">Video</flux:badge>
@endif
</div>
</div>
<div class="p-1.5">
<p class="truncate text-[11px] font-medium text-zinc-600 dark:text-zinc-400">{{ $item->getDisplayName() }}</p>
<p class="truncate text-[10px] text-zinc-400">{{ $item->getHumanFileSize() }}</p>
</div>
</div>
@empty
<div class="col-span-full py-8 text-center">
<flux:text>Keine Medien gefunden. Laden Sie Dateien hoch oder legen Sie externe URLs in der Mediathek an.</flux:text>
</div>
@endforelse
</div>
@if ($mediaItems->hasPages())
<div class="mt-2">
{{ $mediaItems->links() }}
</div>
@endif
</flux:modal>
</div>