mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-09-07 09:35:44 +00:00
features: dynamic terminal components
This commit is contained in:
@@ -485,6 +485,13 @@ export function getRouteDirection(
|
||||
return null;
|
||||
}
|
||||
|
||||
function getStationLocation(value: string): "inside" | "outside" | null {
|
||||
const normalized = value.trim().toLowerCase();
|
||||
if (normalized.startsWith("inside")) return "inside";
|
||||
if (normalized.startsWith("outside")) return "outside";
|
||||
return null;
|
||||
}
|
||||
|
||||
export function calcWagons(containers: ContainerConfig[]): WagonCalcResult {
|
||||
const Ft40Wagons = containers
|
||||
.filter((c) => c.type === "40ft")
|
||||
@@ -495,20 +502,6 @@ export function calcWagons(containers: ContainerConfig[]): WagonCalcResult {
|
||||
const wagonLayout: WagonConfig[] = [];
|
||||
let hasOddUnit = Ft20Wagons % 2 === 1;
|
||||
let sharedWagons = Math.floor(Ft20Wagons / 2);
|
||||
function getStationLocation(value: string): "inside" | "outside" | null {
|
||||
const normalized = value.trim().toLowerCase();
|
||||
if (normalized.startsWith("inside")) return "inside";
|
||||
if (normalized.startsWith("outside")) return "outside";
|
||||
return null;
|
||||
}
|
||||
|
||||
export function calcWagons(
|
||||
type: BookingFormValues["containerType"],
|
||||
qty: number,
|
||||
): WagonCalcResult {
|
||||
if (type === "40ft") {
|
||||
return { totalWagons: qty, hasOddUnit: false, sharedWagons: qty };
|
||||
}
|
||||
|
||||
return {
|
||||
totalWagons: sharedWagons + Ft40Wagons,
|
||||
@@ -518,4 +511,4 @@ export function calcWagons(
|
||||
ft20Wagons: Ft20Wagons,
|
||||
wagonLayout,
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,25 @@
|
||||
import { Controller, type UseFormReturn } from "react-hook-form";
|
||||
import { Flame, MapPin, Snowflake } from "lucide-react";
|
||||
import { Field, Separator, Switch } from "@edr/ui-common";
|
||||
import { Field, SelectItem, Separator, Switch } from "@edr/ui-common";
|
||||
import { type BookingFormValues, getRouteDirection, STATIONS } from "./schema";
|
||||
import { SelectField, SelectOptions, StepHeader, StepLabel } from "./shared";
|
||||
import { useDropdownSettingByCode } from "@/hooks/useDropdownSettings";
|
||||
import { DropdownOption } from "@/types/dropdownSettings";
|
||||
|
||||
type BookingForm = UseFormReturn<BookingFormValues>;
|
||||
|
||||
const STATION_DROPDOWN_CODE = "stations_ter";
|
||||
|
||||
export function Step4Route({ form }: { form: BookingForm }) {
|
||||
const originYard = form.watch("originYard");
|
||||
const destinationYard = form.watch("destinationYard");
|
||||
const {
|
||||
data: stationSetting,
|
||||
isLoading: stationsLoading,
|
||||
isError: stationsError,
|
||||
error: stationsFetchError,
|
||||
} = useDropdownSettingByCode(STATION_DROPDOWN_CODE);
|
||||
const stationOptions = getStationOptions(stationSetting?.children);
|
||||
const direction = getRouteDirection(originYard, destinationYard);
|
||||
const directionStyle: Record<string, string> = {
|
||||
export: "bg-sky-50 text-sky-800 border-sky-200",
|
||||
@@ -16,10 +27,11 @@ export function Step4Route({ form }: { form: BookingForm }) {
|
||||
domestic: "bg-muted text-muted-foreground border-border",
|
||||
};
|
||||
const directionLabel: Record<string, string> = {
|
||||
export: "Export workflow (Ethiopia to Djibouti)",
|
||||
import: "Import workflow (Djibouti to Ethiopia)",
|
||||
export: "Export workflow (inside country to outside country)",
|
||||
import: "Import workflow (outside country to inside country)",
|
||||
domestic: "Domestic corridor",
|
||||
};
|
||||
const stationSelectDisabled = stationsLoading || stationOptions.length === 0;
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
@@ -29,6 +41,7 @@ export function Step4Route({ form }: { form: BookingForm }) {
|
||||
/>
|
||||
|
||||
<div className="space-y-3">
|
||||
<StepLabel>Route</StepLabel>
|
||||
<div className="grid gap-3 sm:grid-cols-2">
|
||||
<Controller
|
||||
name="originYard"
|
||||
@@ -39,9 +52,12 @@ export function Step4Route({ form }: { form: BookingForm }) {
|
||||
error={fieldState.error}
|
||||
label="Origin Yard*"
|
||||
placeholder="Select origin..."
|
||||
disabled={stationSelectDisabled}
|
||||
>
|
||||
<SelectOptions
|
||||
options={STATIONS.filter((s) => s !== destinationYard)}
|
||||
<StationSelectOptions
|
||||
options={stationOptions}
|
||||
excludeValue={destinationYard}
|
||||
isLoading={stationsLoading}
|
||||
/>
|
||||
</SelectField>
|
||||
)}
|
||||
@@ -55,14 +71,25 @@ export function Step4Route({ form }: { form: BookingForm }) {
|
||||
error={fieldState.error}
|
||||
label="Destination Yard *"
|
||||
placeholder="Select destination..."
|
||||
disabled={stationSelectDisabled}
|
||||
>
|
||||
<SelectOptions
|
||||
options={STATIONS.filter((s) => s !== originYard)}
|
||||
<StationSelectOptions
|
||||
options={stationOptions}
|
||||
excludeValue={originYard}
|
||||
isLoading={stationsLoading}
|
||||
/>
|
||||
</SelectField>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
{stationsError && (
|
||||
<AlertBox tone="error">
|
||||
Failed to load stations from the API.{" "}
|
||||
{stationsFetchError instanceof Error
|
||||
? stationsFetchError.message
|
||||
: "Try again later."}
|
||||
</AlertBox>
|
||||
)}
|
||||
{direction && (
|
||||
<div
|
||||
className={`flex items-center gap-2 rounded-lg border px-3 py-2 text-xs font-medium ${directionStyle[direction]}`}
|
||||
@@ -117,3 +144,53 @@ export function Step4Route({ form }: { form: BookingForm }) {
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
function getStationOptions(options?: DropdownOption[]): DropdownOption[] {
|
||||
return [...(options ?? [])].sort((a, b) => a.order - b.order);
|
||||
}
|
||||
|
||||
|
||||
function StationSelectOptions({
|
||||
options,
|
||||
excludeValue,
|
||||
isLoading,
|
||||
}: {
|
||||
options: DropdownOption[];
|
||||
excludeValue: string;
|
||||
isLoading: boolean;
|
||||
}) {
|
||||
if (isLoading) {
|
||||
return (
|
||||
<SelectItem value="__stations_loading" disabled>
|
||||
Loading stations...
|
||||
</SelectItem>
|
||||
);
|
||||
}
|
||||
|
||||
const availableOptions = options.filter(
|
||||
(option) => option.value !== excludeValue,
|
||||
);
|
||||
|
||||
if (availableOptions.length === 0) {
|
||||
return (
|
||||
<SelectItem value="__stations_empty" disabled>
|
||||
No stations available
|
||||
</SelectItem>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{availableOptions.map((option) => (
|
||||
<SelectItem
|
||||
key={option.id}
|
||||
value={option.value}
|
||||
disabled={option.disabled}
|
||||
>
|
||||
{option.label}
|
||||
</SelectItem>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user