294 lines
17 KiB
PHP
294 lines
17 KiB
PHP
<?php
|
||
|
||
use App\Enums\DisplayVersionType;
|
||
use App\Models\CabinetTabletSetting;
|
||
use App\Models\Display;
|
||
use App\Models\DisplayMedia;
|
||
use App\Models\DisplayVersion;
|
||
use App\Models\DisplayVersionItem;
|
||
use function Livewire\Volt\{layout, title, computed};
|
||
|
||
layout('components.layouts.app');
|
||
title('Store Displays');
|
||
|
||
$stats = computed(fn () => [
|
||
'displays' => Display::count(),
|
||
'displays_active' => Display::where('is_active', true)->count(),
|
||
'versions' => DisplayVersion::count(),
|
||
'versions_active' => DisplayVersion::active()->count(),
|
||
'items' => DisplayVersionItem::count(),
|
||
'items_active' => DisplayVersionItem::where('is_active', true)->count(),
|
||
'type_video' => DisplayVersion::ofType(DisplayVersionType::VideoDisplay)->count(),
|
||
'type_b2in' => DisplayVersion::ofType(DisplayVersionType::B2in)->count(),
|
||
'type_offers' => DisplayVersion::ofType(DisplayVersionType::Offers)->count(),
|
||
'media_total' => DisplayMedia::count(),
|
||
'media_uploads' => DisplayMedia::uploads()->count(),
|
||
'media_externals' => DisplayMedia::externals()->count(),
|
||
]);
|
||
|
||
$tabletStatus = computed(function () {
|
||
try {
|
||
$settings = CabinetTabletSetting::current();
|
||
|
||
return $settings->computeStatus()['status'];
|
||
} catch (\Throwable) {
|
||
return null;
|
||
}
|
||
});
|
||
|
||
?>
|
||
|
||
<div>
|
||
<div class="mb-6">
|
||
<flux:heading size="xl">Store Displays</flux:heading>
|
||
<flux:text class="mt-1">Displays, Inhalts-Versionen und Info-Tablet im Cabinet Showroom verwalten.</flux:text>
|
||
</div>
|
||
|
||
<div class="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-5">
|
||
<a href="{{ route('admin.cms.display-media') }}" wire:navigate>
|
||
<flux:card class="hover:border-violet-500 transition-colors">
|
||
<div class="flex items-center gap-3">
|
||
<flux:icon name="photo" class="text-violet-500" />
|
||
<div>
|
||
<flux:heading size="lg">{{ $this->stats['media_total'] }}</flux:heading>
|
||
<flux:text class="text-sm">Medien ({{ $this->stats['media_uploads'] }} Uploads, {{ $this->stats['media_externals'] }} extern)</flux:text>
|
||
</div>
|
||
</div>
|
||
</flux:card>
|
||
</a>
|
||
|
||
<a href="{{ route('admin.cms.display-versions') }}" wire:navigate>
|
||
<flux:card class="hover:border-purple-500 transition-colors">
|
||
<div class="flex items-center gap-3">
|
||
<flux:icon name="rectangle-group" class="text-purple-500" />
|
||
<div>
|
||
<flux:heading size="lg">{{ $this->stats['versions'] }}</flux:heading>
|
||
<flux:text class="text-sm">Versionen ({{ $this->stats['versions_active'] }} aktiv)</flux:text>
|
||
</div>
|
||
</div>
|
||
</flux:card>
|
||
</a>
|
||
|
||
<a href="{{ route('admin.cms.displays') }}" wire:navigate>
|
||
<flux:card class="hover:border-blue-500 transition-colors">
|
||
<div class="flex items-center gap-3">
|
||
<flux:icon name="tv" class="text-blue-500" />
|
||
<div>
|
||
<flux:heading size="lg">{{ $this->stats['displays'] }}</flux:heading>
|
||
<flux:text class="text-sm">Displays ({{ $this->stats['displays_active'] }} aktiv)</flux:text>
|
||
</div>
|
||
</div>
|
||
</flux:card>
|
||
</a>
|
||
|
||
<a href="{{ route('admin.cms.cabinet-tablet') }}" wire:navigate>
|
||
<flux:card class="hover:border-teal-500 transition-colors">
|
||
<div class="flex items-center gap-3">
|
||
<flux:icon name="device-tablet" class="text-teal-500" />
|
||
<div>
|
||
<flux:heading size="lg">Info-Tablet</flux:heading>
|
||
<flux:text class="text-sm">
|
||
@if($this->tabletStatus)
|
||
Status:
|
||
<flux:badge size="sm" :color="match($this->tabletStatus) {
|
||
'open' => 'green',
|
||
'closed' => 'red',
|
||
'notice' => 'amber',
|
||
'warning' => 'orange',
|
||
default => 'zinc',
|
||
}">
|
||
{{ match($this->tabletStatus) {
|
||
'open' => 'Geöffnet',
|
||
'closed' => 'Geschlossen',
|
||
'notice' => 'Hinweis',
|
||
'warning' => 'Warnung',
|
||
default => $this->tabletStatus,
|
||
} }}
|
||
</flux:badge>
|
||
@else
|
||
Öffnungszeiten & Status
|
||
@endif
|
||
</flux:text>
|
||
</div>
|
||
</div>
|
||
</flux:card>
|
||
</a>
|
||
|
||
<flux:card>
|
||
<div class="flex items-center gap-3">
|
||
<flux:icon name="queue-list" class="text-zinc-400" />
|
||
<div>
|
||
<flux:heading size="lg">{{ $this->stats['items'] }}</flux:heading>
|
||
<flux:text class="text-sm">Inhalte gesamt ({{ $this->stats['items_active'] }} aktiv)</flux:text>
|
||
</div>
|
||
</div>
|
||
</flux:card>
|
||
</div>
|
||
|
||
{{-- Versions-Typen Übersicht --}}
|
||
<div class="grid grid-cols-1 gap-4 sm:grid-cols-3 mt-4">
|
||
<flux:card>
|
||
<div class="flex items-center gap-3">
|
||
<flux:icon name="film" class="text-purple-400" />
|
||
<div>
|
||
<flux:text class="text-sm font-medium text-zinc-800 dark:text-zinc-200">Video-Display</flux:text>
|
||
<flux:text class="text-xs">{{ $this->stats['type_video'] }} {{ $this->stats['type_video'] === 1 ? 'Version' : 'Versionen' }}</flux:text>
|
||
</div>
|
||
</div>
|
||
</flux:card>
|
||
<flux:card>
|
||
<div class="flex items-center gap-3">
|
||
<flux:icon name="photo" class="text-blue-400" />
|
||
<div>
|
||
<flux:text class="text-sm font-medium text-zinc-800 dark:text-zinc-200">B2in Display</flux:text>
|
||
<flux:text class="text-xs">{{ $this->stats['type_b2in'] }} {{ $this->stats['type_b2in'] === 1 ? 'Version' : 'Versionen' }}</flux:text>
|
||
</div>
|
||
</div>
|
||
</flux:card>
|
||
<flux:card>
|
||
<div class="flex items-center gap-3">
|
||
<flux:icon name="tag" class="text-amber-400" />
|
||
<div>
|
||
<flux:text class="text-sm font-medium text-zinc-800 dark:text-zinc-200">Angebote</flux:text>
|
||
<flux:text class="text-xs">{{ $this->stats['type_offers'] }} {{ $this->stats['type_offers'] === 1 ? 'Version' : 'Versionen' }}</flux:text>
|
||
</div>
|
||
</div>
|
||
</flux:card>
|
||
</div>
|
||
|
||
{{-- Beschreibung --}}
|
||
<flux:card class="mt-6 max-w-4xl">
|
||
<flux:heading size="lg" class="mb-4">So funktioniert das Display-System</flux:heading>
|
||
<div class="space-y-5 text-sm text-zinc-600 dark:text-zinc-400">
|
||
|
||
<div>
|
||
<flux:heading size="sm" class="mb-1 flex items-center gap-2">
|
||
<flux:icon name="squares-2x2" class="size-4 text-zinc-500" />
|
||
Überblick
|
||
</flux:heading>
|
||
<p>
|
||
Das Display-System steuert alle Bildschirme im Cabinet Showroom Bielefeld.
|
||
Es besteht aus drei Bereichen, die Sie über die Kacheln oben erreichen:
|
||
</p>
|
||
<ul class="mt-2 ml-5 list-disc space-y-1">
|
||
<li><strong class="font-medium text-zinc-800 dark:text-zinc-200">Mediathek</strong> – Zentrale Verwaltung aller Bilder und Videos fuer die Displays. Dateien bis 50 MB direkt hochladen oder groessere Videos als externe URL (Google Drive, OneDrive) einbinden.</li>
|
||
<li><strong class="font-medium text-zinc-800 dark:text-zinc-200">Versionen</strong> – Content-Pakete, die auf den Displays abgespielt werden. Jede Version hat einen bestimmten Typ und enthält passende Inhalte (Videos, Bilder oder Angebots-Slides).</li>
|
||
<li><strong class="font-medium text-zinc-800 dark:text-zinc-200">Displays</strong> – Die physischen Bildschirme im Showroom. Jedem Display werden eine oder mehrere Versionen als Playlist zugewiesen.</li>
|
||
<li><strong class="font-medium text-zinc-800 dark:text-zinc-200">Info-Tablet</strong> – Das Tablet an der Eingangstür des Showrooms. Hier verwalten Sie Öffnungszeiten, den aktuellen Store-Status und Hinweise für Besucher.</li>
|
||
</ul>
|
||
</div>
|
||
|
||
<flux:separator />
|
||
|
||
<div>
|
||
<flux:heading size="sm" class="mb-1 flex items-center gap-2">
|
||
<flux:icon name="photo" class="size-4 text-violet-500" />
|
||
Mediathek
|
||
</flux:heading>
|
||
<p>
|
||
Die <strong class="font-medium text-zinc-800 dark:text-zinc-200">Display-Mediathek</strong> verwaltet alle Bilder und Videos, die auf den Displays im Showroom angezeigt werden.
|
||
Sie ist unabhängig von der Website-Mediathek (Flux CMS) und speziell auf die Anforderungen der Displays zugeschnitten.
|
||
</p>
|
||
<ul class="mt-2 ml-5 list-disc space-y-1">
|
||
<li><strong class="font-medium text-zinc-800 dark:text-zinc-200">Direkt-Upload:</strong> Bilder und Videos bis 50 MB direkt per Drag-and-drop oder Dateiauswahl hochladen. Die Dateien werden auf dem Server gespeichert und stehen sofort zur Verfügung.</li>
|
||
<li><strong class="font-medium text-zinc-800 dark:text-zinc-200">Externe URLs:</strong> Für Videos über 50 MB (z. B. 4K-Showroom-Rundgänge) können Sie einen Freigabe-Link von Google Drive, OneDrive oder anderen Cloud-Diensten hinterlegen. Diese URL wird wie ein normales Medium in der Mediathek verwaltet und kann genauso in Versionen eingebunden werden.</li>
|
||
<li><strong class="font-medium text-zinc-800 dark:text-zinc-200">Sammlungen:</strong> Ordnen Sie Medien in Sammlungen wie <em>immobilien</em>, <em>moebel</em> oder <em>brand</em>, um bei vielen Dateien den Überblick zu behalten.</li>
|
||
<li><strong class="font-medium text-zinc-800 dark:text-zinc-200">Medienauswahl im Editor:</strong> Beim Bearbeiten einer Version erscheint ein „Aus Mediathek"-Button. Darüber öffnen Sie die Medienauswahl und können bestehende Medien wählen oder direkt neue hochladen.</li>
|
||
</ul>
|
||
</div>
|
||
|
||
<flux:separator />
|
||
|
||
<div>
|
||
<flux:heading size="sm" class="mb-1 flex items-center gap-2">
|
||
<flux:icon name="rectangle-group" class="size-4 text-purple-500" />
|
||
Versionen & Versions-Typen
|
||
</flux:heading>
|
||
<p>
|
||
Eine <strong class="font-medium text-zinc-800 dark:text-zinc-200">Version</strong> ist ein Content-Paket mit einem bestimmten Typ.
|
||
Der Typ bestimmt, welche Art von Inhalten hinzugefügt werden können:
|
||
</p>
|
||
<ul class="mt-2 ml-5 list-disc space-y-1">
|
||
<li>
|
||
<strong class="font-medium text-zinc-800 dark:text-zinc-200">Video-Display</strong> –
|
||
Für Video-Playlists mit optionalem Footer. Inhalte: <em>Videos</em> (Dateiname, Titel, Position/Ausschnitt) und <em>Footer-Zeilen</em> (Überschrift, Unterzeile, optionaler QR-Code-Link).
|
||
</li>
|
||
<li>
|
||
<strong class="font-medium text-zinc-800 dark:text-zinc-200">B2in Display</strong> –
|
||
Für Medien-Rotation (Bilder und Videos) im Marken-Design. Inhalte: <em>Media-Items</em> mit Kategorie (Immobilien / Möbel), Überschrift, Unterzeile und Anzeigedauer. Unterstützt Light-/Dark-Theme.
|
||
</li>
|
||
<li>
|
||
<strong class="font-medium text-zinc-800 dark:text-zinc-200">Angebote</strong> –
|
||
Für Produkt-Slides im Angebotsformat. Verschiedene Slide-Layouts: Intro, Produkt-Hero, Produkt-Details und Impuls-Slides mit Preisen, Badges und QR-Codes.
|
||
</li>
|
||
</ul>
|
||
<p class="mt-2">
|
||
Innerhalb einer Version können Sie beliebig viele Inhalte anlegen, die Reihenfolge per Hoch/Runter-Sortierung festlegen und einzelne Einträge aktivieren oder deaktivieren.
|
||
</p>
|
||
</div>
|
||
|
||
<flux:separator />
|
||
|
||
<div>
|
||
<flux:heading size="sm" class="mb-1 flex items-center gap-2">
|
||
<flux:icon name="tv" class="size-4 text-blue-500" />
|
||
Displays & Playlists
|
||
</flux:heading>
|
||
<p>
|
||
Ein <strong class="font-medium text-zinc-800 dark:text-zinc-200">Display</strong> repräsentiert einen physischen Bildschirm im Showroom.
|
||
</p>
|
||
<ul class="mt-2 ml-5 list-disc space-y-1">
|
||
<li><strong class="font-medium text-zinc-800 dark:text-zinc-200">Versions-Zuweisung:</strong> Jedem Display können Sie eine oder mehrere Versionen zuordnen. Die Versionen werden in der festgelegten Reihenfolge als Playlist abgespielt.</li>
|
||
<li><strong class="font-medium text-zinc-800 dark:text-zinc-200">Aktiv/Inaktiv:</strong> Über den Aktiv-Status können Sie einzelne Displays vorübergehend deaktivieren, ohne die Konfiguration zu verlieren.</li>
|
||
<li><strong class="font-medium text-zinc-800 dark:text-zinc-200">API-Anbindung:</strong> Jedes Display ruft seine Inhalte über eine JSON-API ab (<code class="text-xs bg-zinc-100 dark:bg-zinc-800 px-1 py-0.5 rounded">/api/display/{id}/config</code>). Änderungen werden beim nächsten Abruf automatisch übernommen.</li>
|
||
</ul>
|
||
</div>
|
||
|
||
<flux:separator />
|
||
|
||
<div>
|
||
<flux:heading size="sm" class="mb-1 flex items-center gap-2">
|
||
<flux:icon name="device-tablet" class="size-4 text-teal-500" />
|
||
Info-Tablet
|
||
</flux:heading>
|
||
<p>
|
||
Das Info-Tablet zeigt Besuchern am Showroom-Eingang den aktuellen Status und die Öffnungszeiten.
|
||
</p>
|
||
<ul class="mt-2 ml-5 list-disc space-y-1">
|
||
<li>
|
||
<strong class="font-medium text-zinc-800 dark:text-zinc-200">Store-Status:</strong>
|
||
Vier Modi stehen zur Verfügung – <em>Automatisch</em> (berechnet den Status aus den Öffnungszeiten), <em>Geschlossen</em> (manuell), <em>Hinweis</em> (eigene Nachricht) und <em>Warnung</em> (dringende Nachricht).
|
||
</li>
|
||
<li>
|
||
<strong class="font-medium text-zinc-800 dark:text-zinc-200">Öffnungszeiten:</strong>
|
||
Für jeden Wochentag (Montag–Sonntag) können individuelle Öffnungs- und Schließzeiten gepflegt werden. Tage ohne Zeiten gelten als geschlossen.
|
||
</li>
|
||
<li>
|
||
<strong class="font-medium text-zinc-800 dark:text-zinc-200">Tages-Overrides:</strong>
|
||
Für Sonderfälle (z. B. früher schließen) können Sie die Zeiten für den heutigen Tag überschreiben. Diese Überschreibungen werden automatisch um Mitternacht zurückgesetzt.
|
||
</li>
|
||
<li>
|
||
<strong class="font-medium text-zinc-800 dark:text-zinc-200">Kontaktdaten & Termine:</strong>
|
||
Telefonnummer, E-Mail-Adresse und der nächste Termin werden auf dem Tablet angezeigt und können hier zentral gepflegt werden.
|
||
</li>
|
||
</ul>
|
||
</div>
|
||
|
||
<flux:separator />
|
||
|
||
<div>
|
||
<flux:heading size="sm" class="mb-1 flex items-center gap-2">
|
||
<flux:icon name="arrow-path" class="size-4 text-zinc-500" />
|
||
Typischer Workflow
|
||
</flux:heading>
|
||
<ol class="mt-2 ml-5 list-decimal space-y-1">
|
||
<li><strong class="font-medium text-zinc-800 dark:text-zinc-200">Version erstellen</strong> – Unter „Versionen" eine neue Version mit passendem Typ anlegen (z. B. „Frühling 2026 Video").</li>
|
||
<li><strong class="font-medium text-zinc-800 dark:text-zinc-200">Inhalte hinzufügen</strong> – In der Version Videos, Medien oder Slides anlegen, Reihenfolge festlegen und aktivieren.</li>
|
||
<li><strong class="font-medium text-zinc-800 dark:text-zinc-200">Display zuweisen</strong> – Unter „Displays" die Version einem physischen Bildschirm zuordnen.</li>
|
||
<li><strong class="font-medium text-zinc-800 dark:text-zinc-200">Fertig</strong> – Das Display lädt die neuen Inhalte automatisch über die API.</li>
|
||
</ol>
|
||
</div>
|
||
|
||
</div>
|
||
</flux:card>
|
||
</div>
|