CG-Feedback-Monitor/docs/HANDOVER.md
2026-07-02 10:10:06 +02:00

170 lines
7.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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 && <span>...{a.assignmentNumber}</span>}` 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 `<NotationText text={string} />` rendert `+`, `(+)`, `!`, `(!)`, `>`, `[…]` farbig. Überall verwenden wo Protokoll-Text angezeigt wird.
## DB-Backup
`src/utils/dbBackup.ts` exportiert:
```typescript
exportFullBackup(): Promise<void> // 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<instituteeId, categoryId | null>` öffnet pro Institutee genau eine Kategorie gleichzeitig
6. **Dexie v5**: `bulkAdd` schlägt fehl wenn IDs schon vorhanden Backup-Import leert erst alle Tabellen