feat: configureerbare itemnamen per vragenlijst

Voeg item_label en item_label_plural toe aan questionnaires (migratie),
beheerformulier en dynamische teksten in UI en API-fouten. Standaard
blijft Activiteit/Activiteiten.

Negeer tsbuildinfo en geëmitteerde vite.config.js/.d.ts in .gitignore.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-05-10 15:29:44 +02:00
parent 6871126788
commit 93aedf319b
9 changed files with 234 additions and 42 deletions

View File

@@ -28,6 +28,10 @@ export interface User {
created_at: string;
}
/** Default labels for backwards compatibility and empty input */
export const DEFAULT_ITEM_LABEL = 'Activiteit';
export const DEFAULT_ITEM_LABEL_PLURAL = 'Activiteiten';
export interface Questionnaire {
id: number;
uuid: string;
@@ -38,10 +42,22 @@ export interface Questionnaire {
is_private: boolean;
created_by: number;
created_at: string;
item_label: string | null;
item_label_plural: string | null;
creator_name?: string;
activity_count?: number;
}
/** Resolved singular/plural labels for UI and API messages (max 60 chars each). */
export function resolveItemLabels(q: {
item_label?: string | null;
item_label_plural?: string | null;
}): { singular: string; plural: string } {
const singular = (q.item_label?.trim() || DEFAULT_ITEM_LABEL).slice(0, 60);
const plural = (q.item_label_plural?.trim() || DEFAULT_ITEM_LABEL_PLURAL).slice(0, 60);
return { singular, plural };
}
export interface Participant {
id: number;
questionnaire_id: number;
@@ -129,6 +145,18 @@ export function initializeDatabase(): void {
} catch (e) {
// Column already exists, ignore
}
// Migration: configurable inventory item name (singular / plural) per questionnaire
try {
db.exec(`ALTER TABLE questionnaires ADD COLUMN item_label TEXT DEFAULT '${DEFAULT_ITEM_LABEL.replace(/'/g, "''")}'`);
} catch (e) {
// Column already exists, ignore
}
try {
db.exec(`ALTER TABLE questionnaires ADD COLUMN item_label_plural TEXT DEFAULT '${DEFAULT_ITEM_LABEL_PLURAL.replace(/'/g, "''")}'`);
} catch (e) {
// Column already exists, ignore
}
// Participants table
db.exec(`
@@ -267,10 +295,20 @@ export const userOps = {
// Questionnaire operations
export const questionnaireOps = {
create: (uuid: string, slug: string, title: string, description: string | null, ogImage: string | null, isPrivate: boolean, createdBy: number): number => {
create: (
uuid: string,
slug: string,
title: string,
description: string | null,
ogImage: string | null,
isPrivate: boolean,
createdBy: number,
itemLabel: string,
itemLabelPlural: string
): number => {
const result = db.prepare(
'INSERT INTO questionnaires (uuid, slug, title, description, og_image, is_private, created_by) VALUES (?, ?, ?, ?, ?, ?, ?)'
).run(uuid, slug, title, description, ogImage, isPrivate ? 1 : 0, createdBy);
'INSERT INTO questionnaires (uuid, slug, title, description, og_image, is_private, created_by, item_label, item_label_plural) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)'
).run(uuid, slug, title, description, ogImage, isPrivate ? 1 : 0, createdBy, itemLabel, itemLabelPlural);
return result.lastInsertRowid as number;
},
@@ -296,8 +334,19 @@ export const questionnaireOps = {
`).all() as Questionnaire[];
},
update: (id: number, slug: string, title: string, description: string | null, ogImage: string | null, isPrivate: boolean): void => {
db.prepare('UPDATE questionnaires SET slug = ?, title = ?, description = ?, og_image = ?, is_private = ? WHERE id = ?').run(slug, title, description, ogImage, isPrivate ? 1 : 0, id);
update: (
id: number,
slug: string,
title: string,
description: string | null,
ogImage: string | null,
isPrivate: boolean,
itemLabel: string,
itemLabelPlural: string
): void => {
db.prepare(
'UPDATE questionnaires SET slug = ?, title = ?, description = ?, og_image = ?, is_private = ?, item_label = ?, item_label_plural = ? WHERE id = ?'
).run(slug, title, description, ogImage, isPrivate ? 1 : 0, itemLabel, itemLabelPlural, id);
},
delete: (id: number): void => {