Files
edr-platform/apps/edr-freight-web/backoffice/src/pages/ruleEngine/priorityRuleRange.ts
2026-07-17 09:16:35 +00:00

57 lines
1.8 KiB
TypeScript

/**
* Client mirror of the backend's contiguous-range rules for priority configs
* (see PriorityConfigsService.assertNoRangeCollision): ranges per type — per
* currency for CURRENCY — run from 1 with no gaps and no overlaps, so the next
* range always starts at the lowest uncovered wagon count. There is no upper
* ceiling. The backend re-validates on submit AND on approval; this only
* drives the form prefill.
*/
export type PriorityRuleType = "WAGON" | "CURRENCY" | "CUSTOMS";
export interface PriorityRangeRule {
id?: unknown;
type?: unknown;
currency?: unknown;
minWagonCount?: unknown;
maxWagonCount?: unknown;
}
const PRIORITY_RULE_TYPES: PriorityRuleType[] = ["WAGON", "CURRENCY", "CUSTOMS"];
/**
* Where the next range for `type` (+`currency`) must start, excluding
* `excludeId` (the rule being edited). Null only when `type` is not yet a
* known priority rule type — the chain itself is unbounded, so a next start
* always exists.
*/
export function nextPriorityRangeStart(
rules: PriorityRangeRule[],
type: string,
currency: string | null | undefined,
excludeId?: string,
): number | null {
if (!PRIORITY_RULE_TYPES.includes(type as PriorityRuleType)) return null;
const scoped = rules
.filter(
(r) =>
String(r.type ?? "") === type &&
(excludeId === undefined || String(r.id ?? "") !== excludeId) &&
(type !== "CURRENCY" ||
String(r.currency ?? "") === String(currency ?? "")),
)
.map((r) => ({
min: Number(r.minWagonCount ?? 0),
max: Number(r.maxWagonCount ?? 0),
}))
.sort((a, b) => a.min - b.min);
let next = 1;
for (const r of scoped) {
if (r.min > next) break; // gap before this rule — fill it first
next = Math.max(next, r.max + 1);
}
return next;
}