submission form cleanup

This commit is contained in:
makearmy 2025-10-06 20:54:05 -04:00
parent 92ba85c570
commit 01d16881d1

View file

@ -55,7 +55,7 @@ export default function SettingsSubmit({ mode = "create", submissionId, initialV
const [me, setMe] = useState<Me>(null);
const [submitErr, setSubmitErr] = useState<string | null>(null);
// Robust current-user fetch
// Robust current-user fetch (unchanged)
useEffect(() => {
let alive = true;
(async () => {
@ -84,7 +84,7 @@ export default function SettingsSubmit({ mode = "create", submissionId, initialV
return () => { alive = false; };
}, []);
// Options loaders (Directus reads)
// Options loaders (unchanged)
function useOptions(path: string, includeId?: string | null) {
const [opts, setOpts] = useState<Opt[]>([]);
useEffect(() => {
@ -164,7 +164,7 @@ export default function SettingsSubmit({ mode = "create", submissionId, initialV
return { opts };
}
// Enumerations
// Enumerations (unchanged)
const FILL_TYPES: Opt[] = [
{ id: "uni", label: "UniDirectional" },
{ id: "bi", label: "BiDirectional" },
@ -175,13 +175,12 @@ export default function SettingsSubmit({ mode = "create", submissionId, initialV
"threshold", "ordered", "atkinson", "dither", "stucki", "jarvis", "newsprint", "halftone", "sketch", "grayscale",
].map((x) => ({ id: x, label: x[0].toUpperCase() + x.slice(1) }));
// react-hook-form
// react-hook-form (unchanged)
const {
register,
handleSubmit,
control,
reset,
// ↓↓↓ added setValue & getValues for rehydrate-fix
setValue,
getValues,
formState: { isSubmitting },
@ -198,7 +197,6 @@ export default function SettingsSubmit({ mode = "create", submissionId, initialV
// Rig & Optics
laser_soft: "",
source: "",
// keep these blank so Select shows "—"
lens_conf: "",
lens_apt: "",
lens_exp: "",
@ -212,12 +210,12 @@ export default function SettingsSubmit({ mode = "create", submissionId, initialV
},
});
// Repeaters
// Repeaters (unchanged)
const fills = useFieldArray({ control, name: "fill_settings" });
const lines = useFieldArray({ control, name: "line_settings" });
const rasters = useFieldArray({ control, name: "raster_settings" });
// Prefill (edit)
// Prefill (unchanged)
useEffect(() => {
if (!isEdit || !initialValues) return;
reset({
@ -245,7 +243,7 @@ export default function SettingsSubmit({ mode = "create", submissionId, initialV
});
}, [isEdit, initialValues, reset]);
// Option lists (include current IDs to guarantee a visible option)
// Options (unchanged)
const mats = useOptions("material", initialValues?.mat ?? null);
const coats = useOptions("material_coating", initialValues?.mat_coat ?? null);
const colors = useOptions("material_color", initialValues?.mat_color ?? null);
@ -257,49 +255,34 @@ export default function SettingsSubmit({ mode = "create", submissionId, initialV
const apt = useOptions("laser_scan_lens_apt", initialValues?.lens_apt ?? null);
const exp = useOptions("laser_scan_lens_exp", initialValues?.lens_exp ?? null);
// 🔧 Re-apply RHF values when options hydrate (fixes stubborn placeholder)
// Re-apply select values when options hydrate (unchanged fix)
useEffect(() => {
if (!isEdit) return; // only matters in edit-mode
const optionsByName: Record<string, Opt[]> = {
mat: mats.opts,
mat_coat: coats.opts,
mat_color: colors.opts,
mat_opacity: opacs.opts,
laser_soft: soft.opts,
source: srcs.opts,
lens_conf: conf.opts,
lens_apt: apt.opts,
lens_exp: exp.opts,
lens: lens.opts,
};
const names = Object.keys(optionsByName) as Array<keyof typeof optionsByName>;
const current = getValues();
names.forEach((name) => {
const cur = current?.[name as string];
if (cur == null || cur === "") return;
const opts = optionsByName[name] || [];
// if the option exists now (or even if it didn't before), nudge RHF to re-sync
if (!opts.length || opts.some((o) => String(o.id) === String(cur))) {
setValue(name as any, cur, { shouldDirty: false, shouldValidate: false });
const pairs: Array<[keyof any, string | null | undefined, Opt[]]> = [
["mat", initialValues?.mat, mats.opts],
["mat_coat", initialValues?.mat_coat, coats.opts],
["mat_color", initialValues?.mat_color, colors.opts],
["mat_opacity", initialValues?.mat_opacity, opacs.opts],
["laser_soft", initialValues?.laser_soft, soft.opts],
["source", initialValues?.source, srcs.opts],
["lens_conf", initialValues?.lens_conf, conf.opts],
["lens_apt", initialValues?.lens_apt, apt.opts],
["lens_exp", initialValues?.lens_exp, exp.opts],
["lens", initialValues?.lens, lens.opts],
];
pairs.forEach(([name, id, opts]) => {
if (!id || !opts.length) return;
const current = getValues(String(name));
if (!current) {
setValue(String(name), String(id), { shouldDirty: false, shouldValidate: false });
}
});
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [
isEdit,
mats.opts.length,
coats.opts.length,
colors.opts.length,
opacs.opts.length,
soft.opts.length,
srcs.opts.length,
conf.opts.length,
apt.opts.length,
exp.opts.length,
lens.opts.length,
mats.opts, coats.opts, colors.opts, opacs.opts, soft.opts,
srcs.opts, conf.opts, apt.opts, exp.opts, lens.opts,
initialValues, getValues, setValue
]);
// Image files
// Files (unchanged)
const [photoFile, setPhotoFile] = useState<File | null>(null);
const [screenFile, setScreenFile] = useState<File | null>(null);
const onPick = (setter: (f: File | null) => void) => (e: React.ChangeEvent<HTMLInputElement>) => setter(e.target.files?.[0] ?? null);
@ -326,11 +309,10 @@ export default function SettingsSubmit({ mode = "create", submissionId, initialV
lens: values.lens || null,
focus: values.focus === "" ? null : globalThis.Number(values.focus),
repeat_all: values.repeat_all === "" ? null : globalThis.Number(values.repeat_all),
// Repeaters (raw pass-through; api will normalize nums/bools)
// Repeaters
fill_settings: values.fill_settings || [],
line_settings: values.line_settings || [],
raster_settings: values.raster_settings || [],
// If editing with existing asset IDs, the API will accept them
...(initialValues?.photo ? { photo: initialValues.photo } : {}),
...(initialValues?.screen ? { screen: initialValues.screen } : {}),
};
@ -362,7 +344,17 @@ export default function SettingsSubmit({ mode = "create", submissionId, initialV
return (
<div className="space-y-5">
<header className="space-y-1">
<h1 className="text-xl font-semibold">{isEdit ? "Edit CO₂ Galvo Setting" : "Submit CO₂ Galvo Setting"}</h1>
{/* Hide duplicate header in edit mode; keep visible in create mode */}
{!isEdit && (
<h1 className="text-xl font-semibold">
Submit CO Galvo Setting
</h1>
)}
{isEdit && (
<h2 className="text-lg font-semibold">
Edit CO Galvo Setting
</h2>
)}
{meLabel ? <p className="text-sm text-muted-foreground">Submitting as {meLabel}</p> : null}
{submitErr ? <div className="border border-red-500 bg-red-50 text-red-700 rounded px-3 py-2 text-sm">{submitErr}</div> : null}
</header>
@ -406,11 +398,11 @@ export default function SettingsSubmit({ mode = "create", submissionId, initialV
<Select label="Coating" {...{ name: "mat_coat", register, options: coats.opts, required: true }} />
<Select label="Color" {...{ name: "mat_color", register, options: colors.opts, required: true }} />
<Select label="Opacity" {...{ name: "mat_opacity", register, options: opacs.opts, required: true }} />
<Number label="Thickness (mm)" name="mat_thickness" register={register} step="0.01" />
<Number label="Material Thickness (mm)" name="mat_thickness" register={register} step="0.01" />
</div>
</section>
{/* Rig & Optics (order per spec) */}
{/* Rig & Optics */}
<section className="space-y-3">
<h2 className="text-lg font-semibold">Rig & Optics</h2>
@ -440,10 +432,10 @@ export default function SettingsSubmit({ mode = "create", submissionId, initialV
<Repeater
title="Fill"
fields={fills.fields}
onAdd={() => fills.append({ type: "" })} // default to "—"
onAdd={() => fills.append({ type: "" })}
onRemove={(i) => fills.remove(i)}
render={(i) => {
const autoRotate = !!useWatch({ control, name: `fill_settings.${i}.auto` });
const auto = !!useWatch({ control, name: `fill_settings.${i}.auto` });
return (
<div className="grid md:grid-cols-4 gap-2">
<Text label="Name" name={`fill_settings.${i}.name`} register={register} />
@ -455,19 +447,17 @@ export default function SettingsSubmit({ mode = "create", submissionId, initialV
<Number label="Interval (mm)" name={`fill_settings.${i}.interval`} register={register} step="0.001" />
<Number label="Pass" name={`fill_settings.${i}.pass`} register={register} step="1" />
<Number label="Angle (°)" name={`fill_settings.${i}.angle`} register={register} step="1" />
{/* Moved: toggle first, then conditional increment */}
<Check label="Auto Rotate" name={`fill_settings.${i}.auto`} register={register} />
{autoRotate && (
<Number
label="Auto Rotate Increment (°)"
name={`fill_settings.${i}.increment`}
register={register}
step="0.001"
/>
{/* Booleans each on their own lines */}
<div className="col-span-full"><Check label="Auto Rotate" name={`fill_settings.${i}.auto`} register={register} /></div>
{auto && (
<div className="col-span-full">
<Number label="Auto Rotate Increment (°)" name={`fill_settings.${i}.increment`} register={register} step="0.001" />
</div>
)}
<Check label="Crosshatch" name={`fill_settings.${i}.cross`} register={register} />
<Check label="Flood Fill" name={`fill_settings.${i}.flood`} register={register} />
<Check label="Air Assist" name={`fill_settings.${i}.air`} register={register} />
<div className="col-span-full"><Check label="Crosshatch" name={`fill_settings.${i}.cross`} register={register} /></div>
<div className="col-span-full"><Check label="Flood Fill" name={`fill_settings.${i}.flood`} register={register} /></div>
<div className="col-span-full"><Check label="Air Assist" name={`fill_settings.${i}.air`} register={register} /></div>
</div>
);
}}
@ -490,23 +480,22 @@ export default function SettingsSubmit({ mode = "create", submissionId, initialV
<Number label="Power (%)" name={`line_settings.${i}.power`} register={register} step="0.1" />
<Number label="Speed (mm/s)" name={`line_settings.${i}.speed`} register={register} step="0.1" />
{/* Perforation mode toggle */}
<Check label="Perforation Mode" name={`line_settings.${i}.perf`} register={register} />
{/* Pass BEFORE Perforation Mode (requested swap) */}
<Number label="Pass" name={`line_settings.${i}.pass`} register={register} step="1" />
{/* Cut/Skip are integers with mm suffix; only when perf=true */}
<div className="col-span-full">
<Check label="Perforation Mode" name={`line_settings.${i}.perf`} register={register} />
</div>
{perf && (
<>
<Number label="Cut (mm)" name={`line_settings.${i}.cut`} register={register} step="1" />
<Number label="Skip (mm)" name={`line_settings.${i}.skip`} register={register} step="1" />
<Number label="Cut (mm)" name={`line_settings.${i}.cut`} register={register} step="0.001" />
<Number label="Skip (mm)" name={`line_settings.${i}.skip`} register={register} step="0.001" />
</>
)}
<Number label="Pass" name={`line_settings.${i}.pass`} register={register} step="1" />
{/* Wobble toggle */}
<div className="col-span-full">
<Check label="Wobble" name={`line_settings.${i}.wobble`} register={register} />
{/* Step/Size only when wobble=true; mm suffix */}
</div>
{wobble && (
<>
<Number label="Step (mm)" name={`line_settings.${i}.step`} register={register} step="0.001" />
@ -514,7 +503,7 @@ export default function SettingsSubmit({ mode = "create", submissionId, initialV
</>
)}
<Check label="Air Assist" name={`line_settings.${i}.air`} register={register} />
<div className="col-span-full"><Check label="Air Assist" name={`line_settings.${i}.air`} register={register} /></div>
</div>
);
}}
@ -524,7 +513,7 @@ export default function SettingsSubmit({ mode = "create", submissionId, initialV
<Repeater
title="Raster"
fields={rasters.fields}
onAdd={() => rasters.append({ type: "", dither: "" })} // default to "—"
onAdd={() => rasters.append({ type: "", dither: "" })}
onRemove={(i) => rasters.remove(i)}
render={(i) => {
const ditherVal = useWatch({ control, name: `raster_settings.${i}.dither` }) || "";
@ -541,15 +530,17 @@ export default function SettingsSubmit({ mode = "create", submissionId, initialV
{isHalftone && (
<>
<Number label="Halftone Cell" name={`raster_settings.${i}.halftone_cell`} register={register} step="1" />
<Number label="Halftone Angle (°)" name={`raster_settings.${i}.halftone_angle`} register={register} step="1" />
<Number label="Halftone Angle" name={`raster_settings.${i}.halftone_angle`} register={register} step="1" />
</>
)}
<Number label="Interval (mm)" name={`raster_settings.${i}.interval`} register={register} step="0.001" />
<Number label="Dot Width Adjustment (mm)" name={`raster_settings.${i}.dot`} register={register} step="0.1" />
<Number label="Pass" name={`raster_settings.${i}.pass`} register={register} step="1" />
<Check label="Crosshatch" name={`raster_settings.${i}.cross`} register={register} />
<Check label="Inverted" name={`raster_settings.${i}.inversion`} register={register} />
<Check label="Air Assist" name={`raster_settings.${i}.air`} register={register} />
{/* Booleans on their own lines */}
<div className="col-span-full"><Check label="Crosshatch" name={`raster_settings.${i}.cross`} register={register} /></div>
<div className="col-span-full"><Check label="Inverted" name={`raster_settings.${i}.inversion`} register={register} /></div>
<div className="col-span-full"><Check label="Air Assist" name={`raster_settings.${i}.air`} register={register} /></div>
</div>
);
}}
@ -564,7 +555,7 @@ export default function SettingsSubmit({ mode = "create", submissionId, initialV
);
}
/* Small UI */
/* Small UI (unchanged) */
function Select({
label,
name,