# Info-Board Neu - Gruppierungs- und Slot-Modell fuer monitoruebergreifende Layouts ## Ziel Dieses Dokument definiert, wie Screens in Gruppen und Slots organisiert werden. Gruppen und Slots sind notwendig fuer: - **Massenaktionen** — mehrere Screens mit einer Kampagne ansprechen - **Monitorwaende** — Schriftzuege und Layouts auf mehrere Screens verteilen - **zukuenftige Skalierbarkeit** — neue Displays ohne Neustrukturierung hinzufuegen Siehe auch `docs/TEMPLATE-KONZEPT.md` fuer Template-Typen, die Gruppen/Slots verwenden. ## 1. Screen-Gruppen ### Konzept Eine Gruppe ist eine semantische Zusammenfassung mehrerer Screens. **Beispiele:** - `all` — alle Screens im System - `wall-all` — alle 9 Infowand-Screens - `wall-row-1` — die 3 Screens der ersten Reihe - `wall-row-2` — die 3 Screens der zweiten Reihe - `single-all` — alle Einzelanzeigen (z.B. Vertretungsplan-Displays) - `outdoor` — alle Aussenanzeigetafeln ### Typen von Gruppen #### Physische Gruppen Spiegeln die **reale Anordnung** wider: - `wall-all` — alle Displays einer Infowand - `wall-row-1`, `wall-row-2`, `wall-row-3` — Reihen einer Wand - `wall-column-1`, `wall-column-2`, `wall-column-3` — Spalten einer Wand #### Funktionale Gruppen Spiegeln den **Verwendungszweck** wider: - `main-hall-all` — alle Displays im Hauptkorridor - `cafeteria-all` — alle Displays in der Kaffeteria - `info-all` — alle Informationsanzeigen #### Typen-Gruppen Spiegeln das **Geraetemodell** wider: - `portrait-all` — alle Displays im Hochformat - `landscape-all` — alle Displays im Querformat - `4k-displays` — nur 4K-Monitore #### Tenant-Gruppen (Phase 2) Spiegeln die **Mandanten-Zugehoerigkeit** wider: - `tenant-xyz-all` — alle Displays fuer Mandant XYZ - `tenant-xyz-public` — nur oeffentliche Displays des Mandants ### Hierarchische Struktur Gruppen koennen verschachtelt sein: ``` all ├── wall-all │ ├── wall-row-1 │ │ ├── info01 │ │ ├── info02 │ │ └── info03 │ ├── wall-row-2 │ │ ├── info04 │ │ ├── info05 │ │ └── info06 │ └── wall-row-3 │ ├── info07 │ ├── info08 │ └── info09 ├── single-all │ ├── info10 (Vertretungsplan 1) │ └── info11 (Vertretungsplan 2) └── fallback-displays └── [none currently] ``` **Automatische Inferenz:** Ein Screen kann in mehreren Gruppen sein: ``` info01: - all - wall-all - wall-row-1 - portrait-all - online-displays (automatisch basierend auf Status) ``` ## 2. Slot-Modell ### Konzept Slots beschreiben **feste Positionen innerhalb eines Layouts**. Sie werden hauptsaechlich fuer `message_wall`-Templates verwendet, um Ausschnitte von Grossmotiven auf einzelne Screens zu verteilen. **Beispiel: 3x3 Infowand** ``` ┌─────────────────────────────────┐ │ [0,0] [0,1] [0,2] │ Slot wall-r1-c1, wall-r1-c2, wall-r1-c3 ├─────────────────────────────────┤ │ [1,0] [1,1] [1,2] │ Slot wall-r2-c1, wall-r2-c2, wall-r2-c3 ├─────────────────────────────────┤ │ [2,0] [2,1] [2,2] │ Slot wall-r3-c1, wall-r3-c2, wall-r3-c3 └─────────────────────────────────┘ ``` **Slot-Nomenclatur:** - `wall-r{reihe}-c{spalte}` (Zeile/Spalte im 0er-System oder 1er-System) - `wall-slot-{nummer}` (durchnummeriert, z.B. wall-slot-0 bis wall-slot-8) ### Geometrische Definition Fuer jeden Slot wird definiert: ```json { "slot_id": "wall-r1-c1", "row": 0, "col": 0, "layout_name": "3x3_grid", "crop_x": 0, "crop_y": 0, "crop_width": 640, "crop_height": 1080, "assigned_screen_id": "info01" } ``` Diese Werte sind: - **serverseitig generiert** — Admin muss nicht manuell Pixel-Koordinaten eingeben - **automatisch skalierbar** — bei verschiedenen Aufloesungen ## 3. Datenmodell ### Tabelle `screen_groups` ```sql CREATE TABLE screen_groups ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), slug TEXT NOT NULL UNIQUE, name TEXT NOT NULL, description TEXT, group_type TEXT NOT NULL CHECK (group_type IN ( 'physical', 'functional', 'device_type', 'tenant', 'custom' )), parent_group_id UUID REFERENCES screen_groups(id), active BOOLEAN NOT NULL DEFAULT true, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); ``` **Beispiele:** ```sql INSERT INTO screen_groups (slug, name, group_type) VALUES ('all', 'Alle Screens', 'custom'), ('wall-all', 'Infowand - Alle', 'physical'), ('wall-row-1', 'Infowand - Reihe 1', 'physical'), ('single-all', 'Einzelanzeigen', 'functional'), ('portrait-all', 'Hochformat', 'device_type'); ``` ### Tabelle `screen_group_members` ```sql CREATE TABLE screen_group_members ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), group_id UUID NOT NULL REFERENCES screen_groups(id) ON DELETE CASCADE, screen_id UUID NOT NULL REFERENCES screens(id) ON DELETE CASCADE, added_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), UNIQUE(group_id, screen_id) ); ``` **Beispiel:** ```sql INSERT INTO screen_group_members (group_id, screen_id) SELECT (SELECT id FROM screen_groups WHERE slug = 'wall-row-1'), id FROM screens WHERE slug IN ('info01', 'info02', 'info03'); ``` ### Tabelle `layout_definitions` ```sql CREATE TABLE layout_definitions ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), slug TEXT NOT NULL UNIQUE, name TEXT NOT NULL, layout_type TEXT NOT NULL CHECK (layout_type IN ( '3x3_grid', '2x2_grid', '1x9_row', '9x1_column', 'custom' )), rows INT NOT NULL, cols INT NOT NULL, description TEXT, created_at TIMESTAMPTZ NOT NULL DEFAULT NOW() ); ``` **Beispiel:** ```sql INSERT INTO layout_definitions (slug, name, layout_type, rows, cols) VALUES ('3x3_infowand', 'Infowand 3x3', '3x3_grid', 3, 3); ``` ### Tabelle `layout_slots` ```sql CREATE TABLE layout_slots ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), layout_id UUID NOT NULL REFERENCES layout_definitions(id) ON DELETE CASCADE, slot_slug TEXT NOT NULL, row INT NOT NULL, col INT NOT NULL, UNIQUE(layout_id, slot_slug) ); ``` **Beispiel:** ```sql INSERT INTO layout_slots (layout_id, slot_slug, row, col) SELECT (SELECT id FROM layout_definitions WHERE slug = '3x3_infowand'), 'wall-r' || (r) || '-c' || (c), r - 1, c - 1 FROM CROSS JOIN LATERAL (SELECT GENERATE_SERIES(1, 3) AS r) CROSS JOIN LATERAL (SELECT GENERATE_SERIES(1, 3) AS c); ``` ### Tabelle `slot_screen_assignments` ```sql CREATE TABLE slot_screen_assignments ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), layout_id UUID NOT NULL REFERENCES layout_definitions(id), slot_id UUID NOT NULL REFERENCES layout_slots(id) ON DELETE CASCADE, screen_id UUID NOT NULL REFERENCES screens(id), assigned_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), UNIQUE(layout_id, slot_id, screen_id) ); ``` **Beispiel:** ```sql -- Zuordnung: Slot wall-r1-c1 → Screen info01 (in 3x3-Layout) INSERT INTO slot_screen_assignments (layout_id, slot_id, screen_id) SELECT l.id, ls.id, s.id FROM layout_definitions l, layout_slots ls, screens s WHERE l.slug = '3x3_infowand' AND ls.layout_id = l.id AND ls.slot_slug = 'wall-r1-c1' AND s.slug = 'info01'; ``` ## 4. Admin-Verwaltung ### Gruppen verwalten **Seite:** Admin → Gruppen ``` ┌──────────────────────────────────────────┐ │ Screen-Gruppen │ ├──────────────────────────────────────────┤ │ │ │ Gruppe Typ Screens│ │────────────────────────────────────────│ │ all custom 13 │ │ wall-all physical 9 │ │ wall-row-1 physical 3 │ │ wall-row-2 physical 3 │ │ wall-row-3 physical 3 │ │ single-all functional 2 │ │ portrait-all device_type 12 │ │ │ │ [+ Neue Gruppe] [Gruppe bearbeiten] │ └──────────────────────────────────────────┘ ``` ### Gruppe erstellen/bearbeiten ``` ┌──────────────────────────────────────────┐ │ Neue Gruppe │ ├──────────────────────────────────────────┤ │ │ │ Name * │ │ [ Infowand Reihe 2 __________________ ] │ │ slug: wall-row-2 (automatisch) │ │ │ │ Gruppentyp * │ │ ⦿ physical (Wand-Anordnung) │ │ ○ functional (Verwendungszweck) │ │ ○ device_type (Geraetetyp) │ │ ○ tenant (Mandant) │ │ ○ custom (benutzerdefiniert) │ │ │ │ Beschreibung │ │ [ Die obere Reihe der Infowand ______ ] │ │ │ │ Screens hinzufuegen │ │ [ Suchfeld: "info" ] │ │ □ info01 ← obere Reihe │ │ □ info02 ← obere Reihe │ │ ☑ info03 ← obere Reihe │ │ □ info04 │ │ ... (nur unzugeordnete zeigen) │ │ │ │ Ausgewaehlte Screens │ │ info03 (portrait, online) │ │ [ + weitere hinzufuegen ] │ │ │ │ Uebergruppe │ │ [Dropdown: all > wall-all] │ │ (optional, zur Hierarchie) │ │ │ │ [Speichern] [Abbrechen] │ └──────────────────────────────────────────┘ ``` ### Layout-Definition erstellen (fuer Message-Wall) **Seite:** Admin → Layouts ``` ┌──────────────────────────────────────────┐ │ Layout-Definitionen │ ├──────────────────────────────────────────┤ │ │ │ Layout-Name Typ Grid Slots│ │─────────────────────────────────────────│ │ 3x3 Infowand 3x3_grid 3x3 9 │ │ Vertretungsplan 2x2_grid 2x2 4 │ │ News-Lauf 1x9_row 1x9 9 │ │ │ │ [+ Neues Layout] [Bearbeiten] │ └──────────────────────────────────────────┘ ``` Detailseite eines Layouts: ``` Layout: 3x3 Infowand Visualisierung: ┌─────────┬─────────┬─────────┐ │ Slot 1 │ Slot 2 │ Slot 3 │ ├─────────┼─────────┼─────────┤ │ Slot 4 │ Slot 5 │ Slot 6 │ ├─────────┼─────────┼─────────┤ │ Slot 7 │ Slot 8 │ Slot 9 │ └─────────┴─────────┴─────────┘ Slot-Zuordnungen: Slot 1 (wall-r1-c1) → Screen info01 (portrait, 1920x1080) Slot 2 (wall-r1-c2) → Screen info02 (portrait, 1920x1080) ... [Screen-Zuordnungen aendernx] [Layout loeschen] ``` ## 5. Anwendung in Kampagnen ### Kampagne auf Gruppe anwenden **Beispiel:** Admin aktiviert Weihnachtsmotiv auf `wall-all`: ``` Template: Weihnachtsmotiv 2025 (full_screen_media) Zielgruppe auswaehlen: ⦿ Alle Screens ○ Nach Gruppe: [Dropdown: wall-all ] oder wall-row-1, single-all, ... ○ Einzelne Screens → Kampagne wird auf alle 9 Screens in wall-all aktiviert → Jeder Screen zeigt dasselbe Motiv → (Portrait/Landscape-Varianten werden serverseitig beruecksichtigt) ``` ### Message-Wall-Kampagne mit Slot-Modell **Beispiel:** Admin teilt Schriftzug auf Infowand auf: ``` Template: Schriftzug (message_wall) Layout: 3x3 Infowand Zielgruppe: wall-all (auto-expandiert zu Slots) Gesamte Grafik hochladen oder zeichnen ↓ System generiert automatisch: - Slot wall-r1-c1 → Ausschnitt x0-640 y0-1080 → Screen info01 - Slot wall-r1-c2 → Ausschnitt 640-1280 y0-1080 → Screen info02 - Slot wall-r1-c3 → Ausschnitt 1280-1920 y0-1080 → Screen info03 - ... (9 Zuweisungen insgesamt) ↓ Kampagne aktivieren ↓ Jeder Screen ladet seinen zustaendigen Ausschnitt ↓ Schriftzug erscheint verteilt ueber alle 9 Screens ``` ## 6. Automatische Gruppe-Inferenz Der Server kann bestimmte Gruppen automatisch generieren: ```python # Automatisch generierte Gruppen all: - alle Screens im System (manuelle Verwaltung nicht noetig) online-all: - alle Screens, die gerade online sind - wird alle 5 Min aktualisiert offline-all: - alle Screens, die gerade offline sind portrait-all: - alle Screens mit Orientierung = "portrait" landscape-all: - alle Screens mit Orientierung = "landscape" device_type_*: - fuer jeden konfigurieren Screen-Typ (z.B. device_type_raspberry_pi) region_*: - optional: auf Basis von Geo-Daten oder Tags ``` Diese automatischen Gruppen sind **read-only** im Admin-UI, aber voll verwendbar fuer Kampagnen. ## 7. Beispiel: Neuinstallation einer Infowand **Szenario:** Admin installiert neue 3x3-Infowand mit Screens info01-info09. **Schritte:** 1. **Screens anlegen** (via Provisionierungs-UI oder direkt) ``` info01, info02, ..., info09 Alle: Orientierung portrait, Geraetetyp "raspberry_pi" ``` 2. **Gruppen anlegen** ``` screen_groups: - slug: wall-all, name: "Infowand Alle", type: physical - slug: wall-row-1, name: "Infowand Reihe 1", type: physical - slug: wall-row-2, name: "Infowand Reihe 2", type: physical - slug: wall-row-3, name: "Infowand Reihe 3", type: physical ``` 3. **Screens den Gruppen zuordnen** ``` wall-all: info01-info09 wall-row-1: info01, info02, info03 wall-row-2: info04, info05, info06 wall-row-3: info07, info08, info09 ``` 4. **Layout definieren** ``` layout_definitions: - slug: 3x3_infowand, rows: 3, cols: 3 layout_slots: - wall-r1-c1, wall-r1-c2, wall-r1-c3 (row 0) - wall-r2-c1, wall-r2-c2, wall-r2-c3 (row 1) - wall-r3-c1, wall-r3-c2, wall-r3-c3 (row 2) slot_screen_assignments: - wall-r1-c1 → info01 - wall-r1-c2 → info02 - ... (9 gesamt) ``` 5. **Kampagnen verwenden** ``` Template: Schriftzug Zielgruppe: wall-all Layout: 3x3_infowand → Kampagne kann sofort aktiviert werden ``` ## 8. Zusammenfassung Das Gruppierungs- und Slot-Modell: - **ist flexibel** — physische, funktionale und typen-basierte Gruppen - **ist hierarchisch** — Gruppen koennen Untergruppen enthalten - **ist automatisch** — Gruppen wie "all" und "online-all" werden inferiert - **ist geometrisch** — Slots definieren Layouts fuer verteilte Motive - **ist skalierbar** — neue Screens werden einfach Gruppen zugeordnet - **ist intuitiv** — Admin-UI zeigt Zuordnungen und Vorschauen