mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-29 16:28:12 +00:00
add company stamp upload functionality for contract signing
- Introduced StampUpload component for uploading company stamp images. - Integrated stamp upload in contract signing modal, supporting PNG and JPG formats. - Implemented validation for file type and size (max 5 MB). - Added visual feedback for drag-and-drop functionality. - Updated contract-related pages to handle duplicate contract alerts and pricing notices. - Enhanced contract expiry management with a nightly sweep service. - Added unit tests for new features and updated existing tests for contract handling.
This commit is contained in:
@@ -6659,6 +6659,8 @@ export class TrainSchedulingService {
|
||||
: slot.physicalWagonId ?? null;
|
||||
if (physicalId) coveredPhysicalIds.add(physicalId);
|
||||
}
|
||||
// Fallback only — real empty rows below carry the wagon's OWN physical
|
||||
// sequenceNumber, not an invented tail position (see emptyConsistWagons).
|
||||
const maxSlotSequenceNo = Math.max(
|
||||
0,
|
||||
...(schedule.trainSet?.wagons ?? []).map((w) => w.sequenceNo),
|
||||
@@ -6669,7 +6671,11 @@ export class TrainSchedulingService {
|
||||
// Physical wagon id — there is no TrainSetWagon slot behind this
|
||||
// row, so remove/edit affordances must stay disabled (consistOnly).
|
||||
id: wagon.id,
|
||||
sequenceNo: maxSlotSequenceNo + index + 1,
|
||||
// The wagon's REAL coupling position, so an empty wagon in the middle
|
||||
// of the train draws in the middle — not appended after every loaded
|
||||
// slot. Falls back to a tail position only if the wagon somehow has
|
||||
// no sequence number of its own.
|
||||
sequenceNo: wagon.sequenceNumber ?? maxSlotSequenceNo + index + 1,
|
||||
capacityTons: roundTons(Number(wagon.wagonType?.capacityTons ?? 0)),
|
||||
lengthMeters: roundTons(Number(wagon.wagonType?.lengthMeters ?? 0)),
|
||||
assignedWeightTons: 0,
|
||||
@@ -6779,8 +6785,7 @@ export class TrainSchedulingService {
|
||||
maxPullWeightTons: roundTons(Number(loco.maxPullWeightTons)),
|
||||
maxTrainLengthMeters: roundTons(Number(loco.maxTrainLengthMeters)),
|
||||
})),
|
||||
wagons: [...(schedule.trainSet.wagons ?? [])]
|
||||
.sort((a, b) => a.sequenceNo - b.sequenceNo)
|
||||
wagons: (schedule.trainSet.wagons ?? [])
|
||||
.map((wagon) => {
|
||||
// Frozen schedules read the wagon number + allocations from the
|
||||
// snapshot slot; the immutable slot geometry (capacity/type) still
|
||||
@@ -6788,9 +6793,20 @@ export class TrainSchedulingService {
|
||||
const frozenSlot = isWagonAllocationFrozen
|
||||
? snapshotSlotByTrainSetWagonId.get(wagon.id)
|
||||
: undefined;
|
||||
// Draw the slot at its physical wagon's REAL coupling position,
|
||||
// not the planning-time slot index — the two diverge once a
|
||||
// load has been dragged onto a different wagon (moveWagonLoad
|
||||
// repoints physicalWagonId but a slot keeps its own sequenceNo),
|
||||
// or once wagon types were interleaved at pinning time. Frozen
|
||||
// and not-yet-pinned slots have no live physical wagon to trust,
|
||||
// so they keep their own slot sequence.
|
||||
const sequenceNo =
|
||||
frozenSlot || !wagon.physicalWagon
|
||||
? wagon.sequenceNo
|
||||
: (wagon.physicalWagon.sequenceNumber ?? wagon.sequenceNo);
|
||||
return {
|
||||
id: wagon.id,
|
||||
sequenceNo: wagon.sequenceNo,
|
||||
sequenceNo,
|
||||
capacityTons: roundTons(Number(wagon.capacityTons)),
|
||||
lengthMeters: roundTons(Number(wagon.lengthMeters)),
|
||||
assignedWeightTons: roundTons(Number(wagon.assignedWeightTons)),
|
||||
@@ -6859,7 +6875,12 @@ export class TrainSchedulingService {
|
||||
})) ?? [],
|
||||
};
|
||||
})
|
||||
.concat(emptyConsistWagons),
|
||||
.concat(emptyConsistWagons)
|
||||
.sort((a, b) =>
|
||||
schedule.reverseWagonOrder
|
||||
? b.sequenceNo - a.sequenceNo
|
||||
: a.sequenceNo - b.sequenceNo,
|
||||
),
|
||||
}
|
||||
: null,
|
||||
bookings:
|
||||
|
||||
Reference in New Issue
Block a user