diff --git a/apps/edr-freight-web/backoffice/src/components/contracts/GlCreateBookingForm.tsx b/apps/edr-freight-web/backoffice/src/components/contracts/GlCreateBookingForm.tsx index 4a1cb402f..52f7ee40d 100644 --- a/apps/edr-freight-web/backoffice/src/components/contracts/GlCreateBookingForm.tsx +++ b/apps/edr-freight-web/backoffice/src/components/contracts/GlCreateBookingForm.tsx @@ -20,7 +20,6 @@ import { Loader, Modal, Paper, - SegmentedControl, Select, Stack, Switch, @@ -49,7 +48,11 @@ import { X, } from "lucide-react"; import type { Freight } from "@edr/types"; -import { ExportTrainPicker, OperationDatePicker } from "@edr/ui-common"; +import { + CurrencySelector, + ExportTrainPicker, + OperationDatePicker, +} from "@edr/ui-common"; import { api } from "@/services/api"; import { PageContainer } from "@/components/page"; @@ -1713,47 +1716,42 @@ export default function GlCreateBookingForm() { title="Schedule" description="Pick the binding shipment day. Only days with an open train that has enough matching wagons for the cargo can be selected." /> - {cargoQuery === null ? ( - } - > - Enter your cargo details first — available shipment days depend - on the wagons your cargo needs. - - ) : ( - - Shipment day * - - { - setScheduledDate(d); - // A new day invalidates the old train pick. - setTrainScheduleId(""); - }} - /> - - {showErrors && dateError && ( - - {dateError} - - )} - {isExportPick && scheduledDate ? ( - - ) : null} + + Shipment day * + + {/* Calendar stays visible before cargo is entered — all days + disabled with a hint, since availability depends on cargo. */} + { + setScheduledDate(d); + // A new day invalidates the old train pick. + setTrainScheduleId(""); + }} + /> - )} + {showErrors && dateError && ( + + {dateError} + + )} + {isExportPick && scheduledDate ? ( + + ) : null} + )} @@ -1769,16 +1767,10 @@ export default function GlCreateBookingForm() { ? "Requested by the customer on their shipment request." : "The contract is quoted in USD — pick the currency this shipment is invoiced in."} - setPaymentCurrency(v as "USD" | "ETB")} + onChange={setPaymentCurrency} disabled={isIntercity} - data={[ - { label: "USD", value: "USD" }, - { label: "ETB", value: "ETB" }, - ]} - color="edr-green" - radius={10} /> diff --git a/apps/edr-freight-web/portal/src/pages/contracts/NewShipmentPage.tsx b/apps/edr-freight-web/portal/src/pages/contracts/NewShipmentPage.tsx index 33a1343a2..4d423abe8 100644 --- a/apps/edr-freight-web/portal/src/pages/contracts/NewShipmentPage.tsx +++ b/apps/edr-freight-web/portal/src/pages/contracts/NewShipmentPage.tsx @@ -22,7 +22,6 @@ import { Loader, Modal, Paper, - SegmentedControl, Stack, Switch, Text, @@ -50,7 +49,11 @@ import { } from "lucide-react"; import type { Freight } from "@edr/types"; -import { ExportTrainPicker, OperationDatePicker } from "@edr/ui-common"; +import { + CurrencySelector, + ExportTrainPicker, + OperationDatePicker, +} from "@edr/ui-common"; import { api } from "@/services/api"; import { contractsService, @@ -1144,75 +1147,56 @@ function ScheduleStep({ Your contract is quoted in USD. Pick the currency this shipment is invoiced in — the total is converted for you. - {/* Rendered unselected until the customer chooses: SegmentedControl - highlights whatever value it is given, so passing a fallback - here would look like a made choice. */} - field.onChange(v)} - data={[ - { label: "Select…", value: "", disabled: true }, - { label: "USD", value: "USD" }, - { label: "ETB", value: "ETB" }, - ]} - color="edr-green" - radius={10} + error={fieldState.error?.message} /> - {fieldState.error && ( - - {fieldState.error.message} - - )} )} /> - {cargoQuery === null ? ( - } - > - Enter your cargo details first — available shipment days depend on the - wagons your cargo needs. - - ) : ( - ( - - Shipment day * - - { - field.onChange(d); - // A new day invalidates the old train pick. - form.setValue("trainScheduleId", ""); - }} - /> - - {fieldState.error?.message && ( - - {fieldState.error.message} - - )} - {isExportPick && scheduledDate ? ( - form.setValue("trainScheduleId", id)} - /> - ) : null} + ( + + Shipment day * + + {/* Calendar stays visible before cargo is entered — all days + disabled with a hint, since availability depends on cargo. */} + { + field.onChange(d); + // A new day invalidates the old train pick. + form.setValue("trainScheduleId", ""); + }} + /> - )} - /> - )} + {fieldState.error?.message && ( + + {fieldState.error.message} + + )} + {isExportPick && scheduledDate ? ( + form.setValue("trainScheduleId", id)} + /> + ) : null} + + )} + /> ); } diff --git a/packages/ui-common/src/components/CurrencySelector/CurrencySelector.tsx b/packages/ui-common/src/components/CurrencySelector/CurrencySelector.tsx new file mode 100644 index 000000000..4d310efc0 --- /dev/null +++ b/packages/ui-common/src/components/CurrencySelector/CurrencySelector.tsx @@ -0,0 +1,131 @@ +import { Box, Text } from "@mantine/core"; +import { Check } from "lucide-react"; + +export interface CurrencySelectorProps { + /** Selected currency code, or "" when none picked yet. */ + value: string; + onChange: (currency: "USD" | "ETB") => void; + disabled?: boolean; + /** Validation error shown under the cards. */ + error?: string; +} + +const OPTIONS = [ + { + code: "USD", + symbol: "$", + name: "US Dollar", + hint: "As quoted on the contract", + }, + { + code: "ETB", + symbol: "Br", + name: "Ethiopian Birr", + hint: "Converted from the USD total", + }, +] as const; + +/** + * Card-style USD/ETB billing-currency picker. Renders unselected when `value` + * is "" so a required choice never looks pre-made. + */ +export function CurrencySelector({ + value, + onChange, + disabled = false, + error, +}: CurrencySelectorProps) { + return ( + + + {OPTIONS.map((o) => { + const selected = value === o.code; + return ( + + ); + })} + + {error && ( + + {error} + + )} + + ); +} + +export default CurrencySelector; diff --git a/packages/ui-common/src/components/CurrencySelector/index.ts b/packages/ui-common/src/components/CurrencySelector/index.ts new file mode 100644 index 000000000..9e20c6863 --- /dev/null +++ b/packages/ui-common/src/components/CurrencySelector/index.ts @@ -0,0 +1,2 @@ +export { CurrencySelector, default } from "./CurrencySelector"; +export type { CurrencySelectorProps } from "./CurrencySelector"; diff --git a/packages/ui-common/src/components/OperationDatePicker/OperationDatePicker.tsx b/packages/ui-common/src/components/OperationDatePicker/OperationDatePicker.tsx index d8fbcaf85..f8a45e5a3 100644 --- a/packages/ui-common/src/components/OperationDatePicker/OperationDatePicker.tsx +++ b/packages/ui-common/src/components/OperationDatePicker/OperationDatePicker.tsx @@ -18,6 +18,8 @@ export interface OperationDatePickerProps { onChange: (date: string) => void; /** Stretch to the full width of the parent container. */ fullWidth?: boolean; + /** Text shown under the grid when no days are available. */ + emptyMessage?: string; } /** `yyyy-MM-dd` for a local date. */ @@ -58,6 +60,7 @@ export function OperationDatePicker({ value, onChange, fullWidth = false, + emptyMessage = "No scheduled departures found for this route yet.", }: OperationDatePickerProps) { const [month, setMonth] = useState(() => { const now = new Date(); @@ -270,7 +273,7 @@ export function OperationDatePicker({ )} {departureDays.size === 0 && ( - No scheduled departures found for this route yet. + {emptyMessage} )} diff --git a/packages/ui-common/src/index.ts b/packages/ui-common/src/index.ts index 60e5c39f8..c9754ceb4 100644 --- a/packages/ui-common/src/index.ts +++ b/packages/ui-common/src/index.ts @@ -26,6 +26,8 @@ export { OperationDatePicker } from "./components/OperationDatePicker"; export type { OperationDatePickerProps } from "./components/OperationDatePicker"; export { ExportTrainPicker } from "./components/ExportTrainPicker"; export type { ExportTrainPickerProps } from "./components/ExportTrainPicker"; +export { CurrencySelector } from "./components/CurrencySelector"; +export type { CurrencySelectorProps } from "./components/CurrencySelector"; export { CountdownTimer } from "./components/CountdownTimer"; export type { CountdownTimerProps } from "./components/CountdownTimer";