# Phase 1 — Symbol-Architektur Redesign: Detaillierter Plan

> Basierend auf: `docs/roadmap-feedback-fabian.md`
> Ziel: Mandantenspezifische Symbol-Bibliothek mit Template-Import, eigener Kategorisierung und vollständiger Entkopplung von globalen Icons.
> **Status: ABGESCHLOSSEN** ✅ (Sprints A–D implementiert, getestet und gepusht)

---

## 1. Ziel & Konzept

**Problem heute:**
- `TenantSymbol` verweist auf `IconAsset` (global). Mandant kann zwar `customName` setzen, aber nicht die Kategorie ändern, das SVG bearbeiten oder Symbole aus verschiedenen Paketen frei mischen.
- Kategorien (`IconCategory`) sind global mit `tenantId` Override — uneinheitlich.
- Keine Möglichkeit, Vorlagen-Pakete (Feuerwehr CH, THW, Sanität) als Unit zu importieren.

**Lösung:**
- `SymbolTemplate` = globale, read-only Vorlagen (je Paket). Wird einmalig aus bestehenden `public/signaturen/` und `IconAsset` generiert.
- `TenantCategory` = pro Mandant, frei anlegbar/umbenennbar/sortierbar.
- `TenantSymbol` = pro Mandant, **vollständig eigenständig** (`name`, `svgPath`, `categoryId`). Kein Verweis mehr auf globale `IconAsset`.
- Mandant startet mit leerer Bibliothek und importiert Pakete nach Bedarf.

---

## 2. Datenmodell-Änderungen

### 2.1 Neue Modelle

```prisma
model SymbolTemplate {
  id           String   @id @default(uuid())
  packageId    String   // z.B. "feuerwehr-ch"
  packageName  String   // z.B. "Feuerwehr Schweiz"
  categoryName String   // z.B. "Fahrzeuge"
  name         String
  svgPath      String   // Relativer Pfad in public/ oder SVG-Inhalt
  tags         String[] @default([])
  sortOrder    Int      @default(0)

  @@index([packageId])
  @@map("symbol_templates")
}

model TenantCategory {
  id          String   @id @default(uuid())
  tenantId    String
  tenant      Tenant   @relation(fields: [tenantId], references: [id], onDelete: Cascade)
  name        String
  description String?
  sortOrder   Int      @default(0)
  createdAt   DateTime @default(now())
  updatedAt   DateTime @updatedAt

  symbols TenantSymbol[]

  @@unique([tenantId, name])
  @@index([tenantId])
  @@map("tenant_categories")
}
```

### 2.2 Bestehendes `TenantSymbol` umbaut

**Vorher:**
```prisma
model TenantSymbol {
  id         String   @id @default(uuid())
  customName String?
  sortOrder  Int      @default(0)
  tenantId   String
  tenant     Tenant   @relation(fields: [tenantId], references: [id], onDelete: Cascade)
  iconId     String
  icon       IconAsset @relation(fields: [iconId], references: [id], onDelete: Cascade)
}
```

**Nachher:**
```prisma
model TenantSymbol {
  id         String   @id @default(uuid())
  tenantId   String
  tenant     Tenant   @relation(fields: [tenantId], references: [id], onDelete: Cascade)

  categoryId String?
  category   TenantCategory? @relation(fields: [categoryId], references: [id], onDelete: SetNull)

  name       String   // endgültiger Anzeigename (kann aus Template importiert oder custom sein)
  svgPath    String?  // z.B. "signaturen/TLF.svg" oder tenant-spezifischer MinIO-Key
  sortOrder  Int      @default(0)
  isUploaded Boolean  @default(false) // true = eigener Upload, false = aus Template importiert

  createdAt  DateTime @default(now())
  updatedAt  DateTime @updatedAt

  // Legacy-Feld für Migration (kann später entfernt werden)
  migratedFromIconId String?

  @@index([tenantId])
  @@index([categoryId])
  @@map("tenant_symbols")
}
```

> **Hinweis:** `IconAsset`, `IconCategory` und `hiddenIconIds` bleiben vorerst bestehen (read-only Legacy), werden aber nicht mehr für neue Features verwendet. In einer späteren Phase können sie entfernt werden.

---

## 3. Migration & Seed (Sprint A) ✅

- `prisma/migrations/20260520_symbol_architecture/migration.sql` — erstellt `symbol_templates`, `tenant_categories`, neue Spalten in `tenant_symbols`
- `prisma/seed-symbol-templates.ts` — füllt `SymbolTemplate` aus `public/signaturen/*.svg`
- `prisma/migrate-tenant-symbols.ts` — migriert bestehende `TenantSymbol`-Einträge
- `prisma/migrate.js` — erweitert um Migrationsschritte für Produktion
- Alle Seed-Skripte sind idempotent (`upsert` statt `deleteMany`)

---

## 4. API-Design (Sprint B) ✅

### 4.1 Templates

```
GET  /api/templates
     → { packages: [{ packageId, packageName, categoryCount, symbolCount, previewSymbols }] }

POST /api/templates/import
     Body: { packageId }
     → importiert alle Symbole des Pakets als TenantSymbols
     → antwortet mit { imported, skipped, message }
```

**Implementiert:**
- `src/app/api/templates/route.ts` — GET mit `groupBy` Aggregation und Vorschau
- `src/app/api/templates/import/route.ts` — POST mit Auto-Kategorie-Erstellung und Deduplikation

### 4.2 Tenant Categories

```
GET    /api/tenant/categories        → [{ id, name, sortOrder, description }]
POST   /api/tenant/categories        Body: { name } → neue Kategorie
PATCH  /api/tenant/categories        Body: { id, name } → umbenennen
DELETE /api/tenant/categories?id=... → 204 oder 409 wenn nicht leer
```

**Implementiert:** `src/app/api/tenant/categories/route.ts`

### 4.3 Tenant Symbols

```
GET    /api/tenant/symbols?grouped=true|false
       → grouped=true:  { groups: [{ category, symbols }], symbols: [...] }
       → grouped=false: { symbols: [...] }

POST   /api/tenant/symbols
       - Multipart: { file, categoryId? } → Upload nach MinIO
       - JSON: { templateId, categoryId? } → aus SymbolTemplate
       - JSON: { iconId, customName?, categoryId? } → aus IconAsset (Legacy)

PATCH  /api/tenant/symbols          Body: { id, name?, customName?, categoryId?, sortOrder? }

DELETE /api/tenant/symbols          Body: { id } → löscht TenantSymbol
```

**Implementiert:** `src/app/api/tenant/symbols/route.ts` + `src/app/api/tenant/symbols/[id]/image/route.ts`

### 4.4 Icon-Serving (TenantSymbol-First Lookup)

```
GET /api/icons/:id/image
    → 1. TenantSymbol (svgPath aus public/ oder MinIO)
    → 2. Fallback: IconAsset (legacy)

GET /api/tenant/symbols/:id/image
    → TenantSymbol-Bild aus MinIO oder public/
```

**Implementiert:**
- `src/app/api/icons/[id]/image/route.ts`
- `src/app/api/tenant/symbols/[id]/image/route.ts`

---

## 5. Frontend-Änderungen (Sprints C + D) ✅

### 5.1 Admin — Symbol-Manager (`src/components/admin/symbol-manager.tsx`)

**Tabs:**
1. **Meine Symbole** — nach TenantCategory gruppierte Symbol-Liste
   - Expandable Kategorien
   - SymbolCard: Bild, Name (inline-edit), Kategorie-Select, Löschen
   - Suche über alle Symbole
2. **Kategorien** — CRUD für TenantCategory
   - Neue Kategorie erstellen
   - Umbenennen (inline)
   - Löschen nur wenn leer
3. **Vorlagen importieren** — Template-Pakete als Cards
   - Vorschau der ersten 4 Symbole
   - 1-Klick Import

**Actions:**
- Upload-Button → Dialog mit Drag & Drop + Kategorie-Auswahl
- Import-Button → Dialog mit allen verfügbaren Paketen

### 5.2 Sidebar — RightSidebar (`src/components/layout/right-sidebar.tsx`)

- Verwendet neue `tenantSymbolGroups` aus `/api/icons`
- **Meine Symbole** Sektion: nach TenantCategory gruppiert, expandable
- **Bibliothek** Sektion: globale IconAsset-Kategorien (Legacy, read-only)
- Drag & Drop funktioniert mit neuen TenantSymbol-IDs

### 5.3 API /icons — Kompatibilität

`src/app/api/icons/route.ts` liefert:
- `categories` — Legacy IconAsset-Kategorien (für alte Clients)
- `mySymbols` — Legacy TenantSymbol-Liste (für alte Clients)
- `tenantSymbols` — Neue flache TenantSymbol-Liste
- `tenantSymbolGroups` — Neue gruppierte Liste für Sidebar

---

## 6. Dateien: Create / Modify

### Neue Dateien ✅
| Pfad | Beschreibung |
|------|-------------|
| `prisma/migrations/20260520_symbol_architecture/migration.sql` | Migration neue Tabellen + Spalten |
| `prisma/seed-symbol-templates.ts` | Seed: `public/signaturen/*.svg` → `SymbolTemplate` |
| `src/app/api/templates/route.ts` | GET /api/templates |
| `src/app/api/templates/import/route.ts` | POST /api/templates/import |
| `src/app/api/tenant/categories/route.ts` | CRUD TenantCategory |
| `src/app/api/tenant/symbols/[id]/image/route.ts` | TenantSymbol Bild-Serving |

### Modifizierte Dateien ✅
| Pfad | Änderung |
|------|----------|
| `prisma/schema.prisma` | Neue Modelle + TenantSymbol Refactor |
| `src/app/api/tenant/symbols/route.ts` | Refactor: Gruppierung, Upload, CRUD |
| `src/app/api/icons/route.ts` | Liefert tenantSymbolGroups |
| `src/app/api/icons/[id]/image/route.ts` | TenantSymbol-First Lookup |
| `src/components/admin/symbol-manager.tsx` | Vollständiger Umbau mit Kategorien, Import, Upload |
| `src/components/layout/right-sidebar.tsx` | Gruppierte TenantSymbol-Anzeige |

---

## 7. Ausführungsreihenfolge (Tatsächlich)

| Sprint | Status | Inhalt |
|--------|--------|--------|
| **A** | ✅ Fertig | Schema, Migration, Seed, Daten-Migration |
| **B** | ✅ Fertig | Templates API, Categories API, Symbols API erweitert |
| **C** | ✅ Fertig | Admin UI: Symbol-Manager mit Kategorien, Import, Upload |
| **D** | ✅ Fertig | Frontend Sidebar gruppiert nach TenantCategory |

---

## 8. Risiken & Entscheidungen (Umgesetzt)

| Thema | Entscheidung |
|-------|-------------|
| **SVG-Speicherort** | Templates: `public/signaturen/` Pfade in DB (read-only, kein MinIO-Overhead). Uploads: MinIO `tenant-{id}/symbols/`. |
| **Migration alter Tenants** | Vollautomatisch via `prisma/migrate.js` + `prisma/migrate-tenant-symbols.ts`. |
| **Sidebar vs SymbolPalette** | `RightSidebar` direkt angepasst, keine separate `SymbolPalette`-Komponente nötig. |
| **instanceLabel / Stockwerke** | Nicht in Sprint D enthalten (laut Roadmap Phase 1.4 — kann separat geplant werden). |

---

*Plan erstellt: 2026-05-20*
*Phase 1 abgeschlossen: 2026-05-20*
