import "server-only"; import { query, queryOne } from "@/lib/db"; import { loadTranslations, loadTranslationsFor, replaceTranslations, type TranslationTable, } from "./translations"; import type { CodeItem } from "@/lib/types"; // "allergens"/"additives" sind identisch aufgebaute Tabellen (Kürzel + // Bezeichnung in Deutsch, Übersetzungen in der jeweiligen // *_translations-Tabelle). Tabellenname kommt nie aus Nutzereingaben, // sondern immer aus dem literal getypten Parameter unten – sicher für // String-Interpolation in der Query. export type CodeTable = "allergens" | "additives"; const TRANSLATION_TABLE: Record = { allergens: "allergen_translations", additives: "additive_translations", }; interface CodeRow { code: string; name: string; sort: number; } export async function listCodes(table: CodeTable): Promise { const rows = await query(`select code, name, sort from ${table} order by sort`); const translations = await loadTranslations(TRANSLATION_TABLE[table]); return rows.map((r) => ({ ...r, translations: translations.get(r.code) ?? {} })); } export async function createCode( table: CodeTable, input: { code: string; name: string; translations?: Record } ): Promise { const maxRow = await queryOne<{ sort: number }>( `select sort from ${table} order by sort desc limit 1` ); const row = (await queryOne( `insert into ${table} (code, name, sort) values ($1, $2, $3) returning code, name, sort`, [input.code, input.name, (maxRow?.sort ?? 0) + 1] ))!; if (input.translations) { await replaceTranslations(TRANSLATION_TABLE[table], row.code, input.translations); } const translations = await loadTranslationsFor(TRANSLATION_TABLE[table], [row.code]); return { ...row, translations: translations.get(row.code) ?? {} }; } export async function updateCode( table: CodeTable, originalCode: string, input: { code: string; name: string; translations?: Record } ): Promise { const row = await queryOne( `update ${table} set code = $1, name = $2 where code = $3 returning code, name, sort`, [input.code, input.name, originalCode] ); if (!row) return null; if (input.translations) { await replaceTranslations(TRANSLATION_TABLE[table], row.code, input.translations); } const translations = await loadTranslationsFor(TRANSLATION_TABLE[table], [row.code]); return { ...row, translations: translations.get(row.code) ?? {} }; } export async function deleteCode(table: CodeTable, code: string): Promise { await query(`delete from ${table} where code = $1`, [code]); } export interface CodeImportRow { code: string; name: string; translations: Record; } /** CSV-Import: legt neue Kürzel an, bestehende (gleicher Code) werden * aktualisiert – ein doppelter Code innerhalb derselben Datei * aktualisiert also einfach zweimal statt einen Fehler zu werfen. */ export async function importCodes( table: CodeTable, rows: CodeImportRow[] ): Promise<{ created: number; updated: number }> { const existing = await query<{ code: string }>(`select code from ${table}`); const known = new Set(existing.map((r) => r.code)); const maxRow = await queryOne<{ sort: number }>( `select coalesce(max(sort), 0) as sort from ${table}` ); let nextSort = (maxRow?.sort ?? 0) + 1; let created = 0; let updated = 0; for (const row of rows) { if (known.has(row.code)) { await query(`update ${table} set name = $1 where code = $2`, [row.name, row.code]); updated++; } else { await query(`insert into ${table} (code, name, sort) values ($1, $2, $3)`, [ row.code, row.name, nextSort++, ]); known.add(row.code); created++; } if (Object.keys(row.translations).length > 0) { await replaceTranslations(TRANSLATION_TABLE[table], row.code, row.translations); } } return { created, updated }; }