build error fix

This commit is contained in:
makearmy 2025-10-04 20:40:14 -04:00
parent ef7e7cbb24
commit e03911fd51

View file

@ -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 { function idToString(v: any): string {
if (v == null || v === "") return ""; if (v == null || v === "") return "";
@ -139,6 +139,101 @@ function normalizeForReset(iv: EditInitialValues) {
}; };
} }
// ─────────────────────────────────────────────────────────────
// Directus field whitelists + mapper (prevents drift)
// ─────────────────────────────────────────────────────────────
const DIRECTUS_FIELDS: Record<Target, readonly string[]> = {
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 // Props (Create vs Edit) + type guard
// ───────────────────────────────────────────────────────────── // ─────────────────────────────────────────────────────────────
@ -488,7 +583,7 @@ export default function SettingsSubmit(props: CreateProps | EditProps) {
handleSubmit, handleSubmit,
control, control,
reset, reset,
setValue, // used for prefill select syncing setValue,
getValues, getValues,
formState: { isSubmitting }, formState: { isSubmitting },
} = useForm<any>({ } = useForm<any>({
@ -541,7 +636,7 @@ export default function SettingsSubmit(props: CreateProps | EditProps) {
} }
}, [isEdit, edit?.initialValues, reset]); }, [isEdit, edit?.initialValues, reset]);
// After reset, force RHF values once // After reset, force RHF values once (covers early case)
useEffect(() => { useEffect(() => {
if (!isEdit || !current) return; if (!isEdit || !current) return;
@ -597,6 +692,9 @@ export default function SettingsSubmit(props: CreateProps | EditProps) {
} }
const bool = (v: any) => !!v; const bool = (v: any) => !!v;
// ─────────────────────────────────────────────────────────────
// SUBMIT: Build clean Directus payload + include route metadata
// ─────────────────────────────────────────────────────────────
async function onSubmit(values: any) { async function onSubmit(values: any) {
setSubmitErr(null); setSubmitErr(null);
@ -611,9 +709,10 @@ export default function SettingsSubmit(props: CreateProps | EditProps) {
// map UI target -> backend slug as helper // map UI target -> backend slug as helper
const target_slug = typeForOptions; const target_slug = typeForOptions;
const payload: any = { // full UI payload (same shape the form uses)
target, // keep original selector value (e.g. "settings_co2gal") const fullPayload: any = {
target_slug, // slug for server convenience (e.g. "co2-galvo") target,
target_slug,
setting_title: values.setting_title, setting_title: values.setting_title,
setting_notes: values.setting_notes || "", setting_notes: values.setting_notes || "",
mat: values.mat || null, 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) { if (isEdit && edit?.submissionId != null) {
payload.mode = "edit"; meta.mode = "edit";
payload.submission_id = edit.submissionId; meta.submission_id = edit.submissionId;
} }
try { try {
let res: Response; let res: Response;
if (photoFile || screenFile) { if (photoFile || screenFile) {
// multipart: pack everything your route needs under one JSON field
const form = new FormData(); const form = new FormData();
// Directus multipart expects JSON in "data" form.set(
form.set("data", JSON.stringify(payload)); "data",
// Also include top-level fields for our API route compatibility JSON.stringify({
form.set("target", payload.target); target,
form.set("target_slug", payload.target_slug); target_slug,
...meta,
data: directusData,
})
);
if (photoFile) form.set("photo", photoFile, photoFile.name || "photo"); if (photoFile) form.set("photo", photoFile, photoFile.name || "photo");
if (screenFile) form.set("screen", screenFile, screenFile.name || "screen"); if (screenFile) form.set("screen", screenFile, screenFile.name || "screen");
res = await fetch("/api/submit/settings", { method: "POST", body: form, credentials: "include" }); res = await fetch("/api/submit/settings", { method: "POST", body: form, credentials: "include" });
} else { } else {
// JSON parity with multipart
res = await fetch("/api/submit/settings", { res = await fetch("/api/submit/settings", {
method: "POST", method: "POST",
headers: { "Content-Type": "application/json" }, headers: { "Content-Type": "application/json" },
// Directus JSON expects { data: { ... } } body: JSON.stringify({
// Also include top-level fields for our API route compatibility target,
body: JSON.stringify({ data: payload, target: payload.target, target_slug: payload.target_slug }), target_slug,
credentials: "include", ...meta,
data: directusData,
}),
credentials: "include",
}); });
} }
@ -714,7 +833,6 @@ export default function SettingsSubmit(props: CreateProps | EditProps) {
// Success // Success
if (!isEdit) { if (!isEdit) {
// reset only on create
reset(); reset();
setPhotoFile(null); setPhotoFile(null);
setScreenFile(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 ?? ""); const id = (data as any)?.id ? String((data as any).id) : String(edit?.submissionId ?? "");
// back to success (create) or view (edit)
if (isEdit) { if (isEdit) {
// remove ?edit=1
const q = new URLSearchParams(sp.toString()); const q = new URLSearchParams(sp.toString());
q.delete("edit"); q.delete("edit");
router.replace(`/portal/laser-settings?${q.toString()}`); router.replace(`/portal/laser-settings?${q.toString()}`);