submit responsiveness

This commit is contained in:
makearmy 2025-10-05 08:24:51 -04:00
parent 8654653589
commit d04613ffdc

View file

@ -93,7 +93,7 @@ type EditInitialValues = {
laser_soft?: any; laser_soft?: any;
repeat_all?: number | null; repeat_all?: number | null;
// may be present in existing data // may exist on CO2 targets
lens_conf?: number | null; lens_conf?: number | null;
lens_apt?: number | null; lens_apt?: number | null;
lens_exp?: number | null; lens_exp?: number | null;
@ -145,7 +145,7 @@ function normalizeForReset(iv: EditInitialValues) {
} }
// ───────────────────────────────────────────────────────────── // ─────────────────────────────────────────────────────────────
// Directus field whitelists + mapper (prevents drift) /** Directus field whitelists + mapper (prevents drift) */
// ───────────────────────────────────────────────────────────── // ─────────────────────────────────────────────────────────────
const DIRECTUS_FIELDS: Record<Target, readonly string[]> = { const DIRECTUS_FIELDS: Record<Target, readonly string[]> = {
settings_co2gal: [ settings_co2gal: [
@ -610,8 +610,7 @@ export default function SettingsSubmit(props: CreateProps | EditProps) {
focus: "", focus: "",
laser_soft: "", laser_soft: "",
repeat_all: "", // on all targets repeat_all: "", // on all targets
// extras // lens config (may be required on CO2 targets)
uploader: "",
lens_conf: "", lens_conf: "",
lens_apt: "", lens_apt: "",
lens_exp: "", lens_exp: "",
@ -644,8 +643,7 @@ export default function SettingsSubmit(props: CreateProps | EditProps) {
focus: iv.focus ?? "", focus: iv.focus ?? "",
laser_soft: iv.laser_soft ?? "", laser_soft: iv.laser_soft ?? "",
repeat_all: iv.repeat_all ?? "", repeat_all: iv.repeat_all ?? "",
uploader: (me?.username ?? me?.email ?? "") || "", lens_conf: (iv as any).lens_conf ?? "",
lens_conf: (iv as any).lens_conf ?? "",
lens_apt: (iv as any).lens_apt ?? "", lens_apt: (iv as any).lens_apt ?? "",
lens_exp: (iv as any).lens_exp ?? "", lens_exp: (iv as any).lens_exp ?? "",
fill_settings: iv.fill_settings ?? [], fill_settings: iv.fill_settings ?? [],
@ -654,7 +652,7 @@ export default function SettingsSubmit(props: CreateProps | EditProps) {
}); });
} }
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
}, [isEdit, edit?.initialValues, reset, me?.username, me?.email]); }, [isEdit, edit?.initialValues, reset]);
// After reset, force RHF values once (covers early case) // After reset, force RHF values once (covers early case)
useEffect(() => { useEffect(() => {
@ -718,10 +716,13 @@ export default function SettingsSubmit(props: CreateProps | EditProps) {
async function onSubmit(values: any) { async function onSubmit(values: any) {
setSubmitErr(null); setSubmitErr(null);
// In edit mode, allow keeping the existing photo (no new file) if one exists. // Create vs Edit: photo is required unless an existing photo id is present or a new file is picked
const hasExistingPhotoId = const currentPhotoId =
!!(isEdit && typeof edit!.initialValues?.photo === "string" && edit!.initialValues.photo); isEdit && typeof edit!.initialValues?.photo === "string" && edit!.initialValues.photo
if (!photoFile && !hasExistingPhotoId && !isEdit) { ? (edit!.initialValues.photo as string)
: null;
const requirePhoto = !currentPhotoId && !photoFile;
if (requirePhoto) {
(document.querySelector('input[type="file"][data-role="photo"]') as HTMLInputElement | null)?.focus(); (document.querySelector('input[type="file"][data-role="photo"]') as HTMLInputElement | null)?.focus();
return; return;
} }
@ -742,8 +743,10 @@ export default function SettingsSubmit(props: CreateProps | EditProps) {
laser_soft: values.laser_soft || null, // all targets laser_soft: values.laser_soft || null, // all targets
repeat_all: num(values.repeat_all), // all targets repeat_all: num(values.repeat_all), // all targets
// new/extra fields // uploader: set automatically from owner; include only if present on client
uploader: (me?.username ?? me?.email ?? "") || "", ...(me?.username || me?.email ? { uploader: me?.username ?? me?.email! } : {}),
// lens config (required for CO2 targets per your list)
lens_conf: num(values.lens_conf), lens_conf: num(values.lens_conf),
lens_apt: num(values.lens_apt), lens_apt: num(values.lens_apt),
lens_exp: num(values.lens_exp), lens_exp: num(values.lens_exp),
@ -821,7 +824,7 @@ export default function SettingsSubmit(props: CreateProps | EditProps) {
try { try {
const form = new FormData(); const form = new FormData();
form.set("payload", JSON.stringify(flatPayload)); // << prod-compatible key form.set("payload", JSON.stringify(flatPayload)); // prod-compatible key
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");
@ -872,9 +875,9 @@ export default function SettingsSubmit(props: CreateProps | EditProps) {
// Convenience strings for “Current:” (edit mode) // Convenience strings for “Current:” (edit mode)
const currentPhotoId = const currentPhotoId =
isEdit && typeof edit.initialValues?.photo === "string" ? (edit.initialValues.photo as string) : null; isEdit && typeof edit?.initialValues?.photo === "string" ? (edit!.initialValues.photo as string) : null;
const currentScreenId = const currentScreenId =
isEdit && typeof edit.initialValues?.screen === "string" ? (edit.initialValues.screen as string) : null; isEdit && typeof edit?.initialValues?.screen === "string" ? (edit!.initialValues.screen as string) : null;
return ( return (
<div className="max-w-3xl mx-auto space-y-4"> <div className="max-w-3xl mx-auto space-y-4">
@ -922,9 +925,6 @@ export default function SettingsSubmit(props: CreateProps | EditProps) {
<div className="border border-red-500 text-red-600 bg-red-50 rounded p-2 text-sm">{submitErr}</div> <div className="border border-red-500 text-red-600 bg-red-50 rounded p-2 text-sm">{submitErr}</div>
) : null} ) : null}
{/* hidden uploader so it's always sent */}
<input type="hidden" {...register("uploader", { required: true })} value={me?.username ?? me?.email ?? ""} />
<form onSubmit={handleSubmit(onSubmit)} className="space-y-4"> <form onSubmit={handleSubmit(onSubmit)} className="space-y-4">
{/* Title */} {/* Title */}
<div className="grid md:grid-cols-2 gap-3"> <div className="grid md:grid-cols-2 gap-3">
@ -940,7 +940,7 @@ export default function SettingsSubmit(props: CreateProps | EditProps) {
<div className="grid md:grid-cols-2 gap-4"> <div className="grid md:grid-cols-2 gap-4">
<div> <div>
<label className="block text-sm mb-1"> <label className="block text-sm mb-1">
Result Photo {isEdit ? null : <span className="text-red-600">*</span>} Result Photo {!currentPhotoId ? <span className="text-red-600">*</span> : null}
</label> </label>
{currentPhotoId && ( {currentPhotoId && (
@ -953,7 +953,8 @@ export default function SettingsSubmit(props: CreateProps | EditProps) {
type="file" type="file"
accept="image/*" accept="image/*"
data-role="photo" data-role="photo"
required={!isEdit && !currentPhotoId} // Required when creating OR when editing without an existing photo id (until a new file is chosen)
required={!currentPhotoId && !photoFile}
onChange={(e) => onPick(e.target.files?.[0] ?? null, setPhotoFile, setPhotoPreview)} onChange={(e) => onPick(e.target.files?.[0] ?? null, setPhotoFile, setPhotoPreview)}
/> />
<p className="text-xs text-muted-foreground mt-1"> <p className="text-xs text-muted-foreground mt-1">
@ -1069,7 +1070,7 @@ export default function SettingsSubmit(props: CreateProps | EditProps) {
</p> </p>
</div> </div>
{/* Lens Configuration (target-specific requireds) */} {/* Lens Configuration (required on CO2 targets per your list) */}
{(target === "settings_co2gan" || target === "settings_co2gal") && ( {(target === "settings_co2gan" || target === "settings_co2gal") && (
<fieldset className="border rounded p-3 space-y-2"> <fieldset className="border rounded p-3 space-y-2">
<legend className="font-semibold">Lens Configuration</legend> <legend className="font-semibold">Lens Configuration</legend>