From e03911fd51aa53a88c92de3b877e6286da9aaecb Mon Sep 17 00:00:00 2001 From: makearmy Date: Sat, 4 Oct 2025 20:40:14 -0400 Subject: [PATCH] build error fix --- components/forms/SettingsSubmit.tsx | 158 ++++++++++++++++++++++++---- 1 file changed, 137 insertions(+), 21 deletions(-) diff --git a/components/forms/SettingsSubmit.tsx b/components/forms/SettingsSubmit.tsx index 1a0447d8..63128473 100644 --- a/components/forms/SettingsSubmit.tsx +++ b/components/forms/SettingsSubmit.tsx @@ -55,7 +55,7 @@ function shortId(s?: string) { } // ───────────────────────────────────────────────────────────── -/** Normalizers for edit-mode prefill (IDs + enums) */ +// Normalizers for edit-mode prefill (IDs + enums) // ───────────────────────────────────────────────────────────── function idToString(v: any): string { if (v == null || v === "") return ""; @@ -139,6 +139,101 @@ function normalizeForReset(iv: EditInitialValues) { }; } +// ───────────────────────────────────────────────────────────── +// Directus field whitelists + mapper (prevents drift) +// ───────────────────────────────────────────────────────────── +const DIRECTUS_FIELDS: Record = { + settings_co2gal: [ + "setting_title", + "setting_notes", + "photo", + "screen", + "mat", + "mat_coat", + "mat_color", + "mat_opacity", + "mat_thickness", + "source", + "lens", + "focus", + "laser_soft", + "repeat_all", + "fill_settings", + "line_settings", + "raster_settings", + ], + settings_co2gan: [ + "setting_title", + "setting_notes", + "photo", + "screen", + "mat", + "mat_coat", + "mat_color", + "mat_opacity", + "mat_thickness", + "source", + "lens", + "focus", + "laser_soft", + "repeat_all", + "fill_settings", + "line_settings", + "raster_settings", + ], + settings_fiber: [ + "setting_title", + "setting_notes", + "photo", + "screen", + "mat", + "mat_coat", + "mat_color", + "mat_opacity", + "mat_thickness", + "source", + "lens", + "focus", + "laser_soft", + "repeat_all", + "fill_settings", + "line_settings", + "raster_settings", + ], + settings_uv: [ + "setting_title", + "setting_notes", + "photo", + "screen", + "mat", + "mat_coat", + "mat_color", + "mat_opacity", + "mat_thickness", + "source", + "lens", + "focus", + "laser_soft", + "repeat_all", + "fill_settings", + "line_settings", + "raster_settings", + ], +} as const; + +function toDirectusData(target: Target, full: any) { + const allow = new Set(DIRECTUS_FIELDS[target]); + const out: any = {}; + for (const k of Object.keys(full)) { + if (!allow.has(k)) continue; + const v = full[k]; + // Avoid sending empty strings to Directus (common cause of "required" confusion) + if (v === "") continue; + out[k] = v; + } + return out; +} + // ───────────────────────────────────────────────────────────── // Props (Create vs Edit) + type guard // ───────────────────────────────────────────────────────────── @@ -488,7 +583,7 @@ export default function SettingsSubmit(props: CreateProps | EditProps) { handleSubmit, control, reset, - setValue, // used for prefill select syncing + setValue, getValues, formState: { isSubmitting }, } = useForm({ @@ -541,7 +636,7 @@ export default function SettingsSubmit(props: CreateProps | EditProps) { } }, [isEdit, edit?.initialValues, reset]); - // After reset, force RHF values once + // After reset, force RHF values once (covers early case) useEffect(() => { if (!isEdit || !current) return; @@ -597,6 +692,9 @@ export default function SettingsSubmit(props: CreateProps | EditProps) { } const bool = (v: any) => !!v; + // ───────────────────────────────────────────────────────────── + // SUBMIT: Build clean Directus payload + include route metadata + // ───────────────────────────────────────────────────────────── async function onSubmit(values: any) { setSubmitErr(null); @@ -611,9 +709,10 @@ export default function SettingsSubmit(props: CreateProps | EditProps) { // map UI target -> backend slug as helper const target_slug = typeForOptions; - const payload: any = { - target, // keep original selector value (e.g. "settings_co2gal") - target_slug, // slug for server convenience (e.g. "co2-galvo") + // full UI payload (same shape the form uses) + const fullPayload: any = { + target, + target_slug, setting_title: values.setting_title, setting_notes: values.setting_notes || "", mat: values.mat || null, @@ -676,33 +775,53 @@ export default function SettingsSubmit(props: CreateProps | EditProps) { })), }; - // Ensure EDIT gets routed as a PATCH via the API + // ✅ Exact Directus data object (whitelisted fields only, empty strings removed) + const directusData = toDirectusData(target, fullPayload); + + // Early guard for the common required field + if (!directusData.setting_title) { + setSubmitErr("Title is required."); + return; + } + + // Edit meta travels outside `data` + const meta: any = {}; if (isEdit && edit?.submissionId != null) { - payload.mode = "edit"; - payload.submission_id = edit.submissionId; + meta.mode = "edit"; + meta.submission_id = edit.submissionId; } try { let res: Response; if (photoFile || screenFile) { + // multipart: pack everything your route needs under one JSON field const form = new FormData(); - // Directus multipart expects JSON in "data" - form.set("data", JSON.stringify(payload)); - // Also include top-level fields for our API route compatibility - form.set("target", payload.target); - form.set("target_slug", payload.target_slug); + form.set( + "data", + JSON.stringify({ + target, + target_slug, + ...meta, + data: directusData, + }) + ); if (photoFile) form.set("photo", photoFile, photoFile.name || "photo"); if (screenFile) form.set("screen", screenFile, screenFile.name || "screen"); + res = await fetch("/api/submit/settings", { method: "POST", body: form, credentials: "include" }); } else { + // JSON parity with multipart res = await fetch("/api/submit/settings", { method: "POST", headers: { "Content-Type": "application/json" }, - // Directus JSON expects { data: { ... } } - // Also include top-level fields for our API route compatibility - body: JSON.stringify({ data: payload, target: payload.target, target_slug: payload.target_slug }), - credentials: "include", + body: JSON.stringify({ + target, + target_slug, + ...meta, + data: directusData, + }), + credentials: "include", }); } @@ -714,7 +833,6 @@ export default function SettingsSubmit(props: CreateProps | EditProps) { // Success if (!isEdit) { - // reset only on create reset(); setPhotoFile(null); setScreenFile(null); @@ -723,9 +841,7 @@ export default function SettingsSubmit(props: CreateProps | EditProps) { } const id = (data as any)?.id ? String((data as any).id) : String(edit?.submissionId ?? ""); - // back to success (create) or view (edit) if (isEdit) { - // remove ?edit=1 const q = new URLSearchParams(sp.toString()); q.delete("edit"); router.replace(`/portal/laser-settings?${q.toString()}`);