Files
florian.wildeandClaude Sonnet 5 de8914f9cc feat: Umschaltbarer Hinweismodus für Allergene/Zusatzstoffe
Neue Migration 0002_labeling_notice.sql: settings.labeling_mode
("detailed"/"notice") + labeling_notice(_en/_pl). Auf der Seite
"Allergene & Zusatzstoffe" (LabelingNoticeEditor) kann zwischen der
detaillierten Auswahl je Gericht und einem editierbaren Hinweistext
umgeschaltet werden. Im Hinweismodus entfällt auf der öffentlichen
Speisekarte (Desktop + Mobile) die Legende und die Kürzel an den
Gerichten, in der Gerichte-Verwaltung wird die Chip-Auswahl beim
Bearbeiten ausgeblendet. Umschalten/Bearbeiten ist für staff und admin
freigegeben, Farben in /api/settings bleiben admin-only.

Version 1.8.0.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-19 23:57:43 +02:00

70 lines
2.4 KiB
TypeScript

import { NextResponse } from "next/server";
import { query } from "@/lib/db";
import { getSettings, type LabelingMode } from "@/lib/db/settings";
import { authErrorResponse, requireRole } from "@/lib/auth/session";
import { isSameOrigin } from "@/lib/origin";
export async function GET() {
return NextResponse.json(await getSettings());
}
interface SettingsPatchBody {
colors?: Record<string, string>;
labeling_mode?: LabelingMode;
labeling_notice?: string;
labeling_notice_en?: string | null;
labeling_notice_pl?: string | null;
}
export async function PATCH(request: Request) {
if (!isSameOrigin(request)) {
return NextResponse.json({ error: "Nicht erlaubt." }, { status: 403 });
}
try {
// Farben bleiben admin-only wie bisher; die Allergen-/Zusatzstoff-
// Anzeige darf auch Kantinenpersonal umschalten/bearbeiten.
const session = await requireRole(["staff", "admin"]);
const body = (await request.json()) as SettingsPatchBody;
const sets: string[] = [];
const params: unknown[] = [];
if (body.colors !== undefined) {
if (session.user.role !== "admin") {
return NextResponse.json({ error: "forbidden" }, { status: 403 });
}
params.push(JSON.stringify(body.colors));
sets.push(`colors = $${params.length}`);
}
if (body.labeling_mode !== undefined) {
if (body.labeling_mode !== "detailed" && body.labeling_mode !== "notice") {
return NextResponse.json({ error: "invalid_input" }, { status: 400 });
}
params.push(body.labeling_mode);
sets.push(`labeling_mode = $${params.length}`);
}
if (body.labeling_notice !== undefined) {
params.push(body.labeling_notice);
sets.push(`labeling_notice = $${params.length}`);
}
if (body.labeling_notice_en !== undefined) {
params.push(body.labeling_notice_en);
sets.push(`labeling_notice_en = $${params.length}`);
}
if (body.labeling_notice_pl !== undefined) {
params.push(body.labeling_notice_pl);
sets.push(`labeling_notice_pl = $${params.length}`);
}
if (sets.length === 0) {
return NextResponse.json({ error: "invalid_input" }, { status: 400 });
}
await query(`update settings set ${sets.join(", ")} where id = 1`, params);
return NextResponse.json({ ok: true });
} catch (err) {
const res = authErrorResponse(err);
if (res) return res;
throw err;
}
}