@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
@@ -1073,6 +1073,12 @@ export type MtwDemandForecastVariance = {
1073
1073
  scopeType: DemandForecastScope;
1074
1074
  volumeVariance: Scalars['Int']['output'];
1075
1075
  };
1076
+ /** Unit MoveTheWheels shows distances in. */
1077
+ export type MtwDistanceUnit =
1078
+ /** Kilometres */
1079
+ 'KM'
1080
+ /** Miles */
1081
+ | 'MI';
1076
1082
  export type MtwDriver = {
1077
1083
  __typename?: 'MtwDriver';
1078
1084
  baseSalary?: Maybe<Scalars['Float']['output']>;
@@ -2034,6 +2040,63 @@ export type MtwFuelVolumeObservationInput = {
2034
2040
  sourceReference?: InputMaybe<Scalars['String']['input']>;
2035
2041
  sourceRevision?: InputMaybe<Scalars['String']['input']>;
2036
2042
  };
2043
+ export type MtwImportColumn = {
2044
+ __typename?: 'MtwImportColumn';
2045
+ allowedValues?: Maybe<Array<Scalars['String']['output']>>;
2046
+ description: Scalars['String']['output'];
2047
+ max?: Maybe<Scalars['Float']['output']>;
2048
+ maxLength?: Maybe<Scalars['Int']['output']>;
2049
+ min?: Maybe<Scalars['Float']['output']>;
2050
+ name: Scalars['String']['output'];
2051
+ required: Scalars['Boolean']['output'];
2052
+ type: MtwImportColumnType;
2053
+ };
2054
+ export type MtwImportColumnType = 'BOOLEAN' | 'DATE' | 'EMAIL' | 'ENUM' | 'INTEGER' | 'JSON' | 'NUMBER' | 'STRING';
2055
+ export type MtwImportEntitySpec = {
2056
+ __typename?: 'MtwImportEntitySpec';
2057
+ columns: Array<MtwImportColumn>;
2058
+ entityType: MtwImportEntityType;
2059
+ /** Column that must be unique in the workspace, if any. */
2060
+ keyColumn?: Maybe<Scalars['String']['output']>;
2061
+ };
2062
+ export type MtwImportEntityType = 'CARGO_ITEM' | 'CUSTOMER' | 'DRIVER' | 'VEHICLE' | 'WAREHOUSE';
2063
+ export type MtwImportFormat = 'CSV' | 'JSON';
2064
+ export type MtwImportInput = {
2065
+ /** Import the valid rows even when other rows are invalid. */
2066
+ allowPartial?: InputMaybe<Scalars['Boolean']['input']>;
2067
+ /** The CSV (with a header row) or JSON (array of objects) document, at most 2 MB and 1000 rows. */
2068
+ content: Scalars['String']['input'];
2069
+ /** Validate every row without writing anything. */
2070
+ dryRun?: InputMaybe<Scalars['Boolean']['input']>;
2071
+ format: MtwImportFormat;
2072
+ };
2073
+ export type MtwImportResult = {
2074
+ __typename?: 'MtwImportResult';
2075
+ createdIds: Array<Scalars['ID']['output']>;
2076
+ dryRun: Scalars['Boolean']['output'];
2077
+ entityType: MtwImportEntityType;
2078
+ errors: Array<MtwImportRowError>;
2079
+ /** True when more row errors exist than are returned (the first 500 are returned). */
2080
+ errorsTruncated: Scalars['Boolean']['output'];
2081
+ failedRows: Scalars['Int']['output'];
2082
+ importedRows: Scalars['Int']['output'];
2083
+ status: MtwImportStatus;
2084
+ totalRows: Scalars['Int']['output'];
2085
+ validRows: Scalars['Int']['output'];
2086
+ };
2087
+ export type MtwImportRowError = {
2088
+ __typename?: 'MtwImportRowError';
2089
+ field?: Maybe<Scalars['String']['output']>;
2090
+ message: Scalars['String']['output'];
2091
+ /** 1-based data row number (the CSV header is not counted). */
2092
+ row: Scalars['Int']['output'];
2093
+ };
2094
+ /**
2095
+ * DRY_RUN: validated only, nothing written. COMPLETED: every row imported.
2096
+ * PARTIAL: allowPartial was set and some rows failed. REJECTED: some rows were
2097
+ * invalid and allowPartial was not set, so nothing was written.
2098
+ */
2099
+ export type MtwImportStatus = 'COMPLETED' | 'DRY_RUN' | 'PARTIAL' | 'REJECTED';
2037
2100
  /** A recorded manual issuer review; workspace privileges do not establish professional qualifications. */
2038
2101
  export type MtwInspectorQualification = {
2039
2102
  __typename?: 'MtwInspectorQualification';
@@ -2189,6 +2252,30 @@ export type MtwMarketplaceStats = {
2189
2252
  providers: Scalars['Int']['output'];
2190
2253
  verifiedProviders: Scalars['Int']['output'];
2191
2254
  };
2255
+ /**
2256
+ * Workspace operational defaults for MoveTheWheels (BOFF-10488): how amounts,
2257
+ * distances, weights and times are shown, and what new orders start from.
2258
+ * Company profile, notifications, two-factor authentication and integrations
2259
+ * are platform settings and are not part of this type.
2260
+ */
2261
+ export type MtwModuleSettings = {
2262
+ __typename?: 'MtwModuleSettings';
2263
+ /** ISO 4217 code amounts without their own currency are shown in. */
2264
+ defaultCurrency: Scalars['String']['output'];
2265
+ /** Hours from now a new order's suggested delivery time falls (1 to 720). */
2266
+ defaultDeliveryWindowHours: Scalars['Int']['output'];
2267
+ /** Priority a new order starts with. */
2268
+ defaultOrderPriority: DeliveryPriority;
2269
+ distanceUnit: MtwDistanceUnit;
2270
+ /** False until the workspace saves its settings; the values are then the platform defaults. */
2271
+ isCustomized: Scalars['Boolean']['output'];
2272
+ /** IANA time zone dates and times are shown in. Null shows each viewer their own local time. */
2273
+ timezone?: Maybe<Scalars['String']['output']>;
2274
+ updatedAt?: Maybe<Scalars['DateTime']['output']>;
2275
+ updatedBy?: Maybe<Scalars['ID']['output']>;
2276
+ weightUnit: MtwWeightUnit;
2277
+ workspaceId: Scalars['ID']['output'];
2278
+ };
2192
2279
  export type MtwMoveReviewSource = {
2193
2280
  __typename?: 'MtwMoveReviewSource';
2194
2281
  reference: Scalars['String']['output'];
@@ -3688,6 +3775,12 @@ export type MtwWarehouseStats = {
3688
3775
  avgUtilization: Scalars['Float']['output'];
3689
3776
  total: Scalars['Int']['output'];
3690
3777
  };
3778
+ /** Unit MoveTheWheels shows weights in. */
3779
+ export type MtwWeightUnit =
3780
+ /** Kilograms */
3781
+ 'KG'
3782
+ /** Pounds */
3783
+ | 'LB';
3691
3784
  export type Mutation = {
3692
3785
  __typename?: 'Mutation';
3693
3786
  _empty?: Maybe<Scalars['String']['output']>;
@@ -3744,6 +3837,11 @@ export type Mutation = {
3744
3837
  deleteWarehouse: Scalars['Boolean']['output'];
3745
3838
  exportMtwEntities: MtwExportJob;
3746
3839
  holdMtwContainer: MtwContainerHold;
3840
+ importMtwCargoItems: MtwImportResult;
3841
+ importMtwCustomers: MtwImportResult;
3842
+ importMtwDrivers: MtwImportResult;
3843
+ importMtwVehicles: MtwImportResult;
3844
+ importMtwWarehouses: MtwImportResult;
3747
3845
  openMtwContainerCycle: MtwContainerCycle;
3748
3846
  openMtwFreeTimeClock: MtwFreeTimeClock;
3749
3847
  optimizeRoute: MtwRoute;
@@ -3789,6 +3887,8 @@ export type Mutation = {
3789
3887
  updateDriverStatus: MtwDriver;
3790
3888
  updateIntegration: MtwIntegration;
3791
3889
  updateInventoryItem: MtwInventoryItem;
3890
+ /** Save the workspace's MoveTheWheels operational defaults. Every field is validated. */
3891
+ updateMtwModuleSettings: MtwModuleSettings;
3792
3892
  updateRoute: MtwRoute;
3793
3893
  updateServiceOffering: MtwServiceOffering;
3794
3894
  updateServiceProvider: MtwServiceProvider;
@@ -3928,6 +4028,21 @@ export type MutationExportMtwEntitiesArgs = {
3928
4028
  export type MutationHoldMtwContainerArgs = {
3929
4029
  input: HoldMtwContainerInput;
3930
4030
  };
4031
+ export type MutationImportMtwCargoItemsArgs = {
4032
+ input: MtwImportInput;
4033
+ };
4034
+ export type MutationImportMtwCustomersArgs = {
4035
+ input: MtwImportInput;
4036
+ };
4037
+ export type MutationImportMtwDriversArgs = {
4038
+ input: MtwImportInput;
4039
+ };
4040
+ export type MutationImportMtwVehiclesArgs = {
4041
+ input: MtwImportInput;
4042
+ };
4043
+ export type MutationImportMtwWarehousesArgs = {
4044
+ input: MtwImportInput;
4045
+ };
3931
4046
  export type MutationOpenMtwContainerCycleArgs = {
3932
4047
  input: OpenMtwContainerCycleInput;
3933
4048
  };
@@ -4073,6 +4188,9 @@ export type MutationUpdateInventoryItemArgs = {
4073
4188
  id: Scalars['ID']['input'];
4074
4189
  input: UpdateInventoryItemInput;
4075
4190
  };
4191
+ export type MutationUpdateMtwModuleSettingsArgs = {
4192
+ input: UpdateMtwModuleSettingsInput;
4193
+ };
4076
4194
  export type MutationUpdateRouteArgs = {
4077
4195
  id: Scalars['ID']['input'];
4078
4196
  input: UpdateRouteInput;
@@ -4268,6 +4386,15 @@ export type Query = {
4268
4386
  /** Null is not proof of no effect and never authorizes resubmission. Exact original actor/context only. */
4269
4387
  mtwFreightInvoiceComparisonByRequestKey?: Maybe<MtwFreightInvoiceComparison>;
4270
4388
  mtwFreightInvoiceComparisonPage: MtwFreightInvoiceComparisonPage;
4389
+ /**
4390
+ * Columns accepted by each import, for building templates and showing guidance.
4391
+ * This is static reference metadata with no workspace data in it, so it only
4392
+ * needs an authenticated caller; every import mutation enforces its own
4393
+ * entity create permission.
4394
+ */
4395
+ mtwImportSpec: Array<MtwImportEntitySpec>;
4396
+ /** The workspace's MoveTheWheels operational defaults; platform defaults when none were saved. */
4397
+ mtwModuleSettings: MtwModuleSettings;
4271
4398
  mtwPalletGeometryPreview: MtwPalletGeometryPreview;
4272
4399
  mtwPickPreparation: MtwPickPreparation;
4273
4400
  mtwPickReview: MtwPickReview;
@@ -5205,6 +5332,19 @@ export type UpdateInventoryItemInput = {
5205
5332
  safetyStock?: InputMaybe<Scalars['Int']['input']>;
5206
5333
  unitCost?: InputMaybe<Scalars['Float']['input']>;
5207
5334
  };
5335
+ /**
5336
+ * Changes to the workspace's MoveTheWheels defaults. Omitted fields keep their
5337
+ * current value. `timezone: null` (or an empty string) clears the time zone so
5338
+ * each viewer sees their own local time.
5339
+ */
5340
+ export type UpdateMtwModuleSettingsInput = {
5341
+ defaultCurrency?: InputMaybe<Scalars['String']['input']>;
5342
+ defaultDeliveryWindowHours?: InputMaybe<Scalars['Int']['input']>;
5343
+ defaultOrderPriority?: InputMaybe<DeliveryPriority>;
5344
+ distanceUnit?: InputMaybe<MtwDistanceUnit>;
5345
+ timezone?: InputMaybe<Scalars['String']['input']>;
5346
+ weightUnit?: InputMaybe<MtwWeightUnit>;
5347
+ };
5208
5348
  export type UpdateRouteInput = {
5209
5349
  actualDistance?: InputMaybe<Scalars['Float']['input']>;
5210
5350
  actualDuration?: InputMaybe<Scalars['Float']['input']>;
@@ -0,0 +1,68 @@
1
+ import { useMoveTheWheels as e } from "../providers/MoveTheWheelsProvider.js";
2
+ import { executeGraphQL as t } from "../utils/graphqlClient.js";
3
+ import { MTW_IMPORT_SPEC_QUERY as n, importEntity as r, importMutation as i } from "../utils/entityImport.js";
4
+ import { useCallback as a, useEffect as o, useMemo as s, useState as c } from "react";
5
+ //#region src/movethewheels/hooks/useEntityImport.ts
6
+ function l() {
7
+ let { apiGatewayUrl: t, authToken: n, workspaceToken: r, workspaceId: i, tenantId: a, currentUser: o } = e();
8
+ return s(() => ({
9
+ apiGatewayUrl: t,
10
+ authToken: n,
11
+ workspaceToken: r,
12
+ workspaceId: i,
13
+ tenantId: a,
14
+ actorId: o?.id
15
+ }), [
16
+ t,
17
+ n,
18
+ r,
19
+ i,
20
+ a,
21
+ o?.id
22
+ ]);
23
+ }
24
+ function u() {
25
+ let e = l(), [r, i] = c([]), [a, s] = c(!0), [u, d] = c(null);
26
+ return o(() => {
27
+ if (!e.workspaceId) return;
28
+ let r = !1;
29
+ return s(!0), t(e, n).then((e) => {
30
+ r || (i(e.mtwImportSpec), d(null));
31
+ }).catch((e) => {
32
+ r || d(e instanceof Error ? e : /* @__PURE__ */ Error("Could not load import columns"));
33
+ }).finally(() => {
34
+ r || s(!1);
35
+ }), () => {
36
+ r = !0;
37
+ };
38
+ }, [e]), {
39
+ specs: r,
40
+ isLoading: a,
41
+ error: u
42
+ };
43
+ }
44
+ function d() {
45
+ let e = l(), [n, o] = c(!1);
46
+ return {
47
+ run: a(async (n) => {
48
+ if (new Blob([n.content]).size > 2097152) throw Error("The file is larger than 2 MB. Split it into smaller files.");
49
+ let { field: a } = r(n.entityType);
50
+ o(!0);
51
+ try {
52
+ let r = (await t(e, i(n.entityType), { input: {
53
+ format: n.format,
54
+ content: n.content,
55
+ dryRun: n.dryRun,
56
+ allowPartial: n.allowPartial
57
+ } }, n.dryRun ? "RETRY_TRANSIENT" : "SINGLE_ATTEMPT"))[a];
58
+ if (!r) throw Error("The import returned no result");
59
+ return r;
60
+ } finally {
61
+ o(!1);
62
+ }
63
+ }, [e]),
64
+ isRunning: n
65
+ };
66
+ }
67
+ //#endregion
68
+ export { d as useEntityImport, u as useImportSpec };
@@ -1,9 +1,9 @@
1
- import { useTr as e } from "../shared/hooks/useTr.js";
2
- import { VehicleHoldRefusal as t } from "../utils/orderVehicleHold.js";
1
+ import { VehicleHoldRefusal as e } from "../utils/orderVehicleHold.js";
2
+ import { useTr as t } from "../shared/hooks/useTr.js";
3
3
  import { useEffect as n, useRef as r, useState as i } from "react";
4
4
  //#region src/movethewheels/hooks/useOrderDispatch.ts
5
5
  function a(a) {
6
- let o = e(), { order: s, contextKey: c, updateOrder: l, updateOrderStatus: u, refresh: d } = a, f = `movethewheels.order-dispatch.unconfirmed.v1:${c}`, [p, m] = i(() => {
6
+ let o = t(), { order: s, contextKey: c, updateOrder: l, updateOrderStatus: u, refresh: d } = a, f = `movethewheels.order-dispatch.unconfirmed.v1:${c}`, [p, m] = i(() => {
7
7
  try {
8
8
  return sessionStorage.getItem(f) !== null;
9
9
  } catch {
@@ -28,56 +28,56 @@ function a(a) {
28
28
  x.current = !1, S.current && g(!1);
29
29
  };
30
30
  return {
31
- assign: async (e, n) => {
32
- if (!e || !n || !s || s.vehicleId === e && s.driverId === n || !E(a.assignmentReady)) return;
31
+ assign: async (t, n) => {
32
+ if (!t || !n || !s || s.vehicleId === t && s.driverId === n || !E(a.assignmentReady)) return;
33
33
  let r = !1;
34
34
  try {
35
35
  if (await l(s.id, {
36
- vehicleId: e,
36
+ vehicleId: t,
37
37
  driverId: n
38
38
  }, "SINGLE_ATTEMPT"), r = !0, S.current && b({
39
39
  orderId: s.id,
40
- vehicleId: e,
40
+ vehicleId: t,
41
41
  driverId: n
42
42
  }), T(), !S.current || (!(s.vehicleId && s.driverId) && s.status === "confirmed" && (w(), await u(s.id, "assigned"), T()), !S.current)) return;
43
43
  v(o("movethewheels.order.assignmentSaved", "Assignment saved.")), d();
44
- } catch (e) {
45
- if (e instanceof t && e.operation === (r ? "updateDeliveryJobStatus" : "updateDeliveryJob")) {
44
+ } catch (t) {
45
+ if (t instanceof e && t.operation === (r ? "updateDeliveryJobStatus" : "updateDeliveryJob")) {
46
46
  try {
47
47
  T();
48
48
  } catch {
49
49
  S.current && m(!0);
50
50
  }
51
51
  if (!S.current) return;
52
- v(o(e.kind === "driver" ? r ? "movethewheels.order.driverMovementBlocked" : "movethewheels.order.driverAssignmentBlocked" : r ? "movethewheels.order.movementBlocked" : "movethewheels.order.assignmentBlocked", e.kind === "driver" ? r ? "Assignment saved. Driver eligibility blocked movement." : "Assignment blocked by driver eligibility. Choose another driver." : r ? "Assignment saved. Movement blocked." : "Assignment blocked by the recorded vehicle status. Choose another vehicle.")), d();
52
+ v(o(t.kind === "driver" ? r ? "movethewheels.order.driverMovementBlocked" : "movethewheels.order.driverAssignmentBlocked" : r ? "movethewheels.order.movementBlocked" : "movethewheels.order.assignmentBlocked", t.kind === "driver" ? r ? "Assignment saved. Driver eligibility blocked movement." : "Assignment blocked by driver eligibility. Choose another driver." : r ? "Assignment saved. Movement blocked." : "Assignment blocked by the recorded vehicle status. Choose another vehicle.")), d();
53
53
  } else S.current && (m(!0), v(C(r)));
54
54
  } finally {
55
55
  D();
56
56
  }
57
57
  },
58
- changeStatus: async (e) => {
58
+ changeStatus: async (t) => {
59
59
  if (!(!s || [
60
60
  "delivered",
61
61
  "returned",
62
62
  "cancelled",
63
63
  "failed"
64
- ].includes(s.status)) && !(e === "assigned" && (!s.vehicleId || !s.driverId)) && !(!E(![
64
+ ].includes(s.status)) && !(t === "assigned" && (!s.vehicleId || !s.driverId)) && !(!E(![
65
65
  "assigned",
66
66
  "picked_up",
67
67
  "in_transit",
68
68
  "out_for_delivery"
69
- ].includes(e) || a.movementReady) || !s)) try {
70
- if (await u(s.id, e), T(), !S.current) return;
69
+ ].includes(t) || a.movementReady) || !s)) try {
70
+ if (await u(s.id, t), T(), !S.current) return;
71
71
  v(o("movethewheels.order.statusSaved", "Order status updated.")), d();
72
- } catch (e) {
73
- if (e instanceof t && e.operation === "updateDeliveryJobStatus") {
72
+ } catch (t) {
73
+ if (t instanceof e && t.operation === "updateDeliveryJobStatus") {
74
74
  try {
75
75
  T();
76
76
  } catch {
77
77
  S.current && m(!0);
78
78
  }
79
79
  if (!S.current) return;
80
- v(o(e.kind === "driver" ? "movethewheels.order.driverStatusBlocked" : "movethewheels.order.statusBlocked", e.kind === "driver" ? "Movement blocked by driver eligibility. Choose another driver." : "Movement blocked by the recorded vehicle status. Choose another vehicle.")), d();
80
+ v(o(t.kind === "driver" ? "movethewheels.order.driverStatusBlocked" : "movethewheels.order.statusBlocked", t.kind === "driver" ? "Movement blocked by driver eligibility. Choose another driver." : "Movement blocked by the recorded vehicle status. Choose another vehicle.")), d();
81
81
  } else S.current && (m(!0), v(o("movethewheels.order.statusUnknown", "Order status could not be confirmed. Further changes are blocked.")));
82
82
  } finally {
83
83
  D();
@@ -0,0 +1,19 @@
1
+ import { MtwImportEntitySpec, MtwImportEntityType, MtwImportFormat, MtwImportResult } from '../utils/entityImport';
2
+ /** Column specification for every importable entity. */
3
+ export declare function useImportSpec(): {
4
+ specs: MtwImportEntitySpec[];
5
+ isLoading: boolean;
6
+ error: Error | null;
7
+ };
8
+ export interface ImportRequest {
9
+ entityType: MtwImportEntityType;
10
+ format: MtwImportFormat;
11
+ content: string;
12
+ dryRun: boolean;
13
+ allowPartial: boolean;
14
+ }
15
+ /** Runs a validation or an import. Writes are sent once: a retry could create rows twice. */
16
+ export declare function useEntityImport(): {
17
+ run: (request: ImportRequest) => Promise<MtwImportResult>;
18
+ isRunning: boolean;
19
+ };
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Import / Export Page (BOFF-3990)
3
+ *
4
+ * Bulk data in and out of MoveTheWheels. Import validates a CSV or JSON file
5
+ * row by row on the server (dry run first, then import) for customers,
6
+ * vehicles, drivers, warehouses and products. Export starts the same
7
+ * `exportMtwEntities` job the list pages use and downloads the archive.
8
+ */
9
+ export default function ImportExportPage(): import('react').JSX.Element;
@@ -0,0 +1 @@
1
+ export default function SettingsPage(): import('react').JSX.Element;
@@ -0,0 +1,17 @@
1
+ import { FC, PropsWithChildren } from 'react';
2
+ import { MtwModuleSettings, MtwModuleSettingsInput } from '../utils/moduleSettings';
3
+ interface ModuleSettingsContextValue {
4
+ settings: MtwModuleSettings;
5
+ /** True once the workspace's settings were read (or the read failed). */
6
+ isLoaded: boolean;
7
+ error: Error | null;
8
+ save: (input: MtwModuleSettingsInput) => Promise<MtwModuleSettings>;
9
+ reload: () => void;
10
+ }
11
+ export declare const ModuleSettingsProvider: FC<PropsWithChildren>;
12
+ /**
13
+ * The workspace's MoveTheWheels defaults. Outside the provider (stories,
14
+ * isolated component tests) it returns the platform defaults read-only.
15
+ */
16
+ export declare function useModuleSettings(): ModuleSettingsContextValue;
17
+ export {};
@@ -0,0 +1,109 @@
1
+ /**
2
+ * Entity import — client side of wspace-movethewheels-svc's bulk import (BOFF-3990).
3
+ *
4
+ * The service owns parsing and validation; this module only picks the right
5
+ * mutation for an entity, detects the file format, builds CSV templates from
6
+ * the server's column specification and shapes the result for display.
7
+ * Kept free of React so the contract can be unit-tested.
8
+ */
9
+ export declare const MTW_IMPORT_ENTITIES: readonly [{
10
+ readonly entityType: "CUSTOMER";
11
+ readonly label: "Customers";
12
+ readonly noun: "customer";
13
+ readonly field: "importMtwCustomers";
14
+ }, {
15
+ readonly entityType: "VEHICLE";
16
+ readonly label: "Vehicles";
17
+ readonly noun: "vehicle";
18
+ readonly field: "importMtwVehicles";
19
+ }, {
20
+ readonly entityType: "DRIVER";
21
+ readonly label: "Drivers";
22
+ readonly noun: "driver";
23
+ readonly field: "importMtwDrivers";
24
+ }, {
25
+ readonly entityType: "WAREHOUSE";
26
+ readonly label: "Warehouses";
27
+ readonly noun: "warehouse";
28
+ readonly field: "importMtwWarehouses";
29
+ }, {
30
+ readonly entityType: "CARGO_ITEM";
31
+ readonly label: "Products";
32
+ readonly noun: "product";
33
+ readonly field: "importMtwCargoItems";
34
+ }];
35
+ export type MtwImportEntityType = (typeof MTW_IMPORT_ENTITIES)[number]['entityType'];
36
+ export type MtwImportFormat = 'CSV' | 'JSON';
37
+ export type MtwImportStatus = 'DRY_RUN' | 'COMPLETED' | 'PARTIAL' | 'REJECTED';
38
+ /** Mirrors the service limits so an oversized file is refused before upload. */
39
+ export declare const MTW_IMPORT_MAX_BYTES: number;
40
+ export declare const MTW_IMPORT_MAX_ROWS = 1000;
41
+ export declare function isImportEntityType(value: string | null | undefined): value is MtwImportEntityType;
42
+ export declare function importEntity(entityType: MtwImportEntityType): {
43
+ readonly entityType: "CUSTOMER";
44
+ readonly label: "Customers";
45
+ readonly noun: "customer";
46
+ readonly field: "importMtwCustomers";
47
+ } | {
48
+ readonly entityType: "VEHICLE";
49
+ readonly label: "Vehicles";
50
+ readonly noun: "vehicle";
51
+ readonly field: "importMtwVehicles";
52
+ } | {
53
+ readonly entityType: "DRIVER";
54
+ readonly label: "Drivers";
55
+ readonly noun: "driver";
56
+ readonly field: "importMtwDrivers";
57
+ } | {
58
+ readonly entityType: "WAREHOUSE";
59
+ readonly label: "Warehouses";
60
+ readonly noun: "warehouse";
61
+ readonly field: "importMtwWarehouses";
62
+ } | {
63
+ readonly entityType: "CARGO_ITEM";
64
+ readonly label: "Products";
65
+ readonly noun: "product";
66
+ readonly field: "importMtwCargoItems";
67
+ };
68
+ export declare const IMPORT_MUTATIONS: Record<MtwImportEntityType, string>;
69
+ /** The mutation document for one entity's import. */
70
+ export declare function importMutation(entityType: MtwImportEntityType): string;
71
+ export declare const MTW_IMPORT_SPEC_QUERY = "\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";
72
+ export interface MtwImportColumn {
73
+ name: string;
74
+ type: string;
75
+ required: boolean;
76
+ description: string;
77
+ allowedValues: string[] | null;
78
+ maxLength: number | null;
79
+ min: number | null;
80
+ max: number | null;
81
+ }
82
+ export interface MtwImportEntitySpec {
83
+ entityType: MtwImportEntityType;
84
+ keyColumn: string | null;
85
+ columns: MtwImportColumn[];
86
+ }
87
+ export interface MtwImportRowError {
88
+ row: number;
89
+ field: string | null;
90
+ message: string;
91
+ }
92
+ export interface MtwImportResult {
93
+ entityType: MtwImportEntityType;
94
+ status: MtwImportStatus;
95
+ dryRun: boolean;
96
+ totalRows: number;
97
+ validRows: number;
98
+ importedRows: number;
99
+ failedRows: number;
100
+ createdIds: string[];
101
+ errorsTruncated: boolean;
102
+ errors: MtwImportRowError[];
103
+ }
104
+ /** Picks the format from the file name, falling back to the MIME type. */
105
+ export declare function detectImportFormat(fileName: string, mimeType?: string): MtwImportFormat | null;
106
+ /** A CSV template: the header row, required columns first, then optional ones. */
107
+ export declare function buildCsvTemplate(spec: MtwImportEntitySpec): string;
108
+ /** One-line summary of an import result for the status banner. */
109
+ export declare function summariseImport(result: MtwImportResult, noun: string): string;
@@ -1,10 +1,28 @@
1
1
  import { Address } from '../types';
2
+ /**
3
+ * Workspace display preferences (BOFF-10488). The module settings provider
4
+ * applies the workspace's saved MoveTheWheels defaults here once they load,
5
+ * so every formatter below honours them without each call site passing them.
6
+ * Until then the platform defaults apply.
7
+ */
8
+ export interface FormatPreferences {
9
+ /** ISO 4217 code for amounts that carry no currency of their own. */
10
+ currency: string;
11
+ distanceUnit: 'KM' | 'MI';
12
+ weightUnit: 'KG' | 'LB';
13
+ /** IANA zone; null formats in the viewer's own local time. */
14
+ timeZone: string | null;
15
+ }
16
+ export declare const DEFAULT_FORMAT_PREFERENCES: Readonly<FormatPreferences>;
17
+ export declare function setFormatPreferences(next: FormatPreferences): void;
18
+ export declare function getFormatPreferences(): Readonly<FormatPreferences>;
2
19
  /**
3
20
  * Format an address object into a readable string.
4
21
  */
5
22
  export declare function formatAddress(address: Partial<Address>): string;
6
23
  /**
7
- * Format a number as currency.
24
+ * Format a number as currency. Amounts without their own currency use the
25
+ * workspace default currency.
8
26
  */
9
27
  export declare function formatCurrency(amount: number, currency?: string): string;
10
28
  /**
@@ -34,11 +52,13 @@ export declare function formatNumber(num: number): string;
34
52
  */
35
53
  export declare function formatPercentage(value: number, decimals?: number): string;
36
54
  /**
37
- * Format weight with appropriate units.
55
+ * Format a weight given in kilograms, in the workspace weight unit: kg (and
56
+ * tonnes from 1,000 kg) or lb.
38
57
  */
39
58
  export declare function formatWeight(kg: number): string;
40
59
  /**
41
- * Format distance with appropriate units.
60
+ * Format a distance given in kilometres, in the workspace distance unit: km
61
+ * (metres below 1 km) or mi (feet below 0.1 mi).
42
62
  */
43
63
  export declare function formatDistance(km: number): string;
44
64
  /**
@@ -0,0 +1,52 @@
1
+ import { FormatPreferences } from './formatters';
2
+ export type MtwDistanceUnit = 'KM' | 'MI';
3
+ export type MtwWeightUnit = 'KG' | 'LB';
4
+ export type MtwOrderPriority = 'LOW' | 'MEDIUM' | 'HIGH' | 'URGENT' | 'CRITICAL';
5
+ export interface MtwModuleSettings {
6
+ workspaceId: string;
7
+ defaultCurrency: string;
8
+ distanceUnit: MtwDistanceUnit;
9
+ weightUnit: MtwWeightUnit;
10
+ /** IANA zone; null shows each viewer their own local time. */
11
+ timezone: string | null;
12
+ defaultOrderPriority: MtwOrderPriority;
13
+ defaultDeliveryWindowHours: number;
14
+ isCustomized: boolean;
15
+ updatedAt: string | null;
16
+ updatedBy: string | null;
17
+ }
18
+ export type MtwModuleSettingsInput = Partial<Pick<MtwModuleSettings, 'defaultCurrency' | 'distanceUnit' | 'weightUnit' | 'timezone' | 'defaultOrderPriority' | 'defaultDeliveryWindowHours'>>;
19
+ /** Mirrors the service range for the default delivery window. */
20
+ export declare const DELIVERY_WINDOW_MIN_HOURS = 1;
21
+ export declare const DELIVERY_WINDOW_MAX_HOURS = 720;
22
+ /** The service's platform defaults, shown until the workspace's settings load. */
23
+ export declare function platformDefaults(workspaceId?: string): MtwModuleSettings;
24
+ export declare const MTW_MODULE_SETTINGS_QUERY = "\n query MtwModuleSettings {\n mtwModuleSettings {\n \n workspaceId\n defaultCurrency\n distanceUnit\n weightUnit\n timezone\n defaultOrderPriority\n defaultDeliveryWindowHours\n isCustomized\n updatedAt\n updatedBy\n\n }\n }\n";
25
+ export declare const UPDATE_MTW_MODULE_SETTINGS_MUTATION = "\n mutation UpdateMtwModuleSettings($input: UpdateMtwModuleSettingsInput!) {\n updateMtwModuleSettings(input: $input) {\n \n workspaceId\n defaultCurrency\n distanceUnit\n weightUnit\n timezone\n defaultOrderPriority\n defaultDeliveryWindowHours\n isCustomized\n updatedAt\n updatedBy\n\n }\n }\n";
26
+ /** What the formatters need from the settings. */
27
+ export declare function toFormatPreferences(settings: MtwModuleSettings): FormatPreferences;
28
+ export declare const DISTANCE_UNIT_OPTIONS: ReadonlyArray<{
29
+ value: MtwDistanceUnit;
30
+ label: string;
31
+ }>;
32
+ export declare const WEIGHT_UNIT_OPTIONS: ReadonlyArray<{
33
+ value: MtwWeightUnit;
34
+ label: string;
35
+ }>;
36
+ export declare const ORDER_PRIORITY_OPTIONS: ReadonlyArray<{
37
+ value: MtwOrderPriority;
38
+ label: string;
39
+ }>;
40
+ /** The order form's four priorities for a service priority (CRITICAL shows as urgent). */
41
+ export declare function orderFormPriority(priority: MtwOrderPriority): 'low' | 'normal' | 'high' | 'urgent';
42
+ export declare function currencyOptions(current?: string): string[];
43
+ export declare function currencyLabel(code: string): string;
44
+ export declare function timeZoneOptions(current?: string | null): string[];
45
+ /** The browser's own zone, for the "viewer's local time" option label. */
46
+ export declare function browserTimeZone(): string;
47
+ /** Client-side check of the delivery window before it is sent. */
48
+ export declare function deliveryWindowError(value: string): string | null;
49
+ /** Only the fields that differ from the saved settings. */
50
+ export declare function changedSettings(saved: MtwModuleSettings, draft: MtwModuleSettingsInput): MtwModuleSettingsInput;
51
+ /** A `datetime-local` value `hours` from `from`, in the viewer's clock. */
52
+ export declare function deliveryWindowValue(hours: number, from?: Date): string;
@@ -0,0 +1,22 @@
1
+ import { createLucideIcon as e } from "../createLucideIcon.js";
2
+ //#region node_modules/lucide-react/dist/esm/icons/arrow-down-up.js
3
+ var t = e("ArrowDownUp", [
4
+ ["path", {
5
+ d: "m3 16 4 4 4-4",
6
+ key: "1co6wj"
7
+ }],
8
+ ["path", {
9
+ d: "M7 20V4",
10
+ key: "1yoxec"
11
+ }],
12
+ ["path", {
13
+ d: "m21 8-4-4-4 4",
14
+ key: "1c9v7m"
15
+ }],
16
+ ["path", {
17
+ d: "M17 4v16",
18
+ key: "7dpous"
19
+ }]
20
+ ]);
21
+ //#endregion
22
+ export { t as ArrowDownUp };
@@ -0,0 +1,11 @@
1
+ import { createLucideIcon as e } from "../createLucideIcon.js";
2
+ //#region node_modules/lucide-react/dist/esm/icons/bell.js
3
+ var t = e("Bell", [["path", {
4
+ d: "M10.268 21a2 2 0 0 0 3.464 0",
5
+ key: "vwvbt9"
6
+ }], ["path", {
7
+ d: "M3.262 15.326A1 1 0 0 0 4 17h16a1 1 0 0 0 .74-1.673C19.41 13.956 18 12.499 18 8A6 6 0 0 0 6 8c0 4.499-1.411 5.956-2.738 7.326",
8
+ key: "11g9vi"
9
+ }]]);
10
+ //#endregion
11
+ export { t as Bell };