# 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-versions` | Inhalts-„Versionen" |
| `cms/display-versions/{id}/edit` | Editor für eine Version |
| `cms/displays` | Physische Displays + Playlist-Zuweisung |
| `cms/cabinet-tablet` | Info-Tablet (Öffnungszeiten/Status) |

### 1.2 Aktuelles Datenmodell

```
displays  (5 Datensätze live)
└── m:n via display_display_version (sort_order = Playlist-Reihenfolge)
        └── display_versions (5 Datensätze live)
                ├── type: video-display | b2in | offers
                ├── settings: JSON
                └── 1:n display_version_items (17 Datensätze live)
                        ├── 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

1. **Begriff „Versionen" missverständlich** – sind faktisch wiederverwendbare *Inhalts-Bausteine*, nicht „Versionen" im Sinne von „Revisionen".
2. **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".
3. **Kein dediziertes Test-Display** – will man eine Konfiguration vorab prüfen, muss man sie produktiv ausspielen.
4. **Keine visuelle Vorschau im Editor** – beim Bearbeiten eines Slides sieht man nicht, wie er auf dem Display aussehen wird.
5. **Hilfetexte nur im Dashboard** – Listen- und Editor-Seiten haben kaum Erklärungen.
6. **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 werden entsprechend umbenannt: `display-versions` → `display-modules`.

### 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

1. Begriff: **„Modul"** (statt Version/Baustein).
2. Versionierung: **Pro Display-Bespielung** (Live + Entwurf), *nicht* pro Modul.
3. Vorlagen-Konzept: **Ein eigenes Test-Display**, auf dem beliebige Module/Bespielungen ausprobiert werden können.
4. 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`.

```php
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`.

```php
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_test` BOOLEAN (default false) – kennzeichnet das **Test-Display**.
- `preview_token` STRING (nullable, unique) – wird beim Erstellen einer Draft-Bespielung generiert; ermöglicht eine `cabinet.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 bleibt vorerst → wird 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)
- [ ] `DisplayConfigController` liest aus Published-Playlist
- [ ] Neuer `DisplayPreviewController` für Token-Preview
- [ ] Neuer `ModulePreviewController` fü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` (mit 301-Redirect)
- [ ] 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
- [ ] `DISPLAY_CMS_README.md` aktualisiert (in `dev/` ablegen)
- [ ] 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:

1. **„Test-Display"** – soll es **fest „1"** geben (Singleton) oder beliebig viele? *(Empfehlung: genau 1 angelegt durch Seeder, weitere optional erstellbar.)*
2. **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.)*
3. **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.
4. **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.
