CG-Feedback-Monitor/server/db/tableRegistry.ts
Lars 234f87d408 V2-Dev-Stand: Skala 1-10, Benefits/Concerns, DEV-Isolation und Gitea-Doku.
Bündelt die Neuentwicklung in AssigmentMonitorV2 (eigene Ports/DB), die Umstellung auf numerische Bewertungen, Meeting-Checklisten, KI-Tagging und die geplante Feedback-Kaskade — als Basis für Versionsverwaltung in Gitea.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-08 17:35:05 +02:00

208 lines
6.4 KiB
TypeScript

/**
* Einzige Quelle der Wahrheit für alle SQL-Tabellen — treibt sowohl die Schema-Erstellung
* (database.ts) als auch Export/Import (queries/backup.ts) an. Eine neue Tabelle, die hier
* fehlt, fehlt dadurch gleichzeitig im Schema UND im Backup — sichtbarer Fehler statt eines
* Datenverlusts, der erst beim nächsten Restore aus dem Backup auffällt (siehe CLAUDE.md:
* appSettings fehlte einmal in TABLE_NAMES).
*
* 1:1 abgeleitet aus TABLE_NAMES (src/utils/dbBackup.ts) und den Interfaces in src/db/types.ts.
*/
export type ColumnCodec = 'json' | 'bool' | 'plain'
export interface ColumnDef {
name: string
sqlType: 'INTEGER' | 'TEXT' | 'REAL'
codec: ColumnCodec
}
export interface TableDef {
name: string
columns: ColumnDef[]
}
const col = (name: string, sqlType: ColumnDef['sqlType'], codec: ColumnCodec = 'plain'): ColumnDef =>
({ name, sqlType, codec })
export const TABLE_DEFINITIONS: TableDef[] = [
{ name: 'categories', columns: [
col('name', 'TEXT'),
col('order', 'INTEGER'),
]},
{ name: 'criteria', columns: [
col('categoryId', 'INTEGER'),
col('name', 'TEXT'),
col('description', 'TEXT'),
col('weight', 'REAL'),
col('order', 'INTEGER'),
]},
{ name: 'assignmentTypes', columns: [
col('name', 'TEXT'),
col('defaultPhaseKeys', 'TEXT', 'json'),
col('criteriaIds', 'TEXT', 'json'),
]},
{ name: 'phaseTemplates', columns: [
col('assignmentTypeId', 'INTEGER'),
col('phaseKey', 'TEXT'),
col('label', 'TEXT'),
col('withWhom', 'TEXT'),
col('isRepeatable', 'INTEGER', 'bool'),
col('hasConversation', 'INTEGER', 'bool'),
col('hasAssessment', 'INTEGER', 'bool'),
col('hasChecklist', 'INTEGER', 'bool'),
col('criteriaIds', 'TEXT', 'json'),
col('order', 'INTEGER'),
]},
{ name: 'groups', columns: [
col('name', 'TEXT'),
col('cohortYear', 'INTEGER'),
]},
{ name: 'consultants', columns: [
col('groupId', 'INTEGER'),
col('firstName', 'TEXT'),
col('lastName', 'TEXT'),
]},
{ name: 'assignments', columns: [
col('title', 'TEXT'),
col('client', 'TEXT'),
col('assignmentTypeId', 'INTEGER'),
col('instituteeIds', 'TEXT', 'json'),
col('leadName', 'TEXT'),
col('status', 'TEXT'),
col('createdAt', 'TEXT'),
col('assignmentNumber', 'TEXT'),
col('description', 'TEXT'),
col('instituteeEnrollments', 'TEXT', 'json'),
col('deletedAt', 'TEXT'),
col('archivedAt', 'TEXT'),
]},
{ name: 'meetingInstances', columns: [
col('assignmentId', 'INTEGER'),
col('phaseTemplateId', 'INTEGER'),
col('phaseIndex', 'INTEGER'),
col('date', 'TEXT'),
col('status', 'TEXT'),
col('generalNotes', 'TEXT'),
col('deletedAt', 'TEXT'),
col('generalNoteTags', 'TEXT', 'json'),
]},
{ name: 'conversationEntries', columns: [
col('meetingInstanceId', 'INTEGER'),
col('instituteeId', 'INTEGER'),
col('sequenceIndex', 'INTEGER'),
col('note', 'TEXT'),
col('fillerCount', 'INTEGER'),
col('updatedAt', 'TEXT'),
col('lineTags', 'TEXT', 'json'),
]},
{ name: 'conversationSkillScores', columns: [
col('conversationEntryId', 'INTEGER'),
col('criteriaId', 'INTEGER'),
col('score', 'INTEGER'),
]},
{ name: 'assessments', columns: [
col('meetingInstanceId', 'INTEGER'),
col('instituteeId', 'INTEGER'),
col('criteriaId', 'INTEGER'),
col('score', 'INTEGER'),
col('note', 'TEXT'),
col('updatedAt', 'TEXT'),
]},
{ name: 'meetingCategoryRatings', columns: [
col('meetingInstanceId', 'INTEGER'),
col('instituteeId', 'INTEGER'),
col('categoryId', 'INTEGER'),
col('score', 'INTEGER'),
col('note', 'TEXT'),
]},
{ name: 'feedbackDrafts', columns: [
col('assignmentId', 'INTEGER'),
col('instituteeId', 'INTEGER'),
col('content', 'TEXT'),
col('updatedAt', 'TEXT'),
]},
{ name: 'feedbackDimensions', columns: [
col('name', 'TEXT'),
col('order', 'INTEGER'),
]},
{ name: 'feedbackCategories', columns: [
col('dimensionId', 'INTEGER'),
col('name', 'TEXT'),
col('order', 'INTEGER'),
col('description', 'TEXT'),
]},
{ name: 'feedbackCriterionItems', columns: [
col('categoryId', 'INTEGER'),
col('name', 'TEXT'),
col('weight', 'REAL'),
col('order', 'INTEGER'),
col('description', 'TEXT'),
col('learnedPatternHints', 'TEXT'),
]},
{ name: 'assignmentFeedbacks', columns: [
col('assignmentId', 'INTEGER'),
col('instituteeId', 'INTEGER'),
col('status', 'TEXT'),
col('aiModel', 'TEXT'),
col('updatedAt', 'TEXT'),
]},
{ name: 'feedbackCategoryRatings', columns: [
col('assignmentFeedbackId', 'INTEGER'),
col('feedbackCategoryId', 'INTEGER'),
col('rating', 'INTEGER'),
]},
{ name: 'feedbackDimensionTexts', columns: [
col('assignmentFeedbackId', 'INTEGER'),
col('feedbackDimensionId', 'INTEGER'),
col('achievements', 'TEXT'),
col('developmentNeeds', 'TEXT'),
col('suggestedAchievements', 'TEXT'),
col('suggestedDevelopmentNeeds', 'TEXT'),
]},
{ name: 'feedbackDimensionPrompts', columns: [
col('dimensionId', 'INTEGER'),
col('promptTemplate', 'TEXT'),
]},
{ name: 'feedbackCategoryTexts', columns: [
col('assignmentFeedbackId', 'INTEGER'),
col('feedbackCategoryId', 'INTEGER'),
col('benefits', 'TEXT'),
col('concerns', 'TEXT'),
col('suggestedBenefits', 'TEXT'),
col('suggestedConcerns', 'TEXT'),
]},
{ name: 'criterionCategoryMappings', columns: [
col('criterionId', 'INTEGER'),
col('feedbackCategoryId', 'INTEGER'),
]},
{ name: 'criterionLevelDescriptions', columns: [
col('criterionItemId', 'INTEGER'),
col('rating', 'INTEGER'),
col('text', 'TEXT'),
]},
{ name: 'appSettings', columns: [
col('ratingTrendWeightStep', 'REAL'),
col('defaultDimensionPromptTemplate', 'TEXT'),
col('defaultCategoryPromptTemplate', 'TEXT'),
col('criterionTaggingPromptTemplate', 'TEXT'),
col('ratingColorBands', 'TEXT', 'json'),
]},
{ name: 'checklistItemDefs', columns: [
col('phaseTemplateId', 'INTEGER'),
col('label', 'TEXT'),
col('linkedCriterionItemId', 'INTEGER'),
col('scope', 'TEXT'),
col('order', 'INTEGER'),
]},
{ name: 'checklistResponses', columns: [
col('meetingInstanceId', 'INTEGER'),
col('instituteeId', 'INTEGER'),
col('checklistItemId', 'INTEGER'),
col('rating', 'INTEGER'),
col('note', 'TEXT'),
col('criterionItemId', 'INTEGER'),
]},
]
export const TABLE_NAMES = TABLE_DEFINITIONS.map(t => t.name)