12-05-2026 admin, Panel Displays

This commit is contained in:
Kevin Adametz 2026-05-12 18:28:38 +02:00
parent 0762e3beac
commit 6a65354f4c
43 changed files with 3273 additions and 410 deletions

View file

@ -0,0 +1,402 @@
# 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.

View file

@ -0,0 +1,278 @@
# Cabinet Displays Implementierungs-Status
> Konzept: siehe `00-entwicklungskonzept.md`. Diese Datei wird je Phase fortgeschrieben.
## Übersicht
| Phase | Inhalt | Status |
|---|---|---|
| **0** | Konzept-Freigabe | ✅ 11.05.2026 |
| **1** | Datenmodell + Daten-Migration | ✅ 11.05.2026 |
| **2** | API & Player (config + preview + module preview) | ✅ 12.05.2026 |
| **3** | Admin-UI: Displays-Liste mit Live/Entwurf | ✅ 12.05.2026 |
| **4** | Admin-UI: Entwurf-Editor (Iframe-Vorschau) | ✅ 12.05.2026 |
| **5** | Modul-Editor: 3-stufige Vorschau | ✅ 12.05.2026 |
| **6** | Umbenennung Versionen → Module + Onboarding | ✅ 12.05.2026 |
| **7** | Aufräumen + alte Pivot-Tabelle entfernen | ⏳ offen |
Legende: ✅ fertig · 🟡 in Arbeit · ⏳ offen · ⛔ blockiert
---
## Defaults aus §10 (Konzept-Freigabe vom 11.05.2026)
Der User hat das Konzept freigegeben. Da keine abweichende Wahl getroffen wurde, gelten die im Konzept empfohlenen Defaults:
1. **Test-Display:** genau 1 Datensatz per Seeder angelegt, weitere können bei Bedarf erstellt werden.
2. **Entwurf verwerfen:** löscht die Draft-Playlist. Beim erneuten Anlegen wird die Reihenfolge aus Live kopiert.
3. **Module bleiben *shared*:** Modul-Änderungen wirken sofort auf alle Displays, die das Modul live einsetzen. Modul-eigene Versionierung ist *out of scope*.
4. **Polling-Mechanismus** der Player (alle 60 s) reicht; kein Push-Refresh.
---
## Live-Roll-out Hinweise
Auf dem Live-Server reicht für jede Phase:
```bash
git pull
composer install --no-dev --optimize-autoloader # falls neue Composer-Deps
php artisan migrate --force # bringt neue Migrations ein
php artisan config:cache # Caches frisch
php artisan view:clear
```
Alle strukturellen Änderungen liegen **ausschließlich** in `database/migrations/` als datierte Dateien vor auf der Live-DB reicht `php artisan migrate --force`. Datenverlust gibt es nicht: bestehende Pivot-Daten aus `display_display_version` werden in die neue Struktur überführt.
---
## Phase 1 Datenmodell
**Ziel:** neue Tabellen `display_playlists` + `display_playlist_items`, erweiterte `displays`-Spalten (`is_test`, `preview_token`), vollständige verlustfreie Migration der heutigen Pivot-Daten.
**Dateien:**
- `database/migrations/2026_05_11_*_create_display_playlists_table.php`
- `database/migrations/2026_05_11_*_create_display_playlist_items_table.php`
- `database/migrations/2026_05_11_*_add_test_flag_and_preview_token_to_displays_table.php`
- `database/migrations/2026_05_11_*_migrate_pivot_to_display_playlists.php`
- `app/Models/DisplayPlaylist.php`
- `app/Models/DisplayPlaylistItem.php`
- `app/Models/Display.php` (neue Relations, alte `versions()` bleibt kompatibel)
- `database/factories/DisplayPlaylistFactory.php`
- `database/factories/DisplayPlaylistItemFactory.php`
- `tests/Feature/DisplayPlaylistMigrationTest.php`
**Wichtig:** Die alte Tabelle `display_display_version` und die Relation `Display::versions()` bleiben in dieser Phase **erhalten** (Removal erst in Phase 7), damit keine bestehenden Funktionen brechen.
### Stand 11.05.2026 ✅ abgeschlossen
#### Gelieferte Migrationen (Live-Reihenfolge)
| Reihenfolge | Datei | Zweck |
|---|---|---|
| 1 | `2026_05_11_113300_add_test_flag_and_preview_token_to_displays_table.php` | `displays.is_test` + `displays.preview_token` (unique) |
| 2 | `2026_05_11_113310_create_display_playlists_table.php` | Neue Tabelle für Live/Entwurfs-Bespielung pro Display |
| 3 | `2026_05_11_113320_create_display_playlist_items_table.php` | Geordnete Module pro Bespielung |
| 4 | `2026_05_11_113330_migrate_pivot_to_display_playlists.php` | Übernimmt bestehende Pivot-Daten als Published-Playlists (idempotent) |
Die Daten-Migration ist **idempotent** wenn pro Display bereits eine Published-Playlist existiert, wird sie übersprungen. Die alte Pivot-Tabelle `display_display_version` bleibt erhalten.
#### Neue Modelle & Relations
- `App\Models\DisplayPlaylist` mit Scopes `published()`/`draft()`, Relation `modules()` (geordnet)
- `App\Models\DisplayPlaylistItem`
- `App\Models\Display` erweitert um `playlists()`, `livePlaylist()`, `draftPlaylist()`, `liveModules()`, `ensurePreviewToken()`
- `Display::versions()` ist als `@deprecated` markiert, bleibt aber funktional
#### Tests
```text
tests/Feature/DisplayPlaylistMigrationTest.php 11 passed
tests/Feature/DisplayListTest.php ok
tests/Feature/DisplayVersionTest.php ok
tests/Feature/DisplayVersionApiTest.php ok
tests/Feature/DisplayMediaTest.php ok
```
Insgesamt 67 grüne Tests rund um Displays (11 neu + 56 bestand). Keine Linter-Warnungen, Pint clean.
#### Roll-out auf Live
```bash
git pull
php artisan migrate --force
```
Reihenfolge stimmt durch die Timestamps automatisch. Daten-Migration ist idempotent, kann beliebig oft laufen.
---
## Phase 2 API & Player
**Ziel:** bestehende Player-Konfiguration liest künftig aus der Published-Playlist. Zusätzlich gibt es öffentliche Vorschau-Endpunkte für Display-Entwürfe per Token und einzelne Module. Das JSON-Schema bleibt rückwärtskompatibel (`playlist[]`, `updated_at`).
### Stand 12.05.2026 ✅ abgeschlossen
Geplante/aktuelle Dateien:
- `app/Services/DisplayPlaylistConfigBuilder.php`
- `app/Http/Controllers/Api/DisplayVersionApiController.php`
- `app/Http/Controllers/Api/DisplayPreviewController.php`
- `app/Http/Controllers/Api/ModulePreviewController.php`
- `routes/domains.php`
- `public/_cabinet/display/index.html`
- `tests/Feature/DisplayVersionApiTest.php`
Umsetzung:
- Live-Config: `GET /api/display/{display}/config` liest `livePlaylist`
- Live-Check: `GET /api/display/{display}/check` bezieht sich auf die Published-Playlist
- Draft-Preview: `GET /api/display/preview/{token}` liefert die Draft-Playlist
- Modul-Preview: `GET /api/display/module/{module}/preview` liefert ein Einzelmodul im Player-Schema
- Player-Preview-Seiten: `/preview/{token}` und `/preview/module/{module}`
#### Tests
```text
tests/Feature/DisplayVersionApiTest.php 13 passed
tests/Feature/DisplayPlaylistMigrationTest.php ok
```
Insgesamt 24 grüne Tests für Phase 2 und die Playlist-Grundlage. Pint clean.
#### Hinweis
In der lokalen Umgebung musste der alte Route-Cache einmal mit `php artisan route:clear` geleert werden, damit die neuen Preview-Routen sichtbar wurden. Für Live bleibt beim Roll-out `php artisan config:cache` bzw. ein frischer Route-/Config-Cache relevant.
---
## Phase 3 Admin-UI: Displays-Liste mit Live/Entwurf
**Ziel:** Die Display-Liste zeigt pro physischem Display den Live-Stand und optionalen Entwurf nebeneinander. Entwürfe können aus Live angelegt, verworfen und veröffentlicht werden. Das Test-Display ist sichtbar hervorgehoben.
### Stand 12.05.2026 ✅ abgeschlossen
Dateien:
- `app/Livewire/Admin/Cms/DisplayList.php`
- `resources/views/livewire/admin/cms/display-list.blade.php`
- `tests/Feature/DisplayListTest.php`
Umsetzung:
- Display-Karten zeigen `Live` und `Entwurf` als zwei getrennte Spalten
- Der globale Bearbeiten-Button im Display-Kopf wurde entfernt; Live und Entwurf haben jeweils eigene Bearbeiten-Buttons
- Die Live-Card zeigt die Player-URL direkt als kopierbares Feld; der API-Link ist weniger prominent unten rechts platziert
- Live-Spalte nutzt `livePlaylist.modules`
- Entwurf-Spalte nutzt `draftPlaylist.modules`
- Live-Bearbeitung speichert nur die Published-Playlist
- Entwurfs-Bearbeitung speichert nur die Draft-Playlist und erzeugt bei Bedarf den Preview-Token
- Die Modul-Auswahl im Bearbeiten-Dialog zeigt nur noch Module, die in der aktuell bearbeiteten Bespielung noch nicht enthalten sind
- Der Plus-Button fügt auch dann das erste verfügbare Modul hinzu, wenn der Select-Wert wegen des Platzhalters noch nicht explizit gesetzt wurde
- Aktion `Entwurf anlegen` kopiert den aktuellen Live-Stand und erzeugt bei Bedarf den Preview-Token
- Aktion `Veröffentlichen` ersetzt die Published-Playlist durch den Draft und synchronisiert die alte Pivot-Tabelle weiterhin kompatibel
- Aktion `Verwerfen` löscht die Draft-Playlist
- Test-Displays (`is_test`) werden in der Liste hervorgehoben
- Der bestehende Bearbeiten-Dialog pflegt die Live-Bespielung weiter und synchronisiert bis Phase 7 zusätzlich `display_display_version`
#### Tests
```text
tests/Feature/DisplayListTest.php 14 passed
```
Die neuen Tests decken Draft anlegen, verwerfen, veröffentlichen, getrennte Live-/Entwurfs-Bearbeitung, gefilterte Modul-Auswahl sowie die Darstellung von Live-/Entwurf-Modulen ab.
---
## Phase 4 Admin-UI: Entwurf-Editor mit Iframe-Vorschau
**Ziel:** Beim Bearbeiten eines Entwurfs ist die Player-Vorschau direkt sichtbar. Moduländerungen im Entwurf werden sofort in die Draft-Playlist geschrieben und laden die Vorschau neu.
### Stand 12.05.2026 ✅ abgeschlossen
Dateien:
- `app/Livewire/Admin/Cms/DisplayList.php`
- `resources/views/livewire/admin/cms/display-list.blade.php`
- `tests/Feature/DisplayListTest.php`
Umsetzung:
- Der Entwurfs-Editor zeigt rechts eine 9:16-Iframe-Vorschau
- Die Iframe-Vorschau sitzt dauerhaft unterhalb der Aktualisieren-Aktion, damit sie im Desktop-Modal nicht mit dem Formular überlappt
- Die Vorschau lädt `/preview/{token}` mit Cache-Bust-Parameter
- Hinzufügen, Entfernen und Sortieren von Modulen persistiert bei Draft-Bearbeitung sofort in `display_playlist_items`
- Nach jeder Moduländerung wird die Iframe-Vorschau per `wire:key` neu aufgebaut
- Vollbild-Vorschau ist aus dem Editor heraus verlinkt
#### Tests
```text
tests/Feature/DisplayListTest.php 21 passed
```
Die Tests decken das Rendern der Vorschau-URL und das sofortige Persistieren von Draft-Änderungen für Preview-Reloads ab.
---
## Phase 5 Modul-Editor: 3-stufige Vorschau
**Ziel:** Module können beim Bearbeiten visuell geprüft werden: kleine Vorschau je Item, eingebettete Player-Vorschau und Vollbild-Vorschau.
### Stand 12.05.2026 ✅ abgeschlossen
Dateien:
- `app/Livewire/Admin/Cms/DisplayVersionEditor.php`
- `resources/views/livewire/admin/cms/display-version-editor.blade.php`
- `resources/views/livewire/admin/cms/partials/version-editor-video.blade.php`
- `resources/views/livewire/admin/cms/partials/version-editor-b2in.blade.php`
- `resources/views/livewire/admin/cms/partials/version-editor-offers.blade.php`
- `tests/Feature/DisplayVersionTest.php`
Umsetzung:
- Modul-Editor zeigt eine 9:16-Iframe-Vorschau über `/preview/module/{module}`
- Vollbild-Vorschau ist direkt aus dem Editor verlinkt
- Das Item-Bearbeiten-Modal ist breiter und zeigt unterhalb der Aktualisieren-Aktion ebenfalls eine 9:16-Iframe-Vorschau
- `Aktualisieren` im Item-Bearbeiten-Modal schließt das Modal nicht mehr, sondern speichert den Inhalt und lädt die Iframe-Vorschau neu
- Die Modal-Aktionen stehen unterhalb der Iframe-Vorschau und bieten `Aktualisieren`, `Abbrechen` und `Schließen`
- Die Vorschau im Item-Bearbeiten-Modal nutzt eine eigene Item-Preview und zeigt nur den aktuell bearbeiteten Slide statt das komplette Modul
- Der Display-Player rendert seinen Viewport strikt als 9:16-Fläche und skaliert Slide-, Footer- und QR-Elemente proportional zur Player-Fläche
- Harte Player-Elemente wie B2in-Headerlogo/Claim/Footer/QR, Offers-Logo/Brandtext/QR-Labels und Video-Footer-QR-Label sind jetzt als Modul-Einstellungen im CMS pflegbar
- Die Modul-Meta-Einstellungen sind als sichtbarer Block unterhalb der Media-/Slide-Liste editierbar; Angebote vererben Footer-Claim und Web/QR-URL automatisch an alle Slides
- Logo-Alt-Text-Felder wurden aus den Modul-Meta-Einstellungen entfernt; Player nutzen weiterhin feste Fallback-Alt-Texte
- Video-Display unterstützt Mediathek-Upload-URLs wie `/storage/...` ohne Legacy-`assets/`-Prefix
- Die Display-Mediathek und der schnelle Media-Picker akzeptieren SVG-Dateien als Bild-Uploads
- Änderungen an Name, Einstellungen, Items, Reihenfolge und Aktiv-Status laden die Modul-Vorschau neu
- Video/Footer/Media/Slide-Listen zeigen Inline-Mini-Previews je Item; Slide-Previews sind größer dargestellt
- Einzelmodul-Vorschau nutzt weiterhin den Player aus Phase 2
---
## Phase 6 Umbenennung Versionen → Module + Onboarding
**Ziel:** Die Admin-UI verwendet den fachlich korrekten Begriff „Module“. Alte URLs bleiben kompatibel und leiten weiter.
### Stand 12.05.2026 ✅ abgeschlossen
Dateien:
- `routes/admin.php`
- `app/Livewire/Admin/Cms/DisplayVersionList.php`
- `resources/views/livewire/admin/cms/display-version-list.blade.php`
- `resources/views/livewire/admin/cms/display-version-editor.blade.php`
- `resources/views/livewire/admin/cms/display-dashboard.blade.php`
- `resources/views/components/layouts/app/sidebar.blade.php`
- `tests/Feature/DisplayVersionTest.php`
Umsetzung:
- Neue Routen: `admin/cms/display-modules` und `admin/cms/display-modules/{displayVersion}/edit`
- Neue Routennamen: `admin.cms.display-modules` und `admin.cms.display-module-edit`
- Alte `display-versions`-Routen bleiben erhalten und leiten per 301 auf die Modul-Routen weiter
- Sidebar, Dashboard, Listen- und Editor-Texte verwenden „Module“
- Technische Modell-/Klassennamen bleiben bis Phase 7 kompatibel bei `DisplayVersion`
#### Tests
```text
tests/Feature/DisplayVersionTest.php ok
tests/Feature/DisplayVersionApiTest.php ok
tests/Feature/DisplayListTest.php ok
tests/Feature/DisplayPlaylistMigrationTest.php ok
```
Insgesamt 64 grüne Tests für Phasen 5/6 und die angrenzenden Display-Flows. Pint clean.

View file

@ -0,0 +1,16 @@
# Cabinet Displays Re-Konzeption 11.05.2026
Dieser Ordner enthält das laufende Entwicklungskonzept und den Status für die Überarbeitung des `Cabinet Displays`-Bereichs im Admin-Portal.
## Dokumente
| Datei | Inhalt |
|---|---|
| `00-entwicklungskonzept.md` | Freigegebenes Konzept (Ist-Analyse, Zielbild, Datenmodell, Phasen-Plan) |
| `01-status.md` | Fortschritt je Phase |
## Aktueller Status
**11.05.2026** Konzept freigegeben und Umsetzung gestartet. Phase 1 (Datenmodell + Migration) wurde abgeschlossen.
**12.05.2026** Weiterarbeit mit Phase 2 (API & Player).