Files
edr-platform/apps/edr-freight-api/src/modules/train-scheduling/consist-order.util.ts
Marshal 9b13fa2ac6 feat: implement wagon transfer management modals and page
- Add TransferFulfillModal for fulfilling wagon transfer requests.
- Create TransferRequestFormModal for filing new wagon transfer requests.
- Introduce TransferCloseShortModal for closing requests that cannot be fully fulfilled.
- Develop WagonTransfersPage to manage and display wagon transfer requests.
- Implement utility functions for handling wagon transfer request data and UI components.
- Enhance UI with Mantine components for better user experience.
2026-07-26 15:11:50 +00:00

55 lines
2.2 KiB
TypeScript

/**
* Draw order for a schedule's consist.
*
* A slot's stored `sequenceNo` is its place in the wagon PLAN, not its place in
* the train. The train's real coupling order lives on the physical wagons
* (`wagons.sequence_number`), which the caller passes in already ordered — ASC
* normally, DESC for a `reverseWagonOrder` schedule.
*
* Ordering by the physical wagon is what keeps the drawing honest:
* - moving a load between wagons repaints WHICH wagon is loaded and never
* shuffles the train, because each slot is drawn wherever its wagon sits;
* - a train-builder reorder lands on the next read, allocations included,
* since the order is derived on every read instead of copied at pin time.
*
* Slots with no physical wagon (not pinned yet, or a schedule that isn't tied
* to a built train) have no place in the consist — they keep slot order, last.
*/
export interface ConsistOrderable {
sequenceNo: number;
physicalWagonId?: string | null;
}
export interface ConsistOrderOptions {
/**
* Every wagon coupled to the built train, in real coupling order (already
* reversed by the caller for a `reverseWagonOrder` schedule). Empty for a
* frozen schedule or one with no built train — the consist then keeps slot
* order.
*/
physicalWagonIdsInOrder: string[];
reverseWagonOrder?: boolean;
}
export const orderConsistWagons = <T extends ConsistOrderable>(
wagons: T[],
{ physicalWagonIdsInOrder, reverseWagonOrder }: ConsistOrderOptions,
): (T & { position: number })[] => {
const physicalOrder = new Map(physicalWagonIdsInOrder.map((id, index) => [id, index]));
const bySlotSequence = (a: T, b: T) =>
reverseWagonOrder ? b.sequenceNo - a.sequenceNo : a.sequenceNo - b.sequenceNo;
const ordered = physicalOrder.size
? [...wagons].sort((a, b) => {
const ai = a.physicalWagonId ? physicalOrder.get(a.physicalWagonId) : undefined;
const bi = b.physicalWagonId ? physicalOrder.get(b.physicalWagonId) : undefined;
if (ai == null && bi == null) return bySlotSequence(a, b);
if (ai == null) return 1;
if (bi == null) return -1;
return ai - bi;
})
: [...wagons].sort(bySlotSequence);
return ordered.map((wagon, index) => ({ ...wagon, position: index + 1 }));
};