mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-26 18:42:49 +00:00
Merge branch 'freight_feature/contrat' of github.com:Tria-plc/edr-platform into freight_feature/contrat
This commit is contained in:
@@ -200,7 +200,9 @@ export class ContractDocumentViewModelBuilder {
|
||||
serviceType: this.valueOrDash(
|
||||
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),
|
||||
cargoDescription: this.valueOrDash(cargoName),
|
||||
totalWeightVgm: '—',
|
||||
|
||||
@@ -200,9 +200,6 @@ export class ContractsService {
|
||||
lastMileDeliveryLng: dto.lastMileDeliveryLng ?? null,
|
||||
isHazardous: dto.isHazardous ?? false,
|
||||
isReefer: dto.isReefer ?? false,
|
||||
estimatedShipmentDate: dto.estimatedShipmentDate
|
||||
? new Date(dto.estimatedShipmentDate)
|
||||
: null,
|
||||
contractType: dto.contractType ?? null,
|
||||
status: 'DRAFT',
|
||||
clearanceStatus: 'NOT_APPLICABLE',
|
||||
@@ -347,9 +344,6 @@ export class ContractsService {
|
||||
lastMileDeliveryLng: dto.lastMileDeliveryLng ?? existing.lastMileDeliveryLng,
|
||||
contractType: dto.contractType ?? existing.contractType,
|
||||
};
|
||||
if (dto.estimatedShipmentDate) {
|
||||
updates.estimatedShipmentDate = new Date(dto.estimatedShipmentDate);
|
||||
}
|
||||
if (dto.renewalOfId !== undefined) updates.renewalOfId = dto.renewalOfId ?? null;
|
||||
|
||||
// Customs clearing always mirrors the (possibly changed) service type.
|
||||
|
||||
@@ -4,7 +4,6 @@ import {
|
||||
ArrayMinSize,
|
||||
IsArray,
|
||||
IsBoolean,
|
||||
IsDateString,
|
||||
IsIn,
|
||||
IsNumber,
|
||||
IsOptional,
|
||||
@@ -219,14 +218,6 @@ export class CreateContractDto {
|
||||
@Transform(({ value }) => value === 'true' || value === true)
|
||||
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.)' })
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
|
||||
@@ -449,17 +449,6 @@ export default function ContractDetailPage() {
|
||||
routes[0]?.destinationYard?.label ?? "—"
|
||||
}`}
|
||||
/>
|
||||
<MetaItem
|
||||
label="Estimated shipment"
|
||||
icon={<CalendarClock size={15} color={MUTED} />}
|
||||
value={
|
||||
contract.estimatedShipmentDate
|
||||
? new Date(
|
||||
contract.estimatedShipmentDate,
|
||||
).toLocaleDateString()
|
||||
: "—"
|
||||
}
|
||||
/>
|
||||
<MetaItem
|
||||
label="Customs clearance"
|
||||
icon={<FileText size={15} color={MUTED} />}
|
||||
|
||||
@@ -541,13 +541,6 @@ export default function NewContractPage({
|
||||
isHazardous: data.isHazardous,
|
||||
// Reefer is a contract-level flag for both container and bulk.
|
||||
isReefer: data.isRefrigerated,
|
||||
...(data.estimatedShipmentDate
|
||||
? {
|
||||
estimatedShipmentDate: new Date(
|
||||
data.estimatedShipmentDate,
|
||||
).toISOString(),
|
||||
}
|
||||
: {}),
|
||||
...(data.previousContractRef
|
||||
? { renewalOfId: data.previousContractRef }
|
||||
: {}),
|
||||
|
||||
@@ -16,12 +16,6 @@ function directionToOperation(
|
||||
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
|
||||
* by locating which reference cargo-type group owns it.
|
||||
@@ -138,7 +132,6 @@ export function contractToFormValues(
|
||||
originYard: primaryRoute?.originYardId ?? "",
|
||||
destinationYard: primaryRoute?.destinationYardId ?? "",
|
||||
extraRoutes,
|
||||
estimatedShipmentDate: isoToDateInput(contract.estimatedShipmentDate),
|
||||
|
||||
documents: {},
|
||||
};
|
||||
|
||||
@@ -187,8 +187,6 @@ export const contractFormSchema = z
|
||||
}),
|
||||
)
|
||||
.default([]),
|
||||
// Non-binding estimate. The binding scheduled date is captured at booking.
|
||||
estimatedShipmentDate: z.string().default(""),
|
||||
|
||||
documents: z.record(z.string(), z.any()).default({}),
|
||||
notes: z.string().default(""),
|
||||
@@ -222,15 +220,6 @@ export const contractFormSchema = z
|
||||
},
|
||||
)
|
||||
.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") {
|
||||
// Container scope: at least one enabled size.
|
||||
if (data.enabledContainerSizes.length === 0) {
|
||||
@@ -287,7 +276,6 @@ export const initialContractFormValues: DeepPartial<ContractFormValues> = {
|
||||
originYard: "",
|
||||
destinationYard: "",
|
||||
extraRoutes: [],
|
||||
estimatedShipmentDate: "",
|
||||
|
||||
documents: {},
|
||||
notes: "",
|
||||
@@ -311,7 +299,7 @@ export const contractStepFields: Record<
|
||||
"firstMile",
|
||||
"lastMile",
|
||||
],
|
||||
// Step 1 — Cargo & Route: scope, sizes, flags, origin/destination, date.
|
||||
// Step 1 — Cargo & Route: scope, sizes, flags, origin/destination.
|
||||
1: [
|
||||
"cargoType",
|
||||
"enabledContainerSizes",
|
||||
@@ -324,7 +312,6 @@ export const contractStepFields: Record<
|
||||
"originYard",
|
||||
"destinationYard",
|
||||
"extraRoutes",
|
||||
"estimatedShipmentDate",
|
||||
],
|
||||
// Step 2 — Review & Submit. (The separate Documents step was removed — the
|
||||
// company profile documents are attached to the contract automatically.)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import type { Freight } from "@edr/types";
|
||||
import { Box, Button, Group, Skeleton, Stack, Text, TextInput } from "@mantine/core";
|
||||
import { CalendarDays, MapPin, Plus, Trash2 } from "lucide-react";
|
||||
import { Box, Button, Group, Skeleton, Stack, Text } from "@mantine/core";
|
||||
import { MapPin, Plus, Trash2 } from "lucide-react";
|
||||
import { useCallback, useEffect, useMemo } from "react";
|
||||
import {
|
||||
Controller,
|
||||
@@ -125,12 +125,6 @@ export function Step4Route({
|
||||
|
||||
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 (
|
||||
<Stack gap={16}>
|
||||
{isLoading ? (
|
||||
@@ -177,27 +171,6 @@ export function Step4Route({
|
||||
</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>
|
||||
)}
|
||||
|
||||
|
||||
@@ -9,9 +9,7 @@ import {
|
||||
Text,
|
||||
Textarea,
|
||||
} from "@mantine/core";
|
||||
import { format } from "date-fns";
|
||||
import {
|
||||
Calendar,
|
||||
CheckCircle2,
|
||||
Circle,
|
||||
ClipboardCheck,
|
||||
@@ -248,10 +246,6 @@ export function Step8Review({
|
||||
referenceData?.yard.find((y) => y.id === values.destinationYard)?.name ??
|
||||
values.destinationYard;
|
||||
|
||||
const scheduleLabel = values.estimatedShipmentDate
|
||||
? format(new Date(values.estimatedShipmentDate), "EEEE, MMM d, yyyy")
|
||||
: "—";
|
||||
|
||||
const directionLabel = direction
|
||||
? direction.charAt(0) + direction.slice(1).toLowerCase()
|
||||
: "—";
|
||||
@@ -353,11 +347,6 @@ export function Step8Review({
|
||||
: directionLabel
|
||||
}
|
||||
/>
|
||||
<SummaryItem
|
||||
icon={<Calendar size={18} />}
|
||||
label="Estimated shipment"
|
||||
value={scheduleLabel}
|
||||
/>
|
||||
<SummaryItem
|
||||
icon={<Package size={18} />}
|
||||
label="Cargo scope"
|
||||
@@ -485,10 +474,6 @@ export function Step8Review({
|
||||
done={Boolean(values.originYard && values.destinationYard)}
|
||||
label="Route selected"
|
||||
/>
|
||||
<ReadinessItem
|
||||
done={Boolean(values.estimatedShipmentDate)}
|
||||
label="Estimated date selected"
|
||||
/>
|
||||
<ReadinessItem
|
||||
done={
|
||||
values.cargoType === "container"
|
||||
|
||||
Reference in New Issue
Block a user