@burdenoff/microfe-movethewheels 2026.1031.2 → 2026.1031.3
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/MoveTheWheelsRoot.js +144 -136
- package/dist/MoveTheWheelsRoutes.js +61 -57
- package/dist/generated/tracking-quality-operations.d.ts +84 -0
- package/dist/hooks/useEntityImport.js +68 -0
- package/dist/movethewheels/hooks/useEntityImport.d.ts +19 -0
- package/dist/movethewheels/pages/ImportExportPage.d.ts +9 -0
- package/dist/movethewheels/utils/entityImport.d.ts +109 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/arrow-down-up.js +22 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/file-spreadsheet.js +30 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/file-up.js +22 -0
- package/dist/pages/CustomersPage.js +88 -79
- package/dist/pages/DriversPage.js +115 -106
- package/dist/pages/ImportExportPage.js +435 -0
- package/dist/pages/ProductsPage.js +85 -76
- package/dist/pages/VehiclesPage.js +99 -90
- package/dist/pages/WarehousesPage.js +97 -88
- package/dist/utils/entityImport.js +73 -0
- package/package.json +3 -2
- package/src/generated/tracking-quality-operations.ts +122 -0
- package/src/movethewheels/MoveTheWheelsRoot.tsx +8 -0
- package/src/movethewheels/MoveTheWheelsRoutes.tsx +4 -0
- package/src/movethewheels/hooks/useEntityImport.ts +108 -0
- package/src/movethewheels/pages/CustomersPage.tsx +6 -1
- package/src/movethewheels/pages/DriversPage.tsx +6 -0
- package/src/movethewheels/pages/ImportExportPage.tsx +483 -0
- package/src/movethewheels/pages/ProductsPage.tsx +14 -1
- package/src/movethewheels/pages/VehiclesPage.tsx +6 -0
- package/src/movethewheels/pages/WarehousesPage.tsx +6 -1
- package/src/movethewheels/utils/entityImport.ts +239 -0
|
@@ -3,22 +3,23 @@ import { Filter as t } from "../node_modules/lucide-react/dist/esm/icons/filter.
|
|
|
3
3
|
import { Gauge as n } from "../node_modules/lucide-react/dist/esm/icons/gauge.js";
|
|
4
4
|
import { Plus as r } from "../node_modules/lucide-react/dist/esm/icons/plus.js";
|
|
5
5
|
import { RefreshCw as i } from "../node_modules/lucide-react/dist/esm/icons/refresh-cw.js";
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
6
|
+
import { Upload as a } from "../node_modules/lucide-react/dist/esm/icons/upload.js";
|
|
7
|
+
import { Warehouse as o } from "../node_modules/lucide-react/dist/esm/icons/warehouse.js";
|
|
8
|
+
import { joinPath as s } from "../utils/navigation.js";
|
|
9
|
+
import { useMoveTheWheels as c } from "../providers/MoveTheWheelsProvider.js";
|
|
10
|
+
import { useTr as l } from "../shared/hooks/useTr.js";
|
|
11
|
+
import { useWarehouseStats as u, useWarehouses as d } from "../hooks/useInventory.js";
|
|
11
12
|
import "../hooks/index.js";
|
|
12
|
-
import { Badge as
|
|
13
|
-
import { PageLayout as
|
|
14
|
-
import { MetricCard as
|
|
15
|
-
import { DataTable as
|
|
16
|
-
import { useDebouncedValue as
|
|
17
|
-
import { useState as
|
|
18
|
-
import { jsx as
|
|
19
|
-
import { CTAOverflowMenu as
|
|
13
|
+
import { Badge as f, Button as p, Card as m, CardContent as h, Progress as g, Select as _, SelectContent as v, SelectItem as y, SelectTrigger as b, SelectValue as x } from "../components/ui.js";
|
|
14
|
+
import { PageLayout as S } from "../components/PageLayout.js";
|
|
15
|
+
import { MetricCard as C, MetricGrid as w } from "../components/MetricCard.js";
|
|
16
|
+
import { DataTable as T } from "../components/DataTable.js";
|
|
17
|
+
import { useDebouncedValue as E } from "../hooks/useDebouncedValue.js";
|
|
18
|
+
import { useState as D } from "react";
|
|
19
|
+
import { jsx as O, jsxs as k } from "react/jsx-runtime";
|
|
20
|
+
import { CTAOverflowMenu as A, PagePurpose as j } from "@burdenoff/fe-libs/ui";
|
|
20
21
|
//#region src/movethewheels/pages/WarehousesPage.tsx
|
|
21
|
-
var
|
|
22
|
+
var M = [
|
|
22
23
|
{
|
|
23
24
|
value: "all",
|
|
24
25
|
label: "All statuses"
|
|
@@ -32,114 +33,122 @@ var j = [
|
|
|
32
33
|
label: "Inactive"
|
|
33
34
|
}
|
|
34
35
|
];
|
|
35
|
-
function
|
|
36
|
-
let
|
|
37
|
-
limit:
|
|
38
|
-
offset:
|
|
39
|
-
search:
|
|
40
|
-
status:
|
|
41
|
-
}),
|
|
42
|
-
return /* @__PURE__ */
|
|
36
|
+
function N() {
|
|
37
|
+
let N = l(), { basePath: P, navigate: F } = c(), [I, L] = D(0), [R, z] = D(25), [B, V] = D(""), [H, U] = D("all"), W = E(B), { warehouses: G, totalWarehouses: K, hasMore: q, isLoading: J, error: Y, refetch: X } = d({
|
|
38
|
+
limit: R,
|
|
39
|
+
offset: I * R,
|
|
40
|
+
search: W,
|
|
41
|
+
status: H === "all" ? void 0 : H
|
|
42
|
+
}), Z = H !== "all" || B.length > 0, { stats: Q } = u(), $ = (e) => F?.(s(P, e));
|
|
43
|
+
return /* @__PURE__ */ k(S, {
|
|
43
44
|
title: "Warehouses",
|
|
44
|
-
subtitle: `${
|
|
45
|
-
icon: /* @__PURE__ */
|
|
46
|
-
headerActions: /* @__PURE__ */
|
|
45
|
+
subtitle: `${Q.total} warehouses`,
|
|
46
|
+
icon: /* @__PURE__ */ O(o, { size: 24 }),
|
|
47
|
+
headerActions: /* @__PURE__ */ O(A, {
|
|
47
48
|
primary: {
|
|
48
49
|
label: "Add Warehouse",
|
|
49
|
-
icon: /* @__PURE__ */
|
|
50
|
-
onSelect: () =>
|
|
50
|
+
icon: /* @__PURE__ */ O(r, { size: 16 }),
|
|
51
|
+
onSelect: () => $("inventory/warehouses/new")
|
|
51
52
|
},
|
|
52
|
-
actions: [
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
53
|
+
actions: [
|
|
54
|
+
{
|
|
55
|
+
label: N("movethewheels.palletPreview.title", "Pallet preview"),
|
|
56
|
+
onSelect: () => $("inventory/warehouses/pallet-preview")
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
label: "Refresh",
|
|
60
|
+
icon: /* @__PURE__ */ O(i, { size: 16 }),
|
|
61
|
+
onSelect: () => X()
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
label: "Import",
|
|
65
|
+
icon: /* @__PURE__ */ O(a, { size: 16 }),
|
|
66
|
+
onSelect: () => $("import-export?entity=WAREHOUSE")
|
|
67
|
+
}
|
|
68
|
+
]
|
|
60
69
|
}),
|
|
61
70
|
children: [
|
|
62
|
-
/* @__PURE__ */
|
|
63
|
-
icon: /* @__PURE__ */
|
|
71
|
+
/* @__PURE__ */ O(j, {
|
|
72
|
+
icon: /* @__PURE__ */ O(o, { size: 18 }),
|
|
64
73
|
children: "Your storage and fulfilment sites. Watch space and pallet utilization across each warehouse to balance stock, spot capacity crunches, and decide where to fulfil an order from."
|
|
65
74
|
}),
|
|
66
|
-
/* @__PURE__ */
|
|
75
|
+
/* @__PURE__ */ k("div", { children: [/* @__PURE__ */ O("p", {
|
|
67
76
|
className: "mb-3 text-sm font-medium text-text-secondary",
|
|
68
77
|
children: "Network at a glance"
|
|
69
|
-
}), /* @__PURE__ */
|
|
78
|
+
}), /* @__PURE__ */ k(w, {
|
|
70
79
|
columns: 3,
|
|
71
80
|
children: [
|
|
72
|
-
/* @__PURE__ */
|
|
81
|
+
/* @__PURE__ */ O(C, {
|
|
73
82
|
label: "Total sites",
|
|
74
|
-
value:
|
|
75
|
-
icon: /* @__PURE__ */
|
|
83
|
+
value: Q.total,
|
|
84
|
+
icon: /* @__PURE__ */ O(o, { size: 20 }),
|
|
76
85
|
accent: "brand"
|
|
77
86
|
}),
|
|
78
|
-
/* @__PURE__ */
|
|
87
|
+
/* @__PURE__ */ O(C, {
|
|
79
88
|
label: "Active",
|
|
80
|
-
value:
|
|
81
|
-
icon: /* @__PURE__ */
|
|
89
|
+
value: Q.active,
|
|
90
|
+
icon: /* @__PURE__ */ O(e, { size: 20 }),
|
|
82
91
|
accent: "success",
|
|
83
92
|
subtitle: "Fulfilling orders"
|
|
84
93
|
}),
|
|
85
|
-
/* @__PURE__ */
|
|
94
|
+
/* @__PURE__ */ O(C, {
|
|
86
95
|
label: "Avg utilization",
|
|
87
|
-
value:
|
|
88
|
-
icon: /* @__PURE__ */
|
|
96
|
+
value: Q.total > 0 ? `${Math.round(Q.avgUtilization * 100)}%` : "—",
|
|
97
|
+
icon: /* @__PURE__ */ O(n, { size: 20 }),
|
|
89
98
|
accent: "blue"
|
|
90
99
|
})
|
|
91
100
|
]
|
|
92
101
|
})] }),
|
|
93
|
-
/* @__PURE__ */
|
|
102
|
+
/* @__PURE__ */ O(m, { children: /* @__PURE__ */ O(h, {
|
|
94
103
|
className: "p-4",
|
|
95
|
-
children: /* @__PURE__ */
|
|
104
|
+
children: /* @__PURE__ */ k("div", {
|
|
96
105
|
className: "flex flex-wrap items-center gap-4",
|
|
97
106
|
children: [
|
|
98
|
-
/* @__PURE__ */
|
|
107
|
+
/* @__PURE__ */ k("div", {
|
|
99
108
|
className: "flex items-center gap-2",
|
|
100
|
-
children: [/* @__PURE__ */
|
|
109
|
+
children: [/* @__PURE__ */ O(t, {
|
|
101
110
|
size: 16,
|
|
102
111
|
className: "text-text-secondary"
|
|
103
|
-
}), /* @__PURE__ */
|
|
112
|
+
}), /* @__PURE__ */ O("span", {
|
|
104
113
|
className: "text-sm font-medium",
|
|
105
114
|
children: "Filters:"
|
|
106
115
|
})]
|
|
107
116
|
}),
|
|
108
|
-
/* @__PURE__ */
|
|
109
|
-
value:
|
|
117
|
+
/* @__PURE__ */ k(_, {
|
|
118
|
+
value: H,
|
|
110
119
|
onValueChange: (e) => {
|
|
111
|
-
|
|
120
|
+
U(e), L(0);
|
|
112
121
|
},
|
|
113
|
-
children: [/* @__PURE__ */
|
|
122
|
+
children: [/* @__PURE__ */ O(b, {
|
|
114
123
|
className: "w-[180px]",
|
|
115
|
-
children: /* @__PURE__ */
|
|
116
|
-
}), /* @__PURE__ */
|
|
124
|
+
children: /* @__PURE__ */ O(x, { placeholder: "Status" })
|
|
125
|
+
}), /* @__PURE__ */ O(v, { children: M.map((e) => /* @__PURE__ */ O(y, {
|
|
117
126
|
value: e.value,
|
|
118
127
|
children: e.label
|
|
119
128
|
}, e.value)) })]
|
|
120
129
|
}),
|
|
121
|
-
|
|
130
|
+
Z && /* @__PURE__ */ O(p, {
|
|
122
131
|
variant: "ghost",
|
|
123
132
|
size: "sm",
|
|
124
133
|
onClick: () => {
|
|
125
|
-
|
|
134
|
+
U("all"), V(""), L(0);
|
|
126
135
|
},
|
|
127
136
|
children: "Clear filters"
|
|
128
137
|
})
|
|
129
138
|
]
|
|
130
139
|
})
|
|
131
140
|
}) }),
|
|
132
|
-
/* @__PURE__ */
|
|
141
|
+
/* @__PURE__ */ O(T, {
|
|
133
142
|
columns: [
|
|
134
143
|
{
|
|
135
144
|
id: "name",
|
|
136
145
|
header: "Warehouse",
|
|
137
146
|
accessorKey: "name",
|
|
138
147
|
sortable: !0,
|
|
139
|
-
cell: (e) => /* @__PURE__ */
|
|
148
|
+
cell: (e) => /* @__PURE__ */ k("div", { children: [/* @__PURE__ */ O("p", {
|
|
140
149
|
className: "font-medium",
|
|
141
150
|
children: e.name
|
|
142
|
-
}), /* @__PURE__ */
|
|
151
|
+
}), /* @__PURE__ */ O("p", {
|
|
143
152
|
className: "text-sm text-text-secondary",
|
|
144
153
|
children: e.code
|
|
145
154
|
})] })
|
|
@@ -147,7 +156,7 @@ function M() {
|
|
|
147
156
|
{
|
|
148
157
|
id: "location",
|
|
149
158
|
header: "Location",
|
|
150
|
-
cell: (e) => /* @__PURE__ */
|
|
159
|
+
cell: (e) => /* @__PURE__ */ k("div", { children: [/* @__PURE__ */ O("p", { children: [e.address.city, e.address.state].filter(Boolean).join(", ") || "Not provided" }), /* @__PURE__ */ O("p", {
|
|
151
160
|
className: "text-sm text-text-secondary",
|
|
152
161
|
children: e.address.country || "Not provided"
|
|
153
162
|
})] })
|
|
@@ -156,7 +165,7 @@ function M() {
|
|
|
156
165
|
id: "type",
|
|
157
166
|
header: "Type",
|
|
158
167
|
accessorKey: "type",
|
|
159
|
-
cell: (e) => /* @__PURE__ */
|
|
168
|
+
cell: (e) => /* @__PURE__ */ O(f, {
|
|
160
169
|
variant: "outline",
|
|
161
170
|
className: "capitalize",
|
|
162
171
|
children: e.type.replace("_", " ")
|
|
@@ -167,15 +176,15 @@ function M() {
|
|
|
167
176
|
header: "Utilization",
|
|
168
177
|
cell: (e) => {
|
|
169
178
|
let { totalSpace: t, usedSpace: n } = e.capacity, r = typeof t == "number" && t > 0 && typeof n == "number" ? n / t * 100 : void 0;
|
|
170
|
-
return r === void 0 ? /* @__PURE__ */
|
|
179
|
+
return r === void 0 ? /* @__PURE__ */ O("span", {
|
|
171
180
|
className: "text-sm text-text-secondary",
|
|
172
181
|
children: "Not provided"
|
|
173
|
-
}) : /* @__PURE__ */
|
|
182
|
+
}) : /* @__PURE__ */ k("div", {
|
|
174
183
|
className: "flex items-center gap-2",
|
|
175
|
-
children: [/* @__PURE__ */
|
|
184
|
+
children: [/* @__PURE__ */ O(g, {
|
|
176
185
|
value: r,
|
|
177
186
|
className: "w-16 h-2"
|
|
178
|
-
}), /* @__PURE__ */
|
|
187
|
+
}), /* @__PURE__ */ k("span", {
|
|
179
188
|
className: "text-sm",
|
|
180
189
|
children: [r.toFixed(0), "%"]
|
|
181
190
|
})]
|
|
@@ -187,7 +196,7 @@ function M() {
|
|
|
187
196
|
header: "Capacity",
|
|
188
197
|
cell: (e) => {
|
|
189
198
|
let { totalSpace: t, usedSpace: n } = e.capacity;
|
|
190
|
-
return typeof n == "number" && typeof t == "number" ? /* @__PURE__ */
|
|
199
|
+
return typeof n == "number" && typeof t == "number" ? /* @__PURE__ */ k("p", {
|
|
191
200
|
className: "text-sm",
|
|
192
201
|
children: [
|
|
193
202
|
n.toLocaleString(),
|
|
@@ -195,7 +204,7 @@ function M() {
|
|
|
195
204
|
t.toLocaleString(),
|
|
196
205
|
" sq ft"
|
|
197
206
|
]
|
|
198
|
-
}) : /* @__PURE__ */
|
|
207
|
+
}) : /* @__PURE__ */ O("span", {
|
|
199
208
|
className: "text-sm text-text-secondary",
|
|
200
209
|
children: "Not provided"
|
|
201
210
|
});
|
|
@@ -206,37 +215,37 @@ function M() {
|
|
|
206
215
|
header: "Status",
|
|
207
216
|
cell: (e) => {
|
|
208
217
|
let t = e.status?.toUpperCase() === "ACTIVE";
|
|
209
|
-
return e.status ? /* @__PURE__ */
|
|
218
|
+
return e.status ? /* @__PURE__ */ O(f, {
|
|
210
219
|
variant: t ? "default" : "secondary",
|
|
211
220
|
children: t ? "Active" : "Inactive"
|
|
212
|
-
}) : /* @__PURE__ */
|
|
221
|
+
}) : /* @__PURE__ */ O("span", {
|
|
213
222
|
className: "text-sm text-text-secondary",
|
|
214
223
|
children: "Not provided"
|
|
215
224
|
});
|
|
216
225
|
}
|
|
217
226
|
}
|
|
218
227
|
],
|
|
219
|
-
data:
|
|
220
|
-
isLoading:
|
|
221
|
-
error:
|
|
222
|
-
onRetry:
|
|
228
|
+
data: G,
|
|
229
|
+
isLoading: J,
|
|
230
|
+
error: Y,
|
|
231
|
+
onRetry: X,
|
|
223
232
|
searchable: !0,
|
|
224
233
|
searchPlaceholder: "Search warehouses...",
|
|
225
234
|
searchFields: ["name", "code"],
|
|
226
|
-
searchQuery:
|
|
235
|
+
searchQuery: B,
|
|
227
236
|
onSearchQueryChange: (e) => {
|
|
228
|
-
|
|
237
|
+
V(e), L(0);
|
|
229
238
|
},
|
|
230
|
-
onRowClick: (e) =>
|
|
239
|
+
onRowClick: (e) => $(`inventory/warehouses/${e.id}`),
|
|
231
240
|
getRowId: (e) => e.id,
|
|
232
|
-
pageIndex:
|
|
233
|
-
pageSize:
|
|
234
|
-
onPageChange:
|
|
241
|
+
pageIndex: I,
|
|
242
|
+
pageSize: R,
|
|
243
|
+
onPageChange: L,
|
|
235
244
|
onPageSizeChange: (e) => {
|
|
236
|
-
|
|
245
|
+
z(e), L(0);
|
|
237
246
|
},
|
|
238
|
-
totalItems:
|
|
239
|
-
hasNextPage:
|
|
247
|
+
totalItems: K,
|
|
248
|
+
hasNextPage: q,
|
|
240
249
|
emptyIllustration: "empty-data",
|
|
241
250
|
emptyTitle: "No warehouses yet",
|
|
242
251
|
emptyDescription: "Add a warehouse to store inventory and fulfil orders from it."
|
|
@@ -245,4 +254,4 @@ function M() {
|
|
|
245
254
|
});
|
|
246
255
|
}
|
|
247
256
|
//#endregion
|
|
248
|
-
export {
|
|
257
|
+
export { N as default };
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
//#region src/movethewheels/utils/entityImport.ts
|
|
2
|
+
var e = [
|
|
3
|
+
{
|
|
4
|
+
entityType: "CUSTOMER",
|
|
5
|
+
label: "Customers",
|
|
6
|
+
noun: "customer",
|
|
7
|
+
field: "importMtwCustomers"
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
entityType: "VEHICLE",
|
|
11
|
+
label: "Vehicles",
|
|
12
|
+
noun: "vehicle",
|
|
13
|
+
field: "importMtwVehicles"
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
entityType: "DRIVER",
|
|
17
|
+
label: "Drivers",
|
|
18
|
+
noun: "driver",
|
|
19
|
+
field: "importMtwDrivers"
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
entityType: "WAREHOUSE",
|
|
23
|
+
label: "Warehouses",
|
|
24
|
+
noun: "warehouse",
|
|
25
|
+
field: "importMtwWarehouses"
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
entityType: "CARGO_ITEM",
|
|
29
|
+
label: "Products",
|
|
30
|
+
noun: "product",
|
|
31
|
+
field: "importMtwCargoItems"
|
|
32
|
+
}
|
|
33
|
+
], t = 2 * 1024 * 1024, n = 1e3;
|
|
34
|
+
function r(t) {
|
|
35
|
+
return e.some((e) => e.entityType === t);
|
|
36
|
+
}
|
|
37
|
+
function i(t) {
|
|
38
|
+
let n = e.find((e) => e.entityType === t);
|
|
39
|
+
if (!n) throw Error(`Unknown import entity ${t}`);
|
|
40
|
+
return n;
|
|
41
|
+
}
|
|
42
|
+
var a = {
|
|
43
|
+
CUSTOMER: "\n mutation ImportMtwCustomers($input: MtwImportInput!) {\n importMtwCustomers(input: $input) {\n entityType\n status\n dryRun\n totalRows\n validRows\n importedRows\n failedRows\n createdIds\n errorsTruncated\n errors {\n row\n field\n message\n }\n }\n }\n ",
|
|
44
|
+
VEHICLE: "\n mutation ImportMtwVehicles($input: MtwImportInput!) {\n importMtwVehicles(input: $input) {\n entityType\n status\n dryRun\n totalRows\n validRows\n importedRows\n failedRows\n createdIds\n errorsTruncated\n errors {\n row\n field\n message\n }\n }\n }\n ",
|
|
45
|
+
DRIVER: "\n mutation ImportMtwDrivers($input: MtwImportInput!) {\n importMtwDrivers(input: $input) {\n entityType\n status\n dryRun\n totalRows\n validRows\n importedRows\n failedRows\n createdIds\n errorsTruncated\n errors {\n row\n field\n message\n }\n }\n }\n ",
|
|
46
|
+
WAREHOUSE: "\n mutation ImportMtwWarehouses($input: MtwImportInput!) {\n importMtwWarehouses(input: $input) {\n entityType\n status\n dryRun\n totalRows\n validRows\n importedRows\n failedRows\n createdIds\n errorsTruncated\n errors {\n row\n field\n message\n }\n }\n }\n ",
|
|
47
|
+
CARGO_ITEM: "\n mutation ImportMtwCargoItems($input: MtwImportInput!) {\n importMtwCargoItems(input: $input) {\n entityType\n status\n dryRun\n totalRows\n validRows\n importedRows\n failedRows\n createdIds\n errorsTruncated\n errors {\n row\n field\n message\n }\n }\n }\n "
|
|
48
|
+
};
|
|
49
|
+
function o(e) {
|
|
50
|
+
return a[i(e).entityType];
|
|
51
|
+
}
|
|
52
|
+
var s = "\n query MtwImportSpec {\n mtwImportSpec {\n entityType\n keyColumn\n columns {\n name\n type\n required\n description\n allowedValues\n maxLength\n min\n max\n }\n }\n }\n";
|
|
53
|
+
function c(e, t = "") {
|
|
54
|
+
let n = e.toLowerCase();
|
|
55
|
+
return n.endsWith(".csv") ? "CSV" : n.endsWith(".json") ? "JSON" : t.includes("csv") ? "CSV" : t.includes("json") ? "JSON" : null;
|
|
56
|
+
}
|
|
57
|
+
function l(e) {
|
|
58
|
+
return /[",\r\n]/.test(e) ? `"${e.replace(/"/g, "\"\"")}"` : e;
|
|
59
|
+
}
|
|
60
|
+
function u(e) {
|
|
61
|
+
return `${[...e.columns.filter((e) => e.required), ...e.columns.filter((e) => !e.required)].map((e) => l(e.name)).join(",")}\n`;
|
|
62
|
+
}
|
|
63
|
+
function d(e, t) {
|
|
64
|
+
let n = (e) => `${e} ${t}${e === 1 ? "" : "s"}`;
|
|
65
|
+
switch (e.status) {
|
|
66
|
+
case "DRY_RUN": return e.failedRows === 0 ? `All ${n(e.totalRows)} are valid. Nothing was imported yet.` : `${e.validRows} of ${n(e.totalRows)} are valid; ${e.failedRows} need fixing. Nothing was imported.`;
|
|
67
|
+
case "COMPLETED": return `Imported ${n(e.importedRows)}.`;
|
|
68
|
+
case "PARTIAL": return `Imported ${n(e.importedRows)}; ${e.failedRows} row${e.failedRows === 1 ? "" : "s"} failed.`;
|
|
69
|
+
case "REJECTED": return `Nothing was imported: ${e.failedRows} of ${e.totalRows} rows have problems. Fix them, or allow a partial import.`;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
//#endregion
|
|
73
|
+
export { e as MTW_IMPORT_ENTITIES, t as MTW_IMPORT_MAX_BYTES, n as MTW_IMPORT_MAX_ROWS, s as MTW_IMPORT_SPEC_QUERY, u as buildCsvTemplate, c as detectImportFormat, i as importEntity, o as importMutation, r as isImportEntityType, d as summariseImport };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@burdenoff/microfe-movethewheels",
|
|
3
|
-
"version": "2026.1031.
|
|
3
|
+
"version": "2026.1031.3",
|
|
4
4
|
"description": "Burdenoff MoveTheWheels Microfrontend - Logistics & Supply Chain Management Platform",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"files": [
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"format": "prettier --write \"src/**/*.{ts,tsx}\"",
|
|
38
38
|
"type:check": "bash scripts/build-limits.sh tsc --noEmit",
|
|
39
39
|
"validate:schema-drift": "graphql-schema-drift",
|
|
40
|
-
"sanity": "bash scripts/build-limits.sh --gate bash -c 'bun run codegen && bun run validate:schema-drift && bun run lint:sanity && bun run format && bun run type:check && bun run verify:live-logistics-contract && bun run verify:empty-reuse-retry && bun run verify:empty-reuse-recovery && bun run verify:trip-utilization-input && bun run verify:trip-draft-context && bun run verify:stock-binding-input && bun run verify:stock-observation-input && bun run verify:pending-pick-input && bun run verify:pick-review-ack && bun run verify:driver-license-expiry && bun run verify:tracking-alerts && bun run verify:freight-input && bun run verify:freight-recovery && bun run verify:freight-commercial && bun run verify:freight-invoice && bun run verify:procedure-input && bun run verify:consolidation && bun run verify:multimodal-single-attempt && bun run verify:workspace-predispatch && bun run verify:tracking-quality-outcomes && bun run verify:shipment-status-authority && bun run verify:free-time-permissions && bun run verify:core-codegen && bun run verify:order-vehicle-hold && bun run verify:pallet-preview && bun run verify:entity-export && bun run verify:consumed-fuel-preview && bun run build && bun run build-storybook'",
|
|
40
|
+
"sanity": "bash scripts/build-limits.sh --gate bash -c 'bun run codegen && bun run validate:schema-drift && bun run lint:sanity && bun run format && bun run type:check && bun run verify:live-logistics-contract && bun run verify:empty-reuse-retry && bun run verify:empty-reuse-recovery && bun run verify:trip-utilization-input && bun run verify:trip-draft-context && bun run verify:stock-binding-input && bun run verify:stock-observation-input && bun run verify:pending-pick-input && bun run verify:pick-review-ack && bun run verify:driver-license-expiry && bun run verify:tracking-alerts && bun run verify:freight-input && bun run verify:freight-recovery && bun run verify:freight-commercial && bun run verify:freight-invoice && bun run verify:procedure-input && bun run verify:consolidation && bun run verify:multimodal-single-attempt && bun run verify:workspace-predispatch && bun run verify:tracking-quality-outcomes && bun run verify:shipment-status-authority && bun run verify:free-time-permissions && bun run verify:core-codegen && bun run verify:order-vehicle-hold && bun run verify:pallet-preview && bun run verify:entity-export && bun run verify:entity-import && bun run verify:consumed-fuel-preview && bun run build && bun run build-storybook'",
|
|
41
41
|
"storybook": "storybook dev -p 6006",
|
|
42
42
|
"build-storybook": "storybook build",
|
|
43
43
|
"test:storybook": "vitest run --project=storybook",
|
|
@@ -73,6 +73,7 @@
|
|
|
73
73
|
"verify:tracking-alerts": "bun test scripts/check-tracking-alerts.test.ts",
|
|
74
74
|
"verify:core-codegen": "bun test scripts/codegen.test.ts && node --test scripts/check-package-types.test.mjs",
|
|
75
75
|
"verify:entity-export": "bun test scripts/check-entity-export.test.ts",
|
|
76
|
+
"verify:entity-import": "bun test scripts/check-entity-import.test.ts",
|
|
76
77
|
"verify:pallet-preview": "bun test scripts/check-pallet-preview.test.ts && bun scripts/verify-pallet-preview-hook.mjs",
|
|
77
78
|
"verify:consumed-fuel-preview": "bun test scripts/check-consumed-fuel-preview.test.ts && bun scripts/verify-consumed-fuel-preview-hook.mjs && bun scripts/verify-consumed-fuel-preview-component.mjs"
|
|
78
79
|
},
|
|
@@ -2443,6 +2443,91 @@ export type MtwFuelVolumeObservationInput = {
|
|
|
2443
2443
|
sourceRevision?: InputMaybe<Scalars['String']['input']>;
|
|
2444
2444
|
};
|
|
2445
2445
|
|
|
2446
|
+
export type MtwImportColumn = {
|
|
2447
|
+
__typename?: 'MtwImportColumn';
|
|
2448
|
+
allowedValues?: Maybe<Array<Scalars['String']['output']>>;
|
|
2449
|
+
description: Scalars['String']['output'];
|
|
2450
|
+
max?: Maybe<Scalars['Float']['output']>;
|
|
2451
|
+
maxLength?: Maybe<Scalars['Int']['output']>;
|
|
2452
|
+
min?: Maybe<Scalars['Float']['output']>;
|
|
2453
|
+
name: Scalars['String']['output'];
|
|
2454
|
+
required: Scalars['Boolean']['output'];
|
|
2455
|
+
type: MtwImportColumnType;
|
|
2456
|
+
};
|
|
2457
|
+
|
|
2458
|
+
export type MtwImportColumnType =
|
|
2459
|
+
| 'BOOLEAN'
|
|
2460
|
+
| 'DATE'
|
|
2461
|
+
| 'EMAIL'
|
|
2462
|
+
| 'ENUM'
|
|
2463
|
+
| 'INTEGER'
|
|
2464
|
+
| 'JSON'
|
|
2465
|
+
| 'NUMBER'
|
|
2466
|
+
| 'STRING';
|
|
2467
|
+
|
|
2468
|
+
export type MtwImportEntitySpec = {
|
|
2469
|
+
__typename?: 'MtwImportEntitySpec';
|
|
2470
|
+
columns: Array<MtwImportColumn>;
|
|
2471
|
+
entityType: MtwImportEntityType;
|
|
2472
|
+
/** Column that must be unique in the workspace, if any. */
|
|
2473
|
+
keyColumn?: Maybe<Scalars['String']['output']>;
|
|
2474
|
+
};
|
|
2475
|
+
|
|
2476
|
+
export type MtwImportEntityType =
|
|
2477
|
+
| 'CARGO_ITEM'
|
|
2478
|
+
| 'CUSTOMER'
|
|
2479
|
+
| 'DRIVER'
|
|
2480
|
+
| 'VEHICLE'
|
|
2481
|
+
| 'WAREHOUSE';
|
|
2482
|
+
|
|
2483
|
+
export type MtwImportFormat =
|
|
2484
|
+
| 'CSV'
|
|
2485
|
+
| 'JSON';
|
|
2486
|
+
|
|
2487
|
+
export type MtwImportInput = {
|
|
2488
|
+
/** Import the valid rows even when other rows are invalid. */
|
|
2489
|
+
allowPartial?: InputMaybe<Scalars['Boolean']['input']>;
|
|
2490
|
+
/** The CSV (with a header row) or JSON (array of objects) document, at most 2 MB and 1000 rows. */
|
|
2491
|
+
content: Scalars['String']['input'];
|
|
2492
|
+
/** Validate every row without writing anything. */
|
|
2493
|
+
dryRun?: InputMaybe<Scalars['Boolean']['input']>;
|
|
2494
|
+
format: MtwImportFormat;
|
|
2495
|
+
};
|
|
2496
|
+
|
|
2497
|
+
export type MtwImportResult = {
|
|
2498
|
+
__typename?: 'MtwImportResult';
|
|
2499
|
+
createdIds: Array<Scalars['ID']['output']>;
|
|
2500
|
+
dryRun: Scalars['Boolean']['output'];
|
|
2501
|
+
entityType: MtwImportEntityType;
|
|
2502
|
+
errors: Array<MtwImportRowError>;
|
|
2503
|
+
/** True when more row errors exist than are returned (the first 500 are returned). */
|
|
2504
|
+
errorsTruncated: Scalars['Boolean']['output'];
|
|
2505
|
+
failedRows: Scalars['Int']['output'];
|
|
2506
|
+
importedRows: Scalars['Int']['output'];
|
|
2507
|
+
status: MtwImportStatus;
|
|
2508
|
+
totalRows: Scalars['Int']['output'];
|
|
2509
|
+
validRows: Scalars['Int']['output'];
|
|
2510
|
+
};
|
|
2511
|
+
|
|
2512
|
+
export type MtwImportRowError = {
|
|
2513
|
+
__typename?: 'MtwImportRowError';
|
|
2514
|
+
field?: Maybe<Scalars['String']['output']>;
|
|
2515
|
+
message: Scalars['String']['output'];
|
|
2516
|
+
/** 1-based data row number (the CSV header is not counted). */
|
|
2517
|
+
row: Scalars['Int']['output'];
|
|
2518
|
+
};
|
|
2519
|
+
|
|
2520
|
+
/**
|
|
2521
|
+
* DRY_RUN: validated only, nothing written. COMPLETED: every row imported.
|
|
2522
|
+
* PARTIAL: allowPartial was set and some rows failed. REJECTED: some rows were
|
|
2523
|
+
* invalid and allowPartial was not set, so nothing was written.
|
|
2524
|
+
*/
|
|
2525
|
+
export type MtwImportStatus =
|
|
2526
|
+
| 'COMPLETED'
|
|
2527
|
+
| 'DRY_RUN'
|
|
2528
|
+
| 'PARTIAL'
|
|
2529
|
+
| 'REJECTED';
|
|
2530
|
+
|
|
2446
2531
|
/** A recorded manual issuer review; workspace privileges do not establish professional qualifications. */
|
|
2447
2532
|
export type MtwInspectorQualification = {
|
|
2448
2533
|
__typename?: 'MtwInspectorQualification';
|
|
@@ -4523,6 +4608,11 @@ export type Mutation = {
|
|
|
4523
4608
|
deleteWarehouse: Scalars['Boolean']['output'];
|
|
4524
4609
|
exportMtwEntities: MtwExportJob;
|
|
4525
4610
|
holdMtwContainer: MtwContainerHold;
|
|
4611
|
+
importMtwCargoItems: MtwImportResult;
|
|
4612
|
+
importMtwCustomers: MtwImportResult;
|
|
4613
|
+
importMtwDrivers: MtwImportResult;
|
|
4614
|
+
importMtwVehicles: MtwImportResult;
|
|
4615
|
+
importMtwWarehouses: MtwImportResult;
|
|
4526
4616
|
openMtwContainerCycle: MtwContainerCycle;
|
|
4527
4617
|
openMtwFreeTimeClock: MtwFreeTimeClock;
|
|
4528
4618
|
optimizeRoute: MtwRoute;
|
|
@@ -4791,6 +4881,31 @@ export type MutationHoldMtwContainerArgs = {
|
|
|
4791
4881
|
};
|
|
4792
4882
|
|
|
4793
4883
|
|
|
4884
|
+
export type MutationImportMtwCargoItemsArgs = {
|
|
4885
|
+
input: MtwImportInput;
|
|
4886
|
+
};
|
|
4887
|
+
|
|
4888
|
+
|
|
4889
|
+
export type MutationImportMtwCustomersArgs = {
|
|
4890
|
+
input: MtwImportInput;
|
|
4891
|
+
};
|
|
4892
|
+
|
|
4893
|
+
|
|
4894
|
+
export type MutationImportMtwDriversArgs = {
|
|
4895
|
+
input: MtwImportInput;
|
|
4896
|
+
};
|
|
4897
|
+
|
|
4898
|
+
|
|
4899
|
+
export type MutationImportMtwVehiclesArgs = {
|
|
4900
|
+
input: MtwImportInput;
|
|
4901
|
+
};
|
|
4902
|
+
|
|
4903
|
+
|
|
4904
|
+
export type MutationImportMtwWarehousesArgs = {
|
|
4905
|
+
input: MtwImportInput;
|
|
4906
|
+
};
|
|
4907
|
+
|
|
4908
|
+
|
|
4794
4909
|
export type MutationOpenMtwContainerCycleArgs = {
|
|
4795
4910
|
input: OpenMtwContainerCycleInput;
|
|
4796
4911
|
};
|
|
@@ -5253,6 +5368,13 @@ export type Query = {
|
|
|
5253
5368
|
/** Null is not proof of no effect and never authorizes resubmission. Exact original actor/context only. */
|
|
5254
5369
|
mtwFreightInvoiceComparisonByRequestKey?: Maybe<MtwFreightInvoiceComparison>;
|
|
5255
5370
|
mtwFreightInvoiceComparisonPage: MtwFreightInvoiceComparisonPage;
|
|
5371
|
+
/**
|
|
5372
|
+
* Columns accepted by each import, for building templates and showing guidance.
|
|
5373
|
+
* This is static reference metadata with no workspace data in it, so it only
|
|
5374
|
+
* needs an authenticated caller; every import mutation enforces its own
|
|
5375
|
+
* entity create permission.
|
|
5376
|
+
*/
|
|
5377
|
+
mtwImportSpec: Array<MtwImportEntitySpec>;
|
|
5256
5378
|
mtwPalletGeometryPreview: MtwPalletGeometryPreview;
|
|
5257
5379
|
mtwPickPreparation: MtwPickPreparation;
|
|
5258
5380
|
mtwPickReview: MtwPickReview;
|
|
@@ -23,6 +23,7 @@ import {
|
|
|
23
23
|
DollarSign,
|
|
24
24
|
Bot,
|
|
25
25
|
Store,
|
|
26
|
+
ArrowDownUp,
|
|
26
27
|
} from 'lucide-react';
|
|
27
28
|
import { MoveTheWheelsProvider } from './providers/MoveTheWheelsProvider';
|
|
28
29
|
import { FeatureFlagProvider } from '@burdenoff/fe-libs/shared/feature-flags';
|
|
@@ -241,6 +242,13 @@ const NavRegistrar: FC<Pick<MoveTheWheelsRootProps, 'registerNavItems' | 'basePa
|
|
|
241
242
|
icon: <Store size={16} />,
|
|
242
243
|
order: 6,
|
|
243
244
|
},
|
|
245
|
+
{
|
|
246
|
+
id: 'mtw-import-export',
|
|
247
|
+
label: tr('movethewheels.root.nav.importExport', 'Import / Export'),
|
|
248
|
+
path: joinPath(basePath, 'import-export'),
|
|
249
|
+
icon: <ArrowDownUp size={16} />,
|
|
250
|
+
order: 6,
|
|
251
|
+
},
|
|
244
252
|
{
|
|
245
253
|
id: 'mtw-routes',
|
|
246
254
|
label: tr('movethewheels.root.nav.routes', 'Routes'),
|
|
@@ -72,6 +72,7 @@ const WarehouseDetailsPage = lazy(() => import('./pages/WarehouseDetailsPage'));
|
|
|
72
72
|
const CustomersPage = lazy(() => import('./pages/CustomersPage'));
|
|
73
73
|
const CustomerDetailsPage = lazy(() => import('./pages/CustomerDetailsPage'));
|
|
74
74
|
const MarketplacePage = lazy(() => import('./pages/MarketplacePage'));
|
|
75
|
+
const ImportExportPage = lazy(() => import('./pages/ImportExportPage'));
|
|
75
76
|
const RoutesPage = lazy(() => import('./pages/RoutesPage'));
|
|
76
77
|
const RouteDetailsPage = lazy(() => import('./pages/RouteDetailsPage'));
|
|
77
78
|
const AnalyticsPage = lazy(() => import('./pages/AnalyticsPage'));
|
|
@@ -160,6 +161,9 @@ export function MoveTheWheelsRoutes() {
|
|
|
160
161
|
{/* Marketplace — service providers and offerings (BOFF-3988) */}
|
|
161
162
|
<Route path="marketplace" element={<MarketplacePage />} />
|
|
162
163
|
|
|
164
|
+
{/* Bulk CSV/JSON import and dataset export (BOFF-3990) */}
|
|
165
|
+
<Route path="import-export" element={<ImportExportPage />} />
|
|
166
|
+
|
|
163
167
|
{/* Routes */}
|
|
164
168
|
<Route path="routes" element={<RoutesPage />} />
|
|
165
169
|
<Route path="routes/trip-utilization" element={<TripUtilizationPage />} />
|