26 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 - Lokaler Node/Express-Server + SQLite (seit 2026-07-04) — löst Dexie/IndexedDB als primären Datenspeicher ab, siehe eigener Abschnitt unten
- React Router v6
- Capgemini-Brandfarbe:
#0070AD(Tailwind-Klassebrand, JS-KonstanteBRAND_COLORinsrc/config/constants.ts)
Projektstruktur (Stand 2026-07-04)
src/
config/constants.ts ← RATING_OPTIONS, INST_COLORS, AI_CONFIG, RATING_NUM_MAP, BRAND_COLOR, DEFAULT_RATING_TREND_WEIGHT_STEP
db/
index.ts ← Re-Export (types + queries), rpcClient.ts eingebunden
types.ts ← alle Interfaces, FeedbackRating, AppSettings — von Client UND Server importiert
rpcClient.ts ← rpc(module, fn, ...args) — POST /api/rpc, einziger Netzwerk-Kontaktpunkt des Clients
queries/ ← Client-DB-Zugriffsschicht (siehe eigener Abschnitt unten)
schema.ts, seeds/ ← Dexie/IndexedDB-Legacy-Code, wird nicht mehr gelesen (siehe Datenbank-Abschnitt)
pages/
meeting/ ← MeetingView.tsx, ConversationTab.tsx, AssessmentTab.tsx, CriterionTagPicker.tsx
RatingWeightConfig.tsx ← Settings-Tab für Zeitgewichtungs-Faktor (in Configuration.tsx eingebunden)
AiPromptConfig.tsx ← Settings-Tab „KI-Prompts" — ein editierbares Prompt-Template je Dimension, Default-Vorbelegung + Reset (in Configuration.tsx eingebunden, seit 2026-07-07)
...übrige Pages unverändert am alten Ort
utils/
ratingTrend.ts ← zeitgewichteter Bewertungs-Vorschlag + Trend-Erkennung + weightedAverageScore/weightedAverageRating (genutzt von Evaluation.tsx, AssignmentFeedbackPage.tsx, AssessmentTab.tsx, AssignmentDetail.tsx)
aiPrompt.ts ← Prompt-Engine für die KI-Feedback-Generierung (seit 2026-07-07): Platzhalter-Kontext pro Dimension, Rendering, Antwort-Parsing — siehe eigener Abschnitt „KI-Feedback-Generierung" unten
server/
index.ts ← Express-App, ein Endpunkt POST /api/rpc
database.ts ← node:sqlite-Verbindung (server/data/assignment-monitor.sqlite3), Schema aus tableRegistry.ts
rpcModules.ts ← Dispatch-Registry { appSettings, consultants, ..., backup } → server/db/queries/*.ts
db/
tableRegistry.ts ← EINE Quelle der Wahrheit: alle 21 Tabellen + Spalten + JSON-/Bool-Codecs (treibt Schema UND Backup)
crud.ts ← generische SQL-CRUD-Bausteine über die Registry
queries/ ← Server-Pendant zu src/db/queries/*.ts, gleiche Funktionsnamen, echtes SQL statt Dexie
scripts/verifyBackupRoundtrip.ts ← automatisierter Vollständigkeits-Beweis für Backup-Export/-Import
Datenbank: lokaler Server statt IndexedDB (seit 2026-07-04)
Grund: Mehrere Browser (Chrome, Edge, ...) auf demselben Laptop sollen dieselben Daten sehen, unabhängig vom Browser-Cache — als Testschritt vor einem späteren Umzug auf einen Docker-Container auf einem Linux-Server. Browser können keine SQLite-Datei direkt öffnen, daher läuft ein kleiner Node/Express-Server (server/, Port 4000) mit node:sqlite (Node-eingebautes SQLite-Modul, kein better-sqlite3!). Alle Browser sprechen über http://localhost:5173 (Vite-Dev-Proxy /api → http://localhost:4000) denselben Server an.
Abweichung vom ursprünglich gewählten Stack: better-sqlite3 scheiterte lokal an einer fehlenden/kaputten Python-Installation (node-gyp-Kompilierung). node:sqlite bietet dieselbe synchrone API (DatabaseSync, .prepare().run()/.get()/.all()), braucht keine native Kompilierung und ist ab dieser Node-Version (25.x) ohne Flag nutzbar (Node meldet es noch als "experimental" — rein informativ, funktioniert stabil). Falls auf einem anderen Rechner/Server node:sqlite fehlt (ältere Node-Version), wäre better-sqlite3 dort nachzuholen, sofern eine funktionierende Build-Toolchain (Python + C++-Compiler) vorhanden ist.
Spalten-Migration bei bestehenden Tabellen (seit 2026-07-07): database.ts legt fehlende Tabellen per CREATE TABLE IF NOT EXISTS an — das migriert aber nie eine bereits existierende Tabelle. Wird tableRegistry.ts um eine neue Spalte einer bereits vorhandenen Tabelle ergänzt (z.B. suggestedAchievements auf feedbackDimensionTexts), gleicht database.ts das zusätzlich per PRAGMA table_info ab und zieht fehlende Spalten per ALTER TABLE ... ADD COLUMN automatisch nach. Ohne das bleibt die alte Spaltenmenge bestehen und jeder Zugriff auf die neue Spalte scheitert erst zur Laufzeit mit „no column named ..." — genau das ist beim Einführen des KI-Prompt-Systems passiert und wurde dabei gefixt.
RPC statt REST: Ein einziger Endpunkt POST /api/rpc mit Body { module, fn, args } statt ~80 einzelnen Routen — Client (src/db/rpcClient.ts) und Server (server/rpcModules.ts) sind darüber 1:1 gekoppelt: jede Funktion aus src/db/queries/*.ts hat ein gleichnamiges Pendant in server/db/queries/*.ts.
Dexie/IndexedDB ist toter Code, aber noch nicht entfernt: src/db/schema.ts und src/db/seeds/*.ts bleiben unverändert im Repo liegen, werden aber von nichts mehr gelesen (Seed-Aufrufe in main.tsx wurden entfernt). Vollständiges Entfernen ist eine spätere, bewusste Entscheidung.
Backup ist jetzt server-seitig: src/utils/dbBackup.ts ruft rpc('backup', 'exportAll'/'importAll', ...) statt direkt gegen Dexie zu laufen. server/db/queries/backup.ts iteriert exakt tableRegistry.ts — dieselbe Liste, die auch das SQL-Schema erzeugt, damit eine neue Tabelle nicht mehr unbemerkt aus dem Backup fallen kann (das ist genau einmal mit appSettings passiert, siehe Erledigt-Historie). server/scripts/verifyBackupRoundtrip.ts (npm run verify-backup -- <pfad>) beweist automatisiert Zeilenzahl- und Inhaltsgleichheit pro Tabelle nach einem Import→Export-Zyklus.
DB-Zugriffsschicht (src/db/queries/, seit 2026-07-04, intern auf RPC umgestellt)
Alle Komponenten greifen auf Daten ausschließlich über benannte Funktionen aus src/db/queries/*.ts zu (re-exportiert über '../db') — nach außen unverändert seit der ursprünglichen Extraktion, nur die Innenseite ruft jetzt rpc(...) statt db.<table>.* auf. Ein Modul pro Domäne: appSettings.ts, consultants.ts (Groups+Consultants), assignmentTypes.ts (AssignmentTypes+PhaseTemplates), assignments.ts, feedbackStructure.ts (Dimensions/Categories/CriterionItems — filterVisibleFeedbackStructure() bleibt reine Client-Funktion ohne RPC), meetings.ts (MeetingInstances inkl. kanonischem !deletedAt && status==='done'-Filter als listDoneMeetingsChronological(), Cascade-Deletes, Export-Datenaggregation getMeetingExportData()), conversationEntries.ts, assessments.ts, assignmentFeedback.ts.
Entwicklung
npm run dev:all # Frontend (Vite, :5173) + lokaler Server (Express, :4000) zusammen starten
npm run dev # Nur Frontend (Server muss separat laufen, sonst laufen alle DB-Zugriffe ins Leere)
npm run server # Nur den lokalen Server
npx tsc -b # TypeScript-Check über alle 3 Projekte (App/Node/Server) — muss vor jeder Abgabe fehlerfrei sein
Wichtig: npx tsc --noEmit am Root prüft nichts (0 Dateien, tsc --noEmit --listFiles bestätigt das) — der Root-tsconfig.json hat "files": [] und nur references. Ohne -b (Build-Modus) werden referenzierte Projekte nicht ausgewertet. Der bisher in diesem Dokument dokumentierte Workflow npx tsc --noEmit war dadurch vermutlich seit Einführung der Project References ein stiller No-Op — immer npx tsc -b verwenden.
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: filterVisibleFeedbackStructure(allCategories, allItems, criteriaIds) in src/db/queries/feedbackStructure.ts — seit dem db/queries/-Refactoring (2026-07-04) die einzige Implementierung, vorher identisch dupliziert in MeetingView.tsx und AssignmentDetail.tsx. Nicht erneut inline schreiben.
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. Seit dem db/queries/-Refactoring (2026-07-04) eine Implementierung: weightedAverageScore()/weightedAverageRating() in src/utils/ratingTrend.ts, genutzt von buildGroupMeetingScores(), catMode() (AssessmentTab.tsx) und der Meeting-Chip-Anzeige in AssignmentDetail.tsx (dort fehlte der Σweight===0-Guard vorher — beim Konsolidieren mitgefixt). Bei jedem neuen gewichteten Durchschnitt diese Funktion wiederverwenden, nicht neu implementieren.
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. - Cross-Boundary-Imports von
src/nachserver/: Dateien, die (wiesrc/db/types.ts) sowohl vom Client als auch vom Server importiert werden, müssen frei von Laufzeit-Abhängigkeiten auf Browser-Only-Code sein — auchimport typereicht nicht als Schutz, wenn die importierte Datei selbst wieder aus dem'../db'-Barrel importiert (der zieht Dexie/rpcClient/Seeds mit, siehesrc/config/constants.ts-Fix vom 2026-07-07: Import auf'../db/types'direkt umgestellt). Jede neue Datei dieser Art muss zusätzlich explizit intsconfig.server.jsonsincludeaufgenommen werden (sonst „File is not listed within the file list of project" bzw. sie wird stillschweigend nicht mitkompiliert) — Muster:src/db/types.ts,src/config/constants.ts. 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 |
|---|---|---|
| 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 |
| M | Migration der echten Daten aussteht: Aktuelle IndexedDB-Daten müssen noch einmalig über Config → Backup → Export (alte Version) exportiert und über Config → Backup → Import (neue, serverbasierte Version) in die SQLite-DB eingespielt werden. Siehe Datenbank-Abschnitt oben. | — |
| L | Dexie/IndexedDB-Legacy-Code vollständig entfernen (src/db/schema.ts, src/db/seeds/) — aktuell bewusst nur stillgelegt, nicht gelöscht |
src/db/schema.ts, src/db/seeds/ |
| L | Server-seitiges "Seed-if-empty" für Assignment-Typen/Feedback-Struktur (aktuell nur clientseitig vorhanden und nicht mehr wirksam) — nicht dringend, da der Backup-Import denselben Zweck erfüllt | server/db/queries/ (neu) |
| L | SQLite WASM + OPFS Migration — durch den lokalen Server-Ansatz (2026-07-04) überholt/hinfällig | src/db/schema.ts |
Erledigt (2026-07-07): KI-Feedback-Generierung neu konzipiert und umgesetzt — löst den zuvor diagnostizierten response_format/Markdown-Codefence-Bug strukturell statt punktuell. Statt einem Monolith-Call für alle Dimensionen macht generateAllDimensions() (AssignmentFeedbackPage.tsx) jetzt sequenziell einen Call pro Dimension, mit Fortschrittsanzeige und Teil-Fehlerbehandlung (fehlgeschlagene Dimensionen einzeln erneut versuchbar, dimensionErrors). Jede Dimension hat einen vollständig frei editierbaren Prompt (FeedbackDimensionPrompt, neuer Admin-Tab „KI-Prompts" → AiPromptConfig.tsx), der bei addFeedbackDimension() automatisch mit einem Default-Template vorbelegt wird (AI_CONFIG.defaultDimensionPromptTemplate) — neue Dimensionen funktionieren dadurch ohne Zutun. Antwortformat ist jetzt einfacher Text statt JSON (KATEGORIE:/ACHIEVEMENTS:/DEVELOPMENT_NEEDS:-Marker, geparst in src/utils/aiPrompt.ts::parseDimensionAiResponse), da pro Call ohnehin nur eine Dimension behandelt wird — macht den JSON/Codefence-Bug strukturell unmöglich statt ihn nur zu patchen. KI-generierte Texte landen zunächst als Vorschlag (suggestedAchievements/suggestedDevelopmentNeeds auf FeedbackDimensionText, suggestedText auf neuer Tabelle FeedbackCategoryText) und werden erst durch expliziten „Übernehmen"-Klick in die echten Felder kopiert (gleiches Muster wie die bestehende Rating-Vorschlags-Badge) — persistiert, übersteht also einen Seitenwechsel. Neu: kurzer KI-Text zusätzlich auf Kategorie-Ebene (FeedbackCategoryText), nicht nur auf Dimension-Ebene. Cross-Boundary-Falle dabei gefunden: src/config/constants.ts importierte bisher FeedbackRating aus dem Barrel '../db' (re-exportiert Dexie/rpcClient/Seeds) statt direkt aus '../db/types' — beim Import von AI_CONFIG in server/db/queries/feedbackStructure.ts hätte das transitiv Browser-only-Code (fetch, localStorage) in den Server-TS-Scope gezogen; auf direkten Typ-Import umgestellt und src/config/constants.ts zusätzlich explizit in tsconfig.server.jsons include aufgenommen (gleiches Muster wie das dort schon gelistete src/db/types.ts). Nachträglich (gleicher Tag, nach erstem Testlauf) zwei Lücken gefixt/ergänzt: (1) database.ts migrierte nur neue Tabellen, nie Spalten einer bereits bestehenden Tabelle — suggestedAchievements u.ä. auf feedbackDimensionTexts fehlten dadurch auf bereits laufenden Installationen und scheiterten erst zur Laufzeit; jetzt gleicht database.ts zusätzlich per PRAGMA table_info ab und zieht fehlende Spalten per ALTER TABLE ... ADD COLUMN nach (siehe Datenbank-Abschnitt oben). (2) Prompt-Vorschau ergänzt: pro Dimension ein „👁 KI-Prompt ansehen"-Toggle auf AssignmentFeedbackPage.tsx, der den vollständig gerenderten Prompt (Template + aufgelöste Platzhalter) ohne API-Call anzeigt — Feedback war, dass die generierten Texte „brauchbar, aber nicht konkret genug" waren und ohne Einblick in den tatsächlich gesendeten Prompt nicht nachvollziehbar. Zusätzlich neues optionales Feld FeedbackCriterionItem.description (editierbar in FeedbackStructureConfig.tsx, direkt unter dem Kriteriumsnamen), das automatisch in {{KRITERIEN_SCORES}} einfließt (src/utils/aiPrompt.ts) — macht die KI-Vorgabe pro Kriterium einmal zentral pflegbar statt in jedem Dimension-Prompt wiederholt werden zu müssen. Bewusst nicht reaktiviert: die bereits vorhandene, aber seit Langem unverdrahtete Legacy-Tabelle CriterionLevelDescription (Text pro Kriterium UND Rating-Stufe) — wäre eine präzisere Rubrik gewesen, aber deutlich mehr Pflege-/UI-Aufwand; bei Bedarf spätere, bewusste Entscheidung.
Erledigt (2026-07-04, zweite Runde): Lokaler Node/Express-Server mit node:sqlite (statt IndexedDB) eingeführt, RPC-Bridge (POST /api/rpc) zwischen src/db/queries/*.ts (Client) und neuem server/db/queries/*.ts (Server) — siehe eigener Abschnitt "Datenbank: lokaler Server statt IndexedDB" oben für Details, Abweichungen (node:sqlite statt better-sqlite3) und die wichtige npx tsc -b-statt---noEmit-Korrektur. Ziel: mehrere Browser auf demselben Laptop teilen sich dieselben Daten, unabhängig vom Browser-Cache, als Zwischenschritt vor einem Docker/Linux-Deployment.
Erledigt (2026-07-04): db/queries/-Layer eingeführt — alle 20 Komponenten/Utils mit vorherigem direktem db.<table>.*-Zugriff nutzen jetzt benannte Funktionen aus src/db/queries/*.ts (siehe eigener Abschnitt oben), reine Extraktion ohne beabsichtigte Verhaltensänderung. Dabei bewusst mitkonsolidiert: die drei unabhängig implementierten Varianten der gewichteten-Durchschnitt-Logik (ratingTrend.ts, AssessmentTab.tsx, AssignmentDetail.tsx) laufen jetzt über eine einzige Funktion weightedAverageScore()/weightedAverageRating() in ratingTrend.ts — dabei einen fehlenden Σweight===0-Guard in AssignmentDetail.tsx gefixt (identische Bug-Klasse wie die zweite Bug-Klasse oben, dort war er noch nicht behoben). Ebenfalls konsolidiert: die "AssignmentType.criteriaIds schränkt sichtbare Kriterien ein"-Filterlogik (vorher dupliziert in MeetingView.tsx/AssignmentDetail.tsx, jetzt filterVisibleFeedbackStructure()) und die doppelten Datenabfragen in meetingExport.tss Markdown-/JSON-Export (jetzt getMeetingExportData() in meetings.ts). dbBackup.ts und Seed-Code bleiben bewusst bei direktem db-Zugriff (siehe Abschnitt oben). npx tsc --noEmit läuft fehlerfrei.
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 — seit 2026-07-04 in
server/data/assignment-monitor.sqlite3(lokaler Server auf demselben Rechner) statt im Browser-IndexedDB; weiterhin kein Cloud-Upload, Server ist nur unterlocalhosterreichbar (kein Auth-Schutz nötig, solange das so bleibt — siehe Offene Punkte, falls sich das ändert, z.B. bei Docker/Linux-Deployment mit Netzwerkzugriff) server/data/ist per.gitignoreausgeschlossen — Datenbankdatei nie committen- 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