# 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
