@burdenoff/microfe-movethewheels 2026.728.1 → 2026.731.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/MoveTheWheelsRoutes.js +7 -3
- package/dist/hooks/useMultiModalShipments.js +137 -0
- package/dist/movethewheels/hooks/useMultiModalShipments.d.ts +76 -0
- package/dist/movethewheels/pages/MultiModalShipmentPage.d.ts +8 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/calculator.js +56 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/plane.js +8 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/ship.js +26 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/train-front.js +30 -0
- package/dist/pages/DashboardPage.js +156 -151
- package/dist/pages/MultiModalShipmentPage.js +420 -0
- package/package.json +1 -1
- package/src/movethewheels/MoveTheWheelsRoutes.tsx +4 -0
- package/src/movethewheels/hooks/useMultiModalShipments.ts +344 -0
- package/src/movethewheels/pages/DashboardPage.tsx +6 -0
- package/src/movethewheels/pages/MultiModalShipmentPage.tsx +552 -0
|
@@ -0,0 +1,420 @@
|
|
|
1
|
+
import { Calculator as e } from "../node_modules/lucide-react/dist/esm/icons/calculator.js";
|
|
2
|
+
import { Plane as t } from "../node_modules/lucide-react/dist/esm/icons/plane.js";
|
|
3
|
+
import { Plus as n } from "../node_modules/lucide-react/dist/esm/icons/plus.js";
|
|
4
|
+
import { Ship as r } from "../node_modules/lucide-react/dist/esm/icons/ship.js";
|
|
5
|
+
import { TrainFront as i } from "../node_modules/lucide-react/dist/esm/icons/train-front.js";
|
|
6
|
+
import { Trash2 as ee } from "../node_modules/lucide-react/dist/esm/icons/trash-2.js";
|
|
7
|
+
import { Truck as a } from "../node_modules/lucide-react/dist/esm/icons/truck.js";
|
|
8
|
+
import { Badge as o, Button as s, Card as c, CardContent as l, CardHeader as u, CardTitle as d, Input as f, Label as p, Select as m, SelectContent as h, SelectItem as g, SelectTrigger as _, SelectValue as v, Separator as te } from "../components/ui.js";
|
|
9
|
+
import { EmptyState as ne, LoadingState as y, PageLayout as b, PageSection as x } from "../components/PageLayout.js";
|
|
10
|
+
import { FormError as S } from "../components/FormError.js";
|
|
11
|
+
import { useMultiModalShipments as re } from "../hooks/useMultiModalShipments.js";
|
|
12
|
+
import { useState as C } from "react";
|
|
13
|
+
import { Fragment as w, jsx as T, jsxs as E } from "react/jsx-runtime";
|
|
14
|
+
import { EmphasisPanel as D, PagePurpose as O } from "@burdenoff/fe-libs/ui";
|
|
15
|
+
//#region src/movethewheels/pages/MultiModalShipmentPage.tsx
|
|
16
|
+
var k = {
|
|
17
|
+
ROAD: a,
|
|
18
|
+
AIR: t,
|
|
19
|
+
OCEAN: r,
|
|
20
|
+
RAIL: i
|
|
21
|
+
}, A = {
|
|
22
|
+
ROAD: "Road",
|
|
23
|
+
AIR: "Air",
|
|
24
|
+
OCEAN: "Ocean",
|
|
25
|
+
RAIL: "Rail"
|
|
26
|
+
};
|
|
27
|
+
function j(e = "ROAD") {
|
|
28
|
+
return {
|
|
29
|
+
mode: e,
|
|
30
|
+
origin: {},
|
|
31
|
+
destination: {}
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
function M(e, t) {
|
|
35
|
+
if (e == null) return "—";
|
|
36
|
+
try {
|
|
37
|
+
return new Intl.NumberFormat("en-US", {
|
|
38
|
+
style: "currency",
|
|
39
|
+
currency: t ?? "USD"
|
|
40
|
+
}).format(e);
|
|
41
|
+
} catch {
|
|
42
|
+
return `${e.toFixed(2)} ${t ?? "USD"}`;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
function N() {
|
|
46
|
+
let { shipments: t, isLoading: i, error: a, calculateFreightRate: N, planMultiModalShipment: P } = re(), [F, I] = C(""), [L, R] = C(""), [z, B] = C(""), [V, H] = C([j()]), [U, W] = C({}), [G, K] = C({}), [q, J] = C(!1), [Y, X] = C(null), [Z, Q] = C(null), $ = (e, t) => {
|
|
47
|
+
H((n) => n.map((n, r) => r === e ? {
|
|
48
|
+
...n,
|
|
49
|
+
...t
|
|
50
|
+
} : n)), W((t) => ({
|
|
51
|
+
...t,
|
|
52
|
+
[e]: null
|
|
53
|
+
}));
|
|
54
|
+
}, ie = () => H((e) => [...e, j()]), ae = (e) => {
|
|
55
|
+
H((t) => t.filter((t, n) => n !== e)), W((t) => {
|
|
56
|
+
let n = { ...t };
|
|
57
|
+
return delete n[e], n;
|
|
58
|
+
});
|
|
59
|
+
}, oe = async (e) => {
|
|
60
|
+
let t = V[e];
|
|
61
|
+
if (t) {
|
|
62
|
+
K((t) => ({
|
|
63
|
+
...t,
|
|
64
|
+
[e]: null
|
|
65
|
+
}));
|
|
66
|
+
try {
|
|
67
|
+
let n = await N(t);
|
|
68
|
+
W((t) => ({
|
|
69
|
+
...t,
|
|
70
|
+
[e]: n
|
|
71
|
+
}));
|
|
72
|
+
} catch (t) {
|
|
73
|
+
K((n) => ({
|
|
74
|
+
...n,
|
|
75
|
+
[e]: t instanceof Error ? t.message : "Failed to calculate rate"
|
|
76
|
+
}));
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}, se = Object.values(U).reduce((e, t) => e + (t?.calculatedCost ?? 0), 0), ce = new Set(V.map((e) => e.mode));
|
|
80
|
+
return /* @__PURE__ */ E(b, {
|
|
81
|
+
hero: !0,
|
|
82
|
+
titleAccent: "Multi-Modal Freight",
|
|
83
|
+
title: "Multi-Modal Freight",
|
|
84
|
+
subtitle: "Plan a shipment across road, air, ocean and rail legs with real per-mode rate calculation",
|
|
85
|
+
icon: /* @__PURE__ */ T(r, { size: 24 }),
|
|
86
|
+
children: [
|
|
87
|
+
/* @__PURE__ */ T(O, {
|
|
88
|
+
icon: /* @__PURE__ */ T(r, { size: 18 }),
|
|
89
|
+
children: "Compose a single shipment from multiple real transport legs — each leg picks its own mode (road/air/ocean/rail) and gets priced with that mode's real pricing driver (air: chargeable weight; ocean: revenue-ton or per-container; rail: tonne-km; road: distance + weight). Calculate a leg's rate before booking, then plan the full multi-leg shipment in one step."
|
|
90
|
+
}),
|
|
91
|
+
/* @__PURE__ */ T(D, { children: "Live carrier booking/API integration is out of scope for this core (it needs named-carrier partnership agreements) — this plans the shipment and its legs with real calculated costs that your ops team can then execute against your chosen carriers." }),
|
|
92
|
+
/* @__PURE__ */ T(x, {
|
|
93
|
+
title: "Plan a New Multi-Modal Shipment",
|
|
94
|
+
children: /* @__PURE__ */ T(c, { children: /* @__PURE__ */ T(l, { children: /* @__PURE__ */ E("form", {
|
|
95
|
+
onSubmit: async (e) => {
|
|
96
|
+
if (e.preventDefault(), X(null), Q(null), !F.trim()) {
|
|
97
|
+
X("Shipment number is required.");
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
if (V.length === 0) {
|
|
101
|
+
X("Add at least one transport leg.");
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
J(!0);
|
|
105
|
+
try {
|
|
106
|
+
let e = await P({
|
|
107
|
+
shipmentNumber: F.trim(),
|
|
108
|
+
origin: { address: L },
|
|
109
|
+
destination: { address: z },
|
|
110
|
+
legs: V
|
|
111
|
+
});
|
|
112
|
+
Q(`Shipment ${e.shipmentNumber} planned with ${e.legs.length} leg(s).`), I(""), R(""), B(""), H([j()]), W({});
|
|
113
|
+
} catch (e) {
|
|
114
|
+
X(e instanceof Error ? e.message : "Failed to plan shipment");
|
|
115
|
+
} finally {
|
|
116
|
+
J(!1);
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
className: "space-y-6 pt-4",
|
|
120
|
+
children: [
|
|
121
|
+
/* @__PURE__ */ T(S, { message: Y }),
|
|
122
|
+
Z && /* @__PURE__ */ T("div", {
|
|
123
|
+
className: "rounded-lg border border-status-success-border bg-status-success-bg-subtle px-4 py-3 text-sm text-status-success-text",
|
|
124
|
+
children: Z
|
|
125
|
+
}),
|
|
126
|
+
/* @__PURE__ */ E("div", {
|
|
127
|
+
className: "grid grid-cols-1 gap-4 sm:grid-cols-3",
|
|
128
|
+
children: [
|
|
129
|
+
/* @__PURE__ */ E("div", { children: [/* @__PURE__ */ T(p, {
|
|
130
|
+
htmlFor: "shipmentNumber",
|
|
131
|
+
children: "Shipment Number"
|
|
132
|
+
}), /* @__PURE__ */ T(f, {
|
|
133
|
+
id: "shipmentNumber",
|
|
134
|
+
value: F,
|
|
135
|
+
onChange: (e) => I(e.target.value),
|
|
136
|
+
placeholder: "SHP-MM-0001",
|
|
137
|
+
required: !0
|
|
138
|
+
})] }),
|
|
139
|
+
/* @__PURE__ */ E("div", { children: [/* @__PURE__ */ T(p, {
|
|
140
|
+
htmlFor: "originAddress",
|
|
141
|
+
children: "Origin"
|
|
142
|
+
}), /* @__PURE__ */ T(f, {
|
|
143
|
+
id: "originAddress",
|
|
144
|
+
value: L,
|
|
145
|
+
onChange: (e) => R(e.target.value),
|
|
146
|
+
placeholder: "Shanghai, CN"
|
|
147
|
+
})] }),
|
|
148
|
+
/* @__PURE__ */ E("div", { children: [/* @__PURE__ */ T(p, {
|
|
149
|
+
htmlFor: "destinationAddress",
|
|
150
|
+
children: "Destination"
|
|
151
|
+
}), /* @__PURE__ */ T(f, {
|
|
152
|
+
id: "destinationAddress",
|
|
153
|
+
value: z,
|
|
154
|
+
onChange: (e) => B(e.target.value),
|
|
155
|
+
placeholder: "Los Angeles, US"
|
|
156
|
+
})] })
|
|
157
|
+
]
|
|
158
|
+
}),
|
|
159
|
+
/* @__PURE__ */ T(te, {}),
|
|
160
|
+
/* @__PURE__ */ T("div", {
|
|
161
|
+
className: "space-y-4",
|
|
162
|
+
children: V.map((t, n) => {
|
|
163
|
+
let r = k[t.mode], i = U[n], a = G[n];
|
|
164
|
+
return /* @__PURE__ */ E(c, {
|
|
165
|
+
className: "border-border-subtle",
|
|
166
|
+
children: [/* @__PURE__ */ E(u, {
|
|
167
|
+
className: "flex flex-row items-center justify-between",
|
|
168
|
+
children: [/* @__PURE__ */ E(d, {
|
|
169
|
+
className: "flex items-center gap-2 text-base",
|
|
170
|
+
children: [
|
|
171
|
+
/* @__PURE__ */ T(r, { size: 16 }),
|
|
172
|
+
" Leg ",
|
|
173
|
+
n + 1
|
|
174
|
+
]
|
|
175
|
+
}), V.length > 1 && /* @__PURE__ */ T(s, {
|
|
176
|
+
type: "button",
|
|
177
|
+
variant: "ghost",
|
|
178
|
+
size: "sm",
|
|
179
|
+
onClick: () => ae(n),
|
|
180
|
+
"aria-label": `Remove leg ${n + 1}`,
|
|
181
|
+
children: /* @__PURE__ */ T(ee, { size: 14 })
|
|
182
|
+
})]
|
|
183
|
+
}), /* @__PURE__ */ E(l, {
|
|
184
|
+
className: "space-y-4",
|
|
185
|
+
children: [
|
|
186
|
+
/* @__PURE__ */ E("div", {
|
|
187
|
+
className: "grid grid-cols-1 gap-4 sm:grid-cols-3",
|
|
188
|
+
children: [
|
|
189
|
+
/* @__PURE__ */ E("div", { children: [/* @__PURE__ */ T(p, { children: "Mode" }), /* @__PURE__ */ E(m, {
|
|
190
|
+
value: t.mode,
|
|
191
|
+
onValueChange: (e) => $(n, { mode: e }),
|
|
192
|
+
children: [/* @__PURE__ */ T(_, { children: /* @__PURE__ */ T(v, {}) }), /* @__PURE__ */ T(h, { children: [
|
|
193
|
+
"ROAD",
|
|
194
|
+
"AIR",
|
|
195
|
+
"OCEAN",
|
|
196
|
+
"RAIL"
|
|
197
|
+
].map((e) => /* @__PURE__ */ T(g, {
|
|
198
|
+
value: e,
|
|
199
|
+
children: A[e]
|
|
200
|
+
}, e)) })]
|
|
201
|
+
})] }),
|
|
202
|
+
/* @__PURE__ */ E("div", { children: [/* @__PURE__ */ T(p, { children: "Leg Origin" }), /* @__PURE__ */ T(f, {
|
|
203
|
+
value: t.origin.address ?? "",
|
|
204
|
+
onChange: (e) => $(n, { origin: { address: e.target.value } }),
|
|
205
|
+
placeholder: "Origin port/airport/terminal"
|
|
206
|
+
})] }),
|
|
207
|
+
/* @__PURE__ */ E("div", { children: [/* @__PURE__ */ T(p, { children: "Leg Destination" }), /* @__PURE__ */ T(f, {
|
|
208
|
+
value: t.destination.address ?? "",
|
|
209
|
+
onChange: (e) => $(n, { destination: { address: e.target.value } }),
|
|
210
|
+
placeholder: "Destination port/airport/terminal"
|
|
211
|
+
})] })
|
|
212
|
+
]
|
|
213
|
+
}),
|
|
214
|
+
(t.mode === "ROAD" || t.mode === "RAIL") && /* @__PURE__ */ E("div", {
|
|
215
|
+
className: "grid grid-cols-1 gap-4 sm:grid-cols-2",
|
|
216
|
+
children: [/* @__PURE__ */ E("div", { children: [/* @__PURE__ */ T(p, { children: "Distance (km)" }), /* @__PURE__ */ T(f, {
|
|
217
|
+
type: "number",
|
|
218
|
+
min: 0,
|
|
219
|
+
value: t.distanceKm ?? "",
|
|
220
|
+
onChange: (e) => $(n, { distanceKm: Number(e.target.value) || void 0 })
|
|
221
|
+
})] }), /* @__PURE__ */ E("div", { children: [/* @__PURE__ */ T(p, { children: "Weight (kg)" }), /* @__PURE__ */ T(f, {
|
|
222
|
+
type: "number",
|
|
223
|
+
min: 0,
|
|
224
|
+
value: t.weightKg ?? "",
|
|
225
|
+
onChange: (e) => $(n, { weightKg: Number(e.target.value) || void 0 })
|
|
226
|
+
})] })]
|
|
227
|
+
}),
|
|
228
|
+
t.mode === "AIR" && /* @__PURE__ */ E("div", {
|
|
229
|
+
className: "grid grid-cols-1 gap-4 sm:grid-cols-4",
|
|
230
|
+
children: [
|
|
231
|
+
/* @__PURE__ */ E("div", { children: [/* @__PURE__ */ T(p, { children: "Weight (kg)" }), /* @__PURE__ */ T(f, {
|
|
232
|
+
type: "number",
|
|
233
|
+
min: 0,
|
|
234
|
+
value: t.weightKg ?? "",
|
|
235
|
+
onChange: (e) => $(n, { weightKg: Number(e.target.value) || void 0 })
|
|
236
|
+
})] }),
|
|
237
|
+
/* @__PURE__ */ E("div", { children: [/* @__PURE__ */ T(p, { children: "Length (cm)" }), /* @__PURE__ */ T(f, {
|
|
238
|
+
type: "number",
|
|
239
|
+
min: 0,
|
|
240
|
+
value: t.lengthCm ?? "",
|
|
241
|
+
onChange: (e) => $(n, { lengthCm: Number(e.target.value) || void 0 })
|
|
242
|
+
})] }),
|
|
243
|
+
/* @__PURE__ */ E("div", { children: [/* @__PURE__ */ T(p, { children: "Width (cm)" }), /* @__PURE__ */ T(f, {
|
|
244
|
+
type: "number",
|
|
245
|
+
min: 0,
|
|
246
|
+
value: t.widthCm ?? "",
|
|
247
|
+
onChange: (e) => $(n, { widthCm: Number(e.target.value) || void 0 })
|
|
248
|
+
})] }),
|
|
249
|
+
/* @__PURE__ */ E("div", { children: [/* @__PURE__ */ T(p, { children: "Height (cm)" }), /* @__PURE__ */ T(f, {
|
|
250
|
+
type: "number",
|
|
251
|
+
min: 0,
|
|
252
|
+
value: t.heightCm ?? "",
|
|
253
|
+
onChange: (e) => $(n, { heightCm: Number(e.target.value) || void 0 })
|
|
254
|
+
})] })
|
|
255
|
+
]
|
|
256
|
+
}),
|
|
257
|
+
t.mode === "OCEAN" && /* @__PURE__ */ E("div", {
|
|
258
|
+
className: "grid grid-cols-1 gap-4 sm:grid-cols-4",
|
|
259
|
+
children: [/* @__PURE__ */ E("div", { children: [/* @__PURE__ */ T(p, { children: "Container Type" }), /* @__PURE__ */ E(m, {
|
|
260
|
+
value: t.containerType ?? "LCL",
|
|
261
|
+
onValueChange: (e) => $(n, { containerType: e }),
|
|
262
|
+
children: [/* @__PURE__ */ T(_, { children: /* @__PURE__ */ T(v, {}) }), /* @__PURE__ */ E(h, { children: [
|
|
263
|
+
/* @__PURE__ */ T(g, {
|
|
264
|
+
value: "LCL",
|
|
265
|
+
children: "LCL (shared container)"
|
|
266
|
+
}),
|
|
267
|
+
/* @__PURE__ */ T(g, {
|
|
268
|
+
value: "FCL_20FT",
|
|
269
|
+
children: "FCL 20ft"
|
|
270
|
+
}),
|
|
271
|
+
/* @__PURE__ */ T(g, {
|
|
272
|
+
value: "FCL_40FT",
|
|
273
|
+
children: "FCL 40ft"
|
|
274
|
+
}),
|
|
275
|
+
/* @__PURE__ */ T(g, {
|
|
276
|
+
value: "FCL_40FT_HC",
|
|
277
|
+
children: "FCL 40ft High-Cube"
|
|
278
|
+
})
|
|
279
|
+
] })]
|
|
280
|
+
})] }), (t.containerType ?? "LCL") === "LCL" ? /* @__PURE__ */ E(w, { children: [/* @__PURE__ */ E("div", { children: [/* @__PURE__ */ T(p, { children: "Weight (kg)" }), /* @__PURE__ */ T(f, {
|
|
281
|
+
type: "number",
|
|
282
|
+
min: 0,
|
|
283
|
+
value: t.weightKg ?? "",
|
|
284
|
+
onChange: (e) => $(n, { weightKg: Number(e.target.value) || void 0 })
|
|
285
|
+
})] }), /* @__PURE__ */ E("div", { children: [/* @__PURE__ */ T(p, { children: "Volume (CBM)" }), /* @__PURE__ */ T(f, {
|
|
286
|
+
type: "number",
|
|
287
|
+
min: 0,
|
|
288
|
+
value: t.volumeCbm ?? "",
|
|
289
|
+
onChange: (e) => $(n, { volumeCbm: Number(e.target.value) || void 0 })
|
|
290
|
+
})] })] }) : /* @__PURE__ */ E("div", { children: [/* @__PURE__ */ T(p, { children: "Container Count" }), /* @__PURE__ */ T(f, {
|
|
291
|
+
type: "number",
|
|
292
|
+
min: 1,
|
|
293
|
+
value: t.containerCount ?? 1,
|
|
294
|
+
onChange: (e) => $(n, { containerCount: Number(e.target.value) || 1 })
|
|
295
|
+
})] })]
|
|
296
|
+
}),
|
|
297
|
+
/* @__PURE__ */ E("div", {
|
|
298
|
+
className: "flex items-center gap-3",
|
|
299
|
+
children: [
|
|
300
|
+
/* @__PURE__ */ E(s, {
|
|
301
|
+
type: "button",
|
|
302
|
+
variant: "secondary",
|
|
303
|
+
size: "sm",
|
|
304
|
+
onClick: () => void oe(n),
|
|
305
|
+
children: [/* @__PURE__ */ T(e, {
|
|
306
|
+
size: 14,
|
|
307
|
+
className: "mr-1.5"
|
|
308
|
+
}), " Calculate Rate"]
|
|
309
|
+
}),
|
|
310
|
+
i && /* @__PURE__ */ E("span", {
|
|
311
|
+
className: "text-sm font-medium text-text-primary",
|
|
312
|
+
children: [M(i.calculatedCost, i.currency), i.chargeableWeightKg != null && /* @__PURE__ */ E("span", {
|
|
313
|
+
className: "ml-2 text-xs text-text-secondary",
|
|
314
|
+
children: [
|
|
315
|
+
"(",
|
|
316
|
+
i.chargeableWeightKg.toFixed(1),
|
|
317
|
+
"kg chargeable)"
|
|
318
|
+
]
|
|
319
|
+
})]
|
|
320
|
+
}),
|
|
321
|
+
a && /* @__PURE__ */ T("span", {
|
|
322
|
+
className: "text-sm text-status-error-text",
|
|
323
|
+
children: a
|
|
324
|
+
})
|
|
325
|
+
]
|
|
326
|
+
}),
|
|
327
|
+
i && i.breakdown.length > 0 && /* @__PURE__ */ T("ul", {
|
|
328
|
+
className: "space-y-1 rounded-md bg-surface-subtle px-3 py-2 text-xs text-text-secondary",
|
|
329
|
+
children: i.breakdown.map((e) => /* @__PURE__ */ E("li", {
|
|
330
|
+
className: "flex justify-between",
|
|
331
|
+
children: [/* @__PURE__ */ T("span", { children: e.label }), /* @__PURE__ */ T("span", { children: M(e.amount, i.currency) })]
|
|
332
|
+
}, e.label))
|
|
333
|
+
})
|
|
334
|
+
]
|
|
335
|
+
})]
|
|
336
|
+
}, n);
|
|
337
|
+
})
|
|
338
|
+
}),
|
|
339
|
+
/* @__PURE__ */ E("div", {
|
|
340
|
+
className: "flex items-center justify-between",
|
|
341
|
+
children: [/* @__PURE__ */ E(s, {
|
|
342
|
+
type: "button",
|
|
343
|
+
variant: "outline",
|
|
344
|
+
size: "sm",
|
|
345
|
+
onClick: ie,
|
|
346
|
+
children: [/* @__PURE__ */ T(n, {
|
|
347
|
+
size: 14,
|
|
348
|
+
className: "mr-1.5"
|
|
349
|
+
}), " Add Leg"]
|
|
350
|
+
}), /* @__PURE__ */ E("div", {
|
|
351
|
+
className: "flex items-center gap-3",
|
|
352
|
+
children: [ce.size > 1 && /* @__PURE__ */ T(o, {
|
|
353
|
+
variant: "secondary",
|
|
354
|
+
children: "Multi-modal"
|
|
355
|
+
}), /* @__PURE__ */ E("span", {
|
|
356
|
+
className: "text-sm text-text-secondary",
|
|
357
|
+
children: [
|
|
358
|
+
"Estimated total:",
|
|
359
|
+
" ",
|
|
360
|
+
/* @__PURE__ */ T("strong", { children: M(se, U[0]?.currency ?? "USD") })
|
|
361
|
+
]
|
|
362
|
+
})]
|
|
363
|
+
})]
|
|
364
|
+
}),
|
|
365
|
+
/* @__PURE__ */ T(s, {
|
|
366
|
+
type: "submit",
|
|
367
|
+
disabled: q,
|
|
368
|
+
children: q ? "Planning shipment..." : "Plan Shipment"
|
|
369
|
+
})
|
|
370
|
+
]
|
|
371
|
+
}) }) })
|
|
372
|
+
}),
|
|
373
|
+
/* @__PURE__ */ T(x, {
|
|
374
|
+
title: "Planned Multi-Modal Shipments",
|
|
375
|
+
children: i ? /* @__PURE__ */ T(y, { message: "Loading multi-modal shipments..." }) : a ? /* @__PURE__ */ T(S, { message: a.message }) : t.length === 0 ? /* @__PURE__ */ T(ne, {
|
|
376
|
+
icon: /* @__PURE__ */ T(r, { size: 32 }),
|
|
377
|
+
title: "No multi-modal shipments yet",
|
|
378
|
+
description: "Plan a shipment above to see it here once it spans more than one transport mode."
|
|
379
|
+
}) : /* @__PURE__ */ T("div", {
|
|
380
|
+
className: "space-y-4",
|
|
381
|
+
children: t.map((e) => /* @__PURE__ */ E(c, { children: [/* @__PURE__ */ E(u, {
|
|
382
|
+
className: "flex flex-row items-center justify-between",
|
|
383
|
+
children: [/* @__PURE__ */ T(d, {
|
|
384
|
+
className: "text-base",
|
|
385
|
+
children: e.shipmentNumber
|
|
386
|
+
}), /* @__PURE__ */ E("div", {
|
|
387
|
+
className: "flex items-center gap-2",
|
|
388
|
+
children: [/* @__PURE__ */ T(o, {
|
|
389
|
+
variant: "outline",
|
|
390
|
+
children: e.status
|
|
391
|
+
}), /* @__PURE__ */ T("span", {
|
|
392
|
+
className: "text-sm font-medium",
|
|
393
|
+
children: M(e.totalFreightCost, e.freightCurrency)
|
|
394
|
+
})]
|
|
395
|
+
})]
|
|
396
|
+
}), /* @__PURE__ */ T(l, { children: /* @__PURE__ */ T("div", {
|
|
397
|
+
className: "flex flex-wrap gap-2",
|
|
398
|
+
children: e.legs.map((e) => {
|
|
399
|
+
let t = k[e.mode];
|
|
400
|
+
return /* @__PURE__ */ E(o, {
|
|
401
|
+
variant: "secondary",
|
|
402
|
+
className: "flex items-center gap-1",
|
|
403
|
+
children: [
|
|
404
|
+
/* @__PURE__ */ T(t, { size: 12 }),
|
|
405
|
+
" ",
|
|
406
|
+
A[e.mode],
|
|
407
|
+
":",
|
|
408
|
+
" ",
|
|
409
|
+
M(e.calculatedCost, e.currency)
|
|
410
|
+
]
|
|
411
|
+
}, e.id);
|
|
412
|
+
})
|
|
413
|
+
}) })] }, e.id))
|
|
414
|
+
})
|
|
415
|
+
})
|
|
416
|
+
]
|
|
417
|
+
});
|
|
418
|
+
}
|
|
419
|
+
//#endregion
|
|
420
|
+
export { N as default };
|
package/package.json
CHANGED
|
@@ -73,6 +73,7 @@ const ReportsPage = lazy(() => import('./pages/ReportsPage'));
|
|
|
73
73
|
const SettingsPage = lazy(() => import('./pages/SettingsPage'));
|
|
74
74
|
const AIAssistantPage = lazy(() => import('./pages/AIAssistantPage'));
|
|
75
75
|
const DeliveryCapturePage = lazy(() => import('./pages/DeliveryCapturePage'));
|
|
76
|
+
const MultiModalShipmentPage = lazy(() => import('./pages/MultiModalShipmentPage'));
|
|
76
77
|
|
|
77
78
|
export function MoveTheWheelsRoutes() {
|
|
78
79
|
return (
|
|
@@ -94,6 +95,9 @@ export function MoveTheWheelsRoutes() {
|
|
|
94
95
|
{/* Driver ePOD capture (BOFF-4399) */}
|
|
95
96
|
<Route path="deliveries/mine" element={<DeliveryCapturePage />} />
|
|
96
97
|
|
|
98
|
+
{/* Multi-modal freight execution (BOFF-4398) */}
|
|
99
|
+
<Route path="shipments/multi-modal" element={<MultiModalShipmentPage />} />
|
|
100
|
+
|
|
97
101
|
{/* Fleet */}
|
|
98
102
|
<Route path="fleet" element={<FleetPage />} />
|
|
99
103
|
<Route path="fleet/vehicles" element={<VehiclesPage />} />
|