@burdenoff/microfe-movethewheels 2026.1031.2 → 2026.1031.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.
Files changed (74) hide show
  1. package/dist/MoveTheWheelsRoot.js +121 -104
  2. package/dist/MoveTheWheelsRoutes.js +142 -133
  3. package/dist/components/DocumentsCard.js +11 -11
  4. package/dist/components/ProofOfDeliveryCard.js +13 -13
  5. package/dist/components/ShipmentRiskDialog.js +24 -24
  6. package/dist/components/ShippingQuoteResults.js +46 -46
  7. package/dist/components/TrackingAlertList.js +17 -17
  8. package/dist/generated/tracking-quality-operations.d.ts +140 -0
  9. package/dist/hooks/useEntityImport.js +68 -0
  10. package/dist/hooks/useOrderDispatch.js +17 -17
  11. package/dist/movethewheels/hooks/useEntityImport.d.ts +19 -0
  12. package/dist/movethewheels/pages/ImportExportPage.d.ts +9 -0
  13. package/dist/movethewheels/pages/SettingsPage.d.ts +1 -0
  14. package/dist/movethewheels/providers/ModuleSettingsProvider.d.ts +17 -0
  15. package/dist/movethewheels/utils/entityImport.d.ts +109 -0
  16. package/dist/movethewheels/utils/formatters.d.ts +23 -3
  17. package/dist/movethewheels/utils/moduleSettings.d.ts +52 -0
  18. package/dist/node_modules/lucide-react/dist/esm/icons/arrow-down-up.js +22 -0
  19. package/dist/node_modules/lucide-react/dist/esm/icons/bell.js +11 -0
  20. package/dist/node_modules/lucide-react/dist/esm/icons/file-spreadsheet.js +30 -0
  21. package/dist/node_modules/lucide-react/dist/esm/icons/file-up.js +22 -0
  22. package/dist/node_modules/lucide-react/dist/esm/icons/key-round.js +14 -0
  23. package/dist/node_modules/lucide-react/dist/esm/icons/plug.js +22 -0
  24. package/dist/node_modules/lucide-react/dist/esm/icons/rotate-ccw.js +11 -0
  25. package/dist/node_modules/lucide-react/dist/esm/icons/settings.js +13 -0
  26. package/dist/pages/AnalyticsPage.js +27 -27
  27. package/dist/pages/CreateOrderPage.js +340 -317
  28. package/dist/pages/CustomerDetailsPage.js +246 -246
  29. package/dist/pages/CustomersPage.js +88 -79
  30. package/dist/pages/DashboardPage.js +24 -24
  31. package/dist/pages/DeliveryCapturePage.js +74 -74
  32. package/dist/pages/DriverDetailsPage.js +337 -337
  33. package/dist/pages/DriversPage.js +119 -110
  34. package/dist/pages/FinancePage.js +26 -26
  35. package/dist/pages/ImportExportPage.js +435 -0
  36. package/dist/pages/InventoryPage.js +28 -28
  37. package/dist/pages/LiveTrackingPage.js +108 -108
  38. package/dist/pages/MarketplacePage.js +44 -44
  39. package/dist/pages/OrderDetailsPage.js +306 -306
  40. package/dist/pages/OrdersListPage.js +27 -27
  41. package/dist/pages/ProductDetailsPage.js +89 -89
  42. package/dist/pages/ProductsPage.js +86 -77
  43. package/dist/pages/RouteDetailsPage.js +282 -285
  44. package/dist/pages/RoutesPage.js +78 -78
  45. package/dist/pages/SettingsPage.js +459 -0
  46. package/dist/pages/TrackingPage.js +41 -41
  47. package/dist/pages/VehicleDetailsPage.js +207 -207
  48. package/dist/pages/VehiclesPage.js +100 -90
  49. package/dist/pages/WarehouseDetailsPage.js +132 -132
  50. package/dist/pages/WarehousesPage.js +97 -88
  51. package/dist/providers/ModuleSettingsProvider.js +74 -0
  52. package/dist/utils/entityImport.js +73 -0
  53. package/dist/utils/formatters.js +53 -26
  54. package/dist/utils/moduleSettings.js +132 -0
  55. package/package.json +4 -2
  56. package/src/generated/tracking-quality-operations.ts +184 -0
  57. package/src/movethewheels/MoveTheWheelsRoot.tsx +22 -3
  58. package/src/movethewheels/MoveTheWheelsRoutes.tsx +13 -0
  59. package/src/movethewheels/hooks/useEntityImport.ts +108 -0
  60. package/src/movethewheels/pages/CreateOrderPage.tsx +38 -6
  61. package/src/movethewheels/pages/CustomersPage.tsx +6 -1
  62. package/src/movethewheels/pages/DriversPage.tsx +6 -0
  63. package/src/movethewheels/pages/ImportExportPage.tsx +483 -0
  64. package/src/movethewheels/pages/ProductsPage.tsx +16 -3
  65. package/src/movethewheels/pages/RouteDetailsPage.tsx +3 -6
  66. package/src/movethewheels/pages/RoutesPage.tsx +2 -2
  67. package/src/movethewheels/pages/SettingsPage.tsx +516 -0
  68. package/src/movethewheels/pages/VehicleDetailsPage.tsx +4 -2
  69. package/src/movethewheels/pages/VehiclesPage.tsx +8 -1
  70. package/src/movethewheels/pages/WarehousesPage.tsx +6 -1
  71. package/src/movethewheels/providers/ModuleSettingsProvider.tsx +133 -0
  72. package/src/movethewheels/utils/entityImport.ts +239 -0
  73. package/src/movethewheels/utils/formatters.ts +69 -10
  74. package/src/movethewheels/utils/moduleSettings.ts +224 -0
@@ -0,0 +1,435 @@
1
+ import { ArrowDownUp as e } from "../node_modules/lucide-react/dist/esm/icons/arrow-down-up.js";
2
+ import { CircleAlert as t } from "../node_modules/lucide-react/dist/esm/icons/circle-alert.js";
3
+ import { CircleCheckBig as n } from "../node_modules/lucide-react/dist/esm/icons/circle-check-big.js";
4
+ import { Download as r } from "../node_modules/lucide-react/dist/esm/icons/download.js";
5
+ import { FileSpreadsheet as i } from "../node_modules/lucide-react/dist/esm/icons/file-spreadsheet.js";
6
+ import { FileUp as a } from "../node_modules/lucide-react/dist/esm/icons/file-up.js";
7
+ import { Info as o } from "../node_modules/lucide-react/dist/esm/icons/info.js";
8
+ import { LoaderCircle as s } from "../node_modules/lucide-react/dist/esm/icons/loader-circle.js";
9
+ import { ShieldCheck as c } from "../node_modules/lucide-react/dist/esm/icons/shield-check.js";
10
+ import { Upload as l } from "../node_modules/lucide-react/dist/esm/icons/upload.js";
11
+ import { joinPath as u } from "../utils/navigation.js";
12
+ import { useMoveTheWheels as d } from "../providers/MoveTheWheelsProvider.js";
13
+ import { Badge as f, Button as p, Card as m, CardContent as h, CardDescription as g, CardHeader as _, CardTitle as v, Checkbox as ee, Label as y, Select as b, SelectContent as te, SelectItem as ne, SelectTrigger as re, SelectValue as ie, Skeleton as x, Table as S, TableBody as C, TableCell as w, TableHead as T, TableHeader as E, TableRow as D } from "../components/ui.js";
14
+ import { PageLayout as ae } from "../components/PageLayout.js";
15
+ import { useEntityExport as O } from "../hooks/useEntityExport.js";
16
+ import { ExportStatusBanner as k } from "../components/ExportStatusBanner.js";
17
+ import { MTW_IMPORT_ENTITIES as A, MTW_IMPORT_MAX_ROWS as oe, buildCsvTemplate as se, detectImportFormat as ce, importEntity as le, isImportEntityType as j, summariseImport as M } from "../utils/entityImport.js";
18
+ import { useEntityImport as ue, useImportSpec as de } from "../hooks/useEntityImport.js";
19
+ import { useMemo as fe, useRef as pe, useState as N } from "react";
20
+ import { useSearchParams as P } from "react-router";
21
+ import { jsx as F, jsxs as I } from "react/jsx-runtime";
22
+ import { EmphasisPanel as L, PagePurpose as R } from "@burdenoff/fe-libs/ui";
23
+ //#region src/movethewheels/pages/ImportExportPage.tsx
24
+ var z = {
25
+ CUSTOMER: "customers",
26
+ VEHICLE: "fleet/vehicles",
27
+ DRIVER: "fleet/drivers",
28
+ WAREHOUSE: "inventory/warehouses",
29
+ CARGO_ITEM: "inventory/products"
30
+ }, me = [
31
+ {
32
+ entity: "orders",
33
+ label: "Orders",
34
+ noun: "order"
35
+ },
36
+ {
37
+ entity: "customers",
38
+ label: "Customers",
39
+ noun: "customer"
40
+ },
41
+ {
42
+ entity: "vehicles",
43
+ label: "Vehicles",
44
+ noun: "vehicle"
45
+ },
46
+ {
47
+ entity: "drivers",
48
+ label: "Drivers",
49
+ noun: "driver"
50
+ },
51
+ {
52
+ entity: "products",
53
+ label: "Products",
54
+ noun: "product"
55
+ }
56
+ ];
57
+ function he(e, t, n) {
58
+ let r = URL.createObjectURL(new Blob([t], { type: n })), i = document.createElement("a");
59
+ i.href = r, i.download = e, document.body.appendChild(i), i.click(), i.remove(), URL.revokeObjectURL(r);
60
+ }
61
+ function ge(e) {
62
+ if (e.allowedValues?.length) return e.allowedValues.join(", ");
63
+ let t = [e.type.toLowerCase()];
64
+ return e.maxLength !== null && t.push(`max ${e.maxLength} chars`), e.min !== null && e.max !== null ? t.push(`${e.min} to ${e.max}`) : e.min !== null && t.push(`at least ${e.min}`), t.join(", ");
65
+ }
66
+ function _e({ result: e, noun: r, onOpenList: i }) {
67
+ let a = e.status === "REJECTED" || e.dryRun && e.failedRows > 0, o = a ? "border-status-error-border bg-status-error-bg-subtle text-status-error-text" : e.status === "PARTIAL" ? "border-status-warning-border bg-status-warning-bg-subtle text-status-warning-text" : "border-status-success-border bg-status-success-bg-subtle text-status-success-text", s = a ? t : n;
68
+ return /* @__PURE__ */ I("div", {
69
+ className: "space-y-3",
70
+ "data-testid": "mtw-import-result",
71
+ "data-status": e.status,
72
+ children: [
73
+ /* @__PURE__ */ I("div", {
74
+ role: a ? "alert" : "status",
75
+ className: `flex items-start gap-2 rounded-lg border px-4 py-3 text-sm ${o}`,
76
+ children: [
77
+ /* @__PURE__ */ F(s, {
78
+ size: 18,
79
+ className: "mt-0.5 shrink-0"
80
+ }),
81
+ /* @__PURE__ */ F("span", {
82
+ className: "flex-1",
83
+ children: M(e, r)
84
+ }),
85
+ e.importedRows > 0 ? /* @__PURE__ */ I("button", {
86
+ type: "button",
87
+ className: "font-medium underline underline-offset-2",
88
+ onClick: i,
89
+ children: [
90
+ "View ",
91
+ r,
92
+ "s"
93
+ ]
94
+ }) : null
95
+ ]
96
+ }),
97
+ /* @__PURE__ */ I("div", {
98
+ className: "flex flex-wrap gap-2 text-sm",
99
+ children: [
100
+ /* @__PURE__ */ I(f, {
101
+ variant: "outline",
102
+ children: ["Rows: ", e.totalRows]
103
+ }),
104
+ /* @__PURE__ */ I(f, {
105
+ variant: "outline",
106
+ children: ["Valid: ", e.validRows]
107
+ }),
108
+ /* @__PURE__ */ I(f, {
109
+ variant: "outline",
110
+ children: ["Imported: ", e.importedRows]
111
+ }),
112
+ /* @__PURE__ */ I(f, {
113
+ variant: e.failedRows > 0 ? "destructive" : "outline",
114
+ children: ["Problems: ", e.failedRows]
115
+ })
116
+ ]
117
+ }),
118
+ e.errors.length > 0 ? /* @__PURE__ */ F("div", {
119
+ className: "max-h-80 overflow-auto rounded-lg border border-border-default",
120
+ children: /* @__PURE__ */ I(S, { children: [/* @__PURE__ */ F(E, { children: /* @__PURE__ */ I(D, { children: [
121
+ /* @__PURE__ */ F(T, {
122
+ className: "w-20",
123
+ children: "Row"
124
+ }),
125
+ /* @__PURE__ */ F(T, {
126
+ className: "w-40",
127
+ children: "Column"
128
+ }),
129
+ /* @__PURE__ */ F(T, { children: "Problem" })
130
+ ] }) }), /* @__PURE__ */ F(C, { children: e.errors.map((e, t) => /* @__PURE__ */ I(D, { children: [
131
+ /* @__PURE__ */ F(w, { children: e.row }),
132
+ /* @__PURE__ */ F(w, {
133
+ className: "font-mono text-xs",
134
+ children: e.field ?? "—"
135
+ }),
136
+ /* @__PURE__ */ F(w, { children: e.message })
137
+ ] }, `${e.row}-${e.field ?? ""}-${t}`)) })] })
138
+ }) : null,
139
+ e.errorsTruncated ? /* @__PURE__ */ I("p", {
140
+ className: "text-sm text-text-secondary",
141
+ children: [
142
+ "Only the first ",
143
+ e.errors.length,
144
+ " problems are listed. Fix these and validate again to see the rest."
145
+ ]
146
+ }) : null
147
+ ]
148
+ });
149
+ }
150
+ function ve({ entity: e, label: t, noun: n }) {
151
+ let { state: i, isExporting: a, startExport: o, dismiss: c } = O(e, n);
152
+ return /* @__PURE__ */ I("div", {
153
+ className: "space-y-2 rounded-lg border border-border-default p-4",
154
+ children: [/* @__PURE__ */ I("div", {
155
+ className: "flex items-center justify-between gap-3",
156
+ children: [/* @__PURE__ */ I("div", { children: [/* @__PURE__ */ F("p", {
157
+ className: "font-medium text-text-primary",
158
+ children: t
159
+ }), /* @__PURE__ */ I("p", {
160
+ className: "text-sm text-text-secondary",
161
+ children: [
162
+ "Every ",
163
+ n,
164
+ " in this workspace, as CSV"
165
+ ]
166
+ })] }), /* @__PURE__ */ I(p, {
167
+ variant: "outline",
168
+ size: "sm",
169
+ disabled: a,
170
+ "aria-label": `Export ${t}`,
171
+ onClick: o,
172
+ children: [a ? /* @__PURE__ */ F(s, {
173
+ size: 14,
174
+ className: "animate-spin"
175
+ }) : /* @__PURE__ */ F(r, { size: 14 }), /* @__PURE__ */ F("span", {
176
+ className: "ml-1",
177
+ children: a ? "Exporting…" : "Export"
178
+ })]
179
+ })]
180
+ }), /* @__PURE__ */ F(k, {
181
+ state: i,
182
+ onDismiss: c
183
+ })]
184
+ });
185
+ }
186
+ function B() {
187
+ let { basePath: n, navigate: S } = d(), { specs: C, isLoading: w, error: T } = de(), { run: E, isRunning: D } = ue(), [O] = P(), [k, M] = N(() => {
188
+ let e = O.get("entity");
189
+ return j(e) ? e : "CUSTOMER";
190
+ }), [B, V] = N(null), [H, ye] = N(!1), [U, W] = N(null), [G, K] = N(null), [q, J] = N(null), Y = pe(null), X = le(k), Z = fe(() => C.find((e) => e.entityType === k) ?? null, [C, k]), Q = () => {
191
+ W(null), K(null), J(null);
192
+ }, be = (e) => {
193
+ j(e) && (M(e), V(null), Q());
194
+ }, xe = async (e) => {
195
+ let t = e.target.files?.[0];
196
+ if (e.target.value = "", Q(), !t) return;
197
+ let n = ce(t.name, t.type);
198
+ if (!n) {
199
+ V(null), K("Choose a .csv or .json file.");
200
+ return;
201
+ }
202
+ V({
203
+ name: t.name,
204
+ format: n,
205
+ content: await t.text()
206
+ });
207
+ }, $ = async (e) => {
208
+ if (B) {
209
+ K(null), J(e ? "validate" : "import");
210
+ try {
211
+ W(await E({
212
+ entityType: k,
213
+ format: B.format,
214
+ content: B.content,
215
+ dryRun: e,
216
+ allowPartial: H
217
+ }));
218
+ } catch (e) {
219
+ W(null), K(e instanceof Error ? e.message : "The import could not be completed.");
220
+ }
221
+ }
222
+ }, Se = () => {
223
+ Z && he(`${X.label.toLowerCase()}-import-template.csv`, se(Z), "text/csv");
224
+ }, Ce = B !== null && !D && U?.status !== "COMPLETED";
225
+ return /* @__PURE__ */ I(ae, {
226
+ title: "Import / Export",
227
+ subtitle: "Bring master data in from a file, or take a full copy out",
228
+ icon: /* @__PURE__ */ F(e, { size: 24 }),
229
+ children: [
230
+ /* @__PURE__ */ F(R, {
231
+ icon: /* @__PURE__ */ F(e, { size: 18 }),
232
+ children: "Onboard or migrate without re-keying: import customers, vehicles, drivers, warehouses or products from CSV or JSON. Every row is checked before anything is saved, so you can fix a file and try again. Export any dataset for accounting, BI tools or backups."
233
+ }),
234
+ /* @__PURE__ */ F("input", {
235
+ ref: Y,
236
+ type: "file",
237
+ accept: ".csv,.json,text/csv,application/json",
238
+ className: "hidden",
239
+ "data-testid": "mtw-import-file",
240
+ onChange: (e) => void xe(e)
241
+ }),
242
+ /* @__PURE__ */ I("div", {
243
+ className: "grid grid-cols-1 gap-6 xl:grid-cols-3",
244
+ children: [/* @__PURE__ */ I(L, {
245
+ className: "space-y-5 xl:col-span-2",
246
+ children: [
247
+ /* @__PURE__ */ I("div", {
248
+ className: "flex flex-wrap items-center justify-between gap-3",
249
+ children: [/* @__PURE__ */ I("h2", {
250
+ className: "flex items-center gap-2 text-base font-semibold text-text-primary",
251
+ children: [/* @__PURE__ */ F(l, { size: 18 }), " Import data"]
252
+ }), /* @__PURE__ */ I(f, {
253
+ variant: "outline",
254
+ children: [
255
+ "CSV or JSON · up to ",
256
+ oe,
257
+ " rows · 2 MB"
258
+ ]
259
+ })]
260
+ }),
261
+ /* @__PURE__ */ I("div", {
262
+ className: "grid grid-cols-1 gap-4 md:grid-cols-2",
263
+ children: [/* @__PURE__ */ I("div", {
264
+ className: "space-y-2",
265
+ children: [/* @__PURE__ */ F(y, {
266
+ htmlFor: "mtw-import-entity",
267
+ children: "What are you importing?"
268
+ }), /* @__PURE__ */ I(b, {
269
+ value: k,
270
+ onValueChange: be,
271
+ children: [/* @__PURE__ */ F(re, {
272
+ id: "mtw-import-entity",
273
+ "aria-label": "Import type",
274
+ children: /* @__PURE__ */ F(ie, {})
275
+ }), /* @__PURE__ */ F(te, { children: A.map((e) => /* @__PURE__ */ F(ne, {
276
+ value: e.entityType,
277
+ children: e.label
278
+ }, e.entityType)) })]
279
+ })]
280
+ }), /* @__PURE__ */ I("div", {
281
+ className: "space-y-2",
282
+ children: [
283
+ /* @__PURE__ */ F(y, { children: "File" }),
284
+ /* @__PURE__ */ I("div", {
285
+ className: "flex flex-wrap items-center gap-2",
286
+ children: [/* @__PURE__ */ I(p, {
287
+ variant: "outline",
288
+ onClick: () => Y.current?.click(),
289
+ children: [/* @__PURE__ */ F(a, { size: 16 }), /* @__PURE__ */ F("span", {
290
+ className: "ml-1",
291
+ children: B ? "Choose another file" : "Choose file"
292
+ })]
293
+ }), /* @__PURE__ */ I(p, {
294
+ variant: "ghost",
295
+ disabled: !Z,
296
+ onClick: Se,
297
+ "aria-label": `Download ${X.label} CSV template`,
298
+ children: [/* @__PURE__ */ F(i, { size: 16 }), /* @__PURE__ */ F("span", {
299
+ className: "ml-1",
300
+ children: "Template"
301
+ })]
302
+ })]
303
+ }),
304
+ B ? /* @__PURE__ */ I("p", {
305
+ className: "text-sm text-text-secondary",
306
+ "data-testid": "mtw-import-file-name",
307
+ children: [
308
+ B.name,
309
+ " (",
310
+ B.format,
311
+ ")"
312
+ ]
313
+ }) : null
314
+ ]
315
+ })]
316
+ }),
317
+ /* @__PURE__ */ I("div", {
318
+ className: "flex items-start gap-2",
319
+ children: [/* @__PURE__ */ F(ee, {
320
+ id: "mtw-import-partial",
321
+ checked: H,
322
+ onCheckedChange: (e) => {
323
+ ye(e === !0), U && !U.dryRun && Q();
324
+ }
325
+ }), /* @__PURE__ */ F(y, {
326
+ htmlFor: "mtw-import-partial",
327
+ className: "text-sm font-normal leading-snug",
328
+ children: "Import the valid rows even if some rows have problems. When this is off, a file with any problem imports nothing."
329
+ })]
330
+ }),
331
+ /* @__PURE__ */ I("div", {
332
+ className: "flex flex-wrap gap-2",
333
+ children: [/* @__PURE__ */ I(p, {
334
+ variant: "outline",
335
+ disabled: !B || D,
336
+ onClick: () => void $(!0),
337
+ children: [D && q === "validate" ? /* @__PURE__ */ F(s, {
338
+ size: 16,
339
+ className: "animate-spin"
340
+ }) : /* @__PURE__ */ F(c, { size: 16 }), /* @__PURE__ */ F("span", {
341
+ className: "ml-1",
342
+ children: "Validate"
343
+ })]
344
+ }), /* @__PURE__ */ I(p, {
345
+ disabled: !Ce,
346
+ onClick: () => void $(!1),
347
+ children: [D && q === "import" ? /* @__PURE__ */ F(s, {
348
+ size: 16,
349
+ className: "animate-spin"
350
+ }) : /* @__PURE__ */ F(l, { size: 16 }), /* @__PURE__ */ I("span", {
351
+ className: "ml-1",
352
+ children: ["Import ", X.label.toLowerCase()]
353
+ })]
354
+ })]
355
+ }),
356
+ G ? /* @__PURE__ */ I("div", {
357
+ role: "alert",
358
+ "data-testid": "mtw-import-error",
359
+ className: "flex items-start gap-2 rounded-lg border border-status-error-border bg-status-error-bg-subtle px-4 py-3 text-sm text-status-error-text",
360
+ children: [/* @__PURE__ */ F(t, {
361
+ size: 18,
362
+ className: "mt-0.5 shrink-0"
363
+ }), /* @__PURE__ */ F("span", { children: G })]
364
+ }) : null,
365
+ U ? /* @__PURE__ */ F(_e, {
366
+ result: U,
367
+ noun: X.noun,
368
+ onOpenList: () => S?.(u(n, z[k]))
369
+ }) : null
370
+ ]
371
+ }), /* @__PURE__ */ I(m, { children: [/* @__PURE__ */ I(_, { children: [/* @__PURE__ */ I(v, {
372
+ className: "flex items-center gap-2 text-base",
373
+ children: [
374
+ /* @__PURE__ */ F(o, { size: 18 }),
375
+ " ",
376
+ X.label,
377
+ " columns"
378
+ ]
379
+ }), /* @__PURE__ */ F(g, { children: "Use these column names as the CSV header or JSON keys. Values are not case-sensitive for lists." })] }), /* @__PURE__ */ F(h, { children: w ? /* @__PURE__ */ I("div", {
380
+ className: "space-y-2",
381
+ children: [
382
+ /* @__PURE__ */ F(x, { className: "h-6 w-full" }),
383
+ /* @__PURE__ */ F(x, { className: "h-6 w-full" }),
384
+ /* @__PURE__ */ F(x, { className: "h-6 w-2/3" })
385
+ ]
386
+ }) : T ? /* @__PURE__ */ F("p", {
387
+ className: "text-sm text-status-error-text",
388
+ children: "The column guide could not be loaded. Imports still work."
389
+ }) : Z ? /* @__PURE__ */ F("ul", {
390
+ className: "space-y-3",
391
+ "data-testid": "mtw-import-columns",
392
+ children: Z.columns.map((e) => /* @__PURE__ */ I("li", {
393
+ className: "text-sm",
394
+ children: [
395
+ /* @__PURE__ */ I("div", {
396
+ className: "flex flex-wrap items-center gap-2",
397
+ children: [
398
+ /* @__PURE__ */ F("code", {
399
+ className: "font-mono text-text-primary",
400
+ children: e.name
401
+ }),
402
+ e.required ? /* @__PURE__ */ F(f, {
403
+ variant: "default",
404
+ children: "required"
405
+ }) : null,
406
+ Z.keyColumn === e.name ? /* @__PURE__ */ F(f, {
407
+ variant: "outline",
408
+ children: "unique"
409
+ }) : null
410
+ ]
411
+ }),
412
+ /* @__PURE__ */ F("p", {
413
+ className: "text-text-secondary",
414
+ children: e.description
415
+ }),
416
+ /* @__PURE__ */ F("p", {
417
+ className: "text-xs text-text-tertiary",
418
+ children: ge(e)
419
+ })
420
+ ]
421
+ }, e.name))
422
+ }) : null })] })]
423
+ }),
424
+ /* @__PURE__ */ I(m, { children: [/* @__PURE__ */ I(_, { children: [/* @__PURE__ */ I(v, {
425
+ className: "flex items-center gap-2 text-base",
426
+ children: [/* @__PURE__ */ F(r, { size: 18 }), " Export data"]
427
+ }), /* @__PURE__ */ F(g, { children: "Exports run in the background and download as a ZIP. Large exports stay available on the Exports page." })] }), /* @__PURE__ */ F(h, {
428
+ className: "grid grid-cols-1 gap-3 md:grid-cols-2",
429
+ children: me.map((e) => /* @__PURE__ */ F(ve, { ...e }, e.entity))
430
+ })] })
431
+ ]
432
+ });
433
+ }
434
+ //#endregion
435
+ export { B as default };
@@ -14,14 +14,14 @@ import { TriangleAlert as s } from "../node_modules/lucide-react/dist/esm/icons/
14
14
  import { Warehouse as ae } from "../node_modules/lucide-react/dist/esm/icons/warehouse.js";
15
15
  import { joinPath as c } from "../utils/navigation.js";
16
16
  import { useMoveTheWheels as l } from "../providers/MoveTheWheelsProvider.js";
17
- import { useInventory as oe, useInventoryStats as se, useInventoryTransactions as ce } from "../hooks/useInventory.js";
18
- import { Badge as u, Button as d, Card as f, CardContent as p, CardHeader as le, CardTitle as ue, Progress as de } from "../components/ui.js";
19
- import { PageLayout as fe, PageSection as m } from "../components/PageLayout.js";
20
- import { MetricCard as h, MetricCardSkeleton as g, MetricGrid as _ } from "../components/MetricCard.js";
21
- import { StatusBadge as pe } from "../components/StatusBadge.js";
22
- import { DataTable as me } from "../components/DataTable.js";
23
- import { Notice as he, useNotice as ge } from "../components/Notice.js";
24
- import { formatCurrency as _e, formatDate as ve } from "../utils/formatters.js";
17
+ import { formatCurrency as oe, formatDate as se } from "../utils/formatters.js";
18
+ import { useInventory as ce, useInventoryStats as le, useInventoryTransactions as ue } from "../hooks/useInventory.js";
19
+ import { Badge as u, Button as d, Card as f, CardContent as p, CardHeader as de, CardTitle as m, Progress as fe } from "../components/ui.js";
20
+ import { PageLayout as pe, PageSection as h } from "../components/PageLayout.js";
21
+ import { MetricCard as g, MetricCardSkeleton as me, MetricGrid as _ } from "../components/MetricCard.js";
22
+ import { StatusBadge as he } from "../components/StatusBadge.js";
23
+ import { DataTable as ge } from "../components/DataTable.js";
24
+ import { Notice as _e, useNotice as ve } from "../components/Notice.js";
25
25
  import { useDebouncedValue as ye } from "../hooks/useDebouncedValue.js";
26
26
  import { AdjustStockDialog as be, ReceiveStockDialog as xe, TransferStockDialog as Se } from "../components/StockActionDialogs.js";
27
27
  import { useMemo as Ce, useState as v } from "react";
@@ -63,11 +63,11 @@ function O() {
63
63
  let { t: O } = we(), k = (e, t) => {
64
64
  let n = O(e);
65
65
  return n === e ? t : n;
66
- }, { basePath: Te, navigate: Ee } = l(), [A, De] = v(""), [j, M] = v(0), [N, Oe] = v(10), { inventory: P, lowStockItems: F, products: I, warehouses: ke, totalItems: Ae, hasMore: je, isLoading: L, error: R, refetch: z } = oe({
66
+ }, { basePath: Te, navigate: Ee } = l(), [A, De] = v(""), [j, M] = v(0), [N, Oe] = v(10), { inventory: P, lowStockItems: F, products: I, warehouses: ke, totalItems: Ae, hasMore: je, isLoading: L, error: R, refetch: z } = ce({
67
67
  search: ye(A),
68
68
  limit: N,
69
69
  offset: j * N
70
- }), { stats: B, refetch: Me } = se(), { transactions: V, isLoading: Ne, error: H, refetch: Pe } = ce({ limit: 8 }), { message: U, notify: Fe } = ge(6e3), [Ie, W] = v(!1), [G, K] = v(null), [q, J] = v(null), Y = (e) => ({
70
+ }), { stats: B, refetch: Me } = le(), { transactions: V, isLoading: Ne, error: H, refetch: Pe } = ue({ limit: 8 }), { message: U, notify: Fe } = ve(6e3), [Ie, W] = v(!1), [G, K] = v(null), [q, J] = v(null), Y = (e) => ({
71
71
  id: e.id,
72
72
  productName: e.productName,
73
73
  sku: e.sku,
@@ -155,7 +155,7 @@ function O() {
155
155
  {
156
156
  id: "state",
157
157
  header: k("movethewheels.inventory.stock.status", "Status"),
158
- cell: (e) => /* @__PURE__ */ y(pe, {
158
+ cell: (e) => /* @__PURE__ */ y(he, {
159
159
  status: e.stockState === "out_of_stock" ? k("movethewheels.inventory.stock.outOfStock", "Out of stock") : e.stockState === "low" ? k("movethewheels.inventory.stock.low", "Low") : k("movethewheels.inventory.stock.inStock", "In stock"),
160
160
  customColors: w[e.stockState],
161
161
  size: "sm"
@@ -193,7 +193,7 @@ function O() {
193
193
  })
194
194
  }
195
195
  ];
196
- return /* @__PURE__ */ b(fe, {
196
+ return /* @__PURE__ */ b(pe, {
197
197
  title: k("movethewheels.inventory.title", "Inventory"),
198
198
  subtitle: k("movethewheels.inventory.subtitle", "Manage your products and warehouses"),
199
199
  icon: /* @__PURE__ */ y(o, { size: 24 }),
@@ -226,7 +226,7 @@ function O() {
226
226
  }),
227
227
  U && /* @__PURE__ */ y("div", {
228
228
  className: "mb-2",
229
- children: /* @__PURE__ */ y(he, { message: U })
229
+ children: /* @__PURE__ */ y(_e, { message: U })
230
230
  }),
231
231
  R && !L ? /* @__PURE__ */ y(f, {
232
232
  className: "border-status-error-border",
@@ -250,46 +250,46 @@ function O() {
250
250
  })
251
251
  }) : L ? /* @__PURE__ */ y(_, {
252
252
  columns: 6,
253
- children: Array.from({ length: 6 }).map((e, t) => /* @__PURE__ */ y(g, {}, t))
253
+ children: Array.from({ length: 6 }).map((e, t) => /* @__PURE__ */ y(me, {}, t))
254
254
  }) : /* @__PURE__ */ b(_, {
255
255
  columns: 6,
256
256
  children: [
257
- /* @__PURE__ */ y(h, {
257
+ /* @__PURE__ */ y(g, {
258
258
  label: k("movethewheels.inventory.metrics.totalOnHand", "Total on-hand"),
259
259
  value: B.totalOnHand.toLocaleString(),
260
260
  icon: /* @__PURE__ */ y(r, { size: 20 }),
261
261
  accent: "brand",
262
262
  subtitle: k("movethewheels.inventory.metrics.onHandHint", "Units in the network")
263
263
  }),
264
- /* @__PURE__ */ y(h, {
264
+ /* @__PURE__ */ y(g, {
265
265
  label: k("movethewheels.inventory.metrics.available", "Available"),
266
266
  value: B.totalAvailable.toLocaleString(),
267
267
  icon: /* @__PURE__ */ y(a, { size: 20 }),
268
268
  accent: "success",
269
269
  subtitle: k("movethewheels.inventory.metrics.availableHint", "Free to promise")
270
270
  }),
271
- /* @__PURE__ */ y(h, {
271
+ /* @__PURE__ */ y(g, {
272
272
  label: k("movethewheels.inventory.metrics.allocated", "Allocated"),
273
273
  value: B.totalAllocated.toLocaleString(),
274
274
  icon: /* @__PURE__ */ y(te, { size: 20 }),
275
275
  accent: "blue",
276
276
  subtitle: k("movethewheels.inventory.metrics.allocatedHint", "Committed to orders")
277
277
  }),
278
- /* @__PURE__ */ y(h, {
278
+ /* @__PURE__ */ y(g, {
279
279
  label: k("movethewheels.inventory.metrics.lowStock", "Low-stock items"),
280
280
  value: B.lowStockCount.toLocaleString(),
281
281
  icon: /* @__PURE__ */ y(s, { size: 20 }),
282
282
  accent: "warning",
283
283
  subtitle: k("movethewheels.inventory.metrics.lowStockHint", "At or under reorder point")
284
284
  }),
285
- /* @__PURE__ */ y(h, {
285
+ /* @__PURE__ */ y(g, {
286
286
  label: k("movethewheels.inventory.metrics.totalProducts", "Catalog items"),
287
287
  value: B.totalProducts.toLocaleString(),
288
288
  icon: /* @__PURE__ */ y(o, { size: 20 }),
289
289
  accent: "neutral",
290
290
  onClick: () => Z("inventory/products")
291
291
  }),
292
- /* @__PURE__ */ y(h, {
292
+ /* @__PURE__ */ y(g, {
293
293
  label: k("movethewheels.inventory.metrics.warehouses", "Warehouses"),
294
294
  value: B.totalWarehouses.toLocaleString(),
295
295
  icon: /* @__PURE__ */ y(ae, { size: 20 }),
@@ -350,9 +350,9 @@ function O() {
350
350
  className: "grid grid-cols-1 xl:grid-cols-3 gap-6",
351
351
  children: [/* @__PURE__ */ y("div", {
352
352
  className: "xl:col-span-2",
353
- children: /* @__PURE__ */ y(m, {
353
+ children: /* @__PURE__ */ y(h, {
354
354
  title: k("movethewheels.inventory.stock.title", "Stock position"),
355
- children: /* @__PURE__ */ y(me, {
355
+ children: /* @__PURE__ */ y(ge, {
356
356
  columns: Re,
357
357
  data: Le,
358
358
  isLoading: L,
@@ -386,7 +386,7 @@ function O() {
386
386
  })
387
387
  }), /* @__PURE__ */ b(f, {
388
388
  className: "h-fit",
389
- children: [/* @__PURE__ */ y(le, { children: /* @__PURE__ */ b(ue, {
389
+ children: [/* @__PURE__ */ y(de, { children: /* @__PURE__ */ b(m, {
390
390
  className: "flex items-center gap-2",
391
391
  children: [/* @__PURE__ */ y(ee, { size: 18 }), k("movethewheels.inventory.transactions.title", "Recent transactions")]
392
392
  }) }), /* @__PURE__ */ y(p, {
@@ -428,7 +428,7 @@ function O() {
428
428
  }),
429
429
  /* @__PURE__ */ y("p", {
430
430
  className: "text-xs text-text-secondary",
431
- children: ve(r.createdAt, "short")
431
+ children: se(r.createdAt, "short")
432
432
  })
433
433
  ]
434
434
  }), /* @__PURE__ */ y("span", {
@@ -443,7 +443,7 @@ function O() {
443
443
  }),
444
444
  /* @__PURE__ */ b("div", {
445
445
  className: "grid grid-cols-1 lg:grid-cols-2 gap-6",
446
- children: [/* @__PURE__ */ y(m, {
446
+ children: [/* @__PURE__ */ y(h, {
447
447
  title: k("movethewheels.inventory.warehouses.title", "Warehouses"),
448
448
  actions: /* @__PURE__ */ b(d, {
449
449
  variant: "ghost",
@@ -487,7 +487,7 @@ function O() {
487
487
  }), e.utilization === void 0 ? /* @__PURE__ */ y("p", {
488
488
  className: "text-xs text-text-tertiary",
489
489
  children: "Not provided"
490
- }) : /* @__PURE__ */ y(de, {
490
+ }) : /* @__PURE__ */ y(fe, {
491
491
  value: e.utilization,
492
492
  className: "w-24 h-2"
493
493
  })]
@@ -497,7 +497,7 @@ function O() {
497
497
  }, e.id))
498
498
  })
499
499
  }) })
500
- }), /* @__PURE__ */ y(m, {
500
+ }), /* @__PURE__ */ y(h, {
501
501
  title: k("movethewheels.inventory.products.title", "Recent Products"),
502
502
  actions: /* @__PURE__ */ b(d, {
503
503
  variant: "ghost",
@@ -532,7 +532,7 @@ function O() {
532
532
  className: "text-right",
533
533
  children: [/* @__PURE__ */ y("p", {
534
534
  className: "font-medium",
535
- children: e.value === void 0 ? "Not provided" : _e(e.value)
535
+ children: e.value === void 0 ? "Not provided" : oe(e.value)
536
536
  }), /* @__PURE__ */ y(u, {
537
537
  variant: "outline",
538
538
  children: e.category ?? "Not provided"