mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-30 13:28:11 +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,
|
Snowflake,
|
||||||
Trash2,
|
Trash2,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import { useEffect, useMemo } from "react";
|
import { useCallback, useEffect, useMemo } from "react";
|
||||||
import {
|
import {
|
||||||
Controller,
|
Controller,
|
||||||
useFieldArray,
|
useFieldArray,
|
||||||
@@ -80,29 +80,39 @@ export function Step4Route({
|
|||||||
remove: removeRoute,
|
remove: removeRoute,
|
||||||
} = useFieldArray({ control: form.control, name: "extraRoutes" });
|
} = 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(() => {
|
const yardOptions = useMemo(() => {
|
||||||
if (!referenceData?.yard) return [];
|
if (!referenceData?.yard) return [];
|
||||||
return referenceData.yard.map((y) => ({ value: y.id, label: y.name }));
|
return referenceData.yard.map((y) => ({ value: y.id, label: y.name }));
|
||||||
}, [referenceData]);
|
}, [referenceData]);
|
||||||
|
|
||||||
const originData = useMemo(() => {
|
// Filter the yard list to one side of a route: the yards in `country` (the
|
||||||
return yardOptions
|
// operation type fixes which country each end must be in — see originCountry /
|
||||||
.filter((o) => o.value !== destinationYard)
|
// destinationCountry above), excluding the yard already chosen on the other
|
||||||
.filter((o) => {
|
// end of the same route so origin and destination can never match.
|
||||||
if (!originCountry) return true;
|
const yardsForSide = useCallback(
|
||||||
const yard = referenceData?.yard.find((y) => y.id === o.value);
|
(country: string | null, excludeYardId: string) =>
|
||||||
return yard?.country === originCountry;
|
yardOptions
|
||||||
});
|
.filter((o) => o.value !== excludeYardId)
|
||||||
}, [yardOptions, destinationYard, originCountry, referenceData]);
|
.filter((o) => {
|
||||||
const destData = useMemo(() => {
|
if (!country) return true;
|
||||||
return yardOptions
|
const yard = referenceData?.yard.find((y) => y.id === o.value);
|
||||||
.filter((o) => o.value !== originYard)
|
return yard?.country === country;
|
||||||
.filter((o) => {
|
}),
|
||||||
if (!destinationCountry) return true;
|
[yardOptions, referenceData],
|
||||||
const yard = referenceData?.yard.find((y) => y.id === o.value);
|
);
|
||||||
return yard?.country === destinationCountry;
|
|
||||||
});
|
const originData = useMemo(
|
||||||
}, [yardOptions, originYard, destinationCountry, referenceData]);
|
() => 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 origin = referenceData?.yard.find((y) => y.id === originYard);
|
||||||
const dest = referenceData?.yard.find((y) => y.id === destinationYard);
|
const dest = referenceData?.yard.find((y) => y.id === destinationYard);
|
||||||
@@ -123,6 +133,25 @@ export function Step4Route({
|
|||||||
}
|
}
|
||||||
}, [destinationCountry, dest, form]);
|
}, [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> = {
|
const directionStyle: Record<string, string> = {
|
||||||
EXPORT: "bg-sky-50 text-sky-800 border-sky-200",
|
EXPORT: "bg-sky-50 text-sky-800 border-sky-200",
|
||||||
IMPORT: "bg-amber-50 text-amber-800 border-amber-200",
|
IMPORT: "bg-amber-50 text-amber-800 border-amber-200",
|
||||||
@@ -263,57 +292,70 @@ export function Step4Route({
|
|||||||
cover.
|
cover.
|
||||||
</Text>
|
</Text>
|
||||||
<Stack gap={12}>
|
<Stack gap={12}>
|
||||||
{extraRoutes.map((rf, i) => (
|
{extraRoutes.map((rf, i) => {
|
||||||
<Group
|
// Each extra route is constrained by the SAME operation type as the
|
||||||
key={rf.id}
|
// primary route: its origin must sit in originCountry and its
|
||||||
gap={10}
|
// destination in destinationCountry. Watch this row's current values
|
||||||
align="flex-start"
|
// so each side also excludes the yard picked on the other side.
|
||||||
wrap="nowrap"
|
const rowOrigin = watchedExtraRoutes[i]?.originYard ?? "";
|
||||||
className="rounded-xl"
|
const rowDestination =
|
||||||
style={{ border: "1px solid #E6ECF2", padding: 12 }}
|
watchedExtraRoutes[i]?.destinationYard ?? "";
|
||||||
>
|
const rowOriginData = yardsForSide(originCountry, rowDestination);
|
||||||
<Box style={{ flex: 1 }}>
|
const rowDestData = yardsForSide(destinationCountry, rowOrigin);
|
||||||
<Controller
|
return (
|
||||||
name={`extraRoutes.${i}.originYard`}
|
<Group
|
||||||
control={form.control}
|
key={rf.id}
|
||||||
render={({ field, fieldState }) => (
|
gap={10}
|
||||||
<SelectField
|
align="flex-start"
|
||||||
field={field}
|
wrap="nowrap"
|
||||||
error={fieldState.error}
|
className="rounded-xl"
|
||||||
label="Origin"
|
style={{ border: "1px solid #E6ECF2", padding: 12 }}
|
||||||
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)}
|
|
||||||
>
|
>
|
||||||
<Trash2 size={16} />
|
<Box style={{ flex: 1 }}>
|
||||||
</Button>
|
<Controller
|
||||||
</Group>
|
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>
|
</Stack>
|
||||||
</Box>
|
</Box>
|
||||||
)}
|
)}
|
||||||
|
|||||||
Reference in New Issue
Block a user