diff --git a/apps/edr-freight-web/backoffice/src/components/fleet/FleetFormDialog.tsx b/apps/edr-freight-web/backoffice/src/components/fleet/FleetFormDialog.tsx index dfb27ed36..8686f9cdf 100644 --- a/apps/edr-freight-web/backoffice/src/components/fleet/FleetFormDialog.tsx +++ b/apps/edr-freight-web/backoffice/src/components/fleet/FleetFormDialog.tsx @@ -263,6 +263,14 @@ const FleetFormDialog = ({ return map; }, [fields]); + // Emptying one of these means "unset the column", so it submits an explicit + // null instead of being dropped from the payload like other empty fields. + const clearableByName = useMemo(() => { + const map: Record = {}; + fields.forEach((f) => (map[f.name] = Boolean(f.clearable))); + return map; + }, [fields]); + const handleSubmit = () => { // Hard gate: a driver record cannot be saved until its identity is verified // with Fayda. Mirrored server-side in DriversService. @@ -280,8 +288,8 @@ const FleetFormDialog = ({ const payload = Object.fromEntries( Object.entries(submitted) .map(([key, value]) => { - if (value === FLEET_SELECT_NONE || value === "") - return [key, undefined]; + if (value === FLEET_SELECT_NONE || value === "" || value == null) + return [key, clearableByName[key] ? null : undefined]; if (fieldTypeByName[key] === "number") { const num = Number(value); return [key, Number.isNaN(num) ? undefined : num]; @@ -357,6 +365,7 @@ const FleetFormDialog = ({ } error={error} searchable + clearable={field.clearable} disabled={selectOptionsLoading || isDisabled} rightSection={ selectOptionsLoading ? ( diff --git a/apps/edr-freight-web/backoffice/src/pages/fleet/config/resources.ts b/apps/edr-freight-web/backoffice/src/pages/fleet/config/resources.ts index 7a473fdb1..83928e146 100644 --- a/apps/edr-freight-web/backoffice/src/pages/fleet/config/resources.ts +++ b/apps/edr-freight-web/backoffice/src/pages/fleet/config/resources.ts @@ -47,6 +47,13 @@ export interface FleetFormFieldDef extends FormFieldDef { * truth at both render and submit. */ derivedValue?: (values: Record) => string; + /** + * Field can be emptied back to NULL. Empty values are normally dropped from + * the payload (so a PATCH leaves them untouched); a clearable field instead + * submits an explicit `null`, which is what actually unsets the column. Also + * renders a clear button on a `select`. + */ + clearable?: boolean; /** * Field is owned by the Fayda identity — populated only by verification and * never hand-edited. Rendered disabled in the form. @@ -327,6 +334,7 @@ export const FLEET_RESOURCES: FleetResourceConfig[] = [ description: "Odd — Ethiopia → Djibouti runs", placeholder: "e.g. 8001", options: EXPORT_TRAIN_OPTIONS, + clearable: true, }, { name: "importTrainNumber", @@ -336,6 +344,8 @@ export const FLEET_RESOURCES: FleetResourceConfig[] = [ placeholder: "e.g. 8002", derivedValue: (values) => TRAIN_RUN_PAIRS[String(values.exportTrainNumber ?? "")] ?? "", + // Follows the export run to NULL when that is cleared. + clearable: true, }, { name: "wagonNumber", label: "Wagon number", type: "text", required: true }, { name: "wagonTypeId", label: "Wagon type", type: "select", required: true, dynamicOptions: "wagonTypes" },