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

9.3 KiB
Raw Blame History

Technische Architektur — Assignment Monitor

Stack

Schicht Technologie Version Zweck
UI-Framework React 18 Component-basiertes UI
Sprache TypeScript 5 Typsicherheit
Build Vite 6 Dev-Server, HMR, PWA-Plugin
CSS Tailwind CSS v4 Utility-First, @import "tailwindcss"
Datenbank Dexie.js 5 IndexedDB-Wrapper
Routing React Router v6 Client-Side-Routing (SPA)
PWA vite-plugin-pwa Offline-Fähigkeit, installierbar
KI OpenRouter API Feedback-Generierung (extern, opt-in)

Capgemini Brand Color: #0070AD


Projektstruktur

src/
  db/
    index.ts               ← Dexie DB-Klasse, alle Interfaces, Seed-Funktionen, Migrations
  pages/
    Dashboard.tsx           ← Assignment-Liste (Home)
    AssignmentCreate.tsx    ← Neues Assignment anlegen
    AssignmentDetail.tsx    ← Phasen-Flow, Meeting-Übersicht, Bewertungs-Chips
    AssignmentEdit.tsx      ← Assignment nachträglich bearbeiten
    MeetingView.tsx         ← Live-Meeting: Protokoll + Gesamtbewertung
    AssignmentFeedbackPage.tsx ← Strukturiertes Abschluss-Feedback + KI-Generierung
    FeedbackDraftPage.tsx   ← Einfacher Feedback-Entwurf (Legacy)
    Evaluation.tsx          ← Auswertung (teilweise veraltet, nutzt alte Criterion-Tabelle)
    Configuration.tsx       ← Tab-Container für alle Konfigurationsseiten
    FeedbackStructureConfig.tsx ← Dimensionen/Kategorien/Kriterien CRUD + Gewichtung
    AssignmentTypeConfig.tsx ← AssignmentTypen + Phasen + Kriterien-Auswahl (Baum)
    Consultants.tsx         ← Berater/Gruppen-Verwaltung
  utils/
    meetingExport.ts        ← Markdown + JSON Export-Logik
    dbBackup.ts             ← Vollständiges DB-Backup (JSON Export/Import)
    notationParser.tsx      ← +/!/>/[…] Notation → farbiges JSX
  App.tsx                   ← Route-Definitionen, Navigation
  main.tsx                  ← React-Einstiegspunkt, DB-Seed-Aufruf

Datenmodell (IndexedDB via Dexie v5)

DB-Version: 5

// Konfiguration (Legacy, nur noch für Evaluation.tsx)
categories:            '++id, order'
criteria:              '++id, categoryId, order'

// Assignment-Typen
assignmentTypes:       '++id'
phaseTemplates:        '++id, assignmentTypeId, order'

// Stammdaten
groups:                '++id'
consultants:           '++id, groupId'

// Assignments
assignments:           '++id, status, createdAt'
meetingInstances:      '++id, assignmentId, phaseTemplateId'

// Gesprächsprotokoll
conversationEntries:   '++id, meetingInstanceId, instituteeId, sequenceIndex'
conversationSkillScores: '++id, conversationEntryId, criteriaId'

// Meeting-Assessment
assessments:           '++id, meetingInstanceId, instituteeId, criteriaId'

// Feedback-Entwürfe (Legacy)
feedbackDrafts:        '++id, assignmentId, instituteeId'

// Strukturiertes Feedback
feedbackDimensions:    '++id, order'
feedbackCategories:    '++id, dimensionId, order'
feedbackCriterionItems: '++id, categoryId, order'
assignmentFeedbacks:   '++id, assignmentId, instituteeId'
feedbackCategoryRatings: '++id, assignmentFeedbackId, feedbackCategoryId'
feedbackDimensionTexts: '++id, assignmentFeedbackId, feedbackDimensionId'

// Mappings (Legacy, kaum genutzt)
criterionCategoryMappings: '++id, criterionId, feedbackCategoryId'
criterionLevelDescriptions: '++id, criterionItemId, rating'

Wichtige Interface-Details

type FeedbackRating = 'na' | 'not_client_ready' | 'partially_client_ready' | 'nearly_client_ready' | 'client_ready'

interface AssignmentType {
  id?: number
  name: string
  defaultPhaseKeys: string[]
  criteriaIds?: number[]   // FeedbackCriterionItem IDs; leer = alle anzeigen
}

interface Assignment {
  id?: number
  title: string
  client: string
  assignmentTypeId: number
  instituteeIds: number[]
  leadName: string
  status: 'active' | 'done'
  createdAt: string
  assignmentNumber?: string          // z.B. "CGI-2024-001"
  description?: string
  instituteeEnrollments?: Array<{
    instituteeId: number
    from?: string   // ISO date
    to?: string     // ISO date
  }>
}

interface MeetingInstance {
  id?: number
  assignmentId: number
  phaseTemplateId: number
  phaseIndex: number                 // 1 = erste Instanz, 2+ = Wiederholungen
  date: string
  status: 'planned' | 'active' | 'done'
  generalNotes: string
  deletedAt?: string                 // Soft-Delete
}

interface FeedbackCriterionItem {
  id?: number
  categoryId: number
  name: string
  weight?: number    // Gewichtung ×1×5, default 1
  order: number
}

// Assessment.criteriaId → FeedbackCriterionItem.id  (Gesamtbewertung)
// ConversationSkillScore.criteriaId → FeedbackCategory.id  (Schnellbewertung)

Routing

/                                     Dashboard (Assignment-Liste)
/assignment/new                       Neues Assignment anlegen
/assignment/:id                       AssignmentDetail (Phasen-Flow)
/assignment/:id/edit                  Assignment bearbeiten
/assignment/:id/meeting/:meetingId    MeetingView (Protokoll + Bewertung)
/assignment/:id/feedback/:instituteeId        AssignmentFeedbackPage (strukturiertes Feedback)
/assignment/:id/feedback-draft/:instituteeId  FeedbackDraftPage (Legacy)
/evaluation                           Auswertungsübersicht
/consultants                          Berater-Verwaltung
/config                               Konfiguration (3 Tabs: Feedback | Ass.-Typen | Backup)

Konfigurationsbereich

Tab "Feedback" → FeedbackStructureConfig

CRUD für:

  • Feedback-Dimensionen (mit Reihenfolge ↑/↓)
  • Feedback-Kategorien je Dimension
  • Feedback-Kriterium-Items je Kategorie (mit Gewichtung ×1×5)

Tab "Ass.-Typen" → AssignmentTypeConfig

CRUD für:

  • Assignment-Typen (Name, Löschschutz wenn Assignments vorhanden)
  • Phasen-Templates je Typ (Label, Lead/Kunde, Wiederholbar, Protokoll, Bewertung)
  • Kriterien-Auswahl je Typ: Baumstruktur Dimension → Kategorie → Item (mit Checkbox, Indeterminate-State)
    • criteriaIds = [] → alle Kriterien werden angezeigt

Tab "Backup" → in Configuration.tsx

  • JSON-Export aller Tabellen
  • Zweistufiger JSON-Import mit Überschreib-Warnung

Bewertungs-Aggregation (Implementierungsdetail)

const NUM_MAP: Record<FeedbackRating, number> = {
  na: 0, not_client_ready: 1, partially_client_ready: 2, nearly_client_ready: 3, client_ready: 4
}

function catMode(cat: FeedbackCategory, fbItems: FeedbackCriterionItem[], getScore: (itemId: number) => FeedbackRating | null): FeedbackRating | null {
  const scored = fbItems
    .filter(i => i.categoryId === cat.id)
    .map(i => ({ score: getScore(i.id!), w: i.weight ?? 1 }))
    .filter((x): x is { score: FeedbackRating; w: number } => x.score !== null && x.score !== 'na')
  if (scored.length === 0) return null
  const wSum = scored.reduce((s, x) => s + x.w, 0)
  const avg  = scored.reduce((s, x) => s + NUM_MAP[x.score] * x.w, 0) / wSum
  if (avg < 1.5) return 'not_client_ready'
  if (avg < 2.5) return 'partially_client_ready'
  if (avg < 3.5) return 'nearly_client_ready'
  return 'client_ready'
}

Export-Dateiname-Schema

const buildFilename = (ext: string) => {
  const date  = new Date(meeting.date).toLocaleDateString('de-DE').replace(/\./g, '-')
  const label = template.label       // z.B. "Alignment Call"
  const idx   = phaseIndex > 1 ? `-${phaseIndex}` : ''
  const id    = assignment.assignmentNumber ?? assignment.title
  return `${id}_${date}_${label}${idx}.${ext}`
  // Beispiel: CGI-2024-001_02-07-2026_Alignment Call-2.md
}

KI-Integration (AssignmentFeedbackPage)

  • Provider: OpenRouter (https://openrouter.ai) — ermöglicht Modellwechsel ohne eigenen API-Zugang
  • Modelle: GPT-4o mini, GPT-4o, Claude Sonnet 4.5, Claude Haiku 4.5, Gemini Flash 1.5
  • API-Key: Lokal in localStorage gespeichert (ai_api_key, ai_model)
  • Datenweitergabe: Kriterien-Scores, Protokoll-Notizen, Kategorie-Bewertungen werden im Prompt strukturiert übermittelt
  • Hinweis: Der AI-Prompt nutzt noch die alte criteria-Tabelle (Legacy) — Aktualisierung auf feedbackCriterionItems steht aus

Seed-Daten (beim ersten Start)

Die App legt beim ersten Start automatisch an:

  1. 3 Assignment-Typen mit je vollständigem Phasen-Flow (Generisch, Case Interview, Stakeholder Meeting / Pitch)
  2. Legacy-Kriterien in categories + criteria (für Evaluation.tsx)
  3. Feedback-Dimensionsstruktur mit 5 Dimensionen, 14 Kategorien, ~70 Kriterium-Items (Capgemini-Kompetenzmodell)
  4. Criterion-Mappings (Legacy, nicht mehr aktiv genutzt)

Bekannte technische Schulden

Bereich Problem Priorität
Evaluation.tsx Nutzt alte Category/Criterion-Tabellen statt neue feedbackCriterionItems Mittel
meetingExport.ts Bewertungs-Labels im Markdown-Export zeigen FeedbackRating-Keys statt Labels Niedrig
FeedbackStructureConfig.tsx Enthält toten Code (Funktionen für gelöschte AssignmentType-CRUD) Niedrig
AI-Prompt in AssignmentFeedbackPage Nutzt db.criteria (alt), sollte feedbackCriterionItems nutzen Mittel
Dashboard Zeigt keine Assignment-Nummer Niedrig
AssignmentCreate Kein Feld für Assignment-Nummer Niedrig
IndexedDB Keine robuste Persistenz-Garantie (Browser kann löschen) Hoch → SQLite WASM geplant