10-04-2026

This commit is contained in:
Kevin Adametz 2026-04-10 17:18:17 +02:00
parent 4d6b4930b2
commit 4bb89aad8c
836 changed files with 52961 additions and 5950 deletions

View file

@ -0,0 +1,294 @@
<?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.&nbsp;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 (MontagSonntag) 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.&nbsp;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.&nbsp;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>