20 KiB
Cabinet Displays – Entwicklungskonzept
Datum: 11.05.2026 Autor: Konzept-Phase Status: Freigegeben – Umsetzung gestartet am 11.05.2026
1. Ausgangslage
1.1 Was es heute gibt
Im Admin-Portal unter portal.b2in.test/admin/cms/ existiert der Bereich Store Displays mit folgenden Unterseiten:
| Pfad | Bedeutung |
|---|---|
cms/display-dashboard |
Übersicht / Einstieg |
cms/display-media |
Mediathek (eigene Display-Mediathek, getrennt von Flux CMS) |
cms/display-modules |
Inhalts-Module |
cms/display-modules/{id}/edit |
Editor für ein Modul |
cms/displays |
Physische Displays + Playlist-Zuweisung |
cms/cabinet-tablet |
Info-Tablet (Öffnungszeiten/Status) |
1.2 Aktuelles Datenmodell
displays (5 Datensätze live)
└── 1:n display_playlists (Live/Entwurf)
└── 1:n display_playlist_items (sort_order = Playlist-Reihenfolge)
└── display_versions (technisch), fachlich Module
├── type: video-display | b2in | offers
├── settings: JSON
└── 1:n display_version_items
├── item_type: video | footer | media | slide
└── content: JSON
1.3 Echte Live-Daten (Stand heute)
5 Displays:
| # | Name | Standort | Playlist (Reihenfolge) |
|---|---|---|---|
| 1 | Display 1 – Eingang | Schaufenster rechts | Schaufenster Video → B2In Immobilien Dark |
| 2 | Display 2 – Mitte | Schaufenster Mitte | Angebote Schauraum |
| 3 | Display 3 – Rechts | Schaufenster rechts | B2In Immobilien Light → Angebote Schauraum → Schaufenster Video |
| 4 | Display 4 – Innen | Schauraum | B2In Immobilien Dark |
| 5 | Kundenstopper | Draußen | Sideboard Goya → Schaufenster Video |
5 Versionen:
| # | Name | Typ | Items | Verwendet in |
|---|---|---|---|---|
| 1 | Schaufenster Video | video-display | 6 | 3 Displays |
| 2 | B2In Immobilien Dark | b2in | 3 | 2 Displays |
| 3 | B2In Immobilien Light | b2in | 3 | 1 Display |
| 4 | Angebote Schauraum | offers | 4 | 2 Displays |
| 5 | Sideboard Goya | offers | 1 | 1 Display |
→ Dieser Bestand muss migriert, nicht verworfen werden.
1.4 Beobachtete Schwächen
- Begriff „Versionen" missverständlich – sind faktisch wiederverwendbare Inhalts-Bausteine, nicht „Versionen" im Sinne von „Revisionen".
- Kein Live/Entwurfs-Trennung pro Display – jede Änderung an einem Baustein wirkt sofort auf alle Displays, die ihn nutzen. Es gibt kein „in Arbeit, noch nicht veröffentlichen".
- Kein dediziertes Test-Display – will man eine Konfiguration vorab prüfen, muss man sie produktiv ausspielen.
- Keine visuelle Vorschau im Editor – beim Bearbeiten eines Slides sieht man nicht, wie er auf dem Display aussehen wird.
- Hilfetexte nur im Dashboard – Listen- und Editor-Seiten haben kaum Erklärungen.
- Versions-Settings JSON-Wirrwarr – Theme, Footer, Transition etc. werden je Typ unterschiedlich gepflegt, ohne sichtbares Schema.
2. Zielbild
„Pro Display gibt es einen klar veröffentlichten Stand (Live) und optional einen Entwurf, der gefahrlos vorbereitet und getestet werden kann. Veröffentlichen ist ein bewusster Schritt. Module sind wiederverwendbare Bausteine. Beim Bearbeiten sieht man sofort, wie es auf dem Display aussieht."
2.1 Neue Begriffswelt
| Alt | Neu | Erklärung |
|---|---|---|
| Display-Version | Modul | Wiederverwendbarer Inhalts-Baustein eines Typs (Video, B2in, Offers). |
| Display | Display (unverändert) | Physischer Bildschirm im Showroom. |
| Display ↔ Version Pivot | Bespielung (Playlist) | Geordnete Liste von Modulen pro Display. Existiert pro Display in zwei Ausprägungen: Live und Entwurf. |
| Mediathek | Display-Mediathek (unverändert) | Bilder/Videos für Displays. |
| Info-Tablet | Info-Tablet (unverändert) | Eingangs-Tablet mit Öffnungszeiten. |
Routen wurden entsprechend umbenannt: display-versions → display-modules. Die Übergangs-Redirects wurden in Phase 7 entfernt.
2.2 Neues mentales Modell
┌──────────────────────────────────────────────────────────────┐
│ MODULE (Bausteine) │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Modul A │ │ Modul B │ │ Modul C │ ... │
│ │ Type:Video │ │ Type:B2in │ │ Type:Offers │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
└────────┬─────────────────┬─────────────────┬─────────────────┘
│ │ │
▼ ▼ ▼
┌──────────────────────────────────────────────────────────────┐
│ DISPLAYS │
│ ┌────────────────────┐ ┌────────────────────┐ │
│ │ Display 1 │ │ Display 2 │ │
│ │ ┌────────────────┐ │ │ ┌────────────────┐ │ │
│ │ │ ▶ Live │ │ │ │ ▶ Live │ │ │
│ │ │ Modul A→B │ │ │ │ Modul C │ │ │
│ │ ├────────────────┤ │ │ ├────────────────┤ │ │
│ │ │ ✎ Entwurf │ │ │ │ (kein Entwurf) │ │ │
│ │ │ Modul A→B→C │ │ │ └────────────────┘ │ │
│ │ └────────────────┘ │ │ │ │
│ └────────────────────┘ └────────────────────┘ │
│ │
│ ┌────────────────────┐ │
│ │ Test-Display │ ← neu, technisch ein Display │
│ │ Beliebig kombi- │ mit Sonder-Flag is_test=true │
│ │ nierbar zum Testen │ │
│ └────────────────────┘ │
└──────────────────────────────────────────────────────────────┘
2.3 Festlegungen aus Klärung
- Begriff: „Modul" (statt Version/Baustein).
- Versionierung: Pro Display-Bespielung (Live + Entwurf), nicht pro Modul.
- Vorlagen-Konzept: Ein eigenes Test-Display, auf dem beliebige Module/Bespielungen ausprobiert werden können.
- Vorschau: alle drei Varianten – Inline-Thumbnail je Item, Iframe-Live-Preview neben Editor, Vollbild-Vorschau auf Knopfdruck.
3. Datenmodell – Soll-Zustand
3.1 Neue Tabellen
display_playlists
Pro Display max. zwei Datensätze: eine published und eine draft.
Schema::create('display_playlists', function (Blueprint $table) {
$table->id();
$table->foreignId('display_id')->constrained()->cascadeOnDelete();
$table->enum('status', ['published', 'draft']);
$table->timestamp('published_at')->nullable();
$table->foreignId('published_by')->nullable()->constrained('users')->nullOnDelete();
$table->text('notes')->nullable();
$table->timestamps();
$table->unique(['display_id', 'status']);
});
display_playlist_items
Ersetzt funktional display_display_version.
Schema::create('display_playlist_items', function (Blueprint $table) {
$table->id();
$table->foreignId('display_playlist_id')->constrained()->cascadeOnDelete();
$table->foreignId('display_version_id')->constrained()->cascadeOnDelete();
$table->unsignedInteger('sort_order')->default(0);
$table->timestamps();
$table->index(['display_playlist_id', 'sort_order']);
});
3.2 Anpassungen bestehender Tabellen
displays
is_testBOOLEAN (default false) – kennzeichnet das Test-Display.preview_tokenSTRING (nullable, unique) – wird beim Erstellen einer Draft-Bespielung generiert; ermöglicht einecabinet.b2in.eu/preview/{token}Adresse für Tablet-Tests ohne Login.
display_versions → begrifflich „Module"
Tabelle und Modell bleiben technisch erhalten (keine Rename-Migration → keine Risiken bei Live-Daten). Nur die UI-Bezeichnung wechselt zu „Modul".
3.3 Daten-Migration
Eine einmalige Migration überführt alle aktuellen display_display_version-Einträge in das neue Schema:
für jedes Display D:
erstelle display_playlists (display_id=D.id, status='published', published_at=now())
für jeden Eintrag aus display_display_version (display_id=D.id), sortiert nach sort_order:
erstelle display_playlist_items (...)
display_display_version-Tabelle wurde in Phase 7 dropped.
Ergebnis nach Migration: Alle 5 Displays haben eine Live-Bespielung, kein Entwurf. Konsumenten-API liefert exakt das gleiche wie heute.
4. API – Anpassungen
4.1 Endpunkte
| Endpunkt | Bedeutung | Verhalten |
|---|---|---|
GET /api/display/{display}/config |
Live-Konfiguration | Liest aus display_playlists mit Status published. Unverändert für die im Showroom installierten Player. |
GET /api/display/{display}/check |
Modifikationszeit für Polling | Bezieht sich auf die Published-Playlist. |
GET /api/display/preview/{token} (neu) |
Vorschau-Konfiguration | Liefert die Draft-Playlist eines Displays. Token aus displays.preview_token. Kein Login nötig, aber Token rotierbar. |
4.2 Backward Compatibility
Das Antwortschema bleibt identisch (playlist[], updated_at). Player im Showroom muss nicht angefasst werden.
5. UI / UX – Neu
5.1 Navigation
Store Displays
├── Übersicht (Dashboard, klarer Workflow-Erklärung)
├── Mediathek
├── Module ← war „Versionen"
├── Displays ← Live + Entwurf je Display
└── Info-Tablet
5.2 Display-Liste (Hauptansicht)
Pro Display eine Karte mit zwei Spalten:
┌─────────────────────────────────────────────────────────────┐
│ ● Display 1 – Eingang Schaufenster rechts │
│ ─────────────────────────────────────────────────────────── │
│ ▶ LIVE ✎ ENTWURF │
│ [Modul Schaufenster Video] (kein Entwurf) │
│ [Modul B2In Immo Dark] │
│ Aktualisiert: 02.05.2026 [Entwurf anlegen] │
│ [Vorschau] [API] │
│ │
└─────────────────────────────────────────────────────────────┘
Wenn Entwurf existiert:
▶ LIVE ✎ ENTWURF
[Modul A] [Modul A]
[Modul B] [Modul B]
[Modul C] ← neu
Aktualisiert: 02.05.2026 Test-URL: …xY12pq
[Vorschau] [API] [Bearbeiten] [Veröffentlichen] [Verwerfen]
5.3 Entwurf-Editor
Nur ein Modal/Sub-Page, kein weiteres komplexes UI:
- Module hinzufügen aus Bibliothek (Multi-Select)
- Sortierung per ↑/↓
- Sofortige Iframe-Vorschau rechts (lädt
/preview/{token}aus aktuellem Entwurfsstand, auto-reload bei Änderung) - Veröffentlichen-Button mit Confirm
- Verwerfen → löscht Draft
5.4 Test-Display
Eigene Top-Level-Kachel „Test-Display" im Display-Dashboard. Technisch ein normaler Display-Datensatz mit is_test=true. Eigene URL /_cabinet/display/?id=TEST_ID für lokales Vorab-Anschauen.
In der Liste ist es deutlich abgesetzt (anderes Icon, gelbe Border), damit es nicht versehentlich als Produktion-Display verwendet wird.
5.5 Modul-Editor – drei Vorschau-Stufen
Stufe A: Inline-Mini-Preview je Item
In den Listen version-editor-{video|b2in|offers}.blade.php: pro Item-Zeile ein Thumbnail/Mini-Layout neben Headline und Subline. Konkret:
- Video-Item: Video-Thumbnail (erstes Frame oder Standbild) + Filename-Badge
- Footer-Item: kleines Footer-Layout mit Headline+Subline+QR-Stub
- Media-Item: Bild-Thumbnail + Headline-Overlay-Skizze
- Slide-Item: Mini-Skizze des Slide-Layouts (Hero/Details/Impulse erkennbar)
Stufe B: Iframe-Live-Preview neben Editor
Beim Bearbeiten eines Moduls splittet sich die Ansicht:
┌────────────────────────┬──────────────────────────┐
│ Items / Form │ 9:16 Iframe │
│ (links, scrollbar) │ Live-Preview aus │
│ │ /preview/module/{id} │
└────────────────────────┴──────────────────────────┘
Die Iframe-URL lädt das Modul isoliert (ohne Display-Bespielung) – dafür legen wir an: GET /preview/module/{module} (gerendert via vorhandene Display-Player-Templates, aber im Single-Module-Modus).
Stufe C: Vollbild-Vorschau auf Knopfdruck
Button „Vollbild-Vorschau" → öffnet /preview/module/{module} in neuem Tab in voller 9:16-Größe.
5.6 Dashboard
Bleibt strukturell wie heute, aber mit:
- aktualisierten Bezeichnungen
- zusätzlicher Zeile pro Display: Live/Entwurf-Stand
- prominentem „Test-Display"-Eintrag
- ausgebautem Workflow-Block (1. Modul anlegen → 2. Display-Entwurf bauen → 3. Testen → 4. Veröffentlichen)
6. Authentifizierung / Berechtigungen
- Alle Admin-Routen weiterhin
auth + partner.setup. - Vorschau-Routen
/preview/{token}und/preview/module/{id}sind öffentlich, aber per Token bzw. nur auf nicht-aktiven Daten zugreifbar. Token rotierbar per Button im Display-Detail.
7. Tests (Pflicht laut Projekt-Regeln)
| Test | Datei |
|---|---|
| Bestehende Versions-API funktioniert weiter | tests/Feature/DisplayVersionApiTest.php (anpassen) |
Migration display_display_version → display_playlists ist verlustfrei |
tests/Feature/DisplayPlaylistMigrationTest.php (neu) |
| Neuer Workflow: Entwurf anlegen → bearbeiten → veröffentlichen | tests/Feature/DisplayPublishWorkflowTest.php (neu) |
| Vorschau via Token ohne Login erreichbar | tests/Feature/DisplayPreviewTokenTest.php (neu) |
| Test-Display verhält sich korrekt | tests/Feature/DisplayTestFlagTest.php (neu) |
| Modul-Preview-Endpoint funktioniert | tests/Feature/ModulePreviewTest.php (neu) |
Bestehende Tests aus DisplayListTest, DisplayVersionTest, DisplayVersionApiTest werden angepasst – nicht ersetzt.
8. Phasen-Roadmap
Jede Phase liefert ein in sich getestetes, deploybares Inkrement.
Phase 1 – Datenmodell (Tag 1)
- Migration
display_playlists+display_playlist_items - Migration:
displays.is_test,displays.preview_token - Daten-Migration aus
display_display_version - Modelle
DisplayPlaylist,DisplayPlaylistItem - Relations & Factories
- Test: Daten-Migration verlustfrei
Phase 2 – API & Player (Tag 1)
DisplayConfigControllerliest aus Published-Playlist- Neuer
DisplayPreviewControllerfür Token-Preview - Neuer
ModulePreviewControllerfür Modul-Einzelvorschau - Tests aktualisiert + neu
Phase 3 – Admin-UI: Displays-Liste (Tag 2)
- Liste mit Live + Entwurf je Display
- Aktionen: Entwurf anlegen / verwerfen / veröffentlichen
- Test-Display als eigene, hervorgehobene Karte
- Vorschau-Buttons (Live + Entwurf)
Phase 4 – Admin-UI: Entwurf-Editor (Tag 2)
- Modal/Page zum Bearbeiten der Entwurf-Playlist (Module hinzufügen, sortieren, entfernen)
- Iframe-Vorschau rechts
- Auto-Reload der Iframe bei Änderung
Phase 5 – Modul-Editor Vorschau-Ausbau (Tag 3)
- Stufe A: Inline-Mini-Preview je Item-Typ (Video/Footer/Media/Slide)
- Stufe B: Iframe-Live-Preview rechts neben Modul-Editor
- Stufe C: Vollbild-Vorschau-Button
- Player-Templates: Single-Module-Modus
Phase 6 – Umbenennung & Onboarding (Tag 3)
- Routen:
display-versions→display-modules - Komponenten / Views umbenennen
- Dashboard-Texte / Hilfe-Bausteine aktualisieren
- Tooltips an Schlüsselstellen
Phase 7 – Aufräumen (Tag 4)
display_display_version-Tabelle dropped- Alte Routen entfernt
- Entwicklerdoku in
dev/displays-11-05-2026aktualisiert - Vollständiger Test-Run
9. Risiken & Migrationsschutz
| Risiko | Maßnahme |
|---|---|
| Live-Displays brechen | API-Schema unverändert; Daten-Migration zuerst, Live-Tests vorher |
| Konflikte bei parallelen Edits | Optimistic Lock per updated_at auf Playlist (vorerst nicht zwingend) |
| Token-Preview öffentlich | Token mind. 24 Zeichen, rotierbar, nur Draft sichtbar |
| Verwirrung „Modul" vs „Version" | 301 von alten Routen, Hinweis im Dashboard zur Umbenennung |
10. Offene Fragen / Klärungsbedarf vor Phase 1
Bevor wir Phase 1 starten, sind diese Punkte zu bestätigen:
- „Test-Display" – soll es fest „1" geben (Singleton) oder beliebig viele? (Empfehlung: genau 1 angelegt durch Seeder, weitere optional erstellbar.)
- Verwerfen vs. „auf Live zurücksetzen" – beim Verwerfen eines Entwurfs: einfach löschen, oder soll der Entwurf auf den Stand der Live-Bespielung zurückgesetzt werden? (Empfehlung: löschen, beim Neuanlegen wird der Stand aus Live geklont.)
- Modul gleichzeitig in Live und Entwurf eines Displays – darf vorkommen (z.B. Reihenfolge ändern). Module bleiben dabei shared zwischen Displays. Wirken Modul-Änderungen sofort auch auf Live-Displays? Ja (technisch das gleiche Datum). Falls das nicht gewünscht ist, müssten Module ebenfalls versioniert werden – das ist Out-Of-Scope für diesen Konzeptstand.
- Player-Cache – nach Veröffentlichen soll der Polling-Mechanismus (60 s) ausreichen, oder brauchen wir einen Push-Refresh? (Empfehlung: 60 s reicht.)
11. Was passiert mit dem alten Pfad cms/cabinet?
Die ursprüngliche CabinetDisplay-Komponente (mit DisplayVideo + DisplayFooterContent) ist Legacy und gehört zur alten 1-Display-Welt unter /_cabinet/index.html. Sie wird im Konzeptzeitraum nicht angefasst, aber im Anschluss empfohlen zu entfernen, sobald alle Live-Displays auf /_cabinet/display/?id=… umgeschaltet sind.
12. Nächster Schritt
→ Das Konzept wurde am 11.05.2026 freigegeben und die Umsetzung wurde gestartet.
→ Der laufende Fortschritt wird in 01-status.md gepflegt.