7.8 KiB
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"— keintailwind.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)
- Dashboard zeigt keine Assignment-Nummer —
Dashboard.tsxZeile ~57:{a.assignmentNumber && <span>...{a.assignmentNumber}</span>}hinzufügen - AssignmentCreate hat kein Nummer-Feld — Inputfeld für
assignmentNumberhinzufügen - Toter Code in FeedbackStructureConfig — State-Variablen
assignmentTypes,phaseTemplates,showCatalogs,showTypeConfig,expandedType,newTypeName,newPhase,typeError+ alle HandlertoggleCatalogCategory,addAssignmentType,updateAssignmentTypeName,deleteAssignmentType,addPhaseTemplate,updatePhaseTemplate,deletePhaseTemplateentfernen
Mittlere Aufgaben
- Evaluation.tsx neu schreiben — nutzt
db.categories+db.criteria(Legacy). SollfeedbackCriterionItems+assessmentsnutzen und Rating-Labels zeigen - meetingExport.ts anpassen — Score-Aufschlüsselung nutzt
db.criteria, solltefeedbackCriterionItemsnutzen; Kategorie-Namen im Markdown fehlen - AI-Prompt in AssignmentFeedbackPage — nutzt
db.criteria(alt), solltefeedbackCriterionItems+ neue Assessment-Scores nutzen
Große Aufgaben
- 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 oderfieldSizing: '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:
seedIfEmpty()— 3 AssignmentTypen + Legacy-KriterienseedFeedbackStructureIfEmpty()— 5 Dimensionen, 14 Kategorien, ~70 ItemsseedCriterionMappingsIfEmpty()— Legacy-MappingsmigrateToUnifiedCriteriaIfNeeded()— 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
MeetingInstance.generalNotes: Kann in alten Datensätzenundefinedsein → immer?? ''fallbackAssessment.criteriaId: Zeigt aufFeedbackCriterionItem.id, NICHT aufFeedbackCategory.id— das verwirrt, weil der FeldnamecriteriaIdistPhaseKey: Isttype PhaseKey = string(kein Union-Typ mehr)aType2inAssignmentDetail.tsx: Doppelte Variable aus Refactoring —aTypewird zweimal deklariert, zweite heißtaType2. Technische Schuld.expandedCatinMeetingView:Record<instituteeId, categoryId | null>— öffnet pro Institutee genau eine Kategorie gleichzeitig- Dexie v5:
bulkAddschlägt fehl wenn IDs schon vorhanden → Backup-Import leert erst alle Tabellen