refactor: db/index.ts in types, schema und seeds aufgeteilt
- types.ts: alle Interfaces und FeedbackRating-Typ - schema.ts: Dexie-Klasse + Migrationsversionen - seeds/assignmentTypes.ts: seedIfEmpty() - seeds/feedbackStructure.ts: seedFeedbackStructureIfEmpty() - seeds/migrations.ts: seedCriterionMappingsIfEmpty() + migrateToUnifiedCriteriaIfNeeded() - index.ts: reiner Re-Export, alle bestehenden Imports bleiben kompatibel
This commit is contained in:
parent
97e3eeb8a6
commit
068c4cdcfd
610
src/db/index.ts
610
src/db/index.ts
|
|
@ -1,602 +1,8 @@
|
||||||
import Dexie, { type Table } from 'dexie'
|
export * from './types'
|
||||||
|
export { db } from './schema'
|
||||||
// ─── Bewertungs-Skala (einheitlich für Meetings und Feedback) ─────────────────
|
export {
|
||||||
|
seedIfEmpty,
|
||||||
export type FeedbackRating =
|
seedFeedbackStructureIfEmpty,
|
||||||
| 'na'
|
seedCriterionMappingsIfEmpty,
|
||||||
| 'not_client_ready'
|
migrateToUnifiedCriteriaIfNeeded,
|
||||||
| 'partially_client_ready'
|
} from './seeds'
|
||||||
| '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<Category>
|
|
||||||
criteria!: Table<Criterion>
|
|
||||||
assignmentTypes!: Table<AssignmentType>
|
|
||||||
phaseTemplates!: Table<PhaseTemplate>
|
|
||||||
groups!: Table<Group>
|
|
||||||
consultants!: Table<Consultant>
|
|
||||||
assignments!: Table<Assignment>
|
|
||||||
meetingInstances!: Table<MeetingInstance>
|
|
||||||
conversationEntries!: Table<ConversationEntry>
|
|
||||||
conversationSkillScores!: Table<ConversationSkillScore>
|
|
||||||
assessments!: Table<Assessment>
|
|
||||||
feedbackDrafts!: Table<FeedbackDraft>
|
|
||||||
feedbackDimensions!: Table<FeedbackDimension>
|
|
||||||
feedbackCategories!: Table<FeedbackCategory>
|
|
||||||
feedbackCriterionItems!: Table<FeedbackCriterionItem>
|
|
||||||
assignmentFeedbacks!: Table<AssignmentFeedback>
|
|
||||||
feedbackCategoryRatings!: Table<FeedbackCategoryRating>
|
|
||||||
feedbackDimensionTexts!: Table<FeedbackDimensionText>
|
|
||||||
criterionCategoryMappings!: Table<CriterionCategoryMapping>
|
|
||||||
criterionLevelDescriptions!: Table<CriterionLevelDescription>
|
|
||||||
|
|
||||||
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 })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
135
src/db/schema.ts
Normal file
135
src/db/schema.ts
Normal file
|
|
@ -0,0 +1,135 @@
|
||||||
|
import Dexie, { type Table } from 'dexie'
|
||||||
|
import type {
|
||||||
|
Category, Criterion,
|
||||||
|
AssignmentType, PhaseTemplate,
|
||||||
|
Group, Consultant,
|
||||||
|
Assignment, MeetingInstance,
|
||||||
|
ConversationEntry, ConversationSkillScore,
|
||||||
|
Assessment, FeedbackDraft,
|
||||||
|
FeedbackDimension, FeedbackCategory, FeedbackCriterionItem,
|
||||||
|
AssignmentFeedback, FeedbackCategoryRating, FeedbackDimensionText,
|
||||||
|
CriterionCategoryMapping, CriterionLevelDescription,
|
||||||
|
} from './types'
|
||||||
|
|
||||||
|
class AssignmentMonitorDB extends Dexie {
|
||||||
|
categories!: Table<Category>
|
||||||
|
criteria!: Table<Criterion>
|
||||||
|
assignmentTypes!: Table<AssignmentType>
|
||||||
|
phaseTemplates!: Table<PhaseTemplate>
|
||||||
|
groups!: Table<Group>
|
||||||
|
consultants!: Table<Consultant>
|
||||||
|
assignments!: Table<Assignment>
|
||||||
|
meetingInstances!: Table<MeetingInstance>
|
||||||
|
conversationEntries!: Table<ConversationEntry>
|
||||||
|
conversationSkillScores!: Table<ConversationSkillScore>
|
||||||
|
assessments!: Table<Assessment>
|
||||||
|
feedbackDrafts!: Table<FeedbackDraft>
|
||||||
|
feedbackDimensions!: Table<FeedbackDimension>
|
||||||
|
feedbackCategories!: Table<FeedbackCategory>
|
||||||
|
feedbackCriterionItems!: Table<FeedbackCriterionItem>
|
||||||
|
assignmentFeedbacks!: Table<AssignmentFeedback>
|
||||||
|
feedbackCategoryRatings!: Table<FeedbackCategoryRating>
|
||||||
|
feedbackDimensionTexts!: Table<FeedbackDimensionText>
|
||||||
|
criterionCategoryMappings!: Table<CriterionCategoryMapping>
|
||||||
|
criterionLevelDescriptions!: Table<CriterionLevelDescription>
|
||||||
|
|
||||||
|
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',
|
||||||
|
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()
|
||||||
87
src/db/seeds/assignmentTypes.ts
Normal file
87
src/db/seeds/assignmentTypes.ts
Normal file
|
|
@ -0,0 +1,87 @@
|
||||||
|
import { db } from '../schema'
|
||||||
|
import type { PhaseKey } from '../types'
|
||||||
|
|
||||||
|
export async function seedIfEmpty() {
|
||||||
|
const count = await db.assignmentTypes.count()
|
||||||
|
if (count > 0) return
|
||||||
|
|
||||||
|
// ── Gemeinsame Kategorien & Kriterien (Legacy-Tabellen für Evaluation.tsx) ──
|
||||||
|
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 })
|
||||||
|
|
||||||
|
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 })
|
||||||
|
}
|
||||||
65
src/db/seeds/feedbackStructure.ts
Normal file
65
src/db/seeds/feedbackStructure.ts
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
import { db } from '../schema'
|
||||||
|
|
||||||
|
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 })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
3
src/db/seeds/index.ts
Normal file
3
src/db/seeds/index.ts
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
export { seedIfEmpty } from './assignmentTypes'
|
||||||
|
export { seedFeedbackStructureIfEmpty } from './feedbackStructure'
|
||||||
|
export { seedCriterionMappingsIfEmpty, migrateToUnifiedCriteriaIfNeeded } from './migrations'
|
||||||
106
src/db/seeds/migrations.ts
Normal file
106
src/db/seeds/migrations.ts
Normal file
|
|
@ -0,0 +1,106 @@
|
||||||
|
import { db } from '../schema'
|
||||||
|
|
||||||
|
export async function seedCriterionMappingsIfEmpty() {
|
||||||
|
const count = await db.criterionCategoryMappings.count()
|
||||||
|
if (count > 0) return
|
||||||
|
|
||||||
|
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][] = [
|
||||||
|
['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 })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Löscht nur Bewertungs-Scores (keine Textinhalte), verknüpft PhaseTemplates
|
||||||
|
// mit FeedbackCategory-IDs statt veralteten Criterion-IDs.
|
||||||
|
export async function migrateToUnifiedCriteriaIfNeeded() {
|
||||||
|
const cats = await db.feedbackCategories.toArray()
|
||||||
|
if (cats.length === 0) return
|
||||||
|
|
||||||
|
const templates = await db.phaseTemplates.toArray()
|
||||||
|
if (templates.length === 0) return
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
const cat = (name: string) => cats.find(c => c.name === name)?.id
|
||||||
|
|
||||||
|
await db.conversationSkillScores.clear()
|
||||||
|
await db.assessments.clear()
|
||||||
|
|
||||||
|
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 })
|
||||||
|
}
|
||||||
|
}
|
||||||
202
src/db/types.ts
Normal file
202
src/db/types.ts
Normal file
|
|
@ -0,0 +1,202 @@
|
||||||
|
// ─── Bewertungs-Skala ────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
export type FeedbackRating =
|
||||||
|
| 'na'
|
||||||
|
| 'not_client_ready'
|
||||||
|
| 'partially_client_ready'
|
||||||
|
| 'nearly_client_ready'
|
||||||
|
| 'client_ready'
|
||||||
|
|
||||||
|
// ─── Legacy-Konfiguration (nur noch für Evaluation.tsx) ───────────────────────
|
||||||
|
|
||||||
|
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[] // FeedbackCriterionItem IDs; leer = alle 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
|
||||||
|
description?: string
|
||||||
|
instituteeEnrollments?: Array<{
|
||||||
|
instituteeId: number
|
||||||
|
from?: string
|
||||||
|
to?: 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 → FeedbackCategory.id (Schnellbewertung während Gesprächsbeitrag)
|
||||||
|
export interface ConversationSkillScore {
|
||||||
|
id?: number
|
||||||
|
conversationEntryId: number
|
||||||
|
criteriaId: number
|
||||||
|
score: FeedbackRating | null
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Gesamtbewertung ─────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
// criteriaId → FeedbackCriterionItem.id (Gesamtbewertung am Meeting-Ende)
|
||||||
|
export interface Assessment {
|
||||||
|
id?: number
|
||||||
|
meetingInstanceId: number
|
||||||
|
instituteeId: number
|
||||||
|
criteriaId: number
|
||||||
|
score: FeedbackRating | null
|
||||||
|
note: string
|
||||||
|
updatedAt: string
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Feedback-Entwurf (Legacy) ────────────────────────────────────────────────
|
||||||
|
|
||||||
|
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
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Legacy-Mappings ──────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
export interface CriterionCategoryMapping {
|
||||||
|
id?: number
|
||||||
|
criterionId: number
|
||||||
|
feedbackCategoryId: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CriterionLevelDescription {
|
||||||
|
id?: number
|
||||||
|
criterionItemId: number
|
||||||
|
rating: FeedbackRating
|
||||||
|
text: string
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user