- buildAiPrompt (AssignmentFeedbackPage.tsx): 'na' wird jetzt korrekt aus dem Score-Mittelwert ausgeschlossen (war bislang als 0 mitgerechnet) - FeedbackDraftPage.tsx auf feedbackCriterionItems/feedbackCategories migriert, letzter Konsument der Legacy-Tabellen außer den Seeds. Notizen hängen jetzt korrekt pro Kriterium über lineTags, statt ungefiltert an jedes Kriterium. Score-Anzeige nutzt RATING_OPTIONS-Labels statt rohem /5-Wert. - FeedbackStructureConfig.tsx: verwaisten AssignmentType/PhaseTemplate-CRUD-Code entfernt (Funktionalität war bereits nach AssignmentTypeConfig.tsx verschoben) - Doku aktualisiert: CLAUDE.md, docs/HANDOVER.md, docs/STATUS_UND_OFFENE_PUNKTE.md, docs/TECHNISCHE_ARCHITEKTUR.md Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
13 KiB
CLAUDE.md — Assignment Monitor
Progressive Web App zur Live-Bewertung von Capgemini-Trainees (Institutees) während Kunden-Assignments.
Vollständige Dokumentation: docs/ — bei neuer Session zuerst docs/HANDOVER.md lesen.
Stack
- React 18 + TypeScript + Vite (PWA)
- Tailwind CSS v4 —
@import "tailwindcss"in CSS, keintailwind.config.js; Brandfarbe als@theme { --color-brand }insrc/index.css - Dexie.js v5 — IndexedDB-Wrapper, kein Backend
- React Router v6
- Capgemini-Brandfarbe:
#0070AD(Tailwind-Klassebrand, JS-KonstanteBRAND_COLORinsrc/config/constants.ts)
Projektstruktur (nach Architektur-Refactoring, Stand 2026-07-02)
src/
config/constants.ts ← RATING_OPTIONS, INST_COLORS, AI_CONFIG, RATING_NUM_MAP, BRAND_COLOR, DEFAULT_RATING_TREND_WEIGHT_STEP
db/
index.ts ← reiner Re-Export (types + schema + seeds)
types.ts ← alle Interfaces, FeedbackRating, AppSettings
schema.ts ← Dexie-Klasse + Migrationsversionen
seeds/ ← assignmentTypes.ts, feedbackStructure.ts, migrations.ts
pages/
meeting/ ← MeetingView.tsx, ConversationTab.tsx, AssessmentTab.tsx, CriterionTagPicker.tsx
RatingWeightConfig.tsx ← Settings-Tab für Zeitgewichtungs-Faktor (in Configuration.tsx eingebunden)
...übrige Pages unverändert am alten Ort
utils/
ratingTrend.ts ← zeitgewichteter Bewertungs-Vorschlag + Trend-Erkennung (genutzt von Evaluation.tsx & AssignmentFeedbackPage.tsx)
Bestehende Imports aus '../db' funktionieren weiterhin unverändert (Re-Export).
Entwicklung
npm run dev # Dev-Server starten
npx tsc --noEmit # TypeScript-Check — muss vor jeder Abgabe fehlerfrei sein
Kritische Eigenheiten
Doppelte criteriaId-Semantik
Assessment.criteriaId → FeedbackCriterionItem.id (Gesamtbewertung)
ConversationSkillScore.criteriaId → FeedbackCategory.id (Schnellbewertung pro Protokoll-Eintrag)
ConversationLineTag.criterionItemId → FeedbackCriterionItem.id (Kriterium-Zuordnung einer einzelnen Zeile innerhalb eines ConversationEntry.note-Blocks, seit 2026-07-02 — bewusst anders benannt als "criteriaId" um obige Verwechslungsgefahr nicht fortzuschreiben)
AssignmentType.criteriaIds
Referenziert FeedbackCriterionItem.id[]. Leer = alle Kriterien anzeigen.
Filterlogik (immer so anwenden):
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
Rating-Skala
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
// N/A gilt als "nicht gesetzt" — aus allen Berechnungen ausschließen!
Aggregation: gewichteter Durchschnitt → avg < 1.5 Not · < 2.5 Partially · < 3.5 Nearly · ≥ 3.5 Fully
Bug-Klasse, auf die immer prüfen: Filter der Form a.score !== null schließen N/A NICHT aus (N/A ist der String 'na', nicht null!). Richtig ist immer a.score !== null && a.score !== 'na'. Gefunden und gefixt in Evaluation.tsx (2026-07-02); dieselbe Prüfung nötig überall, wo Assessment.score oder ConversationSkillScore.score gemittelt wird.
Zweite Bug-Klasse, auf die immer prüfen (Kriterium-Gewichtung): FeedbackCriterionItem.weight geht seit 2026-07-03 von 0,0 bis 2,0 (Config → Feedback, vorher grobe ×1–×5-Stufen). Jede gewichtete Mittelung MUSS normieren — Σ(score×weight) / Σ(weight), niemals score×weight direkt als Endwert interpretieren (würde die Bewertungsstufe verzerren, z.B. Score 3 „Nearly" × Gewicht 0,5 = 1,5, was fälschlich zwischen Not/Partially läge). Zusätzlich: wenn Σweight === 0 (z.B. alle beteiligten Kriterien einer Kategorie auf Gewicht 0 gesetzt), nicht dividieren — NaN < 1.5/2.5/3.5 ist überall false und fällt sonst still auf den letzten Bucket „Fully" durch. Immer if (wSum === 0) return null (bzw. continue) vor der Division. Umgesetzt in catMode() (AssessmentTab.tsx) und buildGroupMeetingScores() (ratingTrend.ts) — beim nächsten neuen gewichteten Durchschnitt genauso prüfen.
Routing
/assignment/:id/meeting/:meetingId ← meetingId ist MeetingInstance.id, nicht Assignment-ID
MeetingView liest: const { id, meetingId: meetingIdParam } = useParams()
Sonstige Fallstricke
MeetingInstance.generalNoteskannundefinedsein → immer?? ''als FallbackPhaseKeyiststring, kein Union-Typ- Tailwind v4: Brandfarbe ist
brand(Tailwind-Klasse, via@themeinindex.css) — keinbg-[#0070AD]mehr neu schreiben - Auswertungslogik greift auf zwei komplett unabhängige Bewertungs-Strukturen zu, die nicht automatisch synchron sind:
Assessment(Tabelleassessments) — pro Meeting erfasste Kriterium-Bewertungen, Basis für/evaluationFeedbackCategoryRating— ausschließlich manuell aufAssignmentFeedbackPagegesetzt, unabhängig von Meeting-Daten, Basis für das finale Abschluss-Feedback- Seit 2026-07-02:
src/utils/ratingTrend.tsberechnet aus denAssessment-Daten einen zeitgewichteten Vorschlag pro Kategorie (spätere Meetings zählen stärker, Gewichtungsfaktor konfigurierbar unter Config → Gewichtung) inkl. Trend-Erkennung (Verbesserung/Verschlechterung). Der Vorschlag wird aufAssignmentFeedbackPageals Badge angezeigt und bleibt frei überschreibbar — die zwei Strukturen bleiben bewusst getrennt.
- Meeting-Filter bei Aggregationen über mehrere Meetings: immer
!m.deletedAt && m.status === 'done'— sonst fließen Papierkorb-/unfertige Meetings mit ein - Weichmacher/Füllwörter in Formulierungen (z.B. "ein bisschen") werden bewusst nicht über eine eigene Text-Erkennungslogik behandelt — stattdessen legt man dafür ein eigenes, niedrig gewichtetes Kriterium an (z.B. "Sprachliche Präzision", Gewicht < 1) und taggt/notiert entsprechende Zeilen ganz normal (meist mit
(!)). Die bestehende Tagging- + Notation-Vorschlag-Mechanik deckt das automatisch ab. - Der
äh-Zähler (ConversationEntry.fillerCount) bleibt bewusst rein informativ — keine automatische Schwellenwert-Bewertung. Würde eine eigene Design-Entscheidung brauchen (ab wann gilt die Zahl als "zu hoch"), aktuell reicht die reine Anzeige als Grundlage für die manuelle Bewertung. ConversationEntryentspricht weiterhin einem ganzen Sprecher-Turn (ein zusammenhängender, mehrzeiliger Text-Block bis "✓ Schließen") — bewusst so belassen, ein Zwischenstand mit "ein Entry pro Zeile" wurde am 2026-07-02 wieder verworfen, weil er die Lesbarkeit des Protokolls zerstört hat. Kriterium-Zuordnung passiert stattdessen pro Zeile innerhalb eines Entry überConversationEntry.lineTags(Matching per exaktem Zeilentext, nicht per Index — robust gegenüber nachträglichem Einfügen/Löschen von Zeilen). Zeilen splitten immer übersplitNoteLines()insrc/utils/notationParser.tsx, nicht erneutnote.split('\n')inline schreiben (sonst laufen Trim-/Filter-Verhalten auseinander).
Stil-Regeln
- Keine Kommentare außer für nicht-offensichtliche Invarianten (kein „Was", nur „Warum")
- Keine Auto-Resize-Textarea via
rows={1}+ JS — stattdessen View/Edit-Toggle oderfieldSizing: 'content'als Inline-Style RATING_OPTIONS-Array nicht neu definieren — immer aus dem bestehenden Array in der jeweiligen Datei verwenden- Kein
useEffectfür DB-Calls außer bei initialem Load — lieber direkteasync-Funktionen
Offene Punkte (priorisiert)
| Prio | Aufgabe | Datei |
|---|---|---|
| H | KI-Generierung derzeit funktionsunfähig: generateWithAi() schlägt beim Ausführen fehl, vermutlich blockiert das Capgemini-Netzwerk/Firewall den Call zu openrouter.ai. Root Cause noch nicht untersucht — ggf. mit internem IT-Support klären. |
src/pages/AssignmentFeedbackPage.tsx |
| H | Größeres Vorhaben (noch nicht gescoped): Mehrstufiges, pro Dimension konfigurierbares Prompt-System mit Spezialwissen je Dimension + Platzhaltersystem — Ablösung des aktuellen Einzel-Prompts in buildAiPrompt. Braucht eigene Design-Session. |
src/pages/AssignmentFeedbackPage.tsx |
| M | db/queries/-Layer einführen (Vorbereitung SQLite-Migration, DB-Calls aus Komponenten extrahieren) |
src/db/queries/ (neu) |
| M | Selbstlernendes Tagging (Idee, noch nicht gescoped): Zeile-zu-Kriterium-Zuordnungen sammeln und daraus beim Tippen automatische Tag-Vorschläge ableiten (ähnliche Formulierungen → gleiches Kriterium). Braucht eigene Design-Session (Ähnlichkeits-/Lernmechanik, Datenbasis, UI). | src/pages/meeting/CriterionTagPicker.tsx |
| L | SQLite WASM + OPFS Migration (kein festes Datum) | src/db/schema.ts |
Erledigt (2026-07-02): Konfiguration zentralisiert (config/constants.ts), db/index.ts in types/schema/seeds aufgeteilt, MeetingView.tsx in 3 Dateien aufgeteilt (pages/meeting/), Evaluation.tsx auf feedbackCriterionItems migriert + nach Assignment gruppiert + Papierkorb/Status-Filter + N/A-Bug gefixt, zustand entfernt (war ungenutzt). Zeitgewichteter Bewertungs-Vorschlag mit Trend-Erkennung umgesetzt (src/utils/ratingTrend.ts, neue Tabelle appSettings, neuer Config-Tab „Gewichtung“, Vorschlags-Badge + Übernehmen/Trend-in-Text auf AssignmentFeedbackPage, buildAiPrompt um Trend-Abschnitt erweitert und Legacy-db.criteria-Bug dabei gefixt). Kriterium-Zuordnung für Freitext-Notizen umgesetzt: ConversationEntry.lineTags (Zeilentext → Kriterium, nach einem verworfenen Zwischenstand mit "ein Entry pro Zeile" — siehe Sonstige Fallstricke), CriterionTagPicker.tsx (Chip+Popover mit Suche, unverändert seit erster Version) wird pro Zeile direkt an der Zeile via NotationTexts neuer optionaler renderLineAddon-Prop gerendert; Zuordnung passiert bewusst nur nachträglich (nicht live während des Tippens), um den Schreibfluss nicht zu stören. buildAiPrompt führt Assessment-Notizen und getaggte Zeilen jetzt pro Kriterium zusammen, unzugeordnete Zeilen bleiben im allgemeinen Protokoll-Block. (2026-07-03) Vier Folge-Fixes: meetingExport.ts löste Kriterien über die Legacy-Tabelle db.criteria auf (identischer Bug wie der frühere buildAiPrompt-Fix) — dadurch war die Gesamtbewertung im Export faktisch leer; jetzt auf feedbackCriterionItems umgestellt, Markdown-Export zeigt zusätzlich das Kriterium pro getaggter Protokollzeile. dbBackup.tss TABLE_NAMES enthielt appSettings nicht — Voll-Backups verloren die Gewichtungs-Config stillschweigend; jetzt ergänzt samt Kommentar, dass neue Tabellen dort manuell nachgezogen werden müssen. Notation → Bewertungs-Vorschlag: detectNotationRating() in notationParser.tsx (!→Not, (!)→Partially, (+)→Nearly, +→Fully) kombiniert mit lineTags liefert in AssessmentTab.tsx einen "Vorschlag aus Protokoll"-Badge pro Kriterium-Item, unabhängig vom bestehenden "Aus Protokoll"-Button (andere Datenquelle). AssignmentCreate.tsx: Assignment-Nummer ist jetzt Pflichtfeld (nur auf Formular-Ebene, Typ bleibt optional wegen Altdaten). Kriterium-Gewichtung auf 0,0–2,0 in 0,1-Schritten verfeinert (FeedbackStructureConfig.tsx, vorher ×1–×5), dabei einen latenten Division-durch-Null-Bug gefixt, der durch das neu erlaubte Gewicht 0 real wurde (catMode() in AssessmentTab.tsx, buildGroupMeetingScores() in ratingTrend.ts — siehe zweite Bug-Klasse oben). Sprecher eines bereits erfassten Protokoll-Beitrags kann nachträglich geändert werden (ConversationTab.tsx, im Bearbeiten-Modus neben Füllwörtern — ändert sofort ConversationEntry.instituteeId, kein separater Speichern-Schritt nötig). N/A-Aggregationsbug in buildAiPrompt (AssignmentFeedbackPage.tsx) gefixt — war die letzte offene Stelle dieser Bug-Klasse außer der jetzt migrierten FeedbackDraftPage.tsx. FeedbackDraftPage.tsx vollständig auf feedbackCriterionItems/feedbackCategories migriert (letzter Konsument der Legacy-Tabellen ist jetzt nur noch der Seed-Code), Notizen jetzt korrekt pro Kriterium über lineTags statt aller Protokoll-Notizen ungefiltert an jedes Kriterium gehängt, Score-Anzeige auf RATING_OPTIONS-Labels umgestellt (statt /5-Rohwert). Toter Code (verwaiste AssignmentType/PhaseTemplate-CRUD-Reste) aus FeedbackStructureConfig.tsx entfernt. „Dashboard zeigt keine Assignment-Nummer" war bereits gelöst, nur die Doku war veraltet — richtiggestellt.
Datenschutz
- Alle Daten lokal (IndexedDB) — kein Public Cloud Upload
- GDPR-relevant: Institutee-Namen, Kundennamen, Gesprächsnotizen
- KI-API-Key in
localStorage— nie loggen oder ausgeben - Backup-JSON enthält alle personenbezogenen Daten → vertraulich behandeln