import Dexie, { type Table } from 'dexie' // ─── Bewertungs-Skala (einheitlich für Meetings und Feedback) ───────────────── export type FeedbackRating = | 'na' | 'not_client_ready' | 'partially_client_ready' | 'nearly_client_ready' | 'client_ready' // ─── Konfiguration ─────────────────────────────────────────────────────────── export interface Category { id?: number name: string order: number } export interface Criterion { id?: number categoryId: number name: string description: string weight: number order: number } // ─── Assignment-Typen & Phase-Templates ────────────────────────────────────── export type PhaseKey = string export interface AssignmentType { id?: number name: string defaultPhaseKeys: PhaseKey[] criteriaIds?: number[] // FeedbackCategory IDs; leer = alle Kategorien anzeigen } export interface PhaseTemplate { id?: number assignmentTypeId: number phaseKey: PhaseKey label: string withWhom: 'lead' | 'client' isRepeatable: boolean hasConversation: boolean hasAssessment: boolean criteriaIds: number[] order: number } // ─── Stammdaten ─────────────────────────────────────────────────────────────── export interface Group { id?: number name: string cohortYear: number } export interface Consultant { id?: number groupId: number firstName: string lastName: string } // ─── Assignments ───────────────────────────────────────────────────────────── export interface Assignment { id?: number title: string client: string assignmentTypeId: number instituteeIds: number[] leadName: string status: 'active' | 'done' createdAt: string assignmentNumber?: string // Offizielle Assignment-Nummer aus Systemsystemen description?: string instituteeEnrollments?: Array<{ instituteeId: number from?: string // ISO date string to?: string // ISO date string }> } // ─── Meeting-Instanzen ──────────────────────────────────────────────────────── export interface MeetingInstance { id?: number assignmentId: number phaseTemplateId: number phaseIndex: number date: string status: 'planned' | 'active' | 'done' generalNotes: string deletedAt?: string } // ─── Gesprächs-Zeitleiste ───────────────────────────────────────────────────── export interface ConversationEntry { id?: number meetingInstanceId: number instituteeId: number sequenceIndex: number note: string fillerCount: number updatedAt: string } // criteriaId here now holds a FeedbackCategory.id (unified criteria system) export interface ConversationSkillScore { id?: number conversationEntryId: number criteriaId: number // → FeedbackCategory.id score: FeedbackRating | null } // ─── Gesamtbewertung ───────────────────────────────────────────────────────── // criteriaId here now holds a FeedbackCategory.id (unified criteria system) export interface Assessment { id?: number meetingInstanceId: number instituteeId: number criteriaId: number // → FeedbackCategory.id score: FeedbackRating | null note: string updatedAt: string } // ─── Feedback-Entwurf (Legacy, bleibt erhalten) ────────────────────────────── export interface FeedbackDraft { id?: number assignmentId: number instituteeId: number content: string updatedAt: string } // ─── Strukturiertes Assignment-Feedback ─────────────────────────────────────── export interface FeedbackDimension { id?: number name: string order: number } export interface FeedbackCategory { id?: number dimensionId: number name: string order: number } export interface FeedbackCriterionItem { id?: number categoryId: number name: string weight?: number // Gewichtung, default 1 order: number } export interface AssignmentFeedback { id?: number assignmentId: number instituteeId: number status: 'draft' | 'final' aiModel: string updatedAt: string } export interface FeedbackCategoryRating { id?: number assignmentFeedbackId: number feedbackCategoryId: number rating: FeedbackRating } export interface FeedbackDimensionText { id?: number assignmentFeedbackId: number feedbackDimensionId: number achievements: string developmentNeeds: string } // Mapping: Assessment-Kriterium → Feedback-Kategorie (n:m, Legacy-Kompatibilität) export interface CriterionCategoryMapping { id?: number criterionId: number feedbackCategoryId: number } // Beschreibungstext pro FeedbackCriterionItem und Bewertungsstufe export interface CriterionLevelDescription { id?: number criterionItemId: number // → FeedbackCriterionItem.id rating: FeedbackRating text: string } // ─── Dexie DB ──────────────────────────────────────────────────────────────── class AssignmentMonitorDB extends Dexie { categories!: Table criteria!: Table assignmentTypes!: Table phaseTemplates!: Table groups!: Table consultants!: Table assignments!: Table meetingInstances!: Table conversationEntries!: Table conversationSkillScores!: Table assessments!: Table feedbackDrafts!: Table feedbackDimensions!: Table feedbackCategories!: Table feedbackCriterionItems!: Table assignmentFeedbacks!: Table feedbackCategoryRatings!: Table feedbackDimensionTexts!: Table criterionCategoryMappings!: Table criterionLevelDescriptions!: Table constructor() { super('AssignmentMonitorDB') this.version(1).stores({ categories: '++id, order', criteria: '++id, categoryId, order', groups: '++id', consultants: '++id, groupId', meetings: '++id, date, status, groupId', assessments: '++id, meetingId, consultantId, criteriaId', consultantNotes: '++id, meetingId, consultantId', }) this.version(2).stores({ categories: '++id, order', criteria: '++id, categoryId, order', assignmentTypes: '++id', phaseTemplates: '++id, assignmentTypeId, order', groups: '++id', consultants: '++id, groupId', assignments: '++id, status, createdAt', meetingInstances: '++id, assignmentId, phaseTemplateId', conversationEntries: '++id, meetingInstanceId, instituteeId, sequenceIndex', conversationSkillScores: '++id, conversationEntryId, criteriaId', assessments: '++id, meetingInstanceId, instituteeId, criteriaId', feedbackDrafts: '++id, assignmentId, instituteeId', // v1 tables no longer used but kept for safe migration meetings: null, consultantNotes: null, }) this.version(3).stores({ categories: '++id, order', criteria: '++id, categoryId, order', assignmentTypes: '++id', phaseTemplates: '++id, assignmentTypeId, order', groups: '++id', consultants: '++id, groupId', assignments: '++id, status, createdAt', meetingInstances: '++id, assignmentId, phaseTemplateId', conversationEntries: '++id, meetingInstanceId, instituteeId, sequenceIndex', conversationSkillScores: '++id, conversationEntryId, criteriaId', assessments: '++id, meetingInstanceId, instituteeId, criteriaId', feedbackDrafts: '++id, assignmentId, instituteeId', feedbackDimensions: '++id, order', feedbackCategories: '++id, dimensionId, order', feedbackCriterionItems: '++id, categoryId, order', assignmentFeedbacks: '++id, assignmentId, instituteeId', feedbackCategoryRatings: '++id, assignmentFeedbackId, feedbackCategoryId', feedbackDimensionTexts: '++id, assignmentFeedbackId, feedbackDimensionId', meetings: null, consultantNotes: null, }) this.version(4).stores({ categories: '++id, order', criteria: '++id, categoryId, order', assignmentTypes: '++id', phaseTemplates: '++id, assignmentTypeId, order', groups: '++id', consultants: '++id, groupId', assignments: '++id, status, createdAt', meetingInstances: '++id, assignmentId, phaseTemplateId', conversationEntries: '++id, meetingInstanceId, instituteeId, sequenceIndex', conversationSkillScores: '++id, conversationEntryId, criteriaId', assessments: '++id, meetingInstanceId, instituteeId, criteriaId', feedbackDrafts: '++id, assignmentId, instituteeId', feedbackDimensions: '++id, order', feedbackCategories: '++id, dimensionId, order', feedbackCriterionItems: '++id, categoryId, order', assignmentFeedbacks: '++id, assignmentId, instituteeId', feedbackCategoryRatings: '++id, assignmentFeedbackId, feedbackCategoryId', feedbackDimensionTexts: '++id, assignmentFeedbackId, feedbackDimensionId', criterionCategoryMappings: '++id, criterionId, feedbackCategoryId', meetings: null, consultantNotes: null, }) this.version(5).stores({ categories: '++id, order', criteria: '++id, categoryId, order', assignmentTypes: '++id', phaseTemplates: '++id, assignmentTypeId, order', groups: '++id', consultants: '++id, groupId', assignments: '++id, status, createdAt', meetingInstances: '++id, assignmentId, phaseTemplateId', conversationEntries: '++id, meetingInstanceId, instituteeId, sequenceIndex', conversationSkillScores: '++id, conversationEntryId, criteriaId', assessments: '++id, meetingInstanceId, instituteeId, criteriaId', feedbackDrafts: '++id, assignmentId, instituteeId', feedbackDimensions: '++id, order', feedbackCategories: '++id, dimensionId, order', feedbackCriterionItems: '++id, categoryId, order', assignmentFeedbacks: '++id, assignmentId, instituteeId', feedbackCategoryRatings: '++id, assignmentFeedbackId, feedbackCategoryId', feedbackDimensionTexts: '++id, assignmentFeedbackId, feedbackDimensionId', criterionCategoryMappings: '++id, criterionId, feedbackCategoryId', criterionLevelDescriptions: '++id, criterionItemId, rating', meetings: null, consultantNotes: null, }) } } export const db = new AssignmentMonitorDB() // ─── Seed ───────────────────────────────────────────────────────────────────── export async function seedIfEmpty() { const count = await db.assignmentTypes.count() if (count > 0) return // ── Gemeinsame Kategorien & Kriterien ──────────────────────────────────── const catKomm = await db.categories.add({ name: 'Kommunikation', order: 1 }) const catFach = await db.categories.add({ name: 'Fachkompetenz', order: 2 }) const catKunde = await db.categories.add({ name: 'Kundenorientierung', order: 3 }) const catSelf = await db.categories.add({ name: 'Selbstorganisation', order: 4 }) const catCase = await db.categories.add({ name: 'Case-Kompetenz', order: 5 }) const kritKlarheit = await db.criteria.add({ categoryId: catKomm, name: 'Klarheit & Struktur', description: 'Klare, strukturierte Aussagen', weight: 2, order: 1 }) const kritUeberzeug = await db.criteria.add({ categoryId: catKomm, name: 'Überzeugungskraft', description: 'Argumente überzeugend präsentieren', weight: 2, order: 2 }) const kritZuhoeren = await db.criteria.add({ categoryId: catKomm, name: 'Aktives Zuhören', description: 'Eingehen auf Gesprächspartner', weight: 1, order: 3 }) const kritFragen = await db.criteria.add({ categoryId: catKomm, name: 'Umgang mit Fragen', description: 'Reaktion auf Kunden-Fragen', weight: 2, order: 4 }) const kritTiefe = await db.criteria.add({ categoryId: catFach, name: 'Fachliche Tiefe', description: 'Inhaltliche Korrektheit und Tiefe', weight: 3, order: 1 }) const kritProblem = await db.criteria.add({ categoryId: catFach, name: 'Problemlösung', description: 'Analytisches Vorgehen, Hypothesen', weight: 3, order: 2 }) const kritStakeholder = await db.criteria.add({ categoryId: catKunde, name: 'Stakeholder-Verständnis', description: 'Erkennt Bedürfnisse und Erwartungen', weight: 2, order: 1 }) const kritEmpathie = await db.criteria.add({ categoryId: catKunde, name: 'Empathie', description: 'Einfühlungsvermögen im Gespräch', weight: 1, order: 2 }) const kritVorb = await db.criteria.add({ categoryId: catSelf, name: 'Vorbereitung', description: 'Zeigt gute Meeting-Vorbereitung', weight: 2, order: 1 }) const kritInitiative = await db.criteria.add({ categoryId: catSelf, name: 'Eigeninitiative', description: 'Handelt proaktiv ohne Aufforderung', weight: 2, order: 2 }) const kritStruktur = await db.criteria.add({ categoryId: catCase, name: 'Problemstrukturierung', description: 'Systematische Zerlegung des Problems', weight: 3, order: 1 }) const kritHypo = await db.criteria.add({ categoryId: catCase, name: 'Hypothesenbildung', description: 'Klare, testbare Hypothesen', weight: 3, order: 2 }) const kritAnalyse = await db.criteria.add({ categoryId: catCase, name: 'Rechnen & Analyse', description: 'Datenbasiertes Vorgehen, Schätzfähigkeit', weight: 2, order: 3 }) const kritZeitmanage = await db.criteria.add({ categoryId: catCase, name: 'Zeitmanagement', description: 'Effizienter Umgang mit der Case-Zeit', weight: 2, order: 4 }) // ── Hilfsfunktion: PhaseTemplate anlegen ──────────────────────────────── const addPhase = ( assignmentTypeId: number, phaseKey: PhaseKey, label: string, withWhom: 'lead' | 'client', opts: { repeatable?: boolean; conv?: boolean; assess?: boolean; criteriaIds?: number[]; order: number } ) => db.phaseTemplates.add({ assignmentTypeId, phaseKey, label, withWhom, isRepeatable: opts.repeatable ?? false, hasConversation: opts.conv ?? false, hasAssessment: opts.assess ?? false, criteriaIds: opts.criteriaIds ?? [], order: opts.order, }) // ── AssignmentType 1: Generisch ────────────────────────────────────────── const genericId = await db.assignmentTypes.add({ name: 'Generisch', defaultPhaseKeys: ['preBriefing', 'briefing', 'alignmentCall', 'preDelivery', 'delivery', 'feedbackCallClient', 'feedbackCallLead'], }) const genericCriteria = [kritKlarheit, kritUeberzeug, kritZuhoeren, kritTiefe, kritProblem, kritStakeholder, kritEmpathie, kritVorb, kritInitiative] await addPhase(genericId, 'preBriefing', 'Pre-Briefing (Lead)', 'lead', { order: 1 }) await addPhase(genericId, 'briefing', 'Briefing (Kunde)', 'client', { conv: true, assess: true, criteriaIds: genericCriteria, order: 2 }) await addPhase(genericId, 'alignmentCall', 'Alignment Call (Kunde)', 'client', { repeatable: true, conv: true, assess: true, criteriaIds: genericCriteria, order: 3 }) await addPhase(genericId, 'preDelivery', 'Pre-Delivery (Lead)', 'lead', { order: 4 }) await addPhase(genericId, 'delivery', 'Delivery (Kunde)', 'client', { conv: true, assess: true, criteriaIds: genericCriteria, order: 5 }) await addPhase(genericId, 'feedbackCallClient','Feedback Call (Kunde)', 'client', { conv: true, assess: true, criteriaIds: genericCriteria, order: 6 }) await addPhase(genericId, 'feedbackCallLead', 'Feedback Call (Lead)', 'lead', { order: 7 }) // ── AssignmentType 2: Case Interview ──────────────────────────────────── const caseId = await db.assignmentTypes.add({ name: 'Case Interview', defaultPhaseKeys: ['preBriefing', 'briefing', 'delivery', 'feedbackCallClient', 'feedbackCallLead'], }) const caseCriteria = [kritStruktur, kritHypo, kritAnalyse, kritKlarheit, kritZeitmanage, kritUeberzeug, kritFragen] await addPhase(caseId, 'preBriefing', 'Pre-Briefing (Lead)', 'lead', { order: 1 }) await addPhase(caseId, 'briefing', 'Case Briefing (Kunde)', 'client', { conv: true, assess: true, criteriaIds: caseCriteria, order: 2 }) await addPhase(caseId, 'delivery', 'Case Delivery (Kunde)', 'client', { conv: true, assess: true, criteriaIds: caseCriteria, order: 3 }) await addPhase(caseId, 'feedbackCallClient','Feedback Call (Kunde)', 'client', { conv: true, assess: true, criteriaIds: caseCriteria, order: 4 }) await addPhase(caseId, 'feedbackCallLead', 'Feedback Call (Lead)', 'lead', { order: 5 }) // ── AssignmentType 3: Stakeholder Meeting / Pitch ──────────────────────── const pitchId = await db.assignmentTypes.add({ name: 'Stakeholder Meeting / Pitch', defaultPhaseKeys: ['preBriefing', 'briefing', 'alignmentCall', 'preDelivery', 'delivery', 'feedbackCallClient', 'feedbackCallLead'], }) const pitchCriteria = [kritVorb, kritKlarheit, kritUeberzeug, kritStakeholder, kritFragen, kritEmpathie, kritInitiative] await addPhase(pitchId, 'preBriefing', 'Pre-Briefing (Lead)', 'lead', { order: 1 }) await addPhase(pitchId, 'briefing', 'Briefing (Kunde)', 'client', { conv: true, assess: true, criteriaIds: pitchCriteria, order: 2 }) await addPhase(pitchId, 'alignmentCall', 'Alignment Call (Kunde)', 'client', { repeatable: true, conv: true, assess: true, criteriaIds: pitchCriteria, order: 3 }) await addPhase(pitchId, 'preDelivery', 'Pre-Delivery (Lead)', 'lead', { order: 4 }) await addPhase(pitchId, 'delivery', 'Pitch / Delivery (Kunde)', 'client', { conv: true, assess: true, criteriaIds: pitchCriteria, order: 5 }) await addPhase(pitchId, 'feedbackCallClient','Feedback Call (Kunde)', 'client', { conv: true, assess: true, criteriaIds: pitchCriteria, order: 6 }) await addPhase(pitchId, 'feedbackCallLead', 'Feedback Call (Lead)', 'lead', { order: 7 }) } // ─── Seed: Feedback-Dimensionsstruktur ─────────────────────────────────────── export async function seedFeedbackStructureIfEmpty() { const count = await db.feedbackDimensions.count() if (count > 0) return type CatSeed = { name: string; items: string[] } type DimSeed = { name: string; cats: CatSeed[] } const dimensions: DimSeed[] = [ { name: 'Mindset, Commitment, Collaboration', cats: [ { name: 'Persönliche Einstellung', items: ['Ruhig bei Stress', 'Offen für neue Themen', 'Auftreten / Ausstrahlung', 'Pro aktiv', 'Lösungsorientiert'] }, { name: 'Professionalität', items: ['Pünktlichkeit', 'Professionell bei informellen Gesprächen', 'Adressatengerecht', 'Hohes Maß an Selbststeuerung', 'Klarheit', 'Zuverlässigkeit'] }, { name: 'Commitment', items: ['Offen für spontane Änderungen', 'Selbständiger Lösungsansatz', 'Pro aktive Ideen', 'Verantwortungsbewusst', 'Hilfsbereit', 'Teamorientiert'] }, { name: 'Kommunikationsfähigkeit', items: ['To the point', 'Recap-Fähigkeit', 'Active Listening', 'Souveränität'] }, { name: 'Zusammenarbeit', items: ['Regelmäßige Abstimmung', 'Unterstützung bei Bedarf', 'Know-How Sharing', 'Starkes Teamverhalten', 'Verantwortung und Vertrauen', 'Inhaltlich unterstützend'] }, ], }, { name: 'Running Our Projects', cats: [ { name: 'Ergebnisverantwortung', items: ['Fokussierung', 'Priorisierung (Impact Driven)', 'Machbarkeit / Umsetzbarkeit', 'Alternativvorschläge', 'Interne Abstimmung und Feedback'] }, { name: 'Arbeitsstil', items: ['Komplexitätsmanagement', 'Eigenverantwortung', 'Feedbackaffinität', 'Verantwortungsbewusst / vorausschauend', 'Selbstreflektiert, lösungsorientiert', 'Offen, initiativ, kollegial', 'Organisiert, kommunikationsfähig', 'Lernfähig, anpassungsbereit, flexibel'] }, { name: 'Kundenmanagement', items: ['Aktives Erwartungsmanagement', 'Selbstsicherheit und fachliche Überzeugung', 'Aktive Advisory', 'Realistisch', 'Kundenfokussiert'] }, { name: 'Lieferzeiten', items: ['Verlässlichkeit', 'Fortschrittskontrolle und Planadaption', 'Aktives Scoping', 'Meetingsvorbereitung', 'Transparent'] }, ], }, { name: 'Growing our People', cats: [ { name: 'Networking', items: ['Cross-Collaboration', 'Network Growth', 'Network Utilization'] }, { name: 'Learning und Development', items: ['Kritikfähigkeit', 'Wissensaufbau (Kompetenzausstrahlung)', 'Umsetzungsbereitschaft Feedback'] }, ], }, { name: 'Functional & Technical Skills', cats: [ { name: 'Qualität des Inhalts', items: ['Belastbarkeit', 'Nachvollziehbarkeit', 'Logische Storyline'] }, { name: 'Inhaltliche Vollständigkeit', items: ['Wesentliche Elemente vorhanden', 'Realitätsnähe', 'Nutzenfokus'] }, { name: 'Qualität der Unterlage', items: ['Design nach Standardvorgaben', 'Sprachliche Präzision', 'Visualisierungen unterstützen Kernbotschaften'] }, { name: 'Prozesstreue', items: ['Kenne und befolge Prozesse und Vorgaben', 'Nachvollziehbarkeit und Transparenz', 'Qualitätschecks'] }, ], }, { name: 'Innovation und Asset Development', cats: [ { name: 'Innovation', items: ['Aktiver Vorschlag für Verbesserungen', 'Neue Ideen zur Verbesserung', 'Nutze aktuellen Wissensstand', 'Liefere über den Kundenerwartungen'] }, ], }, ] for (let di = 0; di < dimensions.length; di++) { const dim = dimensions[di] const dimId = await db.feedbackDimensions.add({ name: dim.name, order: di + 1 }) for (let ci = 0; ci < dim.cats.length; ci++) { const cat = dim.cats[ci] const catId = await db.feedbackCategories.add({ dimensionId: dimId, name: cat.name, order: ci + 1 }) for (let ii = 0; ii < cat.items.length; ii++) { await db.feedbackCriterionItems.add({ categoryId: catId, name: cat.items[ii], order: ii + 1 }) } } } } // ─── Seed: Kriterien-Mapping ────────────────────────────────────────────────── export async function seedCriterionMappingsIfEmpty() { const count = await db.criterionCategoryMappings.count() if (count > 0) return // Warte bis Kriterien und Feedback-Kategorien vorhanden sind const [crits, cats] = await Promise.all([ db.criteria.toArray(), db.feedbackCategories.toArray(), ]) if (crits.length === 0 || cats.length === 0) return const c = (name: string) => crits.find(x => x.name === name)?.id const f = (name: string) => cats.find(x => x.name === name)?.id const mappings: [string, string][] = [ // Kriterium → Feedback-Kategorie ['Klarheit & Struktur', 'Kommunikationsfähigkeit'], ['Klarheit & Struktur', 'Qualität des Inhalts'], ['Überzeugungskraft', 'Kommunikationsfähigkeit'], ['Überzeugungskraft', 'Kundenmanagement'], ['Aktives Zuhören', 'Kommunikationsfähigkeit'], ['Umgang mit Fragen', 'Kommunikationsfähigkeit'], ['Umgang mit Fragen', 'Kundenmanagement'], ['Fachliche Tiefe', 'Qualität des Inhalts'], ['Fachliche Tiefe', 'Inhaltliche Vollständigkeit'], ['Problemlösung', 'Ergebnisverantwortung'], ['Problemlösung', 'Arbeitsstil'], ['Stakeholder-Verständnis', 'Kundenmanagement'], ['Stakeholder-Verständnis', 'Persönliche Einstellung'], ['Empathie', 'Persönliche Einstellung'], ['Empathie', 'Kommunikationsfähigkeit'], ['Vorbereitung', 'Professionalität'], ['Vorbereitung', 'Lieferzeiten'], ['Eigeninitiative', 'Commitment'], ['Eigeninitiative', 'Persönliche Einstellung'], ['Problemstrukturierung', 'Ergebnisverantwortung'], ['Problemstrukturierung', 'Qualität des Inhalts'], ['Hypothesenbildung', 'Ergebnisverantwortung'], ['Hypothesenbildung', 'Arbeitsstil'], ['Rechnen & Analyse', 'Qualität des Inhalts'], ['Rechnen & Analyse', 'Inhaltliche Vollständigkeit'], ['Zeitmanagement', 'Lieferzeiten'], ['Zeitmanagement', 'Arbeitsstil'], ] for (const [critName, catName] of mappings) { const criterionId = c(critName) const feedbackCategoryId = f(catName) if (criterionId && feedbackCategoryId) { await db.criterionCategoryMappings.add({ criterionId, feedbackCategoryId }) } } } // ─── Migration: Unified Criteria System ────────────────────────────────────── // Löscht nur Bewertungs-Scores (noch nicht befüllt), nie Protokoll-Texte. // Verknüpft PhaseTemplates mit FeedbackCategory-IDs statt alten Criterion-IDs. export async function migrateToUnifiedCriteriaIfNeeded() { // Prüfen ob Migration schon gelaufen: erste PhaseTemplate mit criteriaIds die // auf eine FeedbackCategory zeigen (vs. alte Criterion-IDs die niedrig sein werden) const cats = await db.feedbackCategories.toArray() if (cats.length === 0) return // Feedback-Struktur noch nicht geseeded const templates = await db.phaseTemplates.toArray() if (templates.length === 0) return // Prüfen ob irgendeine Template-criteriaId in den FeedbackCategory-IDs liegt const catIds = new Set(cats.map(c => c.id!)) const firstWithIds = templates.find(t => t.criteriaIds.length > 0) if (firstWithIds && catIds.has(firstWithIds.criteriaIds[0])) return // schon migriert // Kategorien nach Name nachschlagen const cat = (name: string) => cats.find(c => c.name === name)?.id // Scores löschen (keine Textinhalte, nur Bewertungszahlen) await db.conversationSkillScores.clear() await db.assessments.clear() // Phase-Templates mit neuen Kategorie-IDs befüllen // Kategorien-Sets pro Assignment-Typ und Phasentyp const genericCats = [ cat('Kommunikationsfähigkeit'), cat('Persönliche Einstellung'), cat('Kundenmanagement'), cat('Ergebnisverantwortung'), cat('Qualität des Inhalts'), ].filter(Boolean) as number[] const caseCats = [ cat('Kommunikationsfähigkeit'), cat('Ergebnisverantwortung'), cat('Arbeitsstil'), cat('Qualität des Inhalts'), cat('Inhaltliche Vollständigkeit'), ].filter(Boolean) as number[] const pitchCats = [ cat('Kommunikationsfähigkeit'), cat('Persönliche Einstellung'), cat('Kundenmanagement'), cat('Ergebnisverantwortung'), cat('Qualität des Inhalts'), cat('Qualität der Unterlage'), ].filter(Boolean) as number[] const types = await db.assignmentTypes.toArray() for (const tmpl of templates) { if (!tmpl.hasAssessment && !tmpl.hasConversation) { await db.phaseTemplates.update(tmpl.id!, { criteriaIds: [] }) continue } const aType = types.find(t => t.id === tmpl.assignmentTypeId) let newIds: number[] if (aType?.name === 'Case Interview') { newIds = caseCats } else if (aType?.name === 'Stakeholder Meeting / Pitch') { newIds = pitchCats } else { newIds = genericCats } await db.phaseTemplates.update(tmpl.id!, { criteriaIds: newIds }) } }