# 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*