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

7.8 KiB
Raw Blame History

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)

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

// Leer = alle Kriterien anzeigen
// Befüllt = nur diese FeedbackCriterionItem.id anzeigen
criteriaIds?: number[]   // → FeedbackCriterionItem.id

Filterlogik (in MeetingView + AssignmentDetail):

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

`${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-NummerDashboard.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

  1. Evaluation.tsx neu schreiben — nutzt db.categories + db.criteria (Legacy). Soll feedbackCriterionItems + assessments nutzen und Rating-Labels zeigen
  2. meetingExport.ts anpassen — Score-Aufschlüsselung nutzt db.criteria, sollte feedbackCriterionItems nutzen; Kategorie-Namen im Markdown fehlen
  3. AI-Prompt in AssignmentFeedbackPage — nutzt db.criteria (alt), sollte feedbackCriterionItems + neue Assessment-Scores nutzen

Große Aufgaben

  1. 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):
    { 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:

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

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