mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-30 07:38:10 +00:00
Remove estimated shipment date from contract forms and related components
This commit is contained in:
@@ -200,7 +200,9 @@ export class ContractDocumentViewModelBuilder {
|
|||||||
serviceType: this.valueOrDash(
|
serviceType: this.valueOrDash(
|
||||||
contract.serviceType?.serviceName ?? contract.serviceType?.code,
|
contract.serviceType?.serviceName ?? contract.serviceType?.code,
|
||||||
),
|
),
|
||||||
scheduledDate: this.formatDate(contract.estimatedShipmentDate),
|
// Estimated shipment date was removed from the contract wizard; the
|
||||||
|
// binding scheduled date is set per-booking, not on the contract.
|
||||||
|
scheduledDate: this.formatDate(null),
|
||||||
contractType: this.valueOrDash(contract.contractType),
|
contractType: this.valueOrDash(contract.contractType),
|
||||||
cargoDescription: this.valueOrDash(cargoName),
|
cargoDescription: this.valueOrDash(cargoName),
|
||||||
totalWeightVgm: '—',
|
totalWeightVgm: '—',
|
||||||
|
|||||||
@@ -200,9 +200,6 @@ export class ContractsService {
|
|||||||
lastMileDeliveryLng: dto.lastMileDeliveryLng ?? null,
|
lastMileDeliveryLng: dto.lastMileDeliveryLng ?? null,
|
||||||
isHazardous: dto.isHazardous ?? false,
|
isHazardous: dto.isHazardous ?? false,
|
||||||
isReefer: dto.isReefer ?? false,
|
isReefer: dto.isReefer ?? false,
|
||||||
estimatedShipmentDate: dto.estimatedShipmentDate
|
|
||||||
? new Date(dto.estimatedShipmentDate)
|
|
||||||
: null,
|
|
||||||
contractType: dto.contractType ?? null,
|
contractType: dto.contractType ?? null,
|
||||||
status: 'DRAFT',
|
status: 'DRAFT',
|
||||||
clearanceStatus: 'NOT_APPLICABLE',
|
clearanceStatus: 'NOT_APPLICABLE',
|
||||||
@@ -347,9 +344,6 @@ export class ContractsService {
|
|||||||
lastMileDeliveryLng: dto.lastMileDeliveryLng ?? existing.lastMileDeliveryLng,
|
lastMileDeliveryLng: dto.lastMileDeliveryLng ?? existing.lastMileDeliveryLng,
|
||||||
contractType: dto.contractType ?? existing.contractType,
|
contractType: dto.contractType ?? existing.contractType,
|
||||||
};
|
};
|
||||||
if (dto.estimatedShipmentDate) {
|
|
||||||
updates.estimatedShipmentDate = new Date(dto.estimatedShipmentDate);
|
|
||||||
}
|
|
||||||
if (dto.renewalOfId !== undefined) updates.renewalOfId = dto.renewalOfId ?? null;
|
if (dto.renewalOfId !== undefined) updates.renewalOfId = dto.renewalOfId ?? null;
|
||||||
|
|
||||||
// Customs clearing always mirrors the (possibly changed) service type.
|
// Customs clearing always mirrors the (possibly changed) service type.
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import {
|
|||||||
ArrayMinSize,
|
ArrayMinSize,
|
||||||
IsArray,
|
IsArray,
|
||||||
IsBoolean,
|
IsBoolean,
|
||||||
IsDateString,
|
|
||||||
IsIn,
|
IsIn,
|
||||||
IsNumber,
|
IsNumber,
|
||||||
IsOptional,
|
IsOptional,
|
||||||
@@ -219,14 +218,6 @@ export class CreateContractDto {
|
|||||||
@Transform(({ value }) => value === 'true' || value === true)
|
@Transform(({ value }) => value === 'true' || value === true)
|
||||||
isReefer?: boolean;
|
isReefer?: boolean;
|
||||||
|
|
||||||
@ApiPropertyOptional({
|
|
||||||
description: 'Non-binding estimate from the wizard (NOT validated against departures)',
|
|
||||||
example: '2026-07-15T00:00:00.000Z',
|
|
||||||
})
|
|
||||||
@IsOptional()
|
|
||||||
@IsDateString()
|
|
||||||
estimatedShipmentDate?: string;
|
|
||||||
|
|
||||||
@ApiPropertyOptional({ description: 'Contract document type (SPOT, etc.)' })
|
@ApiPropertyOptional({ description: 'Contract document type (SPOT, etc.)' })
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsString()
|
@IsString()
|
||||||
|
|||||||
@@ -449,17 +449,6 @@ export default function ContractDetailPage() {
|
|||||||
routes[0]?.destinationYard?.label ?? "—"
|
routes[0]?.destinationYard?.label ?? "—"
|
||||||
}`}
|
}`}
|
||||||
/>
|
/>
|
||||||
<MetaItem
|
|
||||||
label="Estimated shipment"
|
|
||||||
icon={<CalendarClock size={15} color={MUTED} />}
|
|
||||||
value={
|
|
||||||
contract.estimatedShipmentDate
|
|
||||||
? new Date(
|
|
||||||
contract.estimatedShipmentDate,
|
|
||||||
).toLocaleDateString()
|
|
||||||
: "—"
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
<MetaItem
|
<MetaItem
|
||||||
label="Customs clearance"
|
label="Customs clearance"
|
||||||
icon={<FileText size={15} color={MUTED} />}
|
icon={<FileText size={15} color={MUTED} />}
|
||||||
|
|||||||
@@ -541,13 +541,6 @@ export default function NewContractPage({
|
|||||||
isHazardous: data.isHazardous,
|
isHazardous: data.isHazardous,
|
||||||
// Reefer is a contract-level flag for both container and bulk.
|
// Reefer is a contract-level flag for both container and bulk.
|
||||||
isReefer: data.isRefrigerated,
|
isReefer: data.isRefrigerated,
|
||||||
...(data.estimatedShipmentDate
|
|
||||||
? {
|
|
||||||
estimatedShipmentDate: new Date(
|
|
||||||
data.estimatedShipmentDate,
|
|
||||||
).toISOString(),
|
|
||||||
}
|
|
||||||
: {}),
|
|
||||||
...(data.previousContractRef
|
...(data.previousContractRef
|
||||||
? { renewalOfId: data.previousContractRef }
|
? { renewalOfId: data.previousContractRef }
|
||||||
: {}),
|
: {}),
|
||||||
|
|||||||
@@ -16,12 +16,6 @@ function directionToOperation(
|
|||||||
return "intercity";
|
return "intercity";
|
||||||
}
|
}
|
||||||
|
|
||||||
/** ISO timestamp → `YYYY-MM-DD` for the native date input. "" when absent. */
|
|
||||||
function isoToDateInput(iso: string | null | undefined): string {
|
|
||||||
if (!iso) return "";
|
|
||||||
return iso.slice(0, 10);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Rebuild the cargo-type path `[groupId, commodityId]` from a flat cargoTypeId
|
* Rebuild the cargo-type path `[groupId, commodityId]` from a flat cargoTypeId
|
||||||
* by locating which reference cargo-type group owns it.
|
* by locating which reference cargo-type group owns it.
|
||||||
@@ -138,7 +132,6 @@ export function contractToFormValues(
|
|||||||
originYard: primaryRoute?.originYardId ?? "",
|
originYard: primaryRoute?.originYardId ?? "",
|
||||||
destinationYard: primaryRoute?.destinationYardId ?? "",
|
destinationYard: primaryRoute?.destinationYardId ?? "",
|
||||||
extraRoutes,
|
extraRoutes,
|
||||||
estimatedShipmentDate: isoToDateInput(contract.estimatedShipmentDate),
|
|
||||||
|
|
||||||
documents: {},
|
documents: {},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -187,8 +187,6 @@ export const contractFormSchema = z
|
|||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
.default([]),
|
.default([]),
|
||||||
// Non-binding estimate. The binding scheduled date is captured at booking.
|
|
||||||
estimatedShipmentDate: z.string().default(""),
|
|
||||||
|
|
||||||
documents: z.record(z.string(), z.any()).default({}),
|
documents: z.record(z.string(), z.any()).default({}),
|
||||||
notes: z.string().default(""),
|
notes: z.string().default(""),
|
||||||
@@ -222,15 +220,6 @@ export const contractFormSchema = z
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
.superRefine((data, ctx) => {
|
.superRefine((data, ctx) => {
|
||||||
// Estimated shipment date is a planning value and is required for all
|
|
||||||
// contracts (doc §7 step 4).
|
|
||||||
if (!data.estimatedShipmentDate.trim()) {
|
|
||||||
ctx.addIssue({
|
|
||||||
code: "custom",
|
|
||||||
path: ["estimatedShipmentDate"],
|
|
||||||
message: "Select an estimated shipment date.",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (data.cargoType === "container") {
|
if (data.cargoType === "container") {
|
||||||
// Container scope: at least one enabled size.
|
// Container scope: at least one enabled size.
|
||||||
if (data.enabledContainerSizes.length === 0) {
|
if (data.enabledContainerSizes.length === 0) {
|
||||||
@@ -287,7 +276,6 @@ export const initialContractFormValues: DeepPartial<ContractFormValues> = {
|
|||||||
originYard: "",
|
originYard: "",
|
||||||
destinationYard: "",
|
destinationYard: "",
|
||||||
extraRoutes: [],
|
extraRoutes: [],
|
||||||
estimatedShipmentDate: "",
|
|
||||||
|
|
||||||
documents: {},
|
documents: {},
|
||||||
notes: "",
|
notes: "",
|
||||||
@@ -311,7 +299,7 @@ export const contractStepFields: Record<
|
|||||||
"firstMile",
|
"firstMile",
|
||||||
"lastMile",
|
"lastMile",
|
||||||
],
|
],
|
||||||
// Step 1 — Cargo & Route: scope, sizes, flags, origin/destination, date.
|
// Step 1 — Cargo & Route: scope, sizes, flags, origin/destination.
|
||||||
1: [
|
1: [
|
||||||
"cargoType",
|
"cargoType",
|
||||||
"enabledContainerSizes",
|
"enabledContainerSizes",
|
||||||
@@ -324,7 +312,6 @@ export const contractStepFields: Record<
|
|||||||
"originYard",
|
"originYard",
|
||||||
"destinationYard",
|
"destinationYard",
|
||||||
"extraRoutes",
|
"extraRoutes",
|
||||||
"estimatedShipmentDate",
|
|
||||||
],
|
],
|
||||||
// Step 2 — Review & Submit. (The separate Documents step was removed — the
|
// Step 2 — Review & Submit. (The separate Documents step was removed — the
|
||||||
// company profile documents are attached to the contract automatically.)
|
// company profile documents are attached to the contract automatically.)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { Freight } from "@edr/types";
|
import type { Freight } from "@edr/types";
|
||||||
import { Box, Button, Group, Skeleton, Stack, Text, TextInput } from "@mantine/core";
|
import { Box, Button, Group, Skeleton, Stack, Text } from "@mantine/core";
|
||||||
import { CalendarDays, MapPin, Plus, Trash2 } from "lucide-react";
|
import { MapPin, Plus, Trash2 } from "lucide-react";
|
||||||
import { useCallback, useEffect, useMemo } from "react";
|
import { useCallback, useEffect, useMemo } from "react";
|
||||||
import {
|
import {
|
||||||
Controller,
|
Controller,
|
||||||
@@ -125,12 +125,6 @@ export function Step4Route({
|
|||||||
|
|
||||||
const stationSelectDisabled = yardOptions.length === 0;
|
const stationSelectDisabled = yardOptions.length === 0;
|
||||||
|
|
||||||
const todayISODate = useMemo(() => {
|
|
||||||
const now = new Date();
|
|
||||||
const tz = now.getTimezoneOffset() * 60000;
|
|
||||||
return new Date(now.getTime() - tz).toISOString().slice(0, 10);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Stack gap={16}>
|
<Stack gap={16}>
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
@@ -177,27 +171,6 @@ export function Step4Route({
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Estimated shipment date — a planning value for every contract. The
|
|
||||||
binding scheduled date is captured later at booking time. */}
|
|
||||||
<Box style={{ maxWidth: 280 }}>
|
|
||||||
<Controller
|
|
||||||
name="estimatedShipmentDate"
|
|
||||||
control={form.control}
|
|
||||||
render={({ field, fieldState }) => (
|
|
||||||
<TextInput
|
|
||||||
type="date"
|
|
||||||
label="Estimated shipment date *"
|
|
||||||
description="A non-binding planning estimate. The actual shipment date is chosen when you book against this contract."
|
|
||||||
min={todayISODate}
|
|
||||||
leftSection={<CalendarDays size={16} />}
|
|
||||||
error={fieldState.error?.message}
|
|
||||||
value={field.value ?? ""}
|
|
||||||
onChange={(e) => field.onChange(e.currentTarget.value)}
|
|
||||||
radius="md"
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
@@ -9,9 +9,7 @@ import {
|
|||||||
Text,
|
Text,
|
||||||
Textarea,
|
Textarea,
|
||||||
} from "@mantine/core";
|
} from "@mantine/core";
|
||||||
import { format } from "date-fns";
|
|
||||||
import {
|
import {
|
||||||
Calendar,
|
|
||||||
CheckCircle2,
|
CheckCircle2,
|
||||||
Circle,
|
Circle,
|
||||||
ClipboardCheck,
|
ClipboardCheck,
|
||||||
@@ -248,10 +246,6 @@ export function Step8Review({
|
|||||||
referenceData?.yard.find((y) => y.id === values.destinationYard)?.name ??
|
referenceData?.yard.find((y) => y.id === values.destinationYard)?.name ??
|
||||||
values.destinationYard;
|
values.destinationYard;
|
||||||
|
|
||||||
const scheduleLabel = values.estimatedShipmentDate
|
|
||||||
? format(new Date(values.estimatedShipmentDate), "EEEE, MMM d, yyyy")
|
|
||||||
: "—";
|
|
||||||
|
|
||||||
const directionLabel = direction
|
const directionLabel = direction
|
||||||
? direction.charAt(0) + direction.slice(1).toLowerCase()
|
? direction.charAt(0) + direction.slice(1).toLowerCase()
|
||||||
: "—";
|
: "—";
|
||||||
@@ -353,11 +347,6 @@ export function Step8Review({
|
|||||||
: directionLabel
|
: directionLabel
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<SummaryItem
|
|
||||||
icon={<Calendar size={18} />}
|
|
||||||
label="Estimated shipment"
|
|
||||||
value={scheduleLabel}
|
|
||||||
/>
|
|
||||||
<SummaryItem
|
<SummaryItem
|
||||||
icon={<Package size={18} />}
|
icon={<Package size={18} />}
|
||||||
label="Cargo scope"
|
label="Cargo scope"
|
||||||
@@ -485,10 +474,6 @@ export function Step8Review({
|
|||||||
done={Boolean(values.originYard && values.destinationYard)}
|
done={Boolean(values.originYard && values.destinationYard)}
|
||||||
label="Route selected"
|
label="Route selected"
|
||||||
/>
|
/>
|
||||||
<ReadinessItem
|
|
||||||
done={Boolean(values.estimatedShipmentDate)}
|
|
||||||
label="Estimated date selected"
|
|
||||||
/>
|
|
||||||
<ReadinessItem
|
<ReadinessItem
|
||||||
done={
|
done={
|
||||||
values.cargoType === "container"
|
values.cargoType === "container"
|
||||||
|
|||||||
Reference in New Issue
Block a user