102 lines
3.6 KiB
Markdown
102 lines
3.6 KiB
Markdown
# 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, **kein** `tailwind.config.js`
|
|
- Dexie.js v5 — IndexedDB-Wrapper, kein Backend
|
|
- React Router v6
|
|
- Capgemini-Brandfarbe: `#0070AD`
|
|
|
|
## Entwicklung
|
|
|
|
```bash
|
|
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)
|
|
```
|
|
|
|
### AssignmentType.criteriaIds
|
|
|
|
Referenziert `FeedbackCriterionItem.id[]`. Leer = alle Kriterien anzeigen.
|
|
|
|
Filterlogik (immer so anwenden):
|
|
```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
|
|
```
|
|
|
|
### Rating-Skala
|
|
|
|
```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
|
|
// 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
|
|
|
|
### Routing
|
|
|
|
```
|
|
/assignment/:id/meeting/:meetingId ← meetingId ist MeetingInstance.id, nicht Assignment-ID
|
|
```
|
|
|
|
MeetingView liest: `const { id, meetingId: meetingIdParam } = useParams()`
|
|
|
|
### Sonstige Fallstricke
|
|
|
|
- `MeetingInstance.generalNotes` kann `undefined` sein → immer `?? ''` als Fallback
|
|
- `PhaseKey` ist `string`, kein Union-Typ
|
|
- Tailwind v4: arbitrary values `bg-[#0070AD]` direkt im className — kein Config nötig
|
|
|
|
---
|
|
|
|
## 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 oder `fieldSizing: 'content'` als Inline-Style
|
|
- **`RATING_OPTIONS`-Array** nicht neu definieren — immer aus dem bestehenden Array in der jeweiligen Datei verwenden
|
|
- **Kein `useEffect` für DB-Calls** außer bei initialem Load — lieber direkte `async`-Funktionen
|
|
|
|
---
|
|
|
|
## Offene Punkte (priorisiert)
|
|
|
|
| Prio | Aufgabe | Datei |
|
|
|---|---|---|
|
|
| M | `Evaluation.tsx` auf neue Datenstruktur migrieren | `src/pages/Evaluation.tsx` |
|
|
| M | AI-Prompt auf `feedbackCriterionItems` umstellen | `src/pages/AssignmentFeedbackPage.tsx` |
|
|
| M | `meetingExport.ts` — Kriterien-Auflösung anpassen | `src/utils/meetingExport.ts` |
|
|
| N | Dashboard: Assignment-Nummer anzeigen | `src/pages/Dashboard.tsx` |
|
|
| N | AssignmentCreate: Feld für Assignment-Nummer | `src/pages/AssignmentCreate.tsx` |
|
|
| N | Toter Code in FeedbackStructureConfig entfernen | `src/pages/FeedbackStructureConfig.tsx` |
|
|
| L | SQLite WASM + OPFS Migration (kein festes Datum) | `src/db/index.ts` |
|
|
|
|
---
|
|
|
|
## 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
|