username and api fixes

This commit is contained in:
makearmy 2025-09-28 09:50:09 -04:00
parent d181e4dc27
commit 28d363cdfe

View file

@ -1,3 +1,4 @@
// app/components/forms/SettingsSubmit.tsx
"use client";
import { useEffect, useMemo, useState } from "react";
@ -49,14 +50,7 @@ function useOptions(path: string) {
}
function FilterableSelect({
label,
name,
register,
options,
loading,
onQuery,
placeholder = "—",
required = false,
label, name, register, options, loading, onQuery, placeholder = "—", required = false,
}: {
label: string;
name: string;
@ -68,9 +62,7 @@ function FilterableSelect({
required?: boolean;
}) {
const [filter, setFilter] = useState("");
useEffect(() => {
onQuery?.(filter);
}, [filter, onQuery]);
useEffect(() => { onQuery?.(filter); }, [filter, onQuery]);
const filtered = useMemo(() => {
if (!filter) return options;
@ -90,21 +82,18 @@ function FilterableSelect({
onChange={(e) => setFilter(e.target.value)}
/>
<select className="w-full border rounded px-2 py-1" {...register(name, { required })}>
<option value="">
{placeholder}
{loading ? " (loading…)" : ""}
</option>
<option value="">{placeholder}{loading ? " (loading…)" : ""}</option>
{filtered.map((o) => (
<option key={o.id} value={o.id}>
{o.label}
</option>
<option key={o.id} value={o.id}>{o.label}</option>
))}
</select>
</div>
);
}
function BoolBox({ label, name, register }: { label: string; name: string; register: UseFormRegister<any> }) {
function BoolBox({ label, name, register }:{
label: string; name: string; register: UseFormRegister<any>;
}) {
return (
<label className="flex items-center gap-1 text-sm">
<input type="checkbox" {...register(name)} /> {label}
@ -118,19 +107,14 @@ export default function SettingsSubmit({ initialTarget }: { initialTarget?: Targ
const initialFromQuery = (sp.get("target") as Target) || initialTarget || "settings_fiber";
const [target, setTarget] = useState<Target>(initialFromQuery);
// Map collection -> slug used by options endpoints
// Map collection -> slug used by options endpoints (?target=)
const typeForOptions = useMemo(() => {
switch (target) {
case "settings_fiber":
return "fiber";
case "settings_uv":
return "uv";
case "settings_co2gan":
return "co2-gantry";
case "settings_co2gal":
return "co2-galvo";
default:
return "fiber";
case "settings_fiber": return "fiber";
case "settings_uv": return "uv";
case "settings_co2gal": return "co2-galvo";
case "settings_co2gan": return "co2-gantry";
default: return "fiber";
}
}, [target]);
@ -151,76 +135,46 @@ export default function SettingsSubmit({ initialTarget }: { initialTarget?: Targ
let alive = true;
fetch("/api/me", { cache: "no-store", credentials: "include" })
.then((r) => (r.ok ? r.json() : Promise.reject(r)))
.then((j) => {
if (alive) setMe(j?.data || j || null);
})
.catch(() => {
if (alive) setMeErr("not-signed-in");
});
return () => {
alive = false;
};
.then((j) => { if (alive) setMe(j?.data || j || null); })
.catch(() => { if (alive) setMeErr("not-signed-in"); });
return () => { alive = false; };
}, []);
// Prefer username; avoid falling back to ID
// Prefer username specifically; then usual fallbacks
const meLabel =
(me?.username && me.username.trim()) ||
(me?.email && me.email.trim()) ||
[me?.first_name, me?.last_name].filter(Boolean).join(" ").trim() ||
([me?.first_name, me?.last_name].filter(Boolean).join(" ").trim()) ||
(me?.display_name && me.display_name.trim()) ||
"Unknown user";
// Options
const mats = useOptions("material");
const coats = useOptions("material_coating");
const mats = useOptions("material");
const coats = useOptions("material_coating");
const colors = useOptions("material_color");
const opacs = useOptions("material_opacity");
const soft = useOptions("laser_software");
const opacs = useOptions("material_opacity");
const soft = useOptions("laser_software"); // required for ALL targets
const srcs = useOptions(`laser_source?target=${typeForOptions}`);
const lens = useOptions(`lens?target=${typeForOptions}`);
// IMPORTANT: your API expects ?target=, not ?type=
const srcs = useOptions(`laser_source?target=${typeForOptions}`);
const lens = useOptions(`lens?target=${typeForOptions}`);
// Repeater choice options
const fillType = useOptions(`repeater-choices?target=${target}&group=fill_settings&field=type`);
const rasterType = useOptions(`repeater-choices?target=${target}&group=raster_settings&field=type`);
const rasterDither = useOptions(`repeater-choices?target=${target}&group=raster_settings&field=dither`);
const {
register,
handleSubmit,
control,
reset,
formState: { isSubmitting },
} = useForm<any>({
const { register, handleSubmit, control, reset, formState: { isSubmitting } } = useForm<any>({
defaultValues: {
setting_title: "",
setting_notes: "",
mat: "",
mat_coat: "",
mat_color: "",
mat_opacity: "",
mat_thickness: "",
source: "",
lens: "",
focus: "",
laser_soft: "",
repeat_all: "", // required for ALL targets now
fill_settings: [],
line_settings: [],
raster_settings: [],
mat: "", mat_coat: "", mat_color: "", mat_opacity: "",
mat_thickness: "", source: "", lens: "", focus: "",
laser_soft: "", repeat_all: "",
fill_settings: [], line_settings: [], raster_settings: [],
},
});
const fills = useFieldArray({ control, name: "fill_settings" });
const lines = useFieldArray({ control, name: "line_settings" });
const fills = useFieldArray({ control, name: "fill_settings" });
const lines = useFieldArray({ control, name: "line_settings" });
const rasters = useFieldArray({ control, name: "raster_settings" });
const isGantry = target === "settings_co2gan";
function num(v: any) {
return v === "" || v == null ? null : Number(v);
}
function num(v: any) { return (v === "" || v == null) ? null : Number(v); }
const bool = (v: any) => !!v;
async function onSubmit(values: any) {
@ -243,8 +197,8 @@ export default function SettingsSubmit({ initialTarget }: { initialTarget?: Targ
source: values.source || null,
lens: values.lens || null,
focus: num(values.focus),
laser_soft: values.laser_soft || null, // required for all targets
repeat_all: num(values.repeat_all), // <-- now always included
laser_soft: values.laser_soft || null, // required for ALL targets
repeat_all: num(values.repeat_all), // now sent for ALL targets
fill_settings: (values.fill_settings || []).map((r: any) => ({
name: r.name || "",
power: num(r.power),
@ -333,10 +287,7 @@ export default function SettingsSubmit({ initialTarget }: { initialTarget?: Targ
function onPick(file: File | null, setFile: (f: File | null) => void, setPreview: (s: string) => void) {
setFile(file);
if (!file) {
setPreview("");
return;
}
if (!file) { setPreview(""); return; }
const reader = new FileReader();
reader.onload = () => setPreview(String(reader.result || ""));
reader.readAsDataURL(file);
@ -344,7 +295,7 @@ export default function SettingsSubmit({ initialTarget }: { initialTarget?: Targ
return (
<div className="max-w-3xl mx-auto space-y-4">
{/* Target + Software (Software is required for ALL targets) */}
{/* Target + Software (Software required for ALL targets) */}
<div className="flex flex-wrap gap-3 items-end">
<div>
<label className="block text-sm mb-1">Target</label>
@ -392,9 +343,7 @@ export default function SettingsSubmit({ initialTarget }: { initialTarget?: Targ
{/* Title */}
<div className="grid md:grid-cols-2 gap-3">
<div className="md:col-span-2">
<label className="block text-sm mb-1">
Title <span className="text-red-600">*</span>
</label>
<label className="block text-sm mb-1">Title <span className="text-red-600">*</span></label>
<input className="w-full border rounded px-2 py-1" {...register("setting_title", { required: true })} />
</div>
</div>
@ -402,42 +351,20 @@ export default function SettingsSubmit({ initialTarget }: { initialTarget?: Targ
{/* Images */}
<div className="grid md:grid-cols-2 gap-4">
<div>
<label className="block text-sm mb-1">
Result Photo <span className="text-red-600">*</span>
</label>
<input
type="file"
accept="image/*"
data-role="photo"
required
onChange={(e) => onPick(e.target.files?.[0] ?? null, setPhotoFile, setPhotoPreview)}
/>
<label className="block text-sm mb-1">Result Photo <span className="text-red-600">*</span></label>
<input type="file" accept="image/*" data-role="photo" required
onChange={(e) => onPick(e.target.files?.[0] ?? null, setPhotoFile, setPhotoPreview)} />
<p className="text-xs text-muted-foreground mt-1">
{photoFile ? (
<>
Selected: <span className="font-mono">{photoFile.name}</span>
</>
) : (
"Max 25 MB. JPG/PNG/WebP recommended."
)}
{photoFile ? <>Selected: <span className="font-mono">{photoFile.name}</span></> : "Max 25 MB. JPG/PNG/WebP recommended."}
</p>
{photoPreview ? <img src={photoPreview} alt="Result preview" className="mt-2 rounded border" /> : null}
</div>
<div>
<label className="block text-sm mb-1">Settings Screenshot (optional)</label>
<input
type="file"
accept="image/*"
onChange={(e) => onPick(e.target.files?.[0] ?? null, setScreenFile, setScreenPreview)}
/>
<input type="file" accept="image/*"
onChange={(e) => onPick(e.target.files?.[0] ?? null, setScreenFile, setScreenPreview)} />
<p className="text-xs text-muted-foreground mt-1">
{screenFile ? (
<>
Selected: <span className="font-mono">{screenFile.name}</span>
</>
) : (
"Max 25 MB. JPG/PNG/WebP recommended."
)}
{screenFile ? <>Selected: <span className="font-mono">{screenFile.name}</span></> : "Max 25 MB. JPG/PNG/WebP recommended."}
</p>
{screenPreview ? <img src={screenPreview} alt="Settings preview" className="mt-2 rounded border" /> : null}
</div>
@ -451,205 +378,75 @@ export default function SettingsSubmit({ initialTarget }: { initialTarget?: Targ
{/* Material / Source / Lens */}
<div className="grid md:grid-cols-2 gap-3">
<FilterableSelect
label="Material"
name="mat"
register={register}
options={mats.opts}
loading={mats.loading}
onQuery={mats.setQ}
required
/>
<FilterableSelect
label="Coating"
name="mat_coat"
register={register}
options={coats.opts}
loading={coats.loading}
onQuery={coats.setQ}
required
/>
<FilterableSelect
label="Color"
name="mat_color"
register={register}
options={colors.opts}
loading={colors.loading}
onQuery={colors.setQ}
required
/>
<FilterableSelect
label="Opacity"
name="mat_opacity"
register={register}
options={opacs.opts}
loading={opacs.loading}
onQuery={opacs.setQ}
required
/>
{/* Fixed: pass ?target= to these two */}
<FilterableSelect
label="Laser Source"
name="source"
register={register}
options={srcs.opts}
loading={srcs.loading}
onQuery={srcs.setQ}
required
/>
<FilterableSelect
label="Lens"
name="lens"
register={register}
options={lens.opts}
loading={lens.loading}
onQuery={lens.setQ}
required
/>
<FilterableSelect label="Material" name="mat" register={register} options={mats.opts} loading={mats.loading} onQuery={mats.setQ} required />
<FilterableSelect label="Coating" name="mat_coat" register={register} options={coats.opts} loading={coats.loading} onQuery={coats.setQ} required />
<FilterableSelect label="Color" name="mat_color" register={register} options={colors.opts} loading={colors.loading} onQuery={colors.setQ} required />
<FilterableSelect label="Opacity" name="mat_opacity" register={register} options={opacs.opts} loading={opacs.loading} onQuery={opacs.setQ} required />
<FilterableSelect label="Laser Source" name="source" register={register} options={srcs.opts} loading={srcs.loading} onQuery={srcs.setQ} required />
<FilterableSelect label="Lens" name="lens" register={register} options={lens.opts} loading={lens.loading} onQuery={lens.setQ} required />
</div>
{/* Focus, thickness, repeat_all (repeat_all required for ALL) */}
{/* Focus, thickness, repeat_all */}
<div className="grid md:grid-cols-3 gap-3">
<div>
<label className="block text-sm mb-1">Material Thickness (mm)</label>
<input type="number" step="0.01" className="w-full border rounded px-2 py-1" {...register("mat_thickness")} />
</div>
<div>
<label className="block text-sm mb-1">
Focus (mm) <span className="text-red-600">*</span>
</label>
<input
type="number"
min={-10}
max={10}
step="1"
className="w-full border rounded px-2 py-1"
{...register("focus", { required: true })}
/>
<p className="text-xs text-muted-foreground mt-1">
0 = in focus. Negative = focus closer. Positive = focus further.
</p>
<label className="block text-sm mb-1">Focus (mm) <span className="text-red-600">*</span></label>
<input type="number" min={-10} max={10} step="1" className="w-full border rounded px-2 py-1" {...register("focus", { required: true })} />
<p className="text-xs text-muted-foreground mt-1">0 = in focus. Negative = focus closer. Positive = focus further.</p>
</div>
<div>
<label className="block text-sm mb-1">
Repeat All <span className="text-red-600">*</span>
</label>
<input
type="number"
step="1"
className="w-full border rounded px-2 py-1"
{...register("repeat_all", { required: true })}
/>
<label className="block text-sm mb-1">Repeat All <span className="text-red-600">*</span></label>
<input type="number" step="1" className="w-full border rounded px-2 py-1" {...register("repeat_all", { required: true })} />
</div>
</div>
{/* FILL / LINE / RASTER (unchanged) */}
{/* your existing repeaters stay as-is */}
{/* FILL */}
<fieldset className="border rounded p-3 space-y-2">
<div className="flex items-center justify-between">
<legend className="font-semibold">Fill Settings</legend>
<button type="button" className="px-2 py-1 border rounded" onClick={() => fills.append({})}>
+ Add
</button>
<button type="button" className="px-2 py-1 border rounded" onClick={() => fills.append({})}>+ Add</button>
</div>
{fills.fields.map((f, i) => (
<div key={f.id} className="grid md:grid-cols-4 gap-2">
<input
placeholder="Name"
className="border rounded px-2 py-1 md:col-span-2"
{...register(`fill_settings.${i}.name`)}
/>
<input placeholder="Name" className="border rounded px-2 py-1 md:col-span-2" {...register(`fill_settings.${i}.name`)} />
<FilterableSelect
label="Type"
name={`fill_settings.${i}.type`}
register={register}
options={fillType.opts}
loading={fillType.loading}
onQuery={fillType.setQ}
options={useOptions(`repeater-choices?target=${target}&group=fill_settings&field=type`).opts}
loading={false}
placeholder="Select type"
/>
{!isGantry && (
<>
<input
placeholder="Frequency (kHz)"
type="number"
step="0.1"
className="border rounded px-2 py-1"
{...register(`fill_settings.${i}.frequency`)}
/>
<input
placeholder="Pulse (ns)"
type="number"
step="0.1"
className="border rounded px-2 py-1"
{...register(`fill_settings.${i}.pulse`)}
/>
<input placeholder="Frequency (kHz)" type="number" step="0.1" className="border rounded px-2 py-1" {...register(`fill_settings.${i}.frequency`)} />
<input placeholder="Pulse (ns)" type="number" step="0.1" className="border rounded px-2 py-1" {...register(`fill_settings.${i}.pulse`)} />
</>
)}
<input
placeholder="Power (%)"
type="number"
step="0.1"
className="border rounded px-2 py-1"
{...register(`fill_settings.${i}.power`)}
/>
<input
placeholder="Speed (mm/s)"
type="number"
step="0.1"
className="border rounded px-2 py-1"
{...register(`fill_settings.${i}.speed`)}
/>
<input
placeholder="Interval (mm)"
type="number"
step="0.001"
className="border rounded px-2 py-1"
{...register(`fill_settings.${i}.interval`)}
/>
<input
placeholder="Pass"
type="number"
step="1"
className="border rounded px-2 py-1"
{...register(`fill_settings.${i}.pass`)}
/>
<input placeholder="Power (%)" type="number" step="0.1" className="border rounded px-2 py-1" {...register(`fill_settings.${i}.power`)} />
<input placeholder="Speed (mm/s)" type="number" step="0.1" className="border rounded px-2 py-1" {...register(`fill_settings.${i}.speed`)} />
<input placeholder="Interval (mm)" type="number" step="0.001" className="border rounded px-2 py-1" {...register(`fill_settings.${i}.interval`)} />
<input placeholder="Pass" type="number" step="1" className="border rounded px-2 py-1" {...register(`fill_settings.${i}.pass`)} />
{!isGantry && (
<>
<input
placeholder="Angle (°)"
type="number"
step="1"
className="border rounded px-2 py-1"
{...register(`fill_settings.${i}.angle`)}
/>
<input
placeholder="Increment"
type="number"
step="0.001"
className="border rounded px-2 py-1"
{...register(`fill_settings.${i}.increment`)}
/>
<input placeholder="Angle (°)" type="number" step="1" className="border rounded px-2 py-1" {...register(`fill_settings.${i}.angle`)} />
<input placeholder="Increment" type="number" step="0.001" className="border rounded px-2 py-1" {...register(`fill_settings.${i}.increment`)} />
<div className="flex items-center gap-3">
<BoolBox label="Auto" name={`fill_settings.${i}.auto`} register={register} />
<BoolBox label="Auto" name={`fill_settings.${i}.auto`} register={register} />
<BoolBox label="Cross" name={`fill_settings.${i}.cross`} register={register} />
</div>
</>
)}
<div className="flex items-center gap-3">
<BoolBox label="Flood" name={`fill_settings.${i}.flood`} register={register} />
<BoolBox label="Air" name={`fill_settings.${i}.air`} register={register} />
<BoolBox label="Air" name={`fill_settings.${i}.air`} register={register} />
</div>
<button
type="button"
className="px-2 py-1 border rounded md:col-span-4"
onClick={() => fills.remove(i)}
>
Remove
</button>
<button type="button" className="px-2 py-1 border rounded md:col-span-4" onClick={() => fills.remove(i)}>Remove</button>
</div>
))}
</fieldset>
@ -658,82 +455,32 @@ export default function SettingsSubmit({ initialTarget }: { initialTarget?: Targ
<fieldset className="border rounded p-3 space-y-2">
<div className="flex items-center justify-between">
<legend className="font-semibold">Line Settings</legend>
<button type="button" className="px-2 py-1 border rounded" onClick={() => lines.append({})}>
+ Add
</button>
<button type="button" className="px-2 py-1 border rounded" onClick={() => lines.append({})}>+ Add</button>
</div>
{lines.fields.map((f, i) => (
<div key={f.id} className="grid md:grid-cols-4 gap-2">
<input
placeholder="Name"
className="border rounded px-2 py-1 md:col-span-2"
{...register(`line_settings.${i}.name`)}
/>
<input
placeholder="Power (%)"
type="number"
step="0.1"
className="border rounded px-2 py-1"
{...register(`line_settings.${i}.power`)}
/>
<input
placeholder="Speed (mm/s)"
type="number"
step="0.1"
className="border rounded px-2 py-1"
{...register(`line_settings.${i}.speed`)}
/>
<input placeholder="Name" className="border rounded px-2 py-1 md:col-span-2" {...register(`line_settings.${i}.name`)} />
{!isGantry && (
<>
<input placeholder="Frequency (kHz)" type="number" step="0.1" className="border rounded px-2 py-1" {...register(`line_settings.${i}.frequency`)} />
<input placeholder="Pulse (ns)" type="number" step="0.1" className="border rounded px-2 py-1" {...register(`line_settings.${i}.pulse`)} />
</>
)}
<input placeholder="Power (%)" type="number" step="0.1" className="border rounded px-2 py-1" {...register(`line_settings.${i}.power`)} />
<input placeholder="Speed (mm/s)" type="number" step="0.1" className="border rounded px-2 py-1" {...register(`line_settings.${i}.speed`)} />
<input placeholder="Perf" className="border rounded px-2 py-1" {...register(`line_settings.${i}.perf`)} />
<input placeholder="Cut" className="border rounded px-2 py-1" {...register(`line_settings.${i}.cut`)} />
<input placeholder="Cut" className="border rounded px-2 py-1" {...register(`line_settings.${i}.cut`)} />
<input placeholder="Skip" className="border rounded px-2 py-1" {...register(`line_settings.${i}.skip`)} />
<input
placeholder="Pass"
type="number"
step="1"
className="border rounded px-2 py-1"
{...register(`line_settings.${i}.pass`)}
/>
{/* Extras (non-gantry) */}
<input
placeholder="Frequency (kHz)"
type="number"
step="0.1"
className="border rounded px-2 py-1"
{...register(`line_settings.${i}.frequency`)}
/>
<input
placeholder="Pulse (ns)"
type="number"
step="0.1"
className="border rounded px-2 py-1"
{...register(`line_settings.${i}.pulse`)}
/>
<input
placeholder="Step"
type="number"
step="0.001"
className="border rounded px-2 py-1"
{...register(`line_settings.${i}.step`)}
/>
<input
placeholder="Size"
type="number"
step="0.001"
className="border rounded px-2 py-1"
{...register(`line_settings.${i}.size`)}
/>
<BoolBox label="Wobble" name={`line_settings.${i}.wobble`} register={register} />
<input placeholder="Pass" type="number" step="1" className="border rounded px-2 py-1" {...register(`line_settings.${i}.pass`)} />
{!isGantry && (
<>
<input placeholder="Step" type="number" step="0.001" className="border rounded px-2 py-1" {...register(`line_settings.${i}.step`)} />
<input placeholder="Size" type="number" step="0.001" className="border rounded px-2 py-1" {...register(`line_settings.${i}.size`)} />
<BoolBox label="Wobble" name={`line_settings.${i}.wobble`} register={register} />
</>
)}
<BoolBox label="Air" name={`line_settings.${i}.air`} register={register} />
<button
type="button"
className="px-2 py-1 border rounded md:col-span-4"
onClick={() => lines.remove(i)}
>
Remove
</button>
<button type="button" className="px-2 py-1 border rounded md:col-span-4" onClick={() => lines.remove(i)}>Remove</button>
</div>
))}
</fieldset>
@ -742,108 +489,51 @@ export default function SettingsSubmit({ initialTarget }: { initialTarget?: Targ
<fieldset className="border rounded p-3 space-y-2">
<div className="flex items-center justify-between">
<legend className="font-semibold">Raster Settings</legend>
<button type="button" className="px-2 py-1 border rounded" onClick={() => rasters.append({})}>
+ Add
</button>
<button type="button" className="px-2 py-1 border rounded" onClick={() => rasters.append({})}>+ Add</button>
</div>
{rasters.fields.map((f, i) => (
<div key={f.id} className="grid md:grid-cols-4 gap-2">
<input
placeholder="Name"
className="border rounded px-2 py-1 md:col-span-2"
{...register(`raster_settings.${i}.name`)}
/>
<input placeholder="Name" className="border rounded px-2 py-1 md:col-span-2" {...register(`raster_settings.${i}.name`)} />
{!isGantry && (
<>
<input placeholder="Frequency (kHz)" type="number" step="0.1" className="border rounded px-2 py-1" {...register(`raster_settings.${i}.frequency`)} />
<input placeholder="Pulse (ns)" type="number" step="0.1" className="border rounded px-2 py-1" {...register(`raster_settings.${i}.pulse`)} />
</>
)}
<FilterableSelect
label="Type"
name={`raster_settings.${i}.type`}
register={register}
options={rasterType.opts}
loading={rasterType.loading}
onQuery={rasterType.setQ}
options={useOptions(`repeater-choices?target=${target}&group=raster_settings&field=type`).opts}
loading={false}
placeholder="Select type"
/>
<FilterableSelect
label="Dither"
name={`raster_settings.${i}.dither`}
register={register}
options={rasterDither.opts}
loading={rasterDither.loading}
onQuery={rasterDither.setQ}
options={useOptions(`repeater-choices?target=${target}&group=raster_settings&field=dither`).opts}
loading={false}
placeholder="Select dither"
/>
<input
placeholder="Power (%)"
type="number"
step="0.1"
className="border rounded px-2 py-1"
{...register(`raster_settings.${i}.power`)}
/>
<input
placeholder="Speed (mm/s)"
type="number"
step="0.1"
className="border rounded px-2 py-1"
{...register(`raster_settings.${i}.speed`)}
/>
<input
placeholder="Halftone Cell"
type="number"
step="1"
className="border rounded px-2 py-1"
{...register(`raster_settings.${i}.halftone_cell`)}
/>
<input
placeholder="Halftone Angle"
type="number"
step="1"
className="border rounded px-2 py-1"
{...register(`raster_settings.${i}.halftone_angle`)}
/>
<input
placeholder="Interval (mm)"
type="number"
step="0.001"
className="border rounded px-2 py-1"
{...register(`raster_settings.${i}.interval`)}
/>
<input
placeholder="Dot"
type="number"
step="0.1"
className="border rounded px-2 py-1"
{...register(`raster_settings.${i}.dot`)}
/>
<input
placeholder="Pass"
type="number"
step="1"
className="border rounded px-2 py-1"
{...register(`raster_settings.${i}.pass`)}
/>
<BoolBox label="Cross" name={`raster_settings.${i}.cross`} register={register} />
<input placeholder="Power (%)" type="number" step="0.1" className="border rounded px-2 py-1" {...register(`raster_settings.${i}.power`)} />
<input placeholder="Speed (mm/s)" type="number" step="0.1" className="border rounded px-2 py-1" {...register(`raster_settings.${i}.speed`)} />
<input placeholder="Halftone Cell" type="number" step="1" className="border rounded px-2 py-1" {...register(`raster_settings.${i}.halftone_cell`)} />
<input placeholder="Halftone Angle" type="number" step="1" className="border rounded px-2 py-1" {...register(`raster_settings.${i}.halftone_angle`)} />
<input placeholder="Interval (mm)" type="number" step="0.001" className="border rounded px-2 py-1" {...register(`raster_settings.${i}.interval`)} />
<input placeholder="Dot" type="number" step="0.1" className="border rounded px-2 py-1" {...register(`raster_settings.${i}.dot`)} />
<input placeholder="Pass" type="number" step="1" className="border rounded px-2 py-1" {...register(`raster_settings.${i}.pass`)} />
{!isGantry && <BoolBox label="Cross" name={`raster_settings.${i}.cross`} register={register} />}
<div className="flex items-center gap-3">
<BoolBox label="Inversion" name={`raster_settings.${i}.inversion`} register={register} />
<BoolBox label="Air" name={`raster_settings.${i}.air`} register={register} />
<BoolBox label="Air" name={`raster_settings.${i}.air`} register={register} />
</div>
<button
type="button"
className="px-2 py-1 border rounded md:col-span-4"
onClick={() => rasters.remove(i)}
>
Remove
</button>
<button type="button" className="px-2 py-1 border rounded md:col-span-4" onClick={() => rasters.remove(i)}>Remove</button>
</div>
))}
</fieldset>
<button
disabled={isSubmitting}
className="px-3 py-2 border rounded bg-accent text-background hover:opacity-90 disabled:opacity-50"
>
<button disabled={isSubmitting} className="px-3 py-2 border rounded bg-accent text-background hover:opacity-90 disabled:opacity-50">
{isSubmitting ? "Submitting…" : "Submit Settings"}
</button>
</form>