CG-Feedback-Monitor/server/db/queries/feedbackStructure.ts
2026-07-07 11:14:00 +02:00

92 lines
4.8 KiB
TypeScript

import {
getAllRows, getRowsWhereIn, insertRow, updateRow,
getRowsWhereEquals, deleteRowsWhereEquals, deleteRowById,
} from '../crud'
import type { FeedbackDimension, FeedbackCategory, FeedbackCriterionItem, FeedbackDimensionPrompt } from '../../../src/db/types'
import { AI_CONFIG } from '../../../src/config/constants'
export interface FeedbackStructure {
dimensions: FeedbackDimension[]
categories: FeedbackCategory[]
items: FeedbackCriterionItem[]
}
const byOrder = <T extends { order: number }>(rows: T[]) => [...rows].sort((a, b) => a.order - b.order)
/** Vollständige, nach `order` sortierte Feedback-Taxonomie. */
export function loadFeedbackStructure(): FeedbackStructure {
return {
dimensions: byOrder(getAllRows<FeedbackDimension>('feedbackDimensions')),
categories: byOrder(getAllRows<FeedbackCategory>('feedbackCategories')),
items: byOrder(getAllRows<FeedbackCriterionItem>('feedbackCriterionItems')),
}
}
export const listFeedbackCategoriesOrdered = (): FeedbackCategory[] => byOrder(getAllRows<FeedbackCategory>('feedbackCategories'))
export const listFeedbackCategoriesAll = (): FeedbackCategory[] => getAllRows<FeedbackCategory>('feedbackCategories')
export const listFeedbackCriterionItemsAll = (): FeedbackCriterionItem[] => getAllRows<FeedbackCriterionItem>('feedbackCriterionItems')
export const getFeedbackCriterionItemsByIds = (ids: number[]): FeedbackCriterionItem[] =>
getRowsWhereIn<FeedbackCriterionItem>('feedbackCriterionItems', 'id', ids)
/** Legt zusätzlich einen Default-KI-Prompt für die neue Dimension an, siehe CLAUDE.md „Mehrstufiges Prompt-System". */
export function addFeedbackDimension(d: Omit<FeedbackDimension, 'id'>): number {
const id = insertRow('feedbackDimensions', d)
insertRow('feedbackDimensionPrompts', { dimensionId: id, promptTemplate: AI_CONFIG.defaultDimensionPromptTemplate })
return id
}
export const updateFeedbackDimension = (id: number, patch: Partial<FeedbackDimension>): number =>
updateRow('feedbackDimensions', id, patch)
/** Cascade: Items der Kategorien → Kategorien → Prompt-Konfiguration → Dimension. */
export function deleteFeedbackDimension(id: number): void {
const cats = getRowsWhereEquals<FeedbackCategory>('feedbackCategories', 'dimensionId', id)
for (const cat of cats) {
deleteRowsWhereEquals('feedbackCriterionItems', 'categoryId', cat.id!)
}
deleteRowsWhereEquals('feedbackCategories', 'dimensionId', id)
deleteRowsWhereEquals('feedbackDimensionPrompts', 'dimensionId', id)
deleteRowById('feedbackDimensions', id)
}
/**
* Selbstheilend: Dimensionen, die vor Einführung des Prompt-Systems (2026-07-07) angelegt wurden,
* haben noch keine `feedbackDimensionPrompts`-Zeile — wird hier per Backfill nachgezogen, statt eine
* separate Migration zu brauchen.
*/
export function listFeedbackDimensionPrompts(): FeedbackDimensionPrompt[] {
const dims = getAllRows<FeedbackDimension>('feedbackDimensions')
const existingDimIds = new Set(getAllRows<FeedbackDimensionPrompt>('feedbackDimensionPrompts').map(p => p.dimensionId))
for (const dim of dims) {
if (!existingDimIds.has(dim.id!)) {
insertRow('feedbackDimensionPrompts', { dimensionId: dim.id!, promptTemplate: AI_CONFIG.defaultDimensionPromptTemplate })
}
}
return getAllRows<FeedbackDimensionPrompt>('feedbackDimensionPrompts')
}
/** Selbstheilend wie `listFeedbackDimensionPrompts` — legt die Zeile bei Bedarf lazy an. */
export function getFeedbackDimensionPrompt(dimensionId: number): FeedbackDimensionPrompt {
const existing = getRowsWhereEquals<FeedbackDimensionPrompt>('feedbackDimensionPrompts', 'dimensionId', dimensionId)[0]
if (existing) return existing
const id = insertRow('feedbackDimensionPrompts', { dimensionId, promptTemplate: AI_CONFIG.defaultDimensionPromptTemplate })
return { id, dimensionId, promptTemplate: AI_CONFIG.defaultDimensionPromptTemplate }
}
export const updateFeedbackDimensionPrompt = (id: number, patch: Partial<FeedbackDimensionPrompt>): number =>
updateRow('feedbackDimensionPrompts', id, patch)
export const addFeedbackCategory = (c: Omit<FeedbackCategory, 'id'>): number => insertRow('feedbackCategories', c)
export const updateFeedbackCategory = (id: number, patch: Partial<FeedbackCategory>): number =>
updateRow('feedbackCategories', id, patch)
/** Cascade: Items der Kategorie → Kategorie. */
export function deleteFeedbackCategory(id: number): void {
deleteRowsWhereEquals('feedbackCriterionItems', 'categoryId', id)
deleteRowById('feedbackCategories', id)
}
export const addFeedbackCriterionItem = (i: Omit<FeedbackCriterionItem, 'id'>): number => insertRow('feedbackCriterionItems', i)
export const updateFeedbackCriterionItem = (id: number, patch: Partial<FeedbackCriterionItem>): number =>
updateRow('feedbackCriterionItems', id, patch)
export const deleteFeedbackCriterionItem = (id: number): void => deleteRowById('feedbackCriterionItems', id)