mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-29 18:48:11 +00:00
implement exchange settings management and fallback rate handling
This commit is contained in:
@@ -153,11 +153,13 @@ const RuleEngineFormDialog = ({
|
||||
buildInitialValues(fields, initialRecord),
|
||||
);
|
||||
const [position, setPosition] = useState(RULE_ENGINE_POSITION_END);
|
||||
const [fieldErrors, setFieldErrors] = useState<Record<string, string>>({});
|
||||
|
||||
useEffect(() => {
|
||||
if (open) {
|
||||
setValues(buildInitialValues(fields, initialRecord));
|
||||
setPosition(RULE_ENGINE_POSITION_END);
|
||||
setFieldErrors({});
|
||||
}
|
||||
}, [open, fields, initialRecord]);
|
||||
|
||||
@@ -185,6 +187,9 @@ const RuleEngineFormDialog = ({
|
||||
const formRows = useMemo(() => buildFormRows(visibleFields), [visibleFields]);
|
||||
|
||||
const setField = (name: string, value: unknown) => {
|
||||
setFieldErrors((current) =>
|
||||
current[name] ? { ...current, [name]: "" } : current,
|
||||
);
|
||||
setValues((current) => {
|
||||
const next = { ...current, [name]: value };
|
||||
// Changing what a rate applies to (or its surcharge trigger) can invalidate
|
||||
@@ -223,6 +228,10 @@ const RuleEngineFormDialog = ({
|
||||
const handleSubmit = (event: React.FormEvent) => {
|
||||
event.preventDefault();
|
||||
const payload: Record<string, unknown> = {};
|
||||
// Required selects that are empty block the submit and mark themselves,
|
||||
// rather than posting an incomplete payload for the API to reject.
|
||||
setFieldErrors({});
|
||||
let blocked = false;
|
||||
|
||||
for (const field of visibleFields) {
|
||||
// Derived fields always submit their computed value — never stale state.
|
||||
@@ -241,7 +250,16 @@ const RuleEngineFormDialog = ({
|
||||
field.type === "select" &&
|
||||
(raw === "" || raw === RULE_ENGINE_SELECT_NONE)
|
||||
) {
|
||||
// A required select left empty must not silently submit nothing — the
|
||||
// API rejects the payload with a message that reads as if the admin
|
||||
// skipped a field they never saw cleared (e.g. yards reset by a trade
|
||||
// direction change). Surface it on the field instead.
|
||||
if (!field.required) continue;
|
||||
setFieldErrors((current) => ({
|
||||
...current,
|
||||
[field.name]: `${field.label} is required.`,
|
||||
}));
|
||||
blocked = true;
|
||||
} else if (raw === "" || raw === undefined) {
|
||||
if (!field.required) continue;
|
||||
payload[field.name] = raw;
|
||||
@@ -254,6 +272,8 @@ const RuleEngineFormDialog = ({
|
||||
payload.code = String(payload.code).toUpperCase();
|
||||
}
|
||||
|
||||
if (blocked) return;
|
||||
|
||||
if (!initialRecord && positionOptions && position !== RULE_ENGINE_POSITION_END) {
|
||||
payload.insertAfterId = position;
|
||||
}
|
||||
@@ -340,10 +360,10 @@ const RuleEngineFormDialog = ({
|
||||
value={resolveSelectValue(field, values)}
|
||||
onChange={(v) => setField(field.name, v === RULE_ENGINE_SELECT_NONE ? "" : v)}
|
||||
disabled={selectOptionsLoading}
|
||||
// Native required blocks submit while a mandatory select is empty —
|
||||
// without it the form posts and the API 400s (e.g. a container
|
||||
// customs/lashing rate with no container type picked).
|
||||
// Mantine's Select is not a native input, so `required` only marks it
|
||||
// visually — handleSubmit is what actually blocks an empty one.
|
||||
required={field.required}
|
||||
error={fieldErrors[field.name] || undefined}
|
||||
data={options
|
||||
.filter((opt) => opt.value !== "")
|
||||
.map((opt) => ({
|
||||
|
||||
Reference in New Issue
Block a user