# Handover-Dokument — Assignment Monitor
Dieses Dokument ermöglicht einem neuen Chat-Kontext, die Arbeit am Projekt nahtlos fortzuführen.
---
## Worum geht es?
**Assignment Monitor** ist eine PWA für Lars Stommer (Capgemini Management Consultant Trainee Program Lead). Die App wird auf dem Smartphone genutzt, um Trainees (intern: „Institutees") während ihrer Kunden-Assignments live zu begleiten und zu bewerten. Lars führt die Meetings, beobachtet die Trainees und möchte sofort Notizen und Bewertungen erfassen, ohne Stift/Papier.
## Tech-Stack
- **React 18 + TypeScript + Vite** (PWA)
- **Tailwind CSS v4** (`@import "tailwindcss"` — kein `tailwind.config.js`!)
- **Dexie.js v5** als IndexedDB-Wrapper (lokale DB im Browser, kein Backend)
- **React Router v6**
- **Capgemini-Brandfarbe:** `#0070AD`
- Arbeitsverzeichnis: `C:\dev\AssigmentMonitor`
## Bewertungsskala (kritisch für alle Berechnungen)
```typescript
type FeedbackRating = 'na' | 'not_client_ready' | 'partially_client_ready' | 'nearly_client_ready' | 'client_ready'
// Numerisch: na=0, not=1, partially=2, nearly=3, fully=4
// Labels: N/A, Not, Partially, Nearly, Fully
// Farben: grau, rot, orange, hellgrün, dunkelgrün
// N/A gilt als "nicht gesetzt" — aus allen Berechnungen ausgeschlossen!
```
Aggregation: **gewichteter Durchschnitt** (avg < 1.5 → Not, < 2.5 → Partially, < 3.5 → Nearly, ≥ 3.5 → Fully)
## Datenmodell-Kern (was zu wissen ist)
### Schlüssel-Unterschied: zwei `criteriaId`-Semantiken
```
Assessment.criteriaId → FeedbackCriterionItem.id (Gesamtbewertung nach Meeting)
ConversationSkillScore.criteriaId → FeedbackCategory.id (Schnellbewertung während Gespräch)
```
Das klingt inkonsistent — ist aber so gewollt und funktioniert so.
### AssignmentType.criteriaIds
```typescript
// Leer = alle Kriterien anzeigen
// Befüllt = nur diese FeedbackCriterionItem.id anzeigen
criteriaIds?: number[] // → FeedbackCriterionItem.id
```
Filterlogik (in MeetingView + AssignmentDetail):
```typescript
const itemFilter = aType?.criteriaIds?.length ? new Set(aType.criteriaIds) : null
const filteredItems = itemFilter ? allItems.filter(i => itemFilter.has(i.id!)) : allItems
const visibleCatIds = new Set(filteredItems.map(i => i.categoryId))
const filteredCats = itemFilter ? allCats.filter(c => visibleCatIds.has(c.id!)) : allCats
```
### Dateiname für Meeting-Exporte
```typescript
`${assignment.assignmentNumber ?? assignment.title}_${date}_${template.label}${phaseIndex > 1 ? `-${phaseIndex}` : ''}.${ext}`
// Beispiel: CGI-2024-001_02-07-2026_Alignment Call-2.md
```
## Routing-Übersicht
```
/ Dashboard
/assignment/new Neues Assignment
/assignment/:id AssignmentDetail
/assignment/:id/edit AssignmentEdit
/assignment/:id/meeting/:meetingId MeetingView ← WICHTIG: meetingId != assignmentId
/assignment/:id/feedback/:instituteeId AssignmentFeedbackPage
/assignment/:id/feedback-draft/:instituteeId FeedbackDraftPage (Legacy)
/evaluation Auswertung (veraltet!)
/consultants Berater
/config Konfiguration (3 Tabs)
```
MeetingView liest den Param als: `const { id, meetingId: meetingIdParam } = useParams()`
## Konfigurationsbereich
**Tab "Feedback"** (`FeedbackStructureConfig.tsx`):
- Dimensionen → Kategorien → Kriterium-Items (mit Gewichtung ×1–×5)
- Nur Feedback-Struktur, kein AssignmentType-CRUD mehr!
**Tab "Ass.-Typen"** (`AssignmentTypeConfig.tsx`):
- AssignmentTypen CRUD
- Pro Typ: Sub-Tabs "Phasen" und "Kriterien"
- Kriterien-Tab: Baumstruktur Dimension → Kategorie → Items (Checkbox, Indeterminate)
**Tab "Backup"** (`Configuration.tsx`):
- JSON-Export aller Tabellen via `exportFullBackup()`
- JSON-Import via `importFullBackup(file)` (löscht alles, dann bulkAdd)
## Bekannte offene Punkte (Priorität nach Dringlichkeit)
### Sofort-Fixes (einfach)
1. **Dashboard zeigt keine Assignment-Nummer** — `Dashboard.tsx` Zeile ~57: `{a.assignmentNumber && ...{a.assignmentNumber}}` hinzufügen
2. **AssignmentCreate hat kein Nummer-Feld** — Inputfeld für `assignmentNumber` hinzufügen
3. **Toter Code in FeedbackStructureConfig** — State-Variablen `assignmentTypes`, `phaseTemplates`, `showCatalogs`, `showTypeConfig`, `expandedType`, `newTypeName`, `newPhase`, `typeError` + alle Handler `toggleCatalogCategory`, `addAssignmentType`, `updateAssignmentTypeName`, `deleteAssignmentType`, `addPhaseTemplate`, `updatePhaseTemplate`, `deletePhaseTemplate` entfernen
### Mittlere Aufgaben
4. **Evaluation.tsx neu schreiben** — nutzt `db.categories` + `db.criteria` (Legacy). Soll `feedbackCriterionItems` + `assessments` nutzen und Rating-Labels zeigen
5. **meetingExport.ts anpassen** — Score-Aufschlüsselung nutzt `db.criteria`, sollte `feedbackCriterionItems` nutzen; Kategorie-Namen im Markdown fehlen
6. **AI-Prompt in AssignmentFeedbackPage** — nutzt `db.criteria` (alt), sollte `feedbackCriterionItems` + neue Assessment-Scores nutzen
### Große Aufgaben
7. **SQLite WASM + OPFS** — Migration weg von IndexedDB. Konzept steht, Implementierung steht aus.
## Stil-Regeln (bitte einhalten)
- **Tailwind v4**: kein `bg-[#0070AD]` via config, direkt als CSS-Variable nicht nötig — `bg-[#0070AD]` im Class-String funktioniert direkt
- **Keine Kommentare** außer für nicht-offensichtliche Invarianten
- **Kein `rows={1}` + JS-Autoresize** — besser View/Edit-Toggle oder `fieldSizing: 'content'` via Inline-Style
- **Rating-Buttons** immer mit `RATING_OPTIONS`-Array (nicht neu definieren):
```typescript
{ value, short, color (inaktiv), active (aktiv) }
```
- **Weighted-Average** immer: N/A excludieren, dann `Σ(score×weight)/Σ(weight)`
## Notations-Parser
`src/utils/notationParser.tsx` exportiert `` — rendert `+`, `(+)`, `!`, `(!)`, `>`, `[…]` farbig. Überall verwenden wo Protokoll-Text angezeigt wird.
## DB-Backup
`src/utils/dbBackup.ts` exportiert:
```typescript
exportFullBackup(): Promise // Download als JSON
importFullBackup(file: File): Promise<{ ok: boolean; error?: string }> // Clears + bulkAdd
```
Alle 20 Tabellen-Namen sind in `TABLE_NAMES` aufgelistet — bei neuen Tabellen dort ergänzen.
## Seed-Daten
`src/main.tsx` ruft beim Start auf:
1. `seedIfEmpty()` — 3 AssignmentTypen + Legacy-Kriterien
2. `seedFeedbackStructureIfEmpty()` — 5 Dimensionen, 14 Kategorien, ~70 Items
3. `seedCriterionMappingsIfEmpty()` — Legacy-Mappings
4. `migrateToUnifiedCriteriaIfNeeded()` — Migration auf FeedbackCategory-IDs in PhaseTemplates
## Datenschutz-Hinweise
- Alle Daten lokal, kein Cloud-Backend
- GDPR-relevant: Institutee-Namen + Kundennamen in der DB
- KI-API: OpenRouter, API-Key in localStorage (nicht im Klartext loggen!)
- Backup-JSON enthält alle personenbezogenen Daten → vertraulich behandeln
## TypeScript-Build
```bash
npx tsc --noEmit # Muss ohne Fehler durchlaufen
npm run dev # Dev-Server starten
```
Vor jeder Abgabe: `npx tsc --noEmit` ausführen und alle Fehler beheben.
## Wichtige Eigenheiten / Fallstricke
1. **`MeetingInstance.generalNotes`**: Kann in alten Datensätzen `undefined` sein → immer `?? ''` fallback
2. **`Assessment.criteriaId`**: Zeigt auf `FeedbackCriterionItem.id`, NICHT auf `FeedbackCategory.id` — das verwirrt, weil der Feldname `criteriaId` ist
3. **`PhaseKey`**: Ist `type PhaseKey = string` (kein Union-Typ mehr)
4. **`aType2`** in `AssignmentDetail.tsx`: Doppelte Variable aus Refactoring — `aType` wird zweimal deklariert, zweite heißt `aType2`. Technische Schuld.
5. **`expandedCat`** in `MeetingView`: `Record` — öffnet pro Institutee genau eine Kategorie gleichzeitig
6. **Dexie v5**: `bulkAdd` schlägt fehl wenn IDs schon vorhanden → Backup-Import leert erst alle Tabellen