# Info-Board Neu - Template-Editor fuer globale Kampagnen ## Ziel Der Template-Editor ist ein Bereich des Admin-UI fuer die fachliche Erstellung und Verwaltung globaler Templates und deren operativen Aktivierungen als Kampagnen. Dieses Dokument definiert: - Welche Schritte ein Admin unternimmt, um ein Template zu erstellen - Welche Felder und Optionen der Editor anbietet - Wie Templates zu Kampagnen aktiviert werden - Wie die Abbildung im Datenmodell aussieht Grundlagen zu Template-Typen, Slot-Modell und Message-Wall finden sich in `docs/TEMPLATE-KONZEPT.md`. ## 1. Template-Verwaltung ### Template-Liste **Seite:** Admin → Templates **Anzeige:** Tabelle mit allen Templates: | Name | Typ | Zielgruppe | Szenen | Erstellt | Status | |---|---|---|---|---|---| | Weihnachtsmotiv 2025 | full_screen_media | alle | 1 | 2025-01-15 | draft | | Schriftzug Infowand | message_wall | wall-all | 9 | 2025-02-01 | active | | Event-Tag 25.03 | screen_specific_scene | [info01, info02, ...] | 2 | 2025-03-01 | draft | **Aktionen pro Zeile:** - "Bearbeiten" — öffnet Template-Editor - "Kopieren" — dupliziert als neue Draft - "Löschen" — nur wenn keine aktiven Kampagnen - "Vorschau" — zeigt Layout (fuer message_wall) oder Asset-Galerien - "Aktivieren" — schneller Weg zu Kampagne starten ### Template-Editor (Erstellung/Bearbeitung) #### Phase 1 — Grunddaten ``` ┌─────────────────────────────────────────┐ │ Neues Template erstellen │ ├─────────────────────────────────────────┤ │ │ │ Name * │ │ [ Weihnachtsmotiv 2025_______________ ]│ │ technischer slug wird automatisch │ │ │ │ Template-Typ * │ │ ⦿ full_screen_media │ │ ○ message_wall │ │ ○ screen_specific_scene │ │ │ │ Beschreibung │ │ [ Weihnachtliche Grafik fuer alle___ ] │ │ [ Screens __________________________ ]│ │ │ │ Zielgruppe / Screens * │ │ ⦿ Alle Screens │ │ ○ Nach Gruppe auswaehlen │ │ [Dropdown: wall-all, single-all, ...] │ │ ○ Einzelne Screens auswaehlen │ │ [Checkbox-Liste mit Filterung] │ │ │ │ [Weiter >] [Abbrechen] │ └─────────────────────────────────────────┘ ``` **Validierung:** - Name ist erforderlich - Name ist eindeutig - Template-Typ ist erforderlich - Zielgruppe ist erforderlich (keine leere Zuweisung) #### Phase 2 — Szenen/Inhalte Fuer `full_screen_media`: ``` ┌─────────────────────────────────────────┐ │ Szenen und Inhalte │ ├─────────────────────────────────────────┤ │ │ │ Szene 1: Vollbild-Grafik │ │ │ │ Medientyp * │ │ ○ Bild │ │ ○ Video │ │ ○ PDF │ │ ⦿ Webseite (HTML) │ │ │ │ Portrait-Asset (Hochformat) │ │ [Upload oder URL] │ │ [ Datei auswaehlen ] [Neue URL] │ │ oder vorher gemanagte Assets: [Liste] │ │ │ │ Landscape-Asset (Querformat) [optional] │ │ [ Datei auswaehlen ] [Neue URL] │ │ │ │ Anzeigedauer (Sekunden) │ │ [60_____] Standard: 10 │ │ │ │ Load-Timeout (Sekunden) │ │ [10_____] Standard: 10 │ │ │ │ gueltig ab │ │ [ 2025-03-25 ] [ 00:00 ] │ │ (leer = sofort gueltig) │ │ │ │ gueltig bis │ │ [ 2025-04-01 ] [ 00:00 ] │ │ (leer = unendlich) │ │ │ │ [+ Weitere Szene hinzufuegen] │ │ │ │ [Zurueck <] [Speichern & Aktivieren] │ │ [Speichern] │ │ [Abbrechen] │ └─────────────────────────────────────────┘ ``` Fuer `message_wall`: ``` ┌─────────────────────────────────────────┐ │ Message-Wall Layout │ ├─────────────────────────────────────────┤ │ │ │ Layout-Template │ │ [Dropdown: 3x3-Grid, 2x2-Grid, ...] │ │ │ │ Anzeigedauer (Sekunden) │ │ [10_____] │ │ │ │ Gesamt-Grafik oder Text eingeben │ │ [Rich-Text-Editor oder Bild-Upload] │ │ │ │ Vorschau: [Zeigt Einteilung in Slots] │ │ │ │ Slot-Zuordnung: [Interaktive Zuordnung] │ │ Slot wall-r1-c1 → Screen info01 │ │ Slot wall-r1-c2 → Screen info02 │ │ ... (9 Slots insgesamt) │ │ │ │ [+ Layout-Typ aendernx] [Speichern] │ │ │ │ [Zurueck <] [Speichern & Aktivieren] │ │ [Speichern] │ │ [Abbrechen] │ └─────────────────────────────────────────┘ ``` Fuer `screen_specific_scene`: ``` ┌─────────────────────────────────────────┐ │ Monitorindividuelle Szenen │ ├─────────────────────────────────────────┤ │ │ │ Szene 1: Infowand │ │ │ │ Zielgruppe │ │ ⦿ Gruppe: [Dropdown: wall-all] │ │ ○ Einzelne Screens: [Checkboxen] │ │ │ │ Asset │ │ [Upload oder URL] │ │ │ │ Dauer, Timeout, gueltig_von/bis │ │ [... wie oben ...] │ │ │ │ [+ Weitere Szene hinzufuegen] │ │ │ │ [Zurueck <] [Speichern & Aktivieren] │ └─────────────────────────────────────────┘ ``` ## 2. Kampagnen-Verwaltung Kampagnen sind die operativen Instanzen von Templates. ### Kampagnen-Liste **Seite:** Admin → Kampagnen **Anzeige:** | Name | Template | Aktiv | Zielgruppe | gueltig von | gueltig bis | Betroffene Screens | |---|---|---|---|---|---|---| | Weihnachten Dekoration | Weihnachtsmotiv 2025 | ✓ | alle | 2025-12-01 | 2025-12-26 | 13 Screens | | Schriftzug Januar | Schriftzug Infowand | ✗ | wall-all | 2025-01-06 | 2025-01-31 | 9 Screens | **Aktionen:** - "Bearbeiten" — Kampagnen-Eigenschaften aendern - "Aktivieren/Deaktivieren" — Toggle sofort - "Vorschau" — zeigt betroffene Screens mit Rendering - "Duplizieugen" — als neue Kampagne mit anderem Template - "Loeschen" — wenn inaktiv und abgelaufen ### Neue Kampagne starten **Workflow Option 1 — Von Template aus:** Template-Liste → [Template] → "Aktivieren" ``` ┌─────────────────────────────────────────┐ │ Kampagne starten: Weihnachtsmotiv 2025 │ ├─────────────────────────────────────────┤ │ │ │ Kampagnen-Name │ │ [ Weihnachten 2025 einfuehrung____ ] │ │ │ │ Aktiv ab sofort? │ │ ⦿ Ja │ │ ○ Geplant fuer: [Datum/Zeit auswaehlen]│ │ [ 2025-12-01 ] [ 09:00 ] │ │ │ │ Gueltig von │ │ [ 2025-12-01 ] [ 00:00 ] │ │ │ │ Gueltig bis │ │ [ 2025-12-26 ] [ 23:59 ] │ │ │ │ Prioritaet (gegenueber Playlist) │ │ [1 (hoehere Werte sind wichtiger)] ___ │ │ │ │ Auto-Deaktivierung bei Ablauf? │ │ ⦿ Ja │ │ ○ Nein (Kampagne bleibt inaktiv) │ │ │ │ [Kampagne starten] [Abbrechen] │ └─────────────────────────────────────────┘ ``` **Workflow Option 2 — Neue Kampagne ohne Template:** Admin → Kampagnen → "+ Neue Kampagne" ``` [Template auswaehlen] → [Grunddaten] → [Aktivierung] ``` ### Kampagnen-Detailseite **Anzeige einer laufenden Kampagne:** ``` Kampagne: Weihnachten 2025 einfuehrung Status: AKTIV seit 2025-12-01 09:00 Template: Weihnachtsmotiv 2025 (full_screen_media) Zielgruppe: Alle (13 Screens) Gueltig: 2025-12-01 00:00 bis 2025-12-26 23:59 Prioritaet: 1 Betroffene Screens: ┌──────────────────────────────┐ │ info01 online aktiv │ [Screenshot] │ info02 online aktiv │ [Screenshot] │ info03 offline ausstehend │ │ info04 online aktiv │ [Screenshot] │ ... (10 weitere) ... │ └──────────────────────────────┘ Aktionen: [Deaktivieren] [Bearbeiten] [Vorschau aendernx] Aktivierungsverlauf: 2025-12-01 09:00 — Kampagne gestartet von admin@... 2025-12-01 09:05 — 9 Screens haben gerendert 2025-12-01 10:30 — info03 ging offline, Kampagnen-Inhalt wartet auf Rueckkehr ``` ## 3. Verknuepfung zur Prioritaetsregel Die Regel `campaign > tenant_playlist > fallback` ist: - **hardcoded** im Player - **administrierbar** ueber die Kampagnen-Aktivierung - **vorhersagbar** durch klare Doku ### Abbildung im System ``` Fuer jeden Screen: IF Kampagne fuer diesen Screen aktiv UND gueltig_von <= jetzt <= gueltig_bis THEN Zeige Kampagnen-Inhalt ELSE IF Tenant-Playlist hat gueltige Items THEN Zeige Tenant-Playlist ELSE Zeige Fallback ``` Diese Logik wird: 1. **Serverseitig** berechnet bei jedem Sync-Request (HTTP `/api/v1/screens/{screenSlug}/playlist`) 2. **Spielerseitig** nochmals geprueft beim Rendering (fuer Offline-Robustheit) ### Admin-Sichtbarkeit Die Admin-UI zeigt auf der Seite "Screens" fuer jeden Monitor: ``` info01 ├── Kampagne (AKTIV bis 2025-12-26) │ └── Weihnachten 2025 einfuehrung ├── Fallback (wird nach Kampagnen-Ablauf gezeigt) └── Tenant Playlist ├── Playlist A (Tenant XYZ) │ ├── Bild-1 (gueltig bis 2025-04-01) │ ├── Video-2 (laedt...) │ └── Webseite-3 └── Fallback-Verzeichnis ``` Diese View zeigt, was der Screen **aktuell gerade zeigt** und warum. ## 4. Datenmodell ### Tabelle `templates` ```sql CREATE TABLE templates ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), slug TEXT NOT NULL UNIQUE, name TEXT NOT NULL, description TEXT, template_type TEXT NOT NULL CHECK (template_type IN ('message_wall', 'full_screen_media', 'screen_specific_scene')), created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), created_by_user_id TEXT NOT NULL, -- Serializierte Konfiguration (JSON) config JSONB NOT NULL DEFAULT '{}' -- Beispiele: -- { -- "target_mode": "all_screens" | "group" | "specific_screens", -- "target_group": "wall-all" (wenn target_mode = "group"), -- "target_screen_ids": ["..."] (wenn target_mode = "specific_screens"), -- "scenes": [ -- { -- "media_type": "image|video|pdf|webpage|html", -- "asset_id": "...", -- "portrait_asset_id": "..." (optional), -- "landscape_asset_id": "..." (optional), -- "duration_sec": 10, -- "load_timeout_sec": 10, -- "valid_from": "2025-03-25T00:00:00Z", -- "valid_until": "2025-04-01T23:59:59Z" -- } -- ] -- } ); ``` ### Tabelle `campaigns` ```sql CREATE TABLE campaigns ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), name TEXT NOT NULL, template_id UUID NOT NULL REFERENCES templates(id), active BOOLEAN NOT NULL DEFAULT false, priority INT NOT NULL DEFAULT 1, valid_from TIMESTAMPTZ NOT NULL, valid_until TIMESTAMPTZ, auto_deactivate BOOLEAN NOT NULL DEFAULT true, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), created_by_user_id TEXT NOT NULL, -- ueberschreiben/erweitern Template-Zielgruppe (optional) target_mode TEXT CHECK (target_mode IN ('template', 'all_screens', 'group', 'specific_screens')), target_group TEXT, target_screen_ids UUID[] DEFAULT '{}'::uuid[] ); ``` ### Tabelle `campaign_screen_assignments` (generiert) Diese Tabelle wird **serverseitig** generiert/gepflegt, wenn eine Kampagne aktiv wird. Sie expandiert Gruppen in konkrete Screen-IDs: ```sql CREATE TABLE campaign_screen_assignments ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), campaign_id UUID NOT NULL REFERENCES campaigns(id) ON DELETE CASCADE, screen_id UUID NOT NULL REFERENCES screens(id) ON DELETE CASCADE, assigned_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), UNIQUE(campaign_id, screen_id) ); ``` **Logik:** ``` IF campaign.target_mode = 'template' THEN Fuelle campaign_screen_assignments aus template.config.target_screen_ids ELSE IF campaign.target_mode = 'group' THEN Fuelle campaign_screen_assignments aus allen Screens in campaign.target_group ELSE IF campaign.target_mode = 'specific_screens' THEN Fuelle campaign_screen_assignments aus campaign.target_screen_ids ELSE (alle Screens) ``` ## 5. Praxis-Beispiele ### Beispiel 1 — Weihnachtsplakatierung (full_screen_media) **Szenario:** Admin will ab 01.12.2025 fuer 4 Wochen ein rotes Weihnachtsmotiv auf allen Screens zeigen. **Schritte:** 1. Admin → Templates → "+ Neues Template" - Name: `Weihnachtsmotiv 2025` - Typ: `full_screen_media` - Zielgruppe: `Alle Screens` 2. Szene hinzufuegen: - Bild hochladen (passend fuer Portrait und Landscape) - Dauer: 10 Sekunden 3. Speichern → Editor zeigt Draft mit Vorschau 4. Admin → Templates → [Weihnachtsmotiv 2025] → "Aktivieren" - Kampagnen-Name: `Weihnachten 2025 globale Dekoration` - Gueltig von: 2025-12-01 - Gueltig bis: 2025-12-26 - Aktiv ab: sofort 5. Kampagne speichern → Sofort sichtbar auf allen Screens ### Beispiel 2 — Schriftzug ueber die Infowand (message_wall) **Szenario:** Admin hat eine neue `message_wall`-Gruppe "wall-all" mit 9 Screens. Er will ein riesiges rotes Schriftzug-Motiv aufteilen und auf allen 9 Screens verteilen. **Schritte:** 1. Admin → Templates → "+ Neues Template" - Name: `Rotes Schriftzug auf Infowand` - Typ: `message_wall` - Zielgruppe: `Gruppe: wall-all` 2. Layout waehlen: `3x3-Grid` (passt zu 9 Screens) 3. Gesamte Grafik hochladen (oder als Text eingeben) 4. Slot-Zuordnung: - System zeigt interaktive 3x3-Vorschau - Admin tuen: "Slot 1 → info01", "Slot 2 → info02", ... - System generiert automatisch die Crop-Regionen 5. Speichern + Aktivieren - Jeder Screen zeigt seinen Ausschnitt ### Beispiel 3 — Deaktivierung und Fallback **Szenario:** Kampagne laueft seit 2 Wochen. Admin will sie sofort stoppen, damit Screens auf ihre normalen Playlists zurueckfallen. **Aktion:** Admin → Kampagnen → [Kampagne] → "Deaktivieren" **Folge:** - Server setzt `campaigns.active = false` - Bei naechstem Sync ladet jeder Player wieder die Tenant-Playlist - Fallback-Verzeichnis wird nur noch angezeigt, wenn tenantbezogene Playlist leer ist ## 6. Zusammenfassung Der Template-Editor: - **ist zwei-stufig** — Template-Verwaltung + Kampagnen-Aktivierung - **ist intuitiv** — Multi-Step-Formulare mit Vorschauen - **unterstützt alle Template-Typen** — full_screen, message_wall, screen_specific - **haelt die Prioritaetsregel transparent** — Admin sieht, welche Kampagne welche Screens uebersteuert - **ist zukunftssicher** — Datenmodell skaliert mit neuen Template-Typen