From abe1f6e8cfd61266917c62f5a8f727c948d98245 Mon Sep 17 00:00:00 2001 From: Nick Szaunig Date: Tue, 1 Sep 2026 20:13:36 +0200 Subject: [PATCH] fix: Etiketten-Import zeigt konkreten Grund statt generischem Fehler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Eine KI-erzeugte JSON ohne Kenntnis des echten Schemas (z.B. eigene Feldnamen wie xMm/yMm oder erfundene Elementtypen wie rectangle/svg) scheiterte bisher mit "Datei enthält keine gültige Etikettenvorlage" ohne Hinweis, was falsch ist. Jetzt wird pro Element benannt, was fehlt oder welcher Typ nicht unterstützt wird. --- .../designer/label-designer.tsx | 53 +++++++++++++------ 1 file changed, 36 insertions(+), 17 deletions(-) diff --git a/ApiCore-Frontend/src/app/(app)/settings/label-templates/designer/label-designer.tsx b/ApiCore-Frontend/src/app/(app)/settings/label-templates/designer/label-designer.tsx index 0440357..a89cb43 100644 --- a/ApiCore-Frontend/src/app/(app)/settings/label-templates/designer/label-designer.tsx +++ b/ApiCore-Frontend/src/app/(app)/settings/label-templates/designer/label-designer.tsx @@ -86,20 +86,35 @@ export interface LabelDesignerValue { // Loose runtime check for imported JSON (own export, or hand/AI-authored) - // only verifies the shape is plausible enough to load into the canvas; // the backend's own DTO validation is still the real boundary check on save. -function isLabelElementArray(value: unknown): value is LabelElement[] { - return ( - Array.isArray(value) && - value.every( - (item) => - typeof item === "object" && - item !== null && - "id" in item && - "type" in item && - "x" in item && - "y" in item && - ELEMENT_TYPES.includes((item as { type: unknown }).type as (typeof ELEMENT_TYPES)[number]), - ) - ); +// Returns a specific reason per bad element instead of a flat true/false, so +// an AI-authored file that guessed the wrong schema tells the user exactly +// what to fix rather than a generic "invalid file". +function validateImportedElements(value: unknown): { elements: LabelElement[] } | { errors: string[] } { + if (!Array.isArray(value)) { + return { errors: ['Die Datei enthält kein "elements"-Array.'] }; + } + const errors: string[] = []; + value.forEach((item, index) => { + const label = typeof item === "object" && item && "id" in item ? String((item as { id: unknown }).id) : `#${index + 1}`; + if (typeof item !== "object" || item === null) { + errors.push(`Element ${label}: kein Objekt.`); + return; + } + if (!("x" in item) || !("y" in item)) { + errors.push(`Element ${label}: "x"/"y" fehlen (nicht "xMm"/"yMm").`); + return; + } + const type = (item as { type?: unknown }).type; + if (!ELEMENT_TYPES.includes(type as (typeof ELEMENT_TYPES)[number])) { + errors.push( + `Element ${label}: Typ "${String(type)}" wird nicht unterstützt (nur ${ELEMENT_TYPES.join(", ")}).`, + ); + } + }); + if (errors.length > 0) { + return { errors }; + } + return { elements: value as LabelElement[] }; } function slugify(name: string): string { @@ -235,11 +250,15 @@ export function LabelDesigner({ initial, onSubmit, submitLabel }: LabelDesignerP const importedElements = Array.isArray(parsed) ? parsed : (parsed as { elements?: unknown })?.elements; - if (!isLabelElementArray(importedElements)) { - toast.error("Datei enthält keine gültige Etikettenvorlage."); + const result = validateImportedElements(importedElements); + if ("errors" in result) { + toast.error("Vorlage konnte nicht importiert werden", { + description: result.errors.slice(0, 5).join(" · "), + duration: 15000, + }); return; } - setElements(importedElements.map((element) => ({ ...element, id: newId() }))); + setElements(result.elements.map((element) => ({ ...element, id: newId() }))); if (!Array.isArray(parsed)) { const value = parsed as Partial; if (typeof value.name === "string" && value.name && !name) setName(value.name);