mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-26 18:42:49 +00:00
Merge branch 'freight_feature/profile' of github.com:Tria-plc/edr-platform into freight_feature/profile
This commit is contained in:
@@ -19,7 +19,7 @@ import {
|
||||
Snowflake,
|
||||
Trash2,
|
||||
} from "lucide-react";
|
||||
import { useEffect, useMemo } from "react";
|
||||
import { useCallback, useEffect, useMemo } from "react";
|
||||
import {
|
||||
Controller,
|
||||
useFieldArray,
|
||||
@@ -80,29 +80,39 @@ export function Step4Route({
|
||||
remove: removeRoute,
|
||||
} = useFieldArray({ control: form.control, name: "extraRoutes" });
|
||||
|
||||
// useFieldArray's `fields` don't re-render on value change, so watch the live
|
||||
// route values to filter each row's yard options by what it has selected.
|
||||
const watchedExtraRoutes = form.watch("extraRoutes") ?? [];
|
||||
|
||||
const yardOptions = useMemo(() => {
|
||||
if (!referenceData?.yard) return [];
|
||||
return referenceData.yard.map((y) => ({ value: y.id, label: y.name }));
|
||||
}, [referenceData]);
|
||||
|
||||
const originData = useMemo(() => {
|
||||
return yardOptions
|
||||
.filter((o) => o.value !== destinationYard)
|
||||
.filter((o) => {
|
||||
if (!originCountry) return true;
|
||||
const yard = referenceData?.yard.find((y) => y.id === o.value);
|
||||
return yard?.country === originCountry;
|
||||
});
|
||||
}, [yardOptions, destinationYard, originCountry, referenceData]);
|
||||
const destData = useMemo(() => {
|
||||
return yardOptions
|
||||
.filter((o) => o.value !== originYard)
|
||||
.filter((o) => {
|
||||
if (!destinationCountry) return true;
|
||||
const yard = referenceData?.yard.find((y) => y.id === o.value);
|
||||
return yard?.country === destinationCountry;
|
||||
});
|
||||
}, [yardOptions, originYard, destinationCountry, referenceData]);
|
||||
// Filter the yard list to one side of a route: the yards in `country` (the
|
||||
// operation type fixes which country each end must be in — see originCountry /
|
||||
// destinationCountry above), excluding the yard already chosen on the other
|
||||
// end of the same route so origin and destination can never match.
|
||||
const yardsForSide = useCallback(
|
||||
(country: string | null, excludeYardId: string) =>
|
||||
yardOptions
|
||||
.filter((o) => o.value !== excludeYardId)
|
||||
.filter((o) => {
|
||||
if (!country) return true;
|
||||
const yard = referenceData?.yard.find((y) => y.id === o.value);
|
||||
return yard?.country === country;
|
||||
}),
|
||||
[yardOptions, referenceData],
|
||||
);
|
||||
|
||||
const originData = useMemo(
|
||||
() => yardsForSide(originCountry, destinationYard),
|
||||
[yardsForSide, originCountry, destinationYard],
|
||||
);
|
||||
const destData = useMemo(
|
||||
() => yardsForSide(destinationCountry, originYard),
|
||||
[yardsForSide, destinationCountry, originYard],
|
||||
);
|
||||
|
||||
const origin = referenceData?.yard.find((y) => y.id === originYard);
|
||||
const dest = referenceData?.yard.find((y) => y.id === destinationYard);
|
||||
@@ -123,6 +133,25 @@ export function Step4Route({
|
||||
}
|
||||
}, [destinationCountry, dest, form]);
|
||||
|
||||
// Same cleanup for the extra contract routes: when the operation type changes,
|
||||
// clear any extra-route yard whose country no longer matches the required side
|
||||
// so an added route can't contradict the operation either.
|
||||
useEffect(() => {
|
||||
watchedExtraRoutes.forEach((route, i) => {
|
||||
const ro = referenceData?.yard.find((y) => y.id === route?.originYard);
|
||||
if (originCountry && ro && ro.country !== originCountry) {
|
||||
form.setValue(`extraRoutes.${i}.originYard`, "");
|
||||
}
|
||||
const rd = referenceData?.yard.find(
|
||||
(y) => y.id === route?.destinationYard,
|
||||
);
|
||||
if (destinationCountry && rd && rd.country !== destinationCountry) {
|
||||
form.setValue(`extraRoutes.${i}.destinationYard`, "");
|
||||
}
|
||||
});
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [originCountry, destinationCountry, referenceData, form]);
|
||||
|
||||
const directionStyle: Record<string, string> = {
|
||||
EXPORT: "bg-sky-50 text-sky-800 border-sky-200",
|
||||
IMPORT: "bg-amber-50 text-amber-800 border-amber-200",
|
||||
@@ -263,57 +292,70 @@ export function Step4Route({
|
||||
cover.
|
||||
</Text>
|
||||
<Stack gap={12}>
|
||||
{extraRoutes.map((rf, i) => (
|
||||
<Group
|
||||
key={rf.id}
|
||||
gap={10}
|
||||
align="flex-start"
|
||||
wrap="nowrap"
|
||||
className="rounded-xl"
|
||||
style={{ border: "1px solid #E6ECF2", padding: 12 }}
|
||||
>
|
||||
<Box style={{ flex: 1 }}>
|
||||
<Controller
|
||||
name={`extraRoutes.${i}.originYard`}
|
||||
control={form.control}
|
||||
render={({ field, fieldState }) => (
|
||||
<SelectField
|
||||
field={field}
|
||||
error={fieldState.error}
|
||||
label="Origin"
|
||||
placeholder="Origin..."
|
||||
data={yardOptions}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</Box>
|
||||
<Box style={{ flex: 1 }}>
|
||||
<Controller
|
||||
name={`extraRoutes.${i}.destinationYard`}
|
||||
control={form.control}
|
||||
render={({ field, fieldState }) => (
|
||||
<SelectField
|
||||
field={field}
|
||||
error={fieldState.error}
|
||||
label="Destination"
|
||||
placeholder="Destination..."
|
||||
data={yardOptions}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</Box>
|
||||
<Button
|
||||
variant="subtle"
|
||||
color="red"
|
||||
size="xs"
|
||||
mt={24}
|
||||
px={6}
|
||||
onClick={() => removeRoute(i)}
|
||||
{extraRoutes.map((rf, i) => {
|
||||
// Each extra route is constrained by the SAME operation type as the
|
||||
// primary route: its origin must sit in originCountry and its
|
||||
// destination in destinationCountry. Watch this row's current values
|
||||
// so each side also excludes the yard picked on the other side.
|
||||
const rowOrigin = watchedExtraRoutes[i]?.originYard ?? "";
|
||||
const rowDestination =
|
||||
watchedExtraRoutes[i]?.destinationYard ?? "";
|
||||
const rowOriginData = yardsForSide(originCountry, rowDestination);
|
||||
const rowDestData = yardsForSide(destinationCountry, rowOrigin);
|
||||
return (
|
||||
<Group
|
||||
key={rf.id}
|
||||
gap={10}
|
||||
align="flex-start"
|
||||
wrap="nowrap"
|
||||
className="rounded-xl"
|
||||
style={{ border: "1px solid #E6ECF2", padding: 12 }}
|
||||
>
|
||||
<Trash2 size={16} />
|
||||
</Button>
|
||||
</Group>
|
||||
))}
|
||||
<Box style={{ flex: 1 }}>
|
||||
<Controller
|
||||
name={`extraRoutes.${i}.originYard`}
|
||||
control={form.control}
|
||||
render={({ field, fieldState }) => (
|
||||
<SelectField
|
||||
field={field}
|
||||
error={fieldState.error}
|
||||
label="Origin"
|
||||
placeholder="Origin..."
|
||||
disabled={stationSelectDisabled}
|
||||
data={rowOriginData}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</Box>
|
||||
<Box style={{ flex: 1 }}>
|
||||
<Controller
|
||||
name={`extraRoutes.${i}.destinationYard`}
|
||||
control={form.control}
|
||||
render={({ field, fieldState }) => (
|
||||
<SelectField
|
||||
field={field}
|
||||
error={fieldState.error}
|
||||
label="Destination"
|
||||
placeholder="Destination..."
|
||||
disabled={stationSelectDisabled}
|
||||
data={rowDestData}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</Box>
|
||||
<Button
|
||||
variant="subtle"
|
||||
color="red"
|
||||
size="xs"
|
||||
mt={24}
|
||||
px={6}
|
||||
onClick={() => removeRoute(i)}
|
||||
>
|
||||
<Trash2 size={16} />
|
||||
</Button>
|
||||
</Group>
|
||||
);
|
||||
})}
|
||||
</Stack>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user