mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-09-06 11:15:03 +00:00
Merge branch 'dev' of github.com:Tria-plc/edr-platform into origin/freight_feature/transit
This commit is contained in:
@@ -118,6 +118,7 @@ const blockNegative = (event: KeyboardEvent<HTMLInputElement>) => {
|
||||
|
||||
interface UnitErrors {
|
||||
containerNumber?: string;
|
||||
sealNumber?: string;
|
||||
vgmTons?: string;
|
||||
}
|
||||
|
||||
@@ -886,6 +887,11 @@ export default function GlCreateBookingForm() {
|
||||
} else if ((numberCounts.get(key) ?? 0) > 1) {
|
||||
errs.containerNumber = "Duplicate container number in this shipment.";
|
||||
}
|
||||
// Every container ships sealed and the yard checks the seal against
|
||||
// the booking — required alongside number and VGM (portal parity).
|
||||
if (!u.sealNumber.trim()) {
|
||||
errs.sealNumber = "Seal number is required.";
|
||||
}
|
||||
const vgm = Number(u.vgmTons);
|
||||
if (u.vgmTons.trim() === "" || Number.isNaN(vgm) || vgm <= 0) {
|
||||
errs.vgmTons = "Enter a valid VGM.";
|
||||
@@ -1047,6 +1053,12 @@ export default function GlCreateBookingForm() {
|
||||
|
||||
const dateError =
|
||||
!isIntercity && !scheduledDate ? "Select a shipment date." : undefined;
|
||||
// EXPORT completion locks the booking onto a train. Only raised once a day is
|
||||
// chosen — the picker is hidden until then and the date error covers it.
|
||||
const trainError =
|
||||
isExportPick && scheduledDate && !trainScheduleId
|
||||
? "Select a train for the shipment day."
|
||||
: undefined;
|
||||
const routeError =
|
||||
multiRoute && !contractRouteId ? "Select a route." : undefined;
|
||||
|
||||
@@ -1065,7 +1077,7 @@ export default function GlCreateBookingForm() {
|
||||
!e.returnQuantity,
|
||||
) &&
|
||||
unitErrors.every((line) =>
|
||||
line.every((e) => !e.containerNumber && !e.vgmTons),
|
||||
line.every((e) => !e.containerNumber && !e.sealNumber && !e.vgmTons),
|
||||
) &&
|
||||
!cargoDescriptionError
|
||||
: !bulkErrors.quantity &&
|
||||
@@ -1112,11 +1124,12 @@ export default function GlCreateBookingForm() {
|
||||
line.units.some(
|
||||
(u) =>
|
||||
!ISO_CONTAINER_NUMBER_REGEX.test(u.containerNumber.trim().toUpperCase()) ||
|
||||
!u.sealNumber.trim() ||
|
||||
!(Number(u.vgmTons) > 0),
|
||||
),
|
||||
);
|
||||
if (badUnit) {
|
||||
return `Every ${partner.reference} container needs a valid container number and a VGM above 0.`;
|
||||
return `Every ${partner.reference} container needs a valid container number, a seal number and a VGM above 0.`;
|
||||
}
|
||||
if (!partnerCargoDescription.trim()) {
|
||||
return `Describe the cargo carried in ${partner.reference}'s containers.`;
|
||||
@@ -1142,6 +1155,7 @@ export default function GlCreateBookingForm() {
|
||||
cargoValid &&
|
||||
!oddBlocksSubmit &&
|
||||
!dateError &&
|
||||
!trainError &&
|
||||
!routeError &&
|
||||
!partnerError &&
|
||||
!currencyError;
|
||||
@@ -1185,7 +1199,7 @@ export default function GlCreateBookingForm() {
|
||||
: {}),
|
||||
units: l.units.map((u) => ({
|
||||
containerNumber: u.containerNumber.trim().toUpperCase(),
|
||||
...(u.sealNumber ? { sealNumber: u.sealNumber } : {}),
|
||||
sealNumber: u.sealNumber.trim(),
|
||||
vgmTons: Number(u.vgmTons) || 0,
|
||||
// Per-container handling — the server rolls these into the line
|
||||
// counts and bills each surcharge on the ticked containers only.
|
||||
@@ -1247,7 +1261,7 @@ export default function GlCreateBookingForm() {
|
||||
reeferQuantity: Number(l.reeferQuantity || 0) || undefined,
|
||||
units: l.units.map((u) => ({
|
||||
containerNumber: u.containerNumber.trim().toUpperCase(),
|
||||
...(u.sealNumber ? { sealNumber: u.sealNumber } : {}),
|
||||
sealNumber: u.sealNumber.trim(),
|
||||
vgmTons: Number(u.vgmTons) || 0,
|
||||
isHazardous: Boolean(u.isHazardous),
|
||||
isReefer: Boolean(u.isReefer),
|
||||
@@ -1857,7 +1871,7 @@ export default function GlCreateBookingForm() {
|
||||
Container number *
|
||||
</Text>
|
||||
<Text fz={12} fw={600} c="#10202F" style={{ flex: 1 }}>
|
||||
Seal number
|
||||
Seal number *
|
||||
</Text>
|
||||
<Text fz={12} fw={600} c="#10202F" style={{ flex: 1 }}>
|
||||
VGM (tons) *
|
||||
@@ -1901,8 +1915,13 @@ export default function GlCreateBookingForm() {
|
||||
style={{ flex: 1 }}
|
||||
/>
|
||||
<TextInput
|
||||
placeholder="Optional"
|
||||
placeholder="e.g. SL0123456"
|
||||
value={unit.sealNumber}
|
||||
error={
|
||||
showErrors
|
||||
? unitErrors[lineIdx]?.[unitIdx]?.sealNumber
|
||||
: undefined
|
||||
}
|
||||
onChange={(e) =>
|
||||
patchUnit(lineIdx, unitIdx, {
|
||||
sealNumber: e.currentTarget.value,
|
||||
@@ -2305,12 +2324,19 @@ export default function GlCreateBookingForm() {
|
||||
</Text>
|
||||
)}
|
||||
{isExportPick && scheduledDate ? (
|
||||
<ExportTrainPicker
|
||||
options={exportTrainsQuery.data ?? []}
|
||||
loading={exportTrainsQuery.isLoading}
|
||||
value={trainScheduleId}
|
||||
onChange={setTrainScheduleId}
|
||||
/>
|
||||
<>
|
||||
<ExportTrainPicker
|
||||
options={exportTrainsQuery.data ?? []}
|
||||
loading={exportTrainsQuery.isLoading}
|
||||
value={trainScheduleId}
|
||||
onChange={setTrainScheduleId}
|
||||
/>
|
||||
{showErrors && trainError && (
|
||||
<Text fz="xs" c="red" mt={6}>
|
||||
{trainError}
|
||||
</Text>
|
||||
)}
|
||||
</>
|
||||
) : null}
|
||||
</Box>
|
||||
</StepCard>
|
||||
@@ -2401,7 +2427,11 @@ export default function GlCreateBookingForm() {
|
||||
style={{ flexShrink: 0 }}
|
||||
/>
|
||||
<Text fz={13} fw={500} c="#C0392B">
|
||||
Fix the highlighted fields to review the price.
|
||||
{trainError
|
||||
? "Select a train for the shipment day to review the price."
|
||||
: dateError && isExportPick
|
||||
? "Select a shipment day and a train to review the price."
|
||||
: "Fix the highlighted fields to review the price."}
|
||||
</Text>
|
||||
</>
|
||||
)}
|
||||
|
||||
@@ -196,8 +196,13 @@ export function ConsolidationPartnerPanel({
|
||||
}
|
||||
/>
|
||||
<TextInput
|
||||
label="Seal number"
|
||||
label="Seal number *"
|
||||
value={unit.sealNumber}
|
||||
error={
|
||||
showErrors && !unit.sealNumber.trim()
|
||||
? "Seal number is required."
|
||||
: undefined
|
||||
}
|
||||
onChange={(e) =>
|
||||
patchUnit(lineIdx, unitIdx, {
|
||||
sealNumber: e.currentTarget.value,
|
||||
|
||||
@@ -151,6 +151,11 @@ export async function parseContainerExcel(
|
||||
numberCounts.set(containerNumber, (numberCounts.get(containerNumber) ?? 0) + 1);
|
||||
}
|
||||
|
||||
const sealNumber = cell("sealNumber");
|
||||
if (!sealNumber) {
|
||||
errors.push(`Row ${rowNo}: seal number is required.`);
|
||||
}
|
||||
|
||||
const vgmRaw = cell("vgmTons");
|
||||
const vgm = Number(vgmRaw);
|
||||
if (!vgmRaw || Number.isNaN(vgm) || vgm <= 0) {
|
||||
@@ -160,7 +165,7 @@ export async function parseContainerExcel(
|
||||
rows.push({
|
||||
containerSize: size ?? "",
|
||||
containerNumber,
|
||||
sealNumber: cell("sealNumber"),
|
||||
sealNumber,
|
||||
vgmTons: vgmRaw,
|
||||
hazardous: opts.includeHazardous && parseFlag(cell("hazardous")),
|
||||
reefer: opts.includeReefer && parseFlag(cell("reefer")),
|
||||
|
||||
Reference in New Issue
Block a user