# Agenda-Feature — Plan & Architektur

> Projektbezogene Unterseite `/project/[id]/agenda` für Workshop- und
> Lieferantenbesuchs-Agenden mit Wizard, Template-Engine, Drag-and-Drop-Editor,
> automatischer Zeitberechnung, Teilnehmer-/Logo-Logik und PDF/Excel-Export.
>
> Stand: 2026-05-25 · Branch `feature/agenda` · Status: Plan + Implementierung in Phasen
> KEIN Push, KEINE Migration auf Prod ohne explizite Freigabe (STOPP-Kriterien).

---

## 1. Ist-Zustand im Repo

- **Framework**: Next.js 16 App Router (React 19), Supabase (PostgreSQL + Auth + Storage), Tailwind v4 + shadcn/ui.
- **Routing-Konvention**: `app/project/[id]/<subpage>/page.tsx` (dynamisches Segment heißt `[id]`, **nicht** `[projectId]`).
- **Bestehende Unterseiten**: `dokumente`, `team`, `analyse`, `workshop`, `oee`, `qaf`, `export`, `invest`, `plt`, `stopwatch`, `shift-output`.
- **Layout**: `app/project/[id]/layout.tsx` rendert `AppShell` → `ProjectHeader` (mit `ProjectTabs`) → `children`. Projektkontext + Auth (`getClaims()`) liegen im Layout, jede Unterseite prüft `getClaims()` erneut.
- **Tabs**: `components/project/project-tabs.tsx` — `Übersicht/Dokumente/Team` immer, weitere Tabs typ-gated über `typeCodes`.

## 2. Bestehende relevante Dateien und Komponenten (wiederverwenden)

| Zweck | Datei | Nutzung für Agenda |
|---|---|---|
| Auth/Session | `lib/auth/permissions.ts` (`getUserSession`), `lib/auth/permissions-shared.ts` (`isAtLeastRole`, `ROLE_HIERARCHY`) | `canManage`-Check |
| Server-Supabase | `lib/supabase/server.ts` (`createClient`) | Data-Fetching im Server-Component |
| i18n | `lib/i18n/i18n-context.tsx` — `Locale = 'de'\|'en'\|'es'\|'zh'`, exakt die 4 geforderten Sprachen | Agenda-Sprachauswahl mappt auf `Locale` |
| Drag&Drop | `components/intake/intake-board.tsx` (`@dnd-kit/core` + `@dnd-kit/sortable`, PointerSensor distance:6, optimistic state + Server-Action) | Vorbild für Agenda-Editor-DnD |
| Export | `lib/export/export-service.ts` (jspdf + ExcelJS, **client-side** via dynamic import, `downloadBlob`) | Pattern für Agenda-Export |
| BMW-Logo | `public/brand/bmw-logo.svg`, `bmw-only-grey.svg` (+ white) | statisches Export-Asset |
| Tabs | `components/project/project-tabs.tsx` | Agenda-Tab ergänzen |
| Teilnehmer | `app/project/[id]/team/page.tsx` (`project_consultants` → `consultants`) | BMW-Teilnehmer-Auto-Import |

## 3. Empfohlene Route

`app/project/[id]/agenda/page.tsx` (Repo-Konvention `[id]`, nicht `[projectId]`). Tab „Agenda" in `project-tabs.tsx` direkt nach „Team" — **universell sichtbar** wie Dokumente/Team (nicht typ-gated), da Agenden für alle Workshop-/Besuchstypen relevant sind.

## 4. UI-Konzept

- **Empty-State**: Wenn noch keine Agenda existiert → CTA „Agenda erstellen" startet den **Wizard** (5 Schritte: Sprache → Typ → Modus → Datum/Dauer/Anreisetag → Startzeit-Logik).
- **Editor** (wenn Agenda existiert):
  - PDF-ähnliche **Live-Preview** (Kopf mit Titel, Lieferant, Standort, Datum, Ziel, Teilnehmern, Logos; Tagesblöcke; Tabelle Uhrzeit/Thema/Dauer/Verantwortlich/Infos).
  - **Inline-Editing** + **Side-Panel** für Detailbearbeitung eines Agenda-Punkts.
  - **Drag&Drop** Agenda-Punkte (innerhalb + zwischen Tagen), Zeit-Recompute nach jedem Move.
  - Buttons: Vorschlag neu erzeugen (mit Warn-Dialog vor Überschreiben), Export PDF, Export Excel, Speichern als Entwurf, Finalisieren.
- **Design**: BMW DCT — Primary `#037493` Petrol, 2px Radius, CSS-Variablen aus `globals.css`. Keine hardcoded Farben außerhalb der Palette.

## 5. Datenmodell-Vorschlag

5 Tabellen, alle nach Repo-Konvention (`uuid gen_random_uuid()`, `created_at/updated_at timestamptz default now()`, `created_by/updated_by uuid references auth.users(id)`, `project_id ... references projects(id) on delete cascade`, shared `set_updated_at()`-Trigger):

- `agenda` — Kopf (project_id, title, language, agenda_type, generation_mode, start_date, end_date, first_day_is_travel_day, start_mode, default_day_start/end_time, objective, status, audit-cols).
- `agenda_day` — (agenda_id, date, day_index, title, is_travel_day, start_time, end_time, sort_order).
- `agenda_item` — (agenda_day_id, sort_order, start_time, end_time, duration_minutes, title, description, responsible, participant_group, location, item_type, comments, required_information, is_time_fixed, audit).
- `agenda_participant` — (agenda_id, person_id?, name, company, department, position, role, participant_type [bmw|supplier|external], email?, is_auto_imported_from_project, sort_order).
- `agenda_export` — (agenda_id, export_type [pdf|xlsx], file_path?, generated_by, generated_at, language, includes_supplier_logos, includes_bmw_logo). Dient zugleich als **Export-Audit-Trail**.

**Logo-Storage**: `supplier_master_data.logo_storage_key text` (neue Spalte) + Supabase-Storage-Bucket `supplier-logos` (vorbereitet in Migration). BMW-Logo = statisches Asset, **kein** Upload.

## 6. RLS- und Security-Bewertung

- **Tenant-Isolation**: KEIN `tenant_id` auf Feature-Tabellen (Repo-Konvention, `docs/tenant_isolation_model.md`: physische Isolation = eigenes Supabase-Projekt pro Tenant; BMW Stand-alone). Multi-Tenant kommt später als ALTER-Migration. → **kein tenant_id** auf Agenda-Tabellen.
- **RLS-Pattern** (wie `kapa_workshop_data`, LSC, OEE): pro Tabelle `_own` + `_admin`-Policy.
  - `agenda` (direkt project_id): `USING (project_id IN (SELECT id FROM projects WHERE user_id = auth.uid()))`.
  - Kind-Tabellen: Subquery hoch zu `agenda.project_id IN (...)`.
  - Admin-Escape: `current_user_role() IN ('admin','masteradmin')`.
- **Kein Service-Role** (ADR-020): alle CRUD laufen user-scoped, RLS isoliert. Kein `createAdminClient`.
- **App-Code-Check** (`canManage`): `isAtLeastRole(session,'admin') || project.user_id === session.authUserId || project.project_lead_id === session.userId` (wie team/page.tsx).
- **Audit**: `created_by/updated_by` + `agenda_export`-Rows decken Erstellen/Bearbeiten/Export ab. Vollständiges Trigger-Audit (`audit_log`) als Follow-up (siehe Offene Entscheidungen).

## 7. Export-Architektur PDF

- Client-side, `lib/agenda/export-pdf.ts`, jspdf via dynamic import (Pattern wie `export-service.ts`).
- BMW-Logo: SVG→PNG zur Laufzeit (Browser-Canvas `svgUrlToPngDataUrl`) → `doc.addImage`. Bis zu 2 Supplier-Logos analog, optional.
- Mehrseitig (`autoTable`-frei, manuelles Row-Pagination mit Seitenumbruch), kein abgeschnittener Text, lange Kommentare umbrechen, einheitliche Schriftgrößen, Sprach-Labels aus `lib/agenda/i18n.ts`.
- Dateiname: `Agenda_[Supplier]_[Location]_[YYYYMMDD].pdf`.

## 8. Export-Architektur Excel

- Client-side, `lib/agenda/export-excel.ts`, ExcelJS via dynamic import.
- `ws.pageSetup = { orientation: 'landscape', fitToPage: true, fitToWidth: 1, fitToHeight: 0 }`, `printArea` gesetzt (neues Pattern, im Repo bisher nicht vorhanden).
- Logo via `wb.addImage` (PNG-Buffer) + `ws.addImage`.
- Spalten: Time/Subject/Duration/Responsible/Required Information-Comments (sprachabhängig). Keine Formeln → kein `#REF!`-Risiko. Datums/Zeit als Text-Format.
- Dateiname: `Agenda_[Supplier]_[Location]_[YYYYMMDD].xlsx`.

## 9. Template-Engine-Konzept

- `lib/agenda/templates.ts`: pro Typ (`fabrikanalyse`, `lsc_workshop`, `kapa_workshop`, `sonstiges`) eine Liste von Template-Items mit `i18nKey`, `durationMinutes`, `itemType`, `defaultDay`.
- `lib/agenda/i18n.ts`: Label-Dictionary DE/EN/ES/ZH für Standard-Items, Spaltennamen, Export-Labels (entkoppelt von der App-UI-i18n, da die Agenda-**Inhalts**-Sprache unabhängig von der App-Locale wählbar sein muss).
- Generator: `generateAgenda({ type, language, startDate, endDate, firstDayIsTravel, startMode, customStart })` → vollständige Agenda mit Tagen + Items, Zeiten via Time-Engine berechnet. „Sonstiges" startet generisch/leer.

## 10. Zeitberechnungslogik

`lib/agenda/time-engine.ts` (reine Funktionen, voll test-bar):
- `recomputeDay(items, dayStart)`: erster Punkt startet bei `dayStart`; jeder weitere `start = prev.end`; `end = start + duration`. Fixierte Startzeiten (`is_time_fixed`) verankern, Automatik läuft danach weiter.
- Warnungen: Tagesende überschritten, Dauer ≤ 0, Pflichtfeld fehlt.
- HH:MM-Arithmetik in Minuten, keine Date-Objekte (zeitzonen-sicher).

## 11. Drag-and-Drop-Konzept

- `@dnd-kit/core` + `@dnd-kit/sortable` (Vorbild `intake-board.tsx`).
- Items innerhalb + zwischen Tagen verschiebbar; Tagesblöcke bleiben chronologisch.
- Nach Move: `recomputeDay` für betroffene Tage; Dauer bleibt, Konflikte werden angezeigt.
- Spezielle Item-Typen (Pause/Lunch/Travel/Intern) optisch markiert.

## 12. Risiken

- **Migration nicht applied** → Editor/Wizard können nicht persistieren bis Freigabe. Empty-State + Pure-Logic + Export funktionieren ohne DB (gegen In-Memory-Modell).
- **Supplier-Logo-Infra fehlt komplett** → neue Spalte + Bucket nötig (Teil der Migration). Exporte sind logo-tolerant (funktionieren ohne Supplier-Logo).
- **SVG→PNG** im Browser kann bei manchen SVGs taint-Probleme machen → same-origin Assets, getestet.
- **jspdf** ohne autoTable-Plugin → manuelle Tabellen-Pagination, sorgfältige Tests nötig.
- **i18n ZH** Zeilenumbrüche/Font im PDF — jspdf-Standardfont kann CJK nicht; ZH-PDF braucht eingebetteten CJK-Font (Risiko/Follow-up, siehe Offene Entscheidungen).

## 13. Offene Entscheidungen

1. **Migration anwenden**: Freigabe durch Operator erforderlich bevor auf Prod appliziert wird.
2. **Voll-Audit** via `audit_log`-Trigger jetzt oder Follow-up? (aktuell: created_by/updated_by + agenda_export-Trail).
3. **ZH-PDF-Font**: CJK-Font einbetten (vergrößert Bundle ~3-10 MB) oder ZH nur in Excel/HTML-Preview, PDF-Fallback Latin?
4. **Supplier-Logo-Upload-UI**: im Lieferanten-Stammdatensatz oder pro Agenda? (Vorschlag: Stammdaten, mit Per-Agenda-Override.)
5. **Tab-Sichtbarkeit**: universell (wie Dokumente/Team) — bestätigt als Default-Annahme.

## 14. Phasenplan (Umsetzungsreihenfolge dieser Session)

1. ✅ Analyse + Plan (dieses Doc) + Linear KAR
2. Domain-Typen + Time-Engine + Tests
3. Template-Engine + Agenda-i18n + Tests
4. Migration SQL (Schema + RLS + Rollback) — **vorbereitet, nicht applied**
5. Export PDF + Excel (logo-tolerant) + Tests
6. Route-Scaffold + Tab + Server-Actions + Empty-State
7. Wizard + Editor-UI (Live-Preview, Inline-Edit, DnD, Side-Panel, Teilnehmer, Logos)
8. typecheck + lint + test + Report — **STOPP vor Migration-Apply/Push**

## 15. Konkrete nächste Implementierungsschritte

Phase 2 starten: `lib/agenda/types.ts` + `lib/agenda/time-engine.ts` + `__tests__/agenda-time-engine.test.ts`.

## 16. Voraussichtlich geänderte/neue Dateien

**Neu**:
- `lib/agenda/{types,time-engine,templates,i18n,export-pdf,export-excel,logo}.ts`
- `app/project/[id]/agenda/{page,actions}.tsx`
- `components/agenda/{agenda-wizard,agenda-editor,agenda-preview,agenda-item-panel,agenda-participants,agenda-logo-picker}.tsx`
- `supabase/migrations/supabase-migration-agenda-schema.sql` + `supabase/migrations/supabase-migration-agenda-schema-rollback.sql`
- `__tests__/agenda-{time-engine,templates,export}.test.ts`

**Geändert**:
- `components/project/project-tabs.tsx` (Agenda-Tab)
- `supplier_master_data` (Migration: `logo_storage_key`)
- `CHANGELOG.md`, `TODO.md`, ggf. `PRODUCT_SPEC.md`/`API_SPEC.md` (Feature-Parität iOS)

## 17. Testplan

- Unit: Time-Engine (recompute, fixed times, Warnungen, Edge-Cases leer/0/negativ).
- Unit: Template-Generierung (alle 4 Typen × 4 Sprachen, Anreisetag-Logik, Vormittag/Nachmittag).
- Unit: Export gegen Fixture-Agenda — ohne / 1 / 2 Supplier-Logos, kein Crash, korrekter Dateiname.
- Component (Folge-Session, jsdom): DnD-Sortierung, Inline-Edit.
- E2E/RLS (nach Migration-Apply): Tenant-/Projekt-Isolation, Rechteprüfung.
</invoke>
