mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-26 18:42:49 +00:00
update joins in repository services to use entity classes and enhance booking form with hazardous/reefer toggles
This commit is contained in:
@@ -26,6 +26,70 @@ type BookingForm = UseFormReturn<
|
||||
BookingFormValues
|
||||
>;
|
||||
|
||||
/**
|
||||
* One numbered toggle per container unit in the line — tap units to mark how
|
||||
* many are hazardous/refrigerated (2 hazardous → toggle 2 units on). Selection
|
||||
* fills from unit 1: tapping unit N selects 1..N, tapping a selected unit N
|
||||
* keeps 1..N-1 — the count is always derived, never free-typed, so it can't
|
||||
* exceed the line quantity.
|
||||
*/
|
||||
function UnitCountToggles({
|
||||
total,
|
||||
value,
|
||||
onChange,
|
||||
label,
|
||||
activeBg,
|
||||
activeBorder,
|
||||
activeColor,
|
||||
}: {
|
||||
total: number;
|
||||
value: string;
|
||||
onChange: (v: string) => void;
|
||||
label: string;
|
||||
activeBg: string;
|
||||
activeBorder: string;
|
||||
activeColor: string;
|
||||
}) {
|
||||
const count = Math.min(total, Math.max(0, Math.floor(Number(value) || 0)));
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Text fz={12} fw={600} c="#4A5A68" mb={6}>
|
||||
{label} · {count}/{total} selected
|
||||
</Text>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
{Array.from({ length: total }, (_, i) => {
|
||||
const selected = i < count;
|
||||
return (
|
||||
<button
|
||||
key={i}
|
||||
type="button"
|
||||
aria-pressed={selected}
|
||||
aria-label={`Container ${i + 1}`}
|
||||
onClick={() => onChange(String(selected ? i : i + 1))}
|
||||
className="rounded-lg"
|
||||
style={{
|
||||
minWidth: 40,
|
||||
padding: "6px 10px",
|
||||
fontSize: 12,
|
||||
fontWeight: 700,
|
||||
cursor: "pointer",
|
||||
border: `1.5px solid ${selected ? activeBorder : "#E6ECF2"}`,
|
||||
background: selected ? activeBg : "#fff",
|
||||
color: selected ? activeColor : "#6B7C8E",
|
||||
transition:
|
||||
"background 120ms ease, border-color 120ms ease, color 120ms ease",
|
||||
}}
|
||||
>
|
||||
#{i + 1}
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export function Step5CargoDetails({
|
||||
form,
|
||||
referenceData,
|
||||
@@ -144,16 +208,6 @@ export function Step5CargoDetails({
|
||||
const lineQtyOf = (index: number) =>
|
||||
Math.max(1, Number(form.getValues(`containers.${index}.qty`) ?? 1) || 1);
|
||||
const lineMax = (index: number) => lineQtyOf(index);
|
||||
// When a flag is switched on, default its count to the whole line.
|
||||
const defaultLineQty = (index: number) => lineQtyOf(index).toString();
|
||||
// Clamp a typed value into 1..lineQty (empty stays empty so the field can be
|
||||
// cleared; the schema flags an empty value as required while the switch is on).
|
||||
const clampToLine = (raw: string, index: number) => {
|
||||
if (raw === "") return "";
|
||||
const n = Number(raw);
|
||||
if (Number.isNaN(n)) return raw;
|
||||
return Math.min(lineQtyOf(index), Math.max(1, Math.floor(n))).toString();
|
||||
};
|
||||
// After the line quantity changes, pull any active count back within bounds.
|
||||
const clampDependentQty = (index: number, newLineQty: number) => {
|
||||
const max = Math.max(1, newLineQty);
|
||||
@@ -636,7 +690,7 @@ export function Step5CargoDetails({
|
||||
hazField.onChange(v);
|
||||
form.setValue(
|
||||
`containers.${index}.hazardousQty`,
|
||||
v ? defaultLineQty(index) : "0",
|
||||
v ? "1" : "0",
|
||||
{ shouldDirty: true, shouldValidate: true },
|
||||
);
|
||||
}}
|
||||
@@ -645,22 +699,22 @@ export function Step5CargoDetails({
|
||||
name={`containers.${index}.hazardousQty`}
|
||||
control={form.control}
|
||||
render={({ field: hq, fieldState }) => (
|
||||
<TextInput
|
||||
type="number"
|
||||
size="sm"
|
||||
label="How many hazardous?"
|
||||
min={1}
|
||||
max={lineMax(index)}
|
||||
value={hq.value ?? ""}
|
||||
onChange={(e) =>
|
||||
hq.onChange(
|
||||
clampToLine(e.currentTarget.value, index),
|
||||
)
|
||||
}
|
||||
onBlur={hq.onBlur}
|
||||
error={fieldState.error?.message}
|
||||
radius="md"
|
||||
/>
|
||||
<div>
|
||||
<UnitCountToggles
|
||||
total={lineMax(index)}
|
||||
value={hq.value ?? "0"}
|
||||
onChange={hq.onChange}
|
||||
label="Tap the hazardous containers"
|
||||
activeBg="#FBEAE7"
|
||||
activeBorder="#E4A69B"
|
||||
activeColor="#C0392B"
|
||||
/>
|
||||
{fieldState.error?.message ? (
|
||||
<Text fz={11} c="red.7" mt={4}>
|
||||
{fieldState.error.message}
|
||||
</Text>
|
||||
) : null}
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
</ToggleRow>
|
||||
@@ -681,7 +735,7 @@ export function Step5CargoDetails({
|
||||
reeField.onChange(v);
|
||||
form.setValue(
|
||||
`containers.${index}.reeferQty`,
|
||||
v ? defaultLineQty(index) : "0",
|
||||
v ? "1" : "0",
|
||||
{ shouldDirty: true, shouldValidate: true },
|
||||
);
|
||||
}}
|
||||
@@ -690,22 +744,22 @@ export function Step5CargoDetails({
|
||||
name={`containers.${index}.reeferQty`}
|
||||
control={form.control}
|
||||
render={({ field: rq, fieldState }) => (
|
||||
<TextInput
|
||||
type="number"
|
||||
size="sm"
|
||||
label="How many refrigerated?"
|
||||
min={1}
|
||||
max={lineMax(index)}
|
||||
value={rq.value ?? ""}
|
||||
onChange={(e) =>
|
||||
rq.onChange(
|
||||
clampToLine(e.currentTarget.value, index),
|
||||
)
|
||||
}
|
||||
onBlur={rq.onBlur}
|
||||
error={fieldState.error?.message}
|
||||
radius="md"
|
||||
/>
|
||||
<div>
|
||||
<UnitCountToggles
|
||||
total={lineMax(index)}
|
||||
value={rq.value ?? "0"}
|
||||
onChange={rq.onChange}
|
||||
label="Tap the refrigerated containers"
|
||||
activeBg="#E9F0F8"
|
||||
activeBorder="#A9C2E0"
|
||||
activeColor="#2E5B96"
|
||||
/>
|
||||
{fieldState.error?.message ? (
|
||||
<Text fz={11} c="red.7" mt={4}>
|
||||
{fieldState.error.message}
|
||||
</Text>
|
||||
) : null}
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
</ToggleRow>
|
||||
|
||||
Reference in New Issue
Block a user