@burdenoff/microfe-movethewheels 2026.930.2 → 2026.930.4
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/components/StockActionDialogs.js +385 -0
- package/dist/hooks/useFleet.js +112 -109
- package/dist/hooks/useInventory.js +248 -124
- package/dist/index.js +34 -34
- package/dist/movethewheels/components/StockActionDialogs.d.ts +24 -0
- package/dist/movethewheels/hooks/index.d.ts +1 -1
- package/dist/movethewheels/hooks/useFleet.d.ts +1 -0
- package/dist/movethewheels/hooks/useInventory.d.ts +30 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/package-plus.js +33 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/sliders-horizontal.js +69 -0
- package/dist/pages/DriverDetailsPage.js +401 -347
- package/dist/pages/InventoryPage.js +279 -200
- package/dist/pages/RouteDetailsPage.js +4 -4
- package/package.json +1 -1
- package/src/movethewheels/components/StockActionDialogs.tsx +512 -0
- package/src/movethewheels/hooks/index.ts +3 -0
- package/src/movethewheels/hooks/useFleet.ts +18 -0
- package/src/movethewheels/hooks/useInventory.ts +294 -35
- package/src/movethewheels/pages/DriverDetailsPage.tsx +72 -1
- package/src/movethewheels/pages/InventoryPage.tsx +98 -1
- package/src/movethewheels/pages/RouteDetailsPage.tsx +5 -4
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
import { useProduct as e, useProducts as t, useStockActions as n, useWarehouse as r, useWarehouses as i } from "../hooks/useInventory.js";
|
|
2
|
+
import { Button as a, Input as o, Label as s } from "./ui.js";
|
|
3
|
+
import { FormError as c } from "./FormError.js";
|
|
4
|
+
import { EntityCombobox as l } from "./EntityCombobox.js";
|
|
5
|
+
import { useEffect as u, useState as d } from "react";
|
|
6
|
+
import { Fragment as f, jsx as p, jsxs as m } from "react/jsx-runtime";
|
|
7
|
+
import { ResponsiveDialog as h, ResponsiveDialogContent as g, ResponsiveDialogDescription as _, ResponsiveDialogFooter as v, ResponsiveDialogHeader as y, ResponsiveDialogTitle as b } from "@burdenoff/fe-libs/ui";
|
|
8
|
+
//#region src/movethewheels/components/StockActionDialogs.tsx
|
|
9
|
+
function x(e) {
|
|
10
|
+
let t = e.trim();
|
|
11
|
+
if (!/^[+-]?\d+$/.test(t)) return null;
|
|
12
|
+
let n = Number(t);
|
|
13
|
+
return Number.isSafeInteger(n) ? n : null;
|
|
14
|
+
}
|
|
15
|
+
function S(e, t) {
|
|
16
|
+
return e instanceof Error && e.message ? e.message : t;
|
|
17
|
+
}
|
|
18
|
+
function C({ id: n, value: r, onChange: i, error: a }) {
|
|
19
|
+
let [o, s] = d(""), { products: c, totalProducts: u, hasMore: h, isLoading: g, isLoadingMore: _, loadMore: v } = t({
|
|
20
|
+
search: o,
|
|
21
|
+
limit: 20
|
|
22
|
+
}), { product: y, isLoading: b } = e(r || void 0);
|
|
23
|
+
return /* @__PURE__ */ p(l, {
|
|
24
|
+
id: n,
|
|
25
|
+
value: r,
|
|
26
|
+
onChange: i,
|
|
27
|
+
options: c,
|
|
28
|
+
loading: g,
|
|
29
|
+
required: !0,
|
|
30
|
+
getId: (e) => e.id,
|
|
31
|
+
getLabel: (e) => `${e.name} (${e.sku})`,
|
|
32
|
+
renderOption: (e) => /* @__PURE__ */ m(f, { children: [
|
|
33
|
+
e.name,
|
|
34
|
+
" ",
|
|
35
|
+
/* @__PURE__ */ m("span", {
|
|
36
|
+
className: "text-text-tertiary",
|
|
37
|
+
children: [
|
|
38
|
+
"(",
|
|
39
|
+
e.sku,
|
|
40
|
+
")"
|
|
41
|
+
]
|
|
42
|
+
})
|
|
43
|
+
] }),
|
|
44
|
+
selectedOption: y,
|
|
45
|
+
selectedLoading: b,
|
|
46
|
+
placeholder: "Search your product catalog",
|
|
47
|
+
searchPlaceholder: "Search by name or SKU…",
|
|
48
|
+
emptyText: "No products found.",
|
|
49
|
+
onSearchChange: s,
|
|
50
|
+
hasMore: h,
|
|
51
|
+
totalCount: u,
|
|
52
|
+
onLoadMore: v,
|
|
53
|
+
loadingMore: _,
|
|
54
|
+
error: a
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
function w({ id: e, value: t, onChange: n, excludeId: a, error: o }) {
|
|
58
|
+
let [s, c] = d(""), { warehouses: u, totalWarehouses: h, hasMore: g, isLoading: _, isLoadingMore: v, loadMore: y } = i({
|
|
59
|
+
search: s,
|
|
60
|
+
limit: 20
|
|
61
|
+
}), { warehouse: b, isLoading: x } = r(t || void 0);
|
|
62
|
+
return /* @__PURE__ */ p(l, {
|
|
63
|
+
id: e,
|
|
64
|
+
value: t,
|
|
65
|
+
onChange: n,
|
|
66
|
+
options: a ? u.filter((e) => e.id !== a) : u,
|
|
67
|
+
loading: _,
|
|
68
|
+
required: !0,
|
|
69
|
+
getId: (e) => e.id,
|
|
70
|
+
getLabel: (e) => `${e.name} (${e.code})`,
|
|
71
|
+
renderOption: (e) => /* @__PURE__ */ m(f, { children: [
|
|
72
|
+
e.name,
|
|
73
|
+
" ",
|
|
74
|
+
/* @__PURE__ */ m("span", {
|
|
75
|
+
className: "text-text-tertiary",
|
|
76
|
+
children: [
|
|
77
|
+
"(",
|
|
78
|
+
e.code,
|
|
79
|
+
")"
|
|
80
|
+
]
|
|
81
|
+
})
|
|
82
|
+
] }),
|
|
83
|
+
selectedOption: b,
|
|
84
|
+
selectedLoading: x,
|
|
85
|
+
placeholder: "Search warehouses",
|
|
86
|
+
searchPlaceholder: "Search by name or code…",
|
|
87
|
+
emptyText: "No warehouses found.",
|
|
88
|
+
onSearchChange: c,
|
|
89
|
+
hasMore: g,
|
|
90
|
+
totalCount: h,
|
|
91
|
+
onLoadMore: y,
|
|
92
|
+
loadingMore: v,
|
|
93
|
+
error: o
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
function T({ submitting: e, submitLabel: t, submittingLabel: n, onCancel: r, onSubmit: i }) {
|
|
97
|
+
return /* @__PURE__ */ m(v, { children: [/* @__PURE__ */ p(a, {
|
|
98
|
+
variant: "outline",
|
|
99
|
+
onClick: r,
|
|
100
|
+
disabled: e,
|
|
101
|
+
children: "Cancel"
|
|
102
|
+
}), /* @__PURE__ */ p(a, {
|
|
103
|
+
onClick: i,
|
|
104
|
+
disabled: e,
|
|
105
|
+
children: e ? n : t
|
|
106
|
+
})] });
|
|
107
|
+
}
|
|
108
|
+
function E({ open: e, onOpenChange: t, onDone: r }) {
|
|
109
|
+
let { receiveStock: i } = n(), [a, l] = d(""), [f, v] = d(""), [E, D] = d(""), [O, k] = d(""), [A, j] = d(null), [M, N] = d(!1);
|
|
110
|
+
return u(() => {
|
|
111
|
+
e && (l(""), v(""), D(""), k(""), j(null), N(!1));
|
|
112
|
+
}, [e]), /* @__PURE__ */ p(h, {
|
|
113
|
+
open: e,
|
|
114
|
+
onOpenChange: t,
|
|
115
|
+
children: /* @__PURE__ */ m(g, { children: [
|
|
116
|
+
/* @__PURE__ */ m(y, { children: [/* @__PURE__ */ p(b, { children: "Receive stock" }), /* @__PURE__ */ p(_, { children: "Add units of a product to a warehouse. If the warehouse already stocks it, the units are added to that line; otherwise a new stock line is created." })] }),
|
|
117
|
+
/* @__PURE__ */ m("div", {
|
|
118
|
+
className: "space-y-4 px-1",
|
|
119
|
+
children: [
|
|
120
|
+
/* @__PURE__ */ m("div", {
|
|
121
|
+
className: "space-y-2",
|
|
122
|
+
children: [/* @__PURE__ */ p(s, {
|
|
123
|
+
htmlFor: "receive-product",
|
|
124
|
+
children: "Product *"
|
|
125
|
+
}), /* @__PURE__ */ p(C, {
|
|
126
|
+
id: "receive-product",
|
|
127
|
+
value: a,
|
|
128
|
+
onChange: l
|
|
129
|
+
})]
|
|
130
|
+
}),
|
|
131
|
+
/* @__PURE__ */ m("div", {
|
|
132
|
+
className: "space-y-2",
|
|
133
|
+
children: [/* @__PURE__ */ p(s, {
|
|
134
|
+
htmlFor: "receive-warehouse",
|
|
135
|
+
children: "Warehouse *"
|
|
136
|
+
}), /* @__PURE__ */ p(w, {
|
|
137
|
+
id: "receive-warehouse",
|
|
138
|
+
value: f,
|
|
139
|
+
onChange: v
|
|
140
|
+
})]
|
|
141
|
+
}),
|
|
142
|
+
/* @__PURE__ */ m("div", {
|
|
143
|
+
className: "grid grid-cols-1 gap-4 sm:grid-cols-2",
|
|
144
|
+
children: [/* @__PURE__ */ m("div", {
|
|
145
|
+
className: "space-y-2",
|
|
146
|
+
children: [/* @__PURE__ */ p(s, {
|
|
147
|
+
htmlFor: "receive-quantity",
|
|
148
|
+
children: "Quantity received *"
|
|
149
|
+
}), /* @__PURE__ */ p(o, {
|
|
150
|
+
id: "receive-quantity",
|
|
151
|
+
type: "number",
|
|
152
|
+
inputMode: "numeric",
|
|
153
|
+
min: 1,
|
|
154
|
+
step: 1,
|
|
155
|
+
value: E,
|
|
156
|
+
onChange: (e) => D(e.target.value),
|
|
157
|
+
placeholder: "e.g. 50"
|
|
158
|
+
})]
|
|
159
|
+
}), /* @__PURE__ */ m("div", {
|
|
160
|
+
className: "space-y-2",
|
|
161
|
+
children: [
|
|
162
|
+
/* @__PURE__ */ p(s, {
|
|
163
|
+
htmlFor: "receive-reorder",
|
|
164
|
+
children: "Reorder point (optional)"
|
|
165
|
+
}),
|
|
166
|
+
/* @__PURE__ */ p(o, {
|
|
167
|
+
id: "receive-reorder",
|
|
168
|
+
type: "number",
|
|
169
|
+
inputMode: "numeric",
|
|
170
|
+
min: 0,
|
|
171
|
+
step: 1,
|
|
172
|
+
value: O,
|
|
173
|
+
onChange: (e) => k(e.target.value),
|
|
174
|
+
placeholder: "Low-stock threshold"
|
|
175
|
+
}),
|
|
176
|
+
/* @__PURE__ */ p("p", {
|
|
177
|
+
className: "text-xs text-text-secondary",
|
|
178
|
+
children: "Only used when this creates a new stock line."
|
|
179
|
+
})
|
|
180
|
+
]
|
|
181
|
+
})]
|
|
182
|
+
}),
|
|
183
|
+
/* @__PURE__ */ p(c, { message: A })
|
|
184
|
+
]
|
|
185
|
+
}),
|
|
186
|
+
/* @__PURE__ */ p(T, {
|
|
187
|
+
submitting: M,
|
|
188
|
+
submitLabel: "Receive stock",
|
|
189
|
+
submittingLabel: "Receiving…",
|
|
190
|
+
onCancel: () => t(!1),
|
|
191
|
+
onSubmit: async () => {
|
|
192
|
+
let e = x(E), n = O.trim() === "" ? null : x(O);
|
|
193
|
+
if (!a) return j("Choose the product you received.");
|
|
194
|
+
if (!f) return j("Choose the warehouse it was received into.");
|
|
195
|
+
if (e === null || e <= 0) return j("Quantity must be a whole number above zero.");
|
|
196
|
+
if (n !== null && n < 0) return j("Reorder point must be zero or more.");
|
|
197
|
+
if (O.trim() !== "" && n === null) return j("Reorder point must be a whole number.");
|
|
198
|
+
N(!0), j(null);
|
|
199
|
+
try {
|
|
200
|
+
let o = await i({
|
|
201
|
+
productId: a,
|
|
202
|
+
warehouseId: f,
|
|
203
|
+
quantity: e,
|
|
204
|
+
reorderPoint: n
|
|
205
|
+
});
|
|
206
|
+
t(!1), r(`Received ${e.toLocaleString()} unit(s) — ${o.productName ?? "product"} now has ${o.quantity.toLocaleString()} on hand at ${o.warehouseName ?? "the warehouse"}.`);
|
|
207
|
+
} catch (e) {
|
|
208
|
+
j(S(e, "Failed to receive stock."));
|
|
209
|
+
} finally {
|
|
210
|
+
N(!1);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
})
|
|
214
|
+
] })
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
function D({ line: e, open: t, onOpenChange: r, onDone: i }) {
|
|
218
|
+
let { adjustStock: a } = n(), [l, f] = d(""), [v, C] = d(null), [w, E] = d(!1);
|
|
219
|
+
u(() => {
|
|
220
|
+
t && (f(""), C(null), E(!1));
|
|
221
|
+
}, [t, e?.id]);
|
|
222
|
+
let D = x(l), O = e && D !== null && D !== 0 ? {
|
|
223
|
+
onHand: e.onHand + D,
|
|
224
|
+
available: e.available + D
|
|
225
|
+
} : null, k = O !== null && (O.onHand < 0 || O.available < 0);
|
|
226
|
+
return /* @__PURE__ */ p(h, {
|
|
227
|
+
open: t,
|
|
228
|
+
onOpenChange: r,
|
|
229
|
+
children: /* @__PURE__ */ m(g, { children: [
|
|
230
|
+
/* @__PURE__ */ m(y, { children: [/* @__PURE__ */ p(b, { children: "Adjust stock" }), /* @__PURE__ */ p(_, { children: e ? `${e.productName} (${e.sku}) at ${e.warehouseName}. Use a positive number to add units and a negative one to remove them, e.g. after a count or write-off.` : "Choose a stock line to adjust." })] }),
|
|
231
|
+
e && /* @__PURE__ */ m("div", {
|
|
232
|
+
className: "space-y-4 px-1",
|
|
233
|
+
children: [
|
|
234
|
+
/* @__PURE__ */ m("dl", {
|
|
235
|
+
className: "grid grid-cols-2 gap-3 text-sm",
|
|
236
|
+
children: [/* @__PURE__ */ m("div", { children: [/* @__PURE__ */ p("dt", {
|
|
237
|
+
className: "text-text-secondary",
|
|
238
|
+
children: "On hand"
|
|
239
|
+
}), /* @__PURE__ */ p("dd", {
|
|
240
|
+
className: "font-medium",
|
|
241
|
+
children: e.onHand.toLocaleString()
|
|
242
|
+
})] }), /* @__PURE__ */ m("div", { children: [/* @__PURE__ */ p("dt", {
|
|
243
|
+
className: "text-text-secondary",
|
|
244
|
+
children: "Available"
|
|
245
|
+
}), /* @__PURE__ */ p("dd", {
|
|
246
|
+
className: "font-medium",
|
|
247
|
+
children: e.available.toLocaleString()
|
|
248
|
+
})] })]
|
|
249
|
+
}),
|
|
250
|
+
/* @__PURE__ */ m("div", {
|
|
251
|
+
className: "space-y-2",
|
|
252
|
+
children: [
|
|
253
|
+
/* @__PURE__ */ p(s, {
|
|
254
|
+
htmlFor: "adjust-change",
|
|
255
|
+
children: "Change (units) *"
|
|
256
|
+
}),
|
|
257
|
+
/* @__PURE__ */ p(o, {
|
|
258
|
+
id: "adjust-change",
|
|
259
|
+
type: "number",
|
|
260
|
+
inputMode: "numeric",
|
|
261
|
+
step: 1,
|
|
262
|
+
value: l,
|
|
263
|
+
onChange: (e) => f(e.target.value),
|
|
264
|
+
placeholder: "e.g. 10 or -3"
|
|
265
|
+
}),
|
|
266
|
+
O && /* @__PURE__ */ p("p", {
|
|
267
|
+
className: `text-xs ${k ? "text-status-error-text" : "text-text-secondary"}`,
|
|
268
|
+
children: k ? "This would take stock below zero." : `After this: ${O.onHand.toLocaleString()} on hand, ${O.available.toLocaleString()} available.`
|
|
269
|
+
})
|
|
270
|
+
]
|
|
271
|
+
}),
|
|
272
|
+
/* @__PURE__ */ p(c, { message: v })
|
|
273
|
+
]
|
|
274
|
+
}),
|
|
275
|
+
/* @__PURE__ */ p(T, {
|
|
276
|
+
submitting: w,
|
|
277
|
+
submitLabel: "Apply adjustment",
|
|
278
|
+
submittingLabel: "Applying…",
|
|
279
|
+
onCancel: () => r(!1),
|
|
280
|
+
onSubmit: async () => {
|
|
281
|
+
if (e) {
|
|
282
|
+
if (D === null || D === 0) return C("Enter a whole-number change other than zero, e.g. 10 or -3.");
|
|
283
|
+
if (k) return C(`Only ${e.available.toLocaleString()} unit(s) are available to remove.`);
|
|
284
|
+
E(!0), C(null);
|
|
285
|
+
try {
|
|
286
|
+
let t = await a(e.id, D);
|
|
287
|
+
r(!1), i(`Adjusted ${e.productName} at ${e.warehouseName} by ${D > 0 ? "+" : ""}${D.toLocaleString()} — now ${t.quantity.toLocaleString()} on hand.`);
|
|
288
|
+
} catch (e) {
|
|
289
|
+
C(S(e, "Failed to adjust stock."));
|
|
290
|
+
} finally {
|
|
291
|
+
E(!1);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
})
|
|
296
|
+
] })
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
function O({ line: e, open: t, onOpenChange: r, onDone: i }) {
|
|
300
|
+
let { transferStock: a } = n(), [l, f] = d(""), [v, C] = d(""), [E, D] = d(null), [O, k] = d(!1);
|
|
301
|
+
return u(() => {
|
|
302
|
+
t && (f(""), C(""), D(null), k(!1));
|
|
303
|
+
}, [t, e?.id]), /* @__PURE__ */ p(h, {
|
|
304
|
+
open: t,
|
|
305
|
+
onOpenChange: r,
|
|
306
|
+
children: /* @__PURE__ */ m(g, { children: [
|
|
307
|
+
/* @__PURE__ */ m(y, { children: [/* @__PURE__ */ p(b, { children: "Transfer stock" }), /* @__PURE__ */ p(_, { children: e ? `Move available ${e.productName} (${e.sku}) out of ${e.warehouseName}. Both warehouses are updated together.` : "Choose a stock line to transfer." })] }),
|
|
308
|
+
e && /* @__PURE__ */ m("div", {
|
|
309
|
+
className: "space-y-4 px-1",
|
|
310
|
+
children: [
|
|
311
|
+
/* @__PURE__ */ m("p", {
|
|
312
|
+
className: "text-sm text-text-secondary",
|
|
313
|
+
children: [
|
|
314
|
+
"Available to transfer:",
|
|
315
|
+
" ",
|
|
316
|
+
/* @__PURE__ */ p("span", {
|
|
317
|
+
className: "font-medium text-text-primary",
|
|
318
|
+
children: e.available.toLocaleString()
|
|
319
|
+
})
|
|
320
|
+
]
|
|
321
|
+
}),
|
|
322
|
+
/* @__PURE__ */ m("div", {
|
|
323
|
+
className: "space-y-2",
|
|
324
|
+
children: [/* @__PURE__ */ p(s, {
|
|
325
|
+
htmlFor: "transfer-destination",
|
|
326
|
+
children: "Destination warehouse *"
|
|
327
|
+
}), /* @__PURE__ */ p(w, {
|
|
328
|
+
id: "transfer-destination",
|
|
329
|
+
value: l,
|
|
330
|
+
onChange: f,
|
|
331
|
+
excludeId: e.warehouseId
|
|
332
|
+
})]
|
|
333
|
+
}),
|
|
334
|
+
/* @__PURE__ */ m("div", {
|
|
335
|
+
className: "space-y-2",
|
|
336
|
+
children: [/* @__PURE__ */ p(s, {
|
|
337
|
+
htmlFor: "transfer-quantity",
|
|
338
|
+
children: "Quantity *"
|
|
339
|
+
}), /* @__PURE__ */ p(o, {
|
|
340
|
+
id: "transfer-quantity",
|
|
341
|
+
type: "number",
|
|
342
|
+
inputMode: "numeric",
|
|
343
|
+
min: 1,
|
|
344
|
+
max: e.available,
|
|
345
|
+
step: 1,
|
|
346
|
+
value: v,
|
|
347
|
+
onChange: (e) => C(e.target.value),
|
|
348
|
+
placeholder: "Units to move"
|
|
349
|
+
})]
|
|
350
|
+
}),
|
|
351
|
+
/* @__PURE__ */ p(c, { message: E })
|
|
352
|
+
]
|
|
353
|
+
}),
|
|
354
|
+
/* @__PURE__ */ p(T, {
|
|
355
|
+
submitting: O,
|
|
356
|
+
submitLabel: "Transfer stock",
|
|
357
|
+
submittingLabel: "Transferring…",
|
|
358
|
+
onCancel: () => r(!1),
|
|
359
|
+
onSubmit: async () => {
|
|
360
|
+
if (!e) return;
|
|
361
|
+
let t = x(v);
|
|
362
|
+
if (!l) return D("Choose the destination warehouse.");
|
|
363
|
+
if (l === e.warehouseId) return D("Source and destination warehouses must be different.");
|
|
364
|
+
if (t === null || t <= 0) return D("Quantity must be a whole number above zero.");
|
|
365
|
+
if (t > e.available) return D(`Only ${e.available.toLocaleString()} unit(s) are available to transfer.`);
|
|
366
|
+
k(!0), D(null);
|
|
367
|
+
try {
|
|
368
|
+
let n = await a({
|
|
369
|
+
sourceInventoryId: e.id,
|
|
370
|
+
destinationWarehouseId: l,
|
|
371
|
+
quantity: t
|
|
372
|
+
});
|
|
373
|
+
r(!1), i(`Transferred ${t.toLocaleString()} unit(s) of ${e.productName} from ${e.warehouseName} to ${n.destination.warehouseName ?? "the destination"}.`);
|
|
374
|
+
} catch (e) {
|
|
375
|
+
D(S(e, "Failed to transfer stock."));
|
|
376
|
+
} finally {
|
|
377
|
+
k(!1);
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
})
|
|
381
|
+
] })
|
|
382
|
+
});
|
|
383
|
+
}
|
|
384
|
+
//#endregion
|
|
385
|
+
export { D as AdjustStockDialog, E as ReceiveStockDialog, O as TransferStockDialog };
|