b2in/resources/views/livewire/admin/cms/cabinet-display.blade.php
2026-04-10 17:18:17 +02:00

307 lines
17 KiB
PHP

<div>
<flux:header class="mb-6">
<flux:heading size="xl">{{ __('Cabinet Display - CMS Verwaltung') }}</flux:heading>
<flux:subheading>{{ __('Verwalten Sie die Inhalte der Display-Seite') }}</flux:subheading>
</flux:header>
{{-- Hilfe-Banner --}}
<flux:card class="mb-6 bg-blue-50 dark:bg-blue-900/20 border-blue-200 dark:border-blue-800">
<div class="flex items-start gap-4">
<flux:icon.information-circle class="w-6 h-6 text-blue-600 dark:text-blue-400 flex-shrink-0 mt-0.5" />
<div class="flex-1">
<h3 class="font-semibold text-blue-900 dark:text-blue-100 mb-2">{{ __('Schnellanleitung') }}</h3>
<div class="text-sm text-blue-800 dark:text-blue-200 space-y-1">
<p> <strong>Videos:</strong> Videos müssen aufgrund der Dateigrößer vorab per SFTP hochgeladen werden. Die Position (0-100%) bestimmt den vertikalen Bildausschnitt.</p>
<p> <strong>Footer-Inhalte:</strong> Werden alle 30 Sekunden gewechselt. URLs werden automatisch als QR-Code angezeigt.</p>
<p> <strong>Footer-Inhalte:</strong> Sind alle Inhalte ausgeblendet, wird der Footer ausgeblendet und das Video auf 100% der Höhe angezeigt.</p>
<p> <strong>Display-URL:</strong> <code class="px-2 py-0.5 bg-blue-100 dark:bg-blue-800 rounded">https://cabinet.b2in.eu</code></p>
<p> <strong>API-Endpunkt:</strong> <code class="px-2 py-0.5 bg-blue-100 dark:bg-blue-800 rounded">{{ url('/api/display/config') }}</code></p>
</div>
</div>
</div>
</flux:card>
{{-- Success-Meldungen --}}
@if (session()->has('success'))
<x-success-alert>
{{ session('success') }}
</x-success-alert>
@endif
{{-- Video-Verwaltung --}}
<flux:card class="mb-8">
<div class="flex items-center justify-between mb-6">
<div>
<flux:heading size="lg">{{ __('Video-Playlist') }}</flux:heading>
<flux:subheading>{{ __('Videos werden in der angegebenen Reihenfolge abgespielt') }}</flux:subheading>
</div>
<flux:button wire:click="openVideoModal" icon="plus">
{{ __('Video hinzufügen') }}
</flux:button>
</div>
@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. Fügen Sie Ihr erstes Video hinzu!') }}</p>
</div>
@else
<div class="space-y-3">
@foreach($videos as $index => $video)
<div wire:key="video-{{ $video->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 hover:border-zinc-300 dark:hover:border-zinc-600 transition">
<div class="flex flex-col gap-1">
@if($index > 0)
<flux:button wire:click="moveVideo({{ $video->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="moveVideo({{ $video->id }}, 'down')"
size="xs"
variant="ghost"
icon="chevron-down"
class="text-zinc-400 hover:text-zinc-600">
</flux:button>
@endif
</div>
<div class="flex-1 min-w-0">
<div class="flex items-center gap-3 mb-1">
<flux:badge :color="$video->is_active ? 'green' : 'zinc'" size="sm">
{{ $video->is_active ? __('Aktiv') : __('Inaktiv') }}
</flux:badge>
<span class="font-semibold text-sm">{{ $video->title ?: $video->filename }}</span>
</div>
<div class="text-xs text-zinc-600 dark:text-zinc-400 space-x-4">
<span>📁 {{ $video->filename }}</span>
<span>📍 Position: {{ $video->position }}%</span>
</div>
</div>
<div class="flex items-center gap-2">
<flux:button wire:click="toggleVideoStatus({{ $video->id }})"
size="sm"
variant="ghost"
:icon="$video->is_active ? 'eye-slash' : 'eye'">
</flux:button>
<flux:button wire:click="openVideoModal({{ $video->id }})"
size="sm"
variant="ghost"
icon="pencil">
</flux:button>
<flux:button wire:click="deleteVideo({{ $video->id }})"
wire:confirm="Möchten Sie dieses Video wirklich löschen?"
size="sm"
variant="ghost"
icon="trash"
class="text-red-600 hover:text-red-700">
</flux:button>
</div>
</div>
@endforeach
</div>
@endif
</flux:card>
{{-- Footer-Content-Verwaltung --}}
<flux:card>
<div class="flex items-center justify-between mb-6">
<div>
<flux:heading size="lg">{{ __('Footer-Inhalte') }}</flux:heading>
<flux:subheading>{{ __('Inhalte werden alle 30 Sekunden im Footer gewechselt') }}</flux:subheading>
@if($footerContents->isNotEmpty())
<div class="mt-2 text-sm text-zinc-600 dark:text-zinc-400">
📊 Gesamt-Klicks: <strong>{{ $footerContents->sum('clicks') }}</strong>
</div>
@endif
</div>
<flux:button wire:click="openFooterModal" icon="plus">
{{ __('Inhalt hinzufügen') }}
</flux:button>
</div>
@if($footerContents->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. Fügen Sie den ersten Inhalt hinzu!') }}</p>
</div>
@else
<div class="space-y-3">
@foreach($footerContents as $index => $footer)
<div wire:key="footer-{{ $footer->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 hover:border-zinc-300 dark:hover:border-zinc-600 transition">
<div class="flex flex-col gap-1">
@if($index > 0)
<flux:button wire:click="moveFooter({{ $footer->id }}, 'up')"
size="xs"
variant="ghost"
icon="chevron-up"
class="text-zinc-400 hover:text-zinc-600">
</flux:button>
@endif
@if($index < count($footerContents) - 1)
<flux:button wire:click="moveFooter({{ $footer->id }}, 'down')"
size="xs"
variant="ghost"
icon="chevron-down"
class="text-zinc-400 hover:text-zinc-600">
</flux:button>
@endif
</div>
<div class="flex-1 min-w-0">
<div class="flex items-center gap-3 mb-1">
<flux:badge :color="$footer->is_active ? 'green' : 'zinc'" size="sm">
{{ $footer->is_active ? __('Aktiv') : __('Inaktiv') }}
</flux:badge>
<span class="font-semibold text-sm">{{ $footer->headline }}</span>
<flux:badge color="blue" size="sm">
<flux:icon.cursor-arrow-rays class="w-3 h-3" />
{{ $footer->clicks }} {{ __('Klicks') }}
</flux:badge>
</div>
<div class="text-xs text-zinc-600 dark:text-zinc-400 space-y-1">
<div>{{ $footer->subline }}</div>
@if($footer->url)
<div class="flex items-center gap-1">
<flux:icon.link class="w-3 h-3" />
<span class="font-mono bg-zinc-100 dark:bg-zinc-700 px-2 py-0.5 rounded">
{{ $footer->short_code }}
</span>
<button
onclick="navigator.clipboard.writeText('{{ $footer->short_url }}'); alert('Short-Link kopiert!');"
class="text-blue-600 hover:text-blue-700 dark:text-blue-400 text-xs"
title="Short-Link kopieren">
📋 Short-Link
</button>
</div>
<div class="flex items-center gap-1 text-xs">
<flux:icon.arrow-top-right-on-square class="w-3 h-3" />
<a href="{{ $footer->url }}" target="_blank" class="hover:underline">
{{ Str::limit($footer->url, 50) }}
</a>
</div>
@else
<div class="flex items-center gap-1 text-xs text-zinc-500 dark:text-zinc-500">
<flux:icon.x-circle class="w-3 h-3" />
<span>Kein QR-Code (Keine URL angegeben)</span>
</div>
@endif
</div>
</div>
<div class="flex items-center gap-2">
<flux:button wire:click="toggleFooterStatus({{ $footer->id }})"
size="sm"
variant="ghost"
:icon="$footer->is_active ? 'eye-slash' : 'eye'"
title="{{ $footer->is_active ? 'Deaktivieren' : 'Aktivieren' }}">
</flux:button>
<flux:dropdown>
<flux:button size="sm" variant="ghost" icon="ellipsis-vertical"></flux:button>
<flux:menu>
<flux:menu.item wire:click="openFooterModal({{ $footer->id }})" icon="pencil">
{{ __('Bearbeiten') }}
</flux:menu.item>
<flux:menu.item wire:click="regenerateShortCode({{ $footer->id }})" icon="arrow-path">
{{ __('Short-Code neu generieren') }}
</flux:menu.item>
<flux:menu.item wire:click="resetClicks({{ $footer->id }})"
wire:confirm="Möchten Sie den Klick-Zähler wirklich zurücksetzen?"
icon="arrow-path-rounded-square">
{{ __('Klicks zurücksetzen') }}
</flux:menu.item>
<flux:menu.separator />
<flux:menu.item wire:click="deleteFooter({{ $footer->id }})"
wire:confirm="Möchten Sie diesen Footer-Inhalt wirklich löschen?"
icon="trash"
class="text-red-600">
{{ __('Löschen') }}
</flux:menu.item>
</flux:menu>
</flux:dropdown>
</div>
</div>
@endforeach
</div>
@endif
</flux:card>
{{-- Video Modal --}}
<flux:modal :open="$showVideoModal" wire:model="showVideoModal">
<form wire:submit.prevent="saveVideo">
<div class="space-y-6">
<div>
<flux:heading size="lg">{{ $videoId ? __('Video bearbeiten') : __('Video hinzufügen') }}</flux:heading>
</div>
<flux:select wire:model="videoFilename" label="Video-Datei" placeholder="Wählen Sie ein Video...">
@foreach($availableVideos as $videoFile)
<option value="{{ $videoFile }}">{{ $videoFile }}</option>
@endforeach
</flux:select>
@error('videoFilename') <span class="text-red-600 text-sm">{{ $message }}</span> @enderror
<flux:input wire:model="videoTitle" label="Titel (optional)" placeholder="z.B. Herbst Kollektion 2025" />
<flux:input wire:model="videoPosition" type="number" min="0" max="100" label="Position (%)"
description="Vertikale Position im Video (0 = oben, 100 = unten)" />
@error('videoPosition') <span class="text-red-600 text-sm">{{ $message }}</span> @enderror
<flux:checkbox wire:model="videoIsActive" label="Video aktiv anzeigen" />
<div class="flex justify-end gap-3 pt-4">
<flux:button type="button" wire:click="closeVideoModal" variant="ghost">
{{ __('Abbrechen') }}
</flux:button>
<flux:button type="submit" variant="primary">
{{ $videoId ? __('Aktualisieren') : __('Hinzufügen') }}
</flux:button>
</div>
</div>
</form>
</flux:modal>
{{-- Footer Modal --}}
<flux:modal :open="$showFooterModal" wire:model="showFooterModal">
<form wire:submit.prevent="saveFooter">
<div class="space-y-6">
<div>
<flux:heading size="lg">{{ $footerId ? __('Footer-Inhalt bearbeiten') : __('Footer-Inhalt hinzufügen') }}</flux:heading>
</div>
<flux:input wire:model="footerHeadline" label="Überschrift" placeholder="z.B. Beratung & Termin" />
@error('footerHeadline') <span class="text-red-600 text-sm">{{ $message }}</span> @enderror
<flux:input wire:model="footerSubline" label="Unterzeile" placeholder="z.B. Jetzt Termin vereinbaren." />
@error('footerSubline') <span class="text-red-600 text-sm">{{ $message }}</span> @enderror
<flux:input wire:model="footerUrl" label="URL (optional)" placeholder="https://www.cabinet.de/bielefeld..."
description="Leer lassen = Kein QR-Code wird angezeigt. Mit URL = QR-Code mit Short-Link wird generiert." />
@error('footerUrl') <span class="text-red-600 text-sm">{{ $message }}</span> @enderror
<flux:checkbox wire:model="footerIsActive" label="Footer-Inhalt aktiv anzeigen" />
<div class="flex justify-end gap-3 pt-4">
<flux:button type="button" wire:click="closeFooterModal" variant="ghost">
{{ __('Abbrechen') }}
</flux:button>
<flux:button type="submit" variant="primary">
{{ $footerId ? __('Aktualisieren') : __('Hinzufügen') }}
</flux:button>
</div>
</div>
</form>
</flux:modal>
</div>