@burdenoff/microfe-movethewheels 2026.1017.18 → 2026.1026.1
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/movethewheels/utils/orderPricing.d.ts +9 -0
- package/dist/pages/CreateOrderPage.js +196 -194
- package/dist/utils/orderPricing.js +20 -0
- package/package.json +1 -1
- package/src/movethewheels/pages/CreateOrderPage.tsx +2 -0
- package/src/movethewheels/utils/orderPricing.test.ts +48 -0
- package/src/movethewheels/utils/orderPricing.ts +40 -0
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { OrderItem, OrderPricing } from '../types';
|
|
2
|
+
/**
|
|
3
|
+
* Build the pricing payload used by delivery-job create/update forms.
|
|
4
|
+
* Item `value` is the per-unit declared value, so quantity is applied here.
|
|
5
|
+
* Delivery, handling, taxes, and insurance remain zero unless an existing
|
|
6
|
+
* supported value is being preserved during an edit; this form has no rate
|
|
7
|
+
* source and must not invent one.
|
|
8
|
+
*/
|
|
9
|
+
export declare function calculateOrderPricing(items: OrderItem[], existing?: Partial<OrderPricing> | null): OrderPricing;
|
|
@@ -9,39 +9,40 @@ import { Users as te } from "../node_modules/lucide-react/dist/esm/icons/users.j
|
|
|
9
9
|
import { buildCustomerCreatePath as ne, joinPath as o } from "../utils/navigation.js";
|
|
10
10
|
import { useMoveTheWheels as re } from "../providers/MoveTheWheelsProvider.js";
|
|
11
11
|
import { useRoute as ie, useRoutes as ae } from "../hooks/useRoutes.js";
|
|
12
|
-
import { useOrder as
|
|
13
|
-
import { useProducts as
|
|
12
|
+
import { useOrder as oe, useOrders as se } from "../hooks/useOrders.js";
|
|
13
|
+
import { useProducts as s } from "../hooks/useInventory.js";
|
|
14
14
|
import { useCustomer as ce, useCustomers as le } from "../hooks/useCustomers.js";
|
|
15
15
|
import { Badge as c, Button as l, Card as u, CardContent as d, CardDescription as f, CardHeader as p, CardTitle as m, Input as h, Label as g, Select as ue, SelectContent as de, SelectItem as _, SelectTrigger as fe, SelectValue as pe, Separator as me, Textarea as he } from "../components/ui.js";
|
|
16
16
|
import { EmptyState as ge, PageLayout as _e } from "../components/PageLayout.js";
|
|
17
17
|
import { FormError as ve } from "../components/FormError.js";
|
|
18
|
-
import { EntityCombobox as
|
|
19
|
-
import { CustomerPicker as
|
|
20
|
-
import { OrderItemRow as
|
|
21
|
-
import { validateOrderItems as
|
|
22
|
-
import {
|
|
18
|
+
import { EntityCombobox as v } from "../components/EntityCombobox.js";
|
|
19
|
+
import { CustomerPicker as ye } from "../components/CustomerPicker.js";
|
|
20
|
+
import { OrderItemRow as be } from "../components/OrderItemRow.js";
|
|
21
|
+
import { validateOrderItems as xe } from "../utils/orderValidation.js";
|
|
22
|
+
import { calculateOrderPricing as Se } from "../utils/orderPricing.js";
|
|
23
|
+
import { useEffect as y, useState as b } from "react";
|
|
23
24
|
import { useParams as Ce, useSearchParams as we } from "react-router";
|
|
24
|
-
import { Fragment as Te, jsx as
|
|
25
|
+
import { Fragment as Te, jsx as x, jsxs as S } from "react/jsx-runtime";
|
|
25
26
|
import { EmphasisPanel as Ee, PagePurpose as De, Stepper as Oe } from "@burdenoff/fe-libs/ui";
|
|
26
|
-
import { FormSection as
|
|
27
|
+
import { FormSection as C } from "@burdenoff/fe-libs/form";
|
|
27
28
|
//#region src/movethewheels/pages/CreateOrderPage.tsx
|
|
28
|
-
function
|
|
29
|
+
function w(e) {
|
|
29
30
|
if (!e || Number.isNaN(e.getTime())) return "";
|
|
30
31
|
let t = (e) => String(e).padStart(2, "0");
|
|
31
32
|
return `${e.getFullYear()}-${t(e.getMonth() + 1)}-${t(e.getDate())}T${t(e.getHours())}:${t(e.getMinutes())}`;
|
|
32
33
|
}
|
|
33
|
-
function
|
|
34
|
-
let { orderId:
|
|
34
|
+
function T() {
|
|
35
|
+
let { orderId: T } = Ce(), [ke] = we(), E = ke.get("customerId")?.trim() || void 0, { basePath: D, navigate: O } = re(), { order: k } = oe(T), { createOrder: Ae, updateOrder: je } = se(), [Me, Ne] = b(""), { customers: A, createCustomer: Pe, isLoading: j, isLoadingMore: Fe, error: M, refetch: Ie, loadMore: Le, hasMore: Re, totalCustomers: ze } = le({
|
|
35
36
|
limit: 20,
|
|
36
|
-
filters: { search:
|
|
37
|
-
}), { customer: N } = ce(E ?? k?.customerId), [
|
|
37
|
+
filters: { search: Me || void 0 }
|
|
38
|
+
}), { customer: N } = ce(E ?? k?.customerId), [Be, Ve] = b(""), { routes: P, isLoading: F, error: I, hasMore: He, totalRoutes: Ue, loadMore: L, isLoadingMore: We } = ae({
|
|
38
39
|
status: "planned",
|
|
39
|
-
search:
|
|
40
|
+
search: Be || void 0,
|
|
40
41
|
limit: 20
|
|
41
|
-
}), [R,
|
|
42
|
+
}), [R, Ge] = b(""), { products: Ke, isLoading: qe, isLoadingMore: Je, hasMore: Ye, totalProducts: Xe, loadMore: Ze } = s({
|
|
42
43
|
search: R || void 0,
|
|
43
44
|
limit: 20
|
|
44
|
-
}), z = !!
|
|
45
|
+
}), z = !!T && !!k, [B, V] = b(null), [H, U] = b({
|
|
45
46
|
customerId: E || k?.customerId || "",
|
|
46
47
|
priority: k?.priority || "normal",
|
|
47
48
|
routeId: k?.routeId || "",
|
|
@@ -61,15 +62,15 @@ function w() {
|
|
|
61
62
|
},
|
|
62
63
|
items: k?.items || [],
|
|
63
64
|
trackingNumber: k?.trackingNumber || "",
|
|
64
|
-
scheduledDelivery:
|
|
65
|
+
scheduledDelivery: w(k?.scheduledDelivery),
|
|
65
66
|
specialInstructions: k?.specialInstructions || ""
|
|
66
|
-
}), { route:
|
|
67
|
-
|
|
67
|
+
}), { route: Qe, isLoading: $e } = ie(H.routeId || void 0), [W, G] = b(!1), [et, K] = b(null), [q, J] = b({});
|
|
68
|
+
y(() => {
|
|
68
69
|
N && (V((e) => e?.id === N.id ? e : N), U((e) => e.customerId ? e : {
|
|
69
70
|
...e,
|
|
70
71
|
customerId: N.id
|
|
71
72
|
}));
|
|
72
|
-
}, [N]),
|
|
73
|
+
}, [N]), y(() => {
|
|
73
74
|
k && U({
|
|
74
75
|
customerId: k.customerId || "",
|
|
75
76
|
priority: k.priority || "normal",
|
|
@@ -90,7 +91,7 @@ function w() {
|
|
|
90
91
|
},
|
|
91
92
|
items: k.items || [],
|
|
92
93
|
trackingNumber: k.trackingNumber || "",
|
|
93
|
-
scheduledDelivery:
|
|
94
|
+
scheduledDelivery: w(k.scheduledDelivery),
|
|
94
95
|
specialInstructions: k.specialInstructions || ""
|
|
95
96
|
});
|
|
96
97
|
}, [k]);
|
|
@@ -102,16 +103,16 @@ function w() {
|
|
|
102
103
|
}
|
|
103
104
|
O(o(D, "orders"));
|
|
104
105
|
}
|
|
105
|
-
}, X = () => B ?? A.find((e) => e.id === H.customerId),
|
|
106
|
+
}, X = () => B ?? A.find((e) => e.id === H.customerId), tt = () => {
|
|
106
107
|
if (!H.customerId || !X()) return M ? "Customers could not be loaded. Retry or create a customer before continuing." : "Select a customer for this order.";
|
|
107
108
|
if (!H.pickupAddress.street.trim()) return "Enter a pickup street address.";
|
|
108
109
|
if (!H.deliveryAddress.street.trim()) return "Enter a delivery street address.";
|
|
109
110
|
if (H.items.length === 0) return "Add at least one item to the order.";
|
|
110
|
-
let e =
|
|
111
|
+
let e = xe(H.items);
|
|
111
112
|
return J(e), Object.keys(e).length > 0 ? "Fix the highlighted shipment item fields before continuing." : H.scheduledDelivery && Number.isNaN(Date.parse(H.scheduledDelivery)) ? "Enter a valid scheduled delivery date and time." : null;
|
|
112
|
-
},
|
|
113
|
+
}, nt = async (e) => {
|
|
113
114
|
e.preventDefault(), K(null);
|
|
114
|
-
let t =
|
|
115
|
+
let t = tt();
|
|
115
116
|
if (t) {
|
|
116
117
|
K(t);
|
|
117
118
|
return;
|
|
@@ -135,14 +136,15 @@ function w() {
|
|
|
135
136
|
pickupAddress: H.pickupAddress,
|
|
136
137
|
deliveryAddress: H.deliveryAddress,
|
|
137
138
|
items: H.items,
|
|
139
|
+
pricing: Se(H.items, z ? k?.pricing : void 0),
|
|
138
140
|
trackingNumber: H.trackingNumber.trim() || void 0,
|
|
139
141
|
scheduledDelivery: H.scheduledDelivery ? new Date(H.scheduledDelivery) : null,
|
|
140
142
|
routeId: H.routeId || void 0,
|
|
141
143
|
specialInstructions: H.specialInstructions
|
|
142
144
|
};
|
|
143
|
-
if (z &&
|
|
145
|
+
if (z && T) await je(T, e), O && O(o(D, `orders/${T}`));
|
|
144
146
|
else {
|
|
145
|
-
let t = await
|
|
147
|
+
let t = await Ae(e);
|
|
146
148
|
O ? O(o(D, `orders/${t.id}`)) : Y();
|
|
147
149
|
}
|
|
148
150
|
} catch (e) {
|
|
@@ -163,7 +165,7 @@ function w() {
|
|
|
163
165
|
[t]: n
|
|
164
166
|
}
|
|
165
167
|
}));
|
|
166
|
-
},
|
|
168
|
+
}, rt = (e) => {
|
|
167
169
|
if (!e) {
|
|
168
170
|
Z("routeId", "");
|
|
169
171
|
return;
|
|
@@ -202,7 +204,7 @@ function w() {
|
|
|
202
204
|
sku: ""
|
|
203
205
|
}]
|
|
204
206
|
}));
|
|
205
|
-
},
|
|
207
|
+
}, it = (e) => {
|
|
206
208
|
let t = H.items[e]?.id;
|
|
207
209
|
t && J((e) => {
|
|
208
210
|
let n = { ...e };
|
|
@@ -211,7 +213,7 @@ function w() {
|
|
|
211
213
|
...t,
|
|
212
214
|
items: t.items.filter((t, n) => n !== e)
|
|
213
215
|
}));
|
|
214
|
-
},
|
|
216
|
+
}, at = (e, t, n) => {
|
|
215
217
|
let r = H.items[e]?.id;
|
|
216
218
|
r && q[r] && J((e) => {
|
|
217
219
|
let t = { ...e };
|
|
@@ -223,35 +225,35 @@ function w() {
|
|
|
223
225
|
[t]: n
|
|
224
226
|
} : r)
|
|
225
227
|
}));
|
|
226
|
-
},
|
|
227
|
-
O && O(o(D, ne(z &&
|
|
228
|
-
},
|
|
229
|
-
return /* @__PURE__ */
|
|
228
|
+
}, ot = () => {
|
|
229
|
+
O && O(o(D, ne(z && T ? `orders/${T}/edit` : "orders/new")));
|
|
230
|
+
}, st = H.customerId ? !H.pickupAddress.street || !H.deliveryAddress.street ? 1 : H.items.length === 0 ? 2 : 3 : 0;
|
|
231
|
+
return /* @__PURE__ */ S(_e, {
|
|
230
232
|
title: z ? "Edit Order" : "Create Order",
|
|
231
233
|
subtitle: z ? `Editing ${k?.orderNumber}` : "Create a new delivery order",
|
|
232
|
-
icon: /* @__PURE__ */
|
|
234
|
+
icon: /* @__PURE__ */ x(i, { size: 24 }),
|
|
233
235
|
maxWidth: "2xl",
|
|
234
|
-
headerActions: /* @__PURE__ */
|
|
236
|
+
headerActions: /* @__PURE__ */ S("div", {
|
|
235
237
|
className: "flex w-full flex-col-reverse gap-2 sm:w-auto sm:flex-row",
|
|
236
|
-
children: [/* @__PURE__ */
|
|
238
|
+
children: [/* @__PURE__ */ S(l, {
|
|
237
239
|
type: "button",
|
|
238
240
|
variant: "outline",
|
|
239
241
|
onClick: Y,
|
|
240
242
|
disabled: W,
|
|
241
|
-
children: [/* @__PURE__ */
|
|
242
|
-
}), /* @__PURE__ */
|
|
243
|
+
children: [/* @__PURE__ */ x(e, { size: 16 }), "Cancel"]
|
|
244
|
+
}), /* @__PURE__ */ S(l, {
|
|
243
245
|
type: "submit",
|
|
244
246
|
form: "order-form",
|
|
245
247
|
disabled: W || j,
|
|
246
|
-
children: [/* @__PURE__ */
|
|
248
|
+
children: [/* @__PURE__ */ x(ee, { size: 16 }), W ? "Saving..." : z ? "Save Changes" : "Create Order"]
|
|
247
249
|
})]
|
|
248
250
|
}),
|
|
249
251
|
children: [
|
|
250
|
-
/* @__PURE__ */
|
|
251
|
-
icon: /* @__PURE__ */
|
|
252
|
+
/* @__PURE__ */ x(De, {
|
|
253
|
+
icon: /* @__PURE__ */ x(i, { size: 18 }),
|
|
252
254
|
children: z ? "Update the customer, delivery plan, shipment contents, or handling details before saving." : "Set the customer and delivery plan, add what is being shipped, then choose any timing or handling preferences."
|
|
253
255
|
}),
|
|
254
|
-
/* @__PURE__ */
|
|
256
|
+
/* @__PURE__ */ x(Oe, {
|
|
255
257
|
steps: [
|
|
256
258
|
{
|
|
257
259
|
key: "customer",
|
|
@@ -274,7 +276,7 @@ function w() {
|
|
|
274
276
|
description: "Add optional delivery instructions."
|
|
275
277
|
}
|
|
276
278
|
],
|
|
277
|
-
active:
|
|
279
|
+
active: st,
|
|
278
280
|
onStepClick: (e) => {
|
|
279
281
|
let t = document.getElementById([
|
|
280
282
|
"customer",
|
|
@@ -293,41 +295,41 @@ function w() {
|
|
|
293
295
|
},
|
|
294
296
|
allowSkipAhead: !0
|
|
295
297
|
}),
|
|
296
|
-
/* @__PURE__ */
|
|
298
|
+
/* @__PURE__ */ S("form", {
|
|
297
299
|
id: "order-form",
|
|
298
|
-
onSubmit:
|
|
300
|
+
onSubmit: nt,
|
|
299
301
|
className: "space-y-6",
|
|
300
302
|
children: [
|
|
301
|
-
/* @__PURE__ */
|
|
302
|
-
/* @__PURE__ */
|
|
303
|
+
/* @__PURE__ */ x(ve, { message: et }),
|
|
304
|
+
/* @__PURE__ */ x("section", {
|
|
303
305
|
"aria-labelledby": "order-details-title",
|
|
304
|
-
children: /* @__PURE__ */
|
|
306
|
+
children: /* @__PURE__ */ S(Ee, {
|
|
305
307
|
className: "space-y-5",
|
|
306
|
-
children: [/* @__PURE__ */
|
|
308
|
+
children: [/* @__PURE__ */ S("div", {
|
|
307
309
|
className: "flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between",
|
|
308
|
-
children: [/* @__PURE__ */
|
|
310
|
+
children: [/* @__PURE__ */ S("div", {
|
|
309
311
|
className: "flex items-start gap-3",
|
|
310
|
-
children: [/* @__PURE__ */
|
|
312
|
+
children: [/* @__PURE__ */ x("div", {
|
|
311
313
|
className: "rounded-xl bg-action-primary-bg/10 p-2 text-action-primary-bg",
|
|
312
|
-
children: /* @__PURE__ */
|
|
314
|
+
children: /* @__PURE__ */ x(te, {
|
|
313
315
|
size: 18,
|
|
314
316
|
"aria-hidden": "true"
|
|
315
317
|
})
|
|
316
|
-
}), /* @__PURE__ */
|
|
318
|
+
}), /* @__PURE__ */ S("div", { children: [/* @__PURE__ */ x("h2", {
|
|
317
319
|
id: "order-details-title",
|
|
318
320
|
className: "text-base font-semibold text-text-primary",
|
|
319
321
|
children: "Customer & priority"
|
|
320
|
-
}), /* @__PURE__ */
|
|
322
|
+
}), /* @__PURE__ */ x("p", {
|
|
321
323
|
className: "mt-1 text-sm text-text-secondary",
|
|
322
324
|
children: "Choose who this delivery is for and how urgently it should be handled."
|
|
323
325
|
})] })]
|
|
324
|
-
}), /* @__PURE__ */
|
|
326
|
+
}), /* @__PURE__ */ x(c, {
|
|
325
327
|
variant: "outline",
|
|
326
328
|
children: "Required"
|
|
327
329
|
})]
|
|
328
|
-
}), /* @__PURE__ */
|
|
330
|
+
}), /* @__PURE__ */ S("div", {
|
|
329
331
|
className: "grid grid-cols-1 gap-4 md:grid-cols-2",
|
|
330
|
-
children: [/* @__PURE__ */
|
|
332
|
+
children: [/* @__PURE__ */ x(ye, {
|
|
331
333
|
customers: A,
|
|
332
334
|
value: H.customerId,
|
|
333
335
|
onValueChange: (e) => {
|
|
@@ -336,50 +338,50 @@ function w() {
|
|
|
336
338
|
onCreated: (e) => {
|
|
337
339
|
V(e), Z("customerId", e.id), K(null);
|
|
338
340
|
},
|
|
339
|
-
createCustomer:
|
|
341
|
+
createCustomer: Pe,
|
|
340
342
|
isLoading: j,
|
|
341
343
|
error: M,
|
|
342
|
-
onRetry:
|
|
343
|
-
onOpenCustomers:
|
|
344
|
+
onRetry: Ie,
|
|
345
|
+
onOpenCustomers: ot,
|
|
344
346
|
selectedCustomer: B,
|
|
345
|
-
onSearchQueryChange:
|
|
346
|
-
hasMore:
|
|
347
|
-
totalCount:
|
|
348
|
-
onLoadMore:
|
|
349
|
-
isLoadingMore:
|
|
350
|
-
}), /* @__PURE__ */
|
|
347
|
+
onSearchQueryChange: Ne,
|
|
348
|
+
hasMore: Re,
|
|
349
|
+
totalCount: ze,
|
|
350
|
+
onLoadMore: Le,
|
|
351
|
+
isLoadingMore: Fe
|
|
352
|
+
}), /* @__PURE__ */ S("div", {
|
|
351
353
|
className: "space-y-2",
|
|
352
354
|
children: [
|
|
353
|
-
/* @__PURE__ */
|
|
355
|
+
/* @__PURE__ */ x(g, {
|
|
354
356
|
htmlFor: "priority",
|
|
355
357
|
children: "Priority"
|
|
356
358
|
}),
|
|
357
|
-
/* @__PURE__ */
|
|
359
|
+
/* @__PURE__ */ S(ue, {
|
|
358
360
|
value: H.priority,
|
|
359
361
|
onValueChange: (e) => Z("priority", e),
|
|
360
|
-
children: [/* @__PURE__ */
|
|
362
|
+
children: [/* @__PURE__ */ x(fe, {
|
|
361
363
|
id: "priority",
|
|
362
|
-
children: /* @__PURE__ */
|
|
363
|
-
}), /* @__PURE__ */
|
|
364
|
-
/* @__PURE__ */
|
|
364
|
+
children: /* @__PURE__ */ x(pe, {})
|
|
365
|
+
}), /* @__PURE__ */ S(de, { children: [
|
|
366
|
+
/* @__PURE__ */ x(_, {
|
|
365
367
|
value: "low",
|
|
366
368
|
children: "Low"
|
|
367
369
|
}),
|
|
368
|
-
/* @__PURE__ */
|
|
370
|
+
/* @__PURE__ */ x(_, {
|
|
369
371
|
value: "normal",
|
|
370
372
|
children: "Normal"
|
|
371
373
|
}),
|
|
372
|
-
/* @__PURE__ */
|
|
374
|
+
/* @__PURE__ */ x(_, {
|
|
373
375
|
value: "high",
|
|
374
376
|
children: "High"
|
|
375
377
|
}),
|
|
376
|
-
/* @__PURE__ */
|
|
378
|
+
/* @__PURE__ */ x(_, {
|
|
377
379
|
value: "urgent",
|
|
378
380
|
children: "Urgent"
|
|
379
381
|
})
|
|
380
382
|
] })]
|
|
381
383
|
}),
|
|
382
|
-
/* @__PURE__ */
|
|
384
|
+
/* @__PURE__ */ x("p", {
|
|
383
385
|
className: "text-xs text-text-secondary",
|
|
384
386
|
children: "Normal is selected by default."
|
|
385
387
|
})
|
|
@@ -388,106 +390,106 @@ function w() {
|
|
|
388
390
|
})]
|
|
389
391
|
})
|
|
390
392
|
}),
|
|
391
|
-
/* @__PURE__ */
|
|
393
|
+
/* @__PURE__ */ x("div", {
|
|
392
394
|
id: "locations-section",
|
|
393
|
-
children: /* @__PURE__ */
|
|
394
|
-
title: /* @__PURE__ */
|
|
395
|
+
children: /* @__PURE__ */ S(C, {
|
|
396
|
+
title: /* @__PURE__ */ S("span", {
|
|
395
397
|
className: "flex items-center gap-2",
|
|
396
|
-
children: [/* @__PURE__ */
|
|
398
|
+
children: [/* @__PURE__ */ x(r, {
|
|
397
399
|
size: 18,
|
|
398
400
|
"aria-hidden": "true"
|
|
399
401
|
}), "Route & addresses"]
|
|
400
402
|
}),
|
|
401
403
|
description: "Choose an optional saved route, then review or enter the collection and destination details.",
|
|
402
404
|
children: [
|
|
403
|
-
/* @__PURE__ */
|
|
405
|
+
/* @__PURE__ */ S("div", {
|
|
404
406
|
className: "rounded-card border border-border-subtle bg-bg-sunken/40 p-4",
|
|
405
407
|
children: [
|
|
406
|
-
/* @__PURE__ */
|
|
408
|
+
/* @__PURE__ */ S("div", {
|
|
407
409
|
className: "mb-3 flex flex-col gap-2 sm:flex-row sm:items-start sm:justify-between",
|
|
408
|
-
children: [/* @__PURE__ */
|
|
410
|
+
children: [/* @__PURE__ */ S("div", { children: [/* @__PURE__ */ x("h3", {
|
|
409
411
|
className: "text-sm font-semibold text-text-primary",
|
|
410
412
|
children: "Use a saved route"
|
|
411
|
-
}), /* @__PURE__ */
|
|
413
|
+
}), /* @__PURE__ */ x("p", {
|
|
412
414
|
className: "mt-1 text-xs text-text-secondary",
|
|
413
415
|
children: "Optional — fills only blank address fields; you can always edit them below."
|
|
414
|
-
})] }), /* @__PURE__ */
|
|
416
|
+
})] }), /* @__PURE__ */ x(c, {
|
|
415
417
|
variant: "secondary",
|
|
416
418
|
children: "Optional"
|
|
417
419
|
})]
|
|
418
420
|
}),
|
|
419
|
-
/* @__PURE__ */
|
|
421
|
+
/* @__PURE__ */ x(v, {
|
|
420
422
|
id: "saved-route",
|
|
421
423
|
value: H.routeId,
|
|
422
|
-
onChange:
|
|
424
|
+
onChange: rt,
|
|
423
425
|
options: P,
|
|
424
426
|
loading: F,
|
|
425
427
|
getId: (e) => e.id,
|
|
426
428
|
getLabel: (e) => `${e.name} · ${e.stops.length} stops`,
|
|
427
|
-
renderOption: (e) => /* @__PURE__ */
|
|
429
|
+
renderOption: (e) => /* @__PURE__ */ S(Te, { children: [
|
|
428
430
|
e.name,
|
|
429
431
|
" · ",
|
|
430
432
|
e.stops.length,
|
|
431
433
|
" stops"
|
|
432
434
|
] }),
|
|
433
|
-
selectedOption:
|
|
434
|
-
selectedLoading:
|
|
435
|
+
selectedOption: Qe,
|
|
436
|
+
selectedLoading: $e,
|
|
435
437
|
placeholder: "Choose a saved route",
|
|
436
438
|
searchPlaceholder: "Search saved routes",
|
|
437
439
|
emptyText: "No saved routes found.",
|
|
438
440
|
noneLabel: "No saved route",
|
|
439
441
|
error: I ? "Saved routes are unavailable" : void 0,
|
|
440
|
-
onSearchChange:
|
|
441
|
-
hasMore:
|
|
442
|
-
totalCount:
|
|
443
|
-
onLoadMore:
|
|
444
|
-
loadingMore:
|
|
442
|
+
onSearchChange: Ve,
|
|
443
|
+
hasMore: He,
|
|
444
|
+
totalCount: Ue,
|
|
445
|
+
onLoadMore: L,
|
|
446
|
+
loadingMore: We
|
|
445
447
|
}),
|
|
446
|
-
/* @__PURE__ */
|
|
448
|
+
/* @__PURE__ */ x("p", {
|
|
447
449
|
className: "mt-2 text-xs text-text-secondary",
|
|
448
450
|
role: "status",
|
|
449
451
|
children: F ? "Loading planned routes…" : I ? "Saved routes are unavailable; enter pickup and delivery addresses manually." : "Manual values always remain authoritative when a route is selected."
|
|
450
452
|
})
|
|
451
453
|
]
|
|
452
454
|
}),
|
|
453
|
-
/* @__PURE__ */
|
|
454
|
-
/* @__PURE__ */
|
|
455
|
+
/* @__PURE__ */ x(me, {}),
|
|
456
|
+
/* @__PURE__ */ S("div", {
|
|
455
457
|
className: "grid grid-cols-1 gap-4 lg:grid-cols-[minmax(0,1fr)_auto_minmax(0,1fr)] lg:items-stretch",
|
|
456
458
|
children: [
|
|
457
|
-
/* @__PURE__ */
|
|
459
|
+
/* @__PURE__ */ S(u, {
|
|
458
460
|
className: "border-border-seam shadow-none",
|
|
459
|
-
children: [/* @__PURE__ */
|
|
461
|
+
children: [/* @__PURE__ */ S(p, { children: [/* @__PURE__ */ S(m, {
|
|
460
462
|
className: "flex items-center gap-2 text-base",
|
|
461
463
|
children: [
|
|
462
|
-
/* @__PURE__ */
|
|
464
|
+
/* @__PURE__ */ x(r, {
|
|
463
465
|
size: 17,
|
|
464
466
|
className: "text-text-secondary",
|
|
465
467
|
"aria-hidden": "true"
|
|
466
468
|
}),
|
|
467
469
|
"Pickup address ",
|
|
468
|
-
/* @__PURE__ */
|
|
470
|
+
/* @__PURE__ */ x("span", {
|
|
469
471
|
className: "text-status-error-text",
|
|
470
472
|
children: "*"
|
|
471
473
|
})
|
|
472
474
|
]
|
|
473
|
-
}), /* @__PURE__ */
|
|
475
|
+
}), /* @__PURE__ */ x(f, { children: "Where the shipment is collected." })] }), /* @__PURE__ */ x(d, {
|
|
474
476
|
className: "space-y-4",
|
|
475
|
-
children: /* @__PURE__ */
|
|
477
|
+
children: /* @__PURE__ */ S("fieldset", {
|
|
476
478
|
className: "space-y-4",
|
|
477
479
|
children: [
|
|
478
|
-
/* @__PURE__ */
|
|
480
|
+
/* @__PURE__ */ x("legend", {
|
|
479
481
|
className: "sr-only",
|
|
480
482
|
children: "Pickup address details"
|
|
481
483
|
}),
|
|
482
|
-
/* @__PURE__ */
|
|
484
|
+
/* @__PURE__ */ S("div", {
|
|
483
485
|
className: "space-y-2",
|
|
484
|
-
children: [/* @__PURE__ */
|
|
486
|
+
children: [/* @__PURE__ */ S(g, {
|
|
485
487
|
htmlFor: "pickupStreet",
|
|
486
|
-
children: ["Street address ", /* @__PURE__ */
|
|
488
|
+
children: ["Street address ", /* @__PURE__ */ x("span", {
|
|
487
489
|
className: "text-status-error-text",
|
|
488
490
|
children: "*"
|
|
489
491
|
})]
|
|
490
|
-
}), /* @__PURE__ */
|
|
492
|
+
}), /* @__PURE__ */ x(h, {
|
|
491
493
|
id: "pickupStreet",
|
|
492
494
|
value: H.pickupAddress.street,
|
|
493
495
|
onChange: (e) => Q("pickupAddress", "street", e.target.value),
|
|
@@ -495,25 +497,25 @@ function w() {
|
|
|
495
497
|
"aria-required": "true"
|
|
496
498
|
})]
|
|
497
499
|
}),
|
|
498
|
-
/* @__PURE__ */
|
|
500
|
+
/* @__PURE__ */ S("div", {
|
|
499
501
|
className: "grid grid-cols-1 sm:grid-cols-2 gap-4",
|
|
500
|
-
children: [/* @__PURE__ */
|
|
502
|
+
children: [/* @__PURE__ */ S("div", {
|
|
501
503
|
className: "space-y-2",
|
|
502
|
-
children: [/* @__PURE__ */
|
|
504
|
+
children: [/* @__PURE__ */ x(g, {
|
|
503
505
|
htmlFor: "pickupCity",
|
|
504
506
|
children: "City"
|
|
505
|
-
}), /* @__PURE__ */
|
|
507
|
+
}), /* @__PURE__ */ x(h, {
|
|
506
508
|
id: "pickupCity",
|
|
507
509
|
value: H.pickupAddress.city,
|
|
508
510
|
onChange: (e) => Q("pickupAddress", "city", e.target.value),
|
|
509
511
|
placeholder: "San Francisco"
|
|
510
512
|
})]
|
|
511
|
-
}), /* @__PURE__ */
|
|
513
|
+
}), /* @__PURE__ */ S("div", {
|
|
512
514
|
className: "space-y-2",
|
|
513
|
-
children: [/* @__PURE__ */
|
|
515
|
+
children: [/* @__PURE__ */ x(g, {
|
|
514
516
|
htmlFor: "pickupState",
|
|
515
517
|
children: "State"
|
|
516
|
-
}), /* @__PURE__ */
|
|
518
|
+
}), /* @__PURE__ */ x(h, {
|
|
517
519
|
id: "pickupState",
|
|
518
520
|
value: H.pickupAddress.state,
|
|
519
521
|
onChange: (e) => Q("pickupAddress", "state", e.target.value),
|
|
@@ -521,25 +523,25 @@ function w() {
|
|
|
521
523
|
})]
|
|
522
524
|
})]
|
|
523
525
|
}),
|
|
524
|
-
/* @__PURE__ */
|
|
526
|
+
/* @__PURE__ */ S("div", {
|
|
525
527
|
className: "grid grid-cols-1 sm:grid-cols-2 gap-4",
|
|
526
|
-
children: [/* @__PURE__ */
|
|
528
|
+
children: [/* @__PURE__ */ S("div", {
|
|
527
529
|
className: "space-y-2",
|
|
528
|
-
children: [/* @__PURE__ */
|
|
530
|
+
children: [/* @__PURE__ */ x(g, {
|
|
529
531
|
htmlFor: "pickupPostal",
|
|
530
532
|
children: "Postal Code"
|
|
531
|
-
}), /* @__PURE__ */
|
|
533
|
+
}), /* @__PURE__ */ x(h, {
|
|
532
534
|
id: "pickupPostal",
|
|
533
535
|
value: H.pickupAddress.postalCode,
|
|
534
536
|
onChange: (e) => Q("pickupAddress", "postalCode", e.target.value),
|
|
535
537
|
placeholder: "94103"
|
|
536
538
|
})]
|
|
537
|
-
}), /* @__PURE__ */
|
|
539
|
+
}), /* @__PURE__ */ S("div", {
|
|
538
540
|
className: "space-y-2",
|
|
539
|
-
children: [/* @__PURE__ */
|
|
541
|
+
children: [/* @__PURE__ */ x(g, {
|
|
540
542
|
htmlFor: "pickupCountry",
|
|
541
543
|
children: "Country"
|
|
542
|
-
}), /* @__PURE__ */
|
|
544
|
+
}), /* @__PURE__ */ x(h, {
|
|
543
545
|
id: "pickupCountry",
|
|
544
546
|
value: H.pickupAddress.country,
|
|
545
547
|
onChange: (e) => Q("pickupAddress", "country", e.target.value),
|
|
@@ -551,48 +553,48 @@ function w() {
|
|
|
551
553
|
})
|
|
552
554
|
})]
|
|
553
555
|
}),
|
|
554
|
-
/* @__PURE__ */
|
|
556
|
+
/* @__PURE__ */ x("div", {
|
|
555
557
|
className: "hidden items-center justify-center lg:flex",
|
|
556
558
|
"aria-hidden": "true",
|
|
557
|
-
children: /* @__PURE__ */
|
|
559
|
+
children: /* @__PURE__ */ x(t, {
|
|
558
560
|
size: 18,
|
|
559
561
|
className: "text-text-muted"
|
|
560
562
|
})
|
|
561
563
|
}),
|
|
562
|
-
/* @__PURE__ */
|
|
564
|
+
/* @__PURE__ */ S(u, {
|
|
563
565
|
className: "border-border-seam shadow-none",
|
|
564
|
-
children: [/* @__PURE__ */
|
|
566
|
+
children: [/* @__PURE__ */ S(p, { children: [/* @__PURE__ */ S(m, {
|
|
565
567
|
className: "flex items-center gap-2 text-base",
|
|
566
568
|
children: [
|
|
567
|
-
/* @__PURE__ */
|
|
569
|
+
/* @__PURE__ */ x(r, {
|
|
568
570
|
size: 17,
|
|
569
571
|
className: "text-text-secondary",
|
|
570
572
|
"aria-hidden": "true"
|
|
571
573
|
}),
|
|
572
574
|
"Delivery address ",
|
|
573
|
-
/* @__PURE__ */
|
|
575
|
+
/* @__PURE__ */ x("span", {
|
|
574
576
|
className: "text-status-error-text",
|
|
575
577
|
children: "*"
|
|
576
578
|
})
|
|
577
579
|
]
|
|
578
|
-
}), /* @__PURE__ */
|
|
580
|
+
}), /* @__PURE__ */ x(f, { children: "Where the shipment is delivered." })] }), /* @__PURE__ */ x(d, {
|
|
579
581
|
className: "space-y-4",
|
|
580
|
-
children: /* @__PURE__ */
|
|
582
|
+
children: /* @__PURE__ */ S("fieldset", {
|
|
581
583
|
className: "space-y-4",
|
|
582
584
|
children: [
|
|
583
|
-
/* @__PURE__ */
|
|
585
|
+
/* @__PURE__ */ x("legend", {
|
|
584
586
|
className: "sr-only",
|
|
585
587
|
children: "Delivery address details"
|
|
586
588
|
}),
|
|
587
|
-
/* @__PURE__ */
|
|
589
|
+
/* @__PURE__ */ S("div", {
|
|
588
590
|
className: "space-y-2",
|
|
589
|
-
children: [/* @__PURE__ */
|
|
591
|
+
children: [/* @__PURE__ */ S(g, {
|
|
590
592
|
htmlFor: "deliveryStreet",
|
|
591
|
-
children: ["Street address ", /* @__PURE__ */
|
|
593
|
+
children: ["Street address ", /* @__PURE__ */ x("span", {
|
|
592
594
|
className: "text-status-error-text",
|
|
593
595
|
children: "*"
|
|
594
596
|
})]
|
|
595
|
-
}), /* @__PURE__ */
|
|
597
|
+
}), /* @__PURE__ */ x(h, {
|
|
596
598
|
id: "deliveryStreet",
|
|
597
599
|
value: H.deliveryAddress.street,
|
|
598
600
|
onChange: (e) => Q("deliveryAddress", "street", e.target.value),
|
|
@@ -600,25 +602,25 @@ function w() {
|
|
|
600
602
|
"aria-required": "true"
|
|
601
603
|
})]
|
|
602
604
|
}),
|
|
603
|
-
/* @__PURE__ */
|
|
605
|
+
/* @__PURE__ */ S("div", {
|
|
604
606
|
className: "grid grid-cols-1 gap-4 sm:grid-cols-2",
|
|
605
|
-
children: [/* @__PURE__ */
|
|
607
|
+
children: [/* @__PURE__ */ S("div", {
|
|
606
608
|
className: "space-y-2",
|
|
607
|
-
children: [/* @__PURE__ */
|
|
609
|
+
children: [/* @__PURE__ */ x(g, {
|
|
608
610
|
htmlFor: "deliveryCity",
|
|
609
611
|
children: "City"
|
|
610
|
-
}), /* @__PURE__ */
|
|
612
|
+
}), /* @__PURE__ */ x(h, {
|
|
611
613
|
id: "deliveryCity",
|
|
612
614
|
value: H.deliveryAddress.city,
|
|
613
615
|
onChange: (e) => Q("deliveryAddress", "city", e.target.value),
|
|
614
616
|
placeholder: "Los Angeles"
|
|
615
617
|
})]
|
|
616
|
-
}), /* @__PURE__ */
|
|
618
|
+
}), /* @__PURE__ */ S("div", {
|
|
617
619
|
className: "space-y-2",
|
|
618
|
-
children: [/* @__PURE__ */
|
|
620
|
+
children: [/* @__PURE__ */ x(g, {
|
|
619
621
|
htmlFor: "deliveryState",
|
|
620
622
|
children: "State"
|
|
621
|
-
}), /* @__PURE__ */
|
|
623
|
+
}), /* @__PURE__ */ x(h, {
|
|
622
624
|
id: "deliveryState",
|
|
623
625
|
value: H.deliveryAddress.state,
|
|
624
626
|
onChange: (e) => Q("deliveryAddress", "state", e.target.value),
|
|
@@ -626,25 +628,25 @@ function w() {
|
|
|
626
628
|
})]
|
|
627
629
|
})]
|
|
628
630
|
}),
|
|
629
|
-
/* @__PURE__ */
|
|
631
|
+
/* @__PURE__ */ S("div", {
|
|
630
632
|
className: "grid grid-cols-1 gap-4 sm:grid-cols-2",
|
|
631
|
-
children: [/* @__PURE__ */
|
|
633
|
+
children: [/* @__PURE__ */ S("div", {
|
|
632
634
|
className: "space-y-2",
|
|
633
|
-
children: [/* @__PURE__ */
|
|
635
|
+
children: [/* @__PURE__ */ x(g, {
|
|
634
636
|
htmlFor: "deliveryPostal",
|
|
635
637
|
children: "Postal code"
|
|
636
|
-
}), /* @__PURE__ */
|
|
638
|
+
}), /* @__PURE__ */ x(h, {
|
|
637
639
|
id: "deliveryPostal",
|
|
638
640
|
value: H.deliveryAddress.postalCode,
|
|
639
641
|
onChange: (e) => Q("deliveryAddress", "postalCode", e.target.value),
|
|
640
642
|
placeholder: "90210"
|
|
641
643
|
})]
|
|
642
|
-
}), /* @__PURE__ */
|
|
644
|
+
}), /* @__PURE__ */ S("div", {
|
|
643
645
|
className: "space-y-2",
|
|
644
|
-
children: [/* @__PURE__ */
|
|
646
|
+
children: [/* @__PURE__ */ x(g, {
|
|
645
647
|
htmlFor: "deliveryCountry",
|
|
646
648
|
children: "Country"
|
|
647
|
-
}), /* @__PURE__ */
|
|
649
|
+
}), /* @__PURE__ */ x(h, {
|
|
648
650
|
id: "deliveryCountry",
|
|
649
651
|
value: H.deliveryAddress.country,
|
|
650
652
|
onChange: (e) => Q("deliveryAddress", "country", e.target.value),
|
|
@@ -661,141 +663,141 @@ function w() {
|
|
|
661
663
|
]
|
|
662
664
|
})
|
|
663
665
|
}),
|
|
664
|
-
/* @__PURE__ */
|
|
666
|
+
/* @__PURE__ */ x("div", {
|
|
665
667
|
id: "items-section",
|
|
666
|
-
children: /* @__PURE__ */
|
|
667
|
-
title: /* @__PURE__ */
|
|
668
|
+
children: /* @__PURE__ */ x(C, {
|
|
669
|
+
title: /* @__PURE__ */ S("span", {
|
|
668
670
|
className: "flex items-center gap-2",
|
|
669
671
|
children: [
|
|
670
|
-
/* @__PURE__ */
|
|
672
|
+
/* @__PURE__ */ x(i, {
|
|
671
673
|
size: 18,
|
|
672
674
|
"aria-hidden": "true"
|
|
673
675
|
}),
|
|
674
676
|
"Shipment contents ",
|
|
675
|
-
/* @__PURE__ */
|
|
677
|
+
/* @__PURE__ */ x(c, {
|
|
676
678
|
variant: "secondary",
|
|
677
679
|
children: H.items.length
|
|
678
680
|
})
|
|
679
681
|
]
|
|
680
682
|
}),
|
|
681
683
|
description: "Add each item being shipped. Use the catalog to prefill details or enter a one-off item manually.",
|
|
682
|
-
actions: /* @__PURE__ */
|
|
684
|
+
actions: /* @__PURE__ */ S(l, {
|
|
683
685
|
type: "button",
|
|
684
686
|
variant: "outline",
|
|
685
687
|
size: "sm",
|
|
686
688
|
onClick: $,
|
|
687
|
-
children: [/* @__PURE__ */
|
|
689
|
+
children: [/* @__PURE__ */ x(a, { size: 16 }), "Add item"]
|
|
688
690
|
}),
|
|
689
|
-
children: H.items.length === 0 ? /* @__PURE__ */
|
|
691
|
+
children: H.items.length === 0 ? /* @__PURE__ */ x(ge, {
|
|
690
692
|
illustration: "empty-files",
|
|
691
693
|
title: "No items added yet",
|
|
692
694
|
description: "Add the products this order will ship — each with a name, SKU, quantity and value.",
|
|
693
|
-
action: /* @__PURE__ */
|
|
695
|
+
action: /* @__PURE__ */ S(l, {
|
|
694
696
|
type: "button",
|
|
695
697
|
variant: "outline",
|
|
696
698
|
onClick: $,
|
|
697
|
-
children: [/* @__PURE__ */
|
|
699
|
+
children: [/* @__PURE__ */ x(a, {
|
|
698
700
|
size: 16,
|
|
699
701
|
className: "mr-1.5"
|
|
700
702
|
}), "Add your first item"]
|
|
701
703
|
})
|
|
702
|
-
}) : /* @__PURE__ */
|
|
704
|
+
}) : /* @__PURE__ */ S("div", {
|
|
703
705
|
className: "space-y-4",
|
|
704
|
-
children: [/* @__PURE__ */
|
|
706
|
+
children: [/* @__PURE__ */ S("div", {
|
|
705
707
|
className: "rounded-card border border-border-subtle bg-bg-sunken/40 p-4",
|
|
706
708
|
children: [
|
|
707
|
-
/* @__PURE__ */
|
|
709
|
+
/* @__PURE__ */ x(g, {
|
|
708
710
|
htmlFor: "item-product-search",
|
|
709
711
|
children: "Search product catalog"
|
|
710
712
|
}),
|
|
711
|
-
/* @__PURE__ */
|
|
713
|
+
/* @__PURE__ */ x(h, {
|
|
712
714
|
id: "item-product-search",
|
|
713
715
|
value: R,
|
|
714
|
-
onChange: (e) =>
|
|
716
|
+
onChange: (e) => Ge(e.target.value),
|
|
715
717
|
placeholder: "Search by name or SKU",
|
|
716
718
|
className: "mt-2"
|
|
717
719
|
}),
|
|
718
|
-
/* @__PURE__ */
|
|
720
|
+
/* @__PURE__ */ x("p", {
|
|
719
721
|
className: "mt-2 text-xs text-text-secondary",
|
|
720
722
|
children: "This filters the catalog picker for every item below. You can still edit any autofilled value or enter item details manually."
|
|
721
723
|
})
|
|
722
724
|
]
|
|
723
|
-
}), /* @__PURE__ */
|
|
725
|
+
}), /* @__PURE__ */ x("ol", {
|
|
724
726
|
className: "space-y-4",
|
|
725
727
|
"aria-label": "Shipment items",
|
|
726
|
-
children: H.items.map((e, t) => /* @__PURE__ */
|
|
728
|
+
children: H.items.map((e, t) => /* @__PURE__ */ x("li", { children: /* @__PURE__ */ x(be, {
|
|
727
729
|
item: e,
|
|
728
730
|
index: t,
|
|
729
|
-
products:
|
|
730
|
-
productsLoading:
|
|
731
|
-
productsLoadingMore:
|
|
732
|
-
productsHasMore:
|
|
733
|
-
productsTotalCount:
|
|
734
|
-
onLoadMoreProducts:
|
|
731
|
+
products: Ke,
|
|
732
|
+
productsLoading: qe,
|
|
733
|
+
productsLoadingMore: Je,
|
|
734
|
+
productsHasMore: Ye,
|
|
735
|
+
productsTotalCount: Xe,
|
|
736
|
+
onLoadMoreProducts: Ze,
|
|
735
737
|
errors: q[e.id],
|
|
736
|
-
onUpdate:
|
|
737
|
-
onRemove:
|
|
738
|
+
onUpdate: at,
|
|
739
|
+
onRemove: it
|
|
738
740
|
}) }, e.id))
|
|
739
741
|
})]
|
|
740
742
|
})
|
|
741
743
|
})
|
|
742
744
|
}),
|
|
743
|
-
/* @__PURE__ */
|
|
744
|
-
title: /* @__PURE__ */
|
|
745
|
+
/* @__PURE__ */ x(C, {
|
|
746
|
+
title: /* @__PURE__ */ S("span", {
|
|
745
747
|
className: "flex items-center gap-2",
|
|
746
|
-
children: [/* @__PURE__ */
|
|
748
|
+
children: [/* @__PURE__ */ x(n, {
|
|
747
749
|
size: 18,
|
|
748
750
|
"aria-hidden": "true"
|
|
749
751
|
}), "Timing & handling"]
|
|
750
752
|
}),
|
|
751
753
|
description: "Optional delivery timing and instructions for the carrier.",
|
|
752
|
-
children: /* @__PURE__ */
|
|
754
|
+
children: /* @__PURE__ */ S("div", {
|
|
753
755
|
className: "grid grid-cols-1 gap-6 lg:grid-cols-3",
|
|
754
756
|
children: [
|
|
755
|
-
/* @__PURE__ */
|
|
757
|
+
/* @__PURE__ */ S("div", {
|
|
756
758
|
className: "space-y-2",
|
|
757
759
|
children: [
|
|
758
|
-
/* @__PURE__ */
|
|
760
|
+
/* @__PURE__ */ x(g, {
|
|
759
761
|
htmlFor: "trackingNumber",
|
|
760
762
|
children: "Tracking / reference number"
|
|
761
763
|
}),
|
|
762
|
-
/* @__PURE__ */
|
|
764
|
+
/* @__PURE__ */ x(h, {
|
|
763
765
|
id: "trackingNumber",
|
|
764
766
|
value: H.trackingNumber,
|
|
765
767
|
onChange: (e) => Z("trackingNumber", e.target.value),
|
|
766
768
|
placeholder: "Optional carrier reference"
|
|
767
769
|
}),
|
|
768
|
-
/* @__PURE__ */
|
|
770
|
+
/* @__PURE__ */ x("p", {
|
|
769
771
|
className: "text-xs text-text-secondary",
|
|
770
772
|
children: "Use this when a carrier or customer already supplied a tracking reference."
|
|
771
773
|
})
|
|
772
774
|
]
|
|
773
775
|
}),
|
|
774
|
-
/* @__PURE__ */
|
|
776
|
+
/* @__PURE__ */ S("div", {
|
|
775
777
|
className: "space-y-2",
|
|
776
778
|
children: [
|
|
777
|
-
/* @__PURE__ */
|
|
779
|
+
/* @__PURE__ */ x(g, {
|
|
778
780
|
htmlFor: "scheduledDelivery",
|
|
779
781
|
children: "Scheduled delivery"
|
|
780
782
|
}),
|
|
781
|
-
/* @__PURE__ */
|
|
783
|
+
/* @__PURE__ */ x(h, {
|
|
782
784
|
id: "scheduledDelivery",
|
|
783
785
|
type: "datetime-local",
|
|
784
786
|
value: H.scheduledDelivery,
|
|
785
787
|
onChange: (e) => Z("scheduledDelivery", e.target.value)
|
|
786
788
|
}),
|
|
787
|
-
/* @__PURE__ */
|
|
789
|
+
/* @__PURE__ */ x("p", {
|
|
788
790
|
className: "text-xs text-text-secondary",
|
|
789
791
|
children: "Leave blank to dispatch as soon as a driver is available."
|
|
790
792
|
})
|
|
791
793
|
]
|
|
792
794
|
}),
|
|
793
|
-
/* @__PURE__ */
|
|
795
|
+
/* @__PURE__ */ S("div", {
|
|
794
796
|
className: "space-y-2 lg:col-span-1",
|
|
795
|
-
children: [/* @__PURE__ */
|
|
797
|
+
children: [/* @__PURE__ */ x(g, {
|
|
796
798
|
htmlFor: "specialInstructions",
|
|
797
799
|
children: "Special instructions"
|
|
798
|
-
}), /* @__PURE__ */
|
|
800
|
+
}), /* @__PURE__ */ x(he, {
|
|
799
801
|
id: "specialInstructions",
|
|
800
802
|
value: H.specialInstructions,
|
|
801
803
|
onChange: (e) => Z("specialInstructions", e.target.value),
|
|
@@ -812,4 +814,4 @@ function w() {
|
|
|
812
814
|
});
|
|
813
815
|
}
|
|
814
816
|
//#endregion
|
|
815
|
-
export {
|
|
817
|
+
export { T as default };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
//#region src/movethewheels/utils/orderPricing.ts
|
|
2
|
+
var e = (e) => Math.round(e * 100) / 100, t = (e) => typeof e == "number" && Number.isFinite(e) && e >= 0 ? e : 0;
|
|
3
|
+
function n(n, r) {
|
|
4
|
+
let i = e(n.reduce((e, n) => e + t(n.quantity) * t(n.value), 0)), a = t(r?.taxes), o = t(r?.fees?.delivery), s = t(r?.fees?.handling), c = t(r?.fees?.insurance);
|
|
5
|
+
return {
|
|
6
|
+
subtotal: i,
|
|
7
|
+
taxes: a,
|
|
8
|
+
fees: {
|
|
9
|
+
delivery: o,
|
|
10
|
+
handling: s,
|
|
11
|
+
...c > 0 ? { insurance: c } : {}
|
|
12
|
+
},
|
|
13
|
+
total: e(i + a + o + s + c),
|
|
14
|
+
currency: r?.currency || "USD",
|
|
15
|
+
paymentMethod: r?.paymentMethod || "unknown",
|
|
16
|
+
paymentStatus: r?.paymentStatus || "pending"
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
//#endregion
|
|
20
|
+
export { n as calculateOrderPricing };
|
package/package.json
CHANGED
|
@@ -49,6 +49,7 @@ import { EntityCombobox } from '../components/EntityCombobox';
|
|
|
49
49
|
import { OrderItemRow } from '../components/OrderItemRow';
|
|
50
50
|
import type { Customer } from '../types';
|
|
51
51
|
import { validateOrderItems, type OrderItemFieldErrors } from '../utils/orderValidation';
|
|
52
|
+
import { calculateOrderPricing } from '../utils/orderPricing';
|
|
52
53
|
|
|
53
54
|
/**
|
|
54
55
|
* `<input type="datetime-local">` needs a `YYYY-MM-DDTHH:mm` string in LOCAL
|
|
@@ -272,6 +273,7 @@ export default function CreateOrderPage() {
|
|
|
272
273
|
pickupAddress: formData.pickupAddress,
|
|
273
274
|
deliveryAddress: formData.deliveryAddress,
|
|
274
275
|
items: formData.items,
|
|
276
|
+
pricing: calculateOrderPricing(formData.items, isEditing ? order?.pricing : undefined),
|
|
275
277
|
// `null` clears an existing slot on edit; the service field is nullable.
|
|
276
278
|
trackingNumber: formData.trackingNumber.trim() || undefined,
|
|
277
279
|
scheduledDelivery: formData.scheduledDelivery ? new Date(formData.scheduledDelivery) : null,
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { describe, expect, it } from 'vitest';
|
|
2
|
+
import { calculateOrderPricing } from './orderPricing';
|
|
3
|
+
import type { OrderItem } from '../types';
|
|
4
|
+
|
|
5
|
+
const item = (quantity: number, value: number): OrderItem => ({
|
|
6
|
+
id: `${quantity}-${value}`,
|
|
7
|
+
productId: '',
|
|
8
|
+
name: 'Declared cargo',
|
|
9
|
+
quantity,
|
|
10
|
+
weight: 1,
|
|
11
|
+
dimensions: { length: 1, width: 1, height: 1, unit: 'cm' },
|
|
12
|
+
value,
|
|
13
|
+
sku: 'CARGO-1',
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
describe('calculateOrderPricing', () => {
|
|
17
|
+
it('calculates subtotal as quantity multiplied by each item value', () => {
|
|
18
|
+
expect(calculateOrderPricing([item(2, 12.5), item(3, 0.1)])).toEqual({
|
|
19
|
+
subtotal: 25.3,
|
|
20
|
+
taxes: 0,
|
|
21
|
+
fees: { delivery: 0, handling: 0 },
|
|
22
|
+
total: 25.3,
|
|
23
|
+
currency: 'USD',
|
|
24
|
+
paymentMethod: 'unknown',
|
|
25
|
+
paymentStatus: 'pending',
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('preserves existing supported charges during edit without inventing rates', () => {
|
|
30
|
+
expect(
|
|
31
|
+
calculateOrderPricing([item(2, 10)], {
|
|
32
|
+
taxes: 1.25,
|
|
33
|
+
fees: { delivery: 4, handling: 2, insurance: 0.5 },
|
|
34
|
+
currency: 'EUR',
|
|
35
|
+
paymentMethod: 'invoice',
|
|
36
|
+
paymentStatus: 'paid',
|
|
37
|
+
})
|
|
38
|
+
).toEqual({
|
|
39
|
+
subtotal: 20,
|
|
40
|
+
taxes: 1.25,
|
|
41
|
+
fees: { delivery: 4, handling: 2, insurance: 0.5 },
|
|
42
|
+
total: 27.75,
|
|
43
|
+
currency: 'EUR',
|
|
44
|
+
paymentMethod: 'invoice',
|
|
45
|
+
paymentStatus: 'paid',
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
});
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { OrderItem, OrderPricing } from '../types';
|
|
2
|
+
|
|
3
|
+
const roundCurrency = (amount: number): number => Math.round(amount * 100) / 100;
|
|
4
|
+
|
|
5
|
+
const nonNegativeFinite = (value: unknown): number =>
|
|
6
|
+
typeof value === 'number' && Number.isFinite(value) && value >= 0 ? value : 0;
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Build the pricing payload used by delivery-job create/update forms.
|
|
10
|
+
* Item `value` is the per-unit declared value, so quantity is applied here.
|
|
11
|
+
* Delivery, handling, taxes, and insurance remain zero unless an existing
|
|
12
|
+
* supported value is being preserved during an edit; this form has no rate
|
|
13
|
+
* source and must not invent one.
|
|
14
|
+
*/
|
|
15
|
+
export function calculateOrderPricing(
|
|
16
|
+
items: OrderItem[],
|
|
17
|
+
existing?: Partial<OrderPricing> | null
|
|
18
|
+
): OrderPricing {
|
|
19
|
+
const subtotal = roundCurrency(
|
|
20
|
+
items.reduce(
|
|
21
|
+
(sum, item) => sum + nonNegativeFinite(item.quantity) * nonNegativeFinite(item.value),
|
|
22
|
+
0
|
|
23
|
+
)
|
|
24
|
+
);
|
|
25
|
+
const taxes = nonNegativeFinite(existing?.taxes);
|
|
26
|
+
const delivery = nonNegativeFinite(existing?.fees?.delivery);
|
|
27
|
+
const handling = nonNegativeFinite(existing?.fees?.handling);
|
|
28
|
+
const insurance = nonNegativeFinite(existing?.fees?.insurance);
|
|
29
|
+
const fees = { delivery, handling, ...(insurance > 0 ? { insurance } : {}) };
|
|
30
|
+
|
|
31
|
+
return {
|
|
32
|
+
subtotal,
|
|
33
|
+
taxes,
|
|
34
|
+
fees,
|
|
35
|
+
total: roundCurrency(subtotal + taxes + delivery + handling + insurance),
|
|
36
|
+
currency: existing?.currency || 'USD',
|
|
37
|
+
paymentMethod: existing?.paymentMethod || 'unknown',
|
|
38
|
+
paymentStatus: existing?.paymentStatus || 'pending',
|
|
39
|
+
};
|
|
40
|
+
}
|