/** * 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('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'), ]}, { name: 'meetingInstances', columns: [ col('assignmentId', 'INTEGER'), col('phaseTemplateId', 'INTEGER'), col('phaseIndex', 'INTEGER'), col('date', 'TEXT'), col('status', 'TEXT'), col('generalNotes', 'TEXT'), col('deletedAt', 'TEXT'), ]}, { 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', 'TEXT'), ]}, { name: 'assessments', columns: [ col('meetingInstanceId', 'INTEGER'), col('instituteeId', 'INTEGER'), col('criteriaId', 'INTEGER'), col('score', 'TEXT'), col('note', 'TEXT'), col('updatedAt', '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'), ]}, { name: 'feedbackCriterionItems', columns: [ col('categoryId', 'INTEGER'), col('name', 'TEXT'), col('weight', 'REAL'), col('order', 'INTEGER'), ]}, { 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', 'TEXT'), ]}, { 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('text', 'TEXT'), col('suggestedText', 'TEXT'), ]}, { name: 'criterionCategoryMappings', columns: [ col('criterionId', 'INTEGER'), col('feedbackCategoryId', 'INTEGER'), ]}, { name: 'criterionLevelDescriptions', columns: [ col('criterionItemId', 'INTEGER'), col('rating', 'TEXT'), col('text', 'TEXT'), ]}, { name: 'appSettings', columns: [ col('ratingTrendWeightStep', 'REAL'), ]}, ] export const TABLE_NAMES = TABLE_DEFINITIONS.map(t => t.name)