@burdenoff/microfe-movethewheels 2026.924.1 → 2026.925.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -292,7 +292,7 @@ export type DeleteMovethewheelsOrderMutation = { __typename?: 'Mutation', delete
292
292
  export type GetMovethewheelsDashboardQueryVariables = Types.Exact<{ [key: string]: never; }>;
293
293
 
294
294
 
295
- export type GetMovethewheelsDashboardQuery = { __typename?: 'Query', dashboardMetrics: { __typename?: 'MtwDashboardMetrics', totalDeliveryJobs: number, activeDeliveries: number, availableDrivers: number, activeVehicles: number, lowStockItems: number, totalRevenue: number, recentDeliveryJobs: Array<{ __typename?: 'MtwDeliveryJob', id: string, deliveryJobNumber: string, status: Types.DeliveryJobStatus, priority: Types.DeliveryPriority, customerId: string, trackingNumber?: string | null, pickupAddress: Record<string, unknown>, deliveryAddress: Record<string, unknown>, cargoItems: Record<string, unknown>, pricing?: Record<string, unknown> | null, scheduledDelivery?: string | null, vehicleId?: string | null, driverId?: string | null, createdAt: string, updatedAt: string }> }, deliveryJobStats: { __typename?: 'MtwDeliveryJobStats', total: number, pending: number, inTransit: number, delivered: number, cancelled: number, failed: number }, fleetStats: { __typename?: 'MtwFleetStats', totalVehicles: number, activeVehicles: number, inTransitVehicles: number, maintenanceVehicles: number, totalDrivers: number, availableDrivers: number, onDeliveryDrivers: number }, financeMetrics: { __typename?: 'MtwFinanceMetrics', fuelCost: number, openInvoices: number, revenue: { __typename?: 'MtwRevenueMetrics', totalRevenue: number, averageOrderValue: number, deliveredRevenue: number } }, analytics: { __typename?: 'MtwAnalyticsSeries', deliveryTrend: Array<{ __typename?: 'MtwAnalyticsPoint', label: string, value: number }>, orderTrend: Array<{ __typename?: 'MtwAnalyticsPoint', label: string, value: number }>, revenueTrend: Array<{ __typename?: 'MtwAnalyticsPoint', label: string, value: number }> } };
295
+ export type GetMovethewheelsDashboardQuery = { __typename?: 'Query', dashboardMetrics: { __typename?: 'MtwDashboardMetrics', totalDeliveryJobs: number, activeDeliveries: number, availableDrivers: number, activeVehicles: number, lowStockItems: number, totalRevenue: number, recentDeliveryJobs: Array<{ __typename?: 'MtwDeliveryJob', id: string, deliveryJobNumber: string, status: Types.DeliveryJobStatus, priority: Types.DeliveryPriority, customerId: string, trackingNumber?: string | null, pickupAddress: Record<string, unknown>, deliveryAddress: Record<string, unknown>, cargoItems: Record<string, unknown>, pricing?: Record<string, unknown> | null, scheduledDelivery?: string | null, vehicleId?: string | null, driverId?: string | null, createdAt: string, updatedAt: string }> }, deliveryJobStats: { __typename?: 'MtwDeliveryJobStats', total: number, pending: number, confirmed: number, assigned: number, pickedUp: number, inTransit: number, outForDelivery: number, delivered: number, cancelled: number, failed: number, returned: number }, fleetStats: { __typename?: 'MtwFleetStats', totalVehicles: number, activeVehicles: number, inTransitVehicles: number, maintenanceVehicles: number, totalDrivers: number, availableDrivers: number, onDeliveryDrivers: number }, financeMetrics: { __typename?: 'MtwFinanceMetrics', fuelCost: number, openInvoices: number, revenue: { __typename?: 'MtwRevenueMetrics', totalRevenue: number, averageOrderValue: number, deliveredRevenue: number } }, analytics: { __typename?: 'MtwAnalyticsSeries', deliveryTrend: Array<{ __typename?: 'MtwAnalyticsPoint', label: string, value: number }>, orderTrend: Array<{ __typename?: 'MtwAnalyticsPoint', label: string, value: number }>, revenueTrend: Array<{ __typename?: 'MtwAnalyticsPoint', label: string, value: number }> } };
296
296
 
297
297
  export type ListCustomersQueryVariables = Types.Exact<{
298
298
  filter?: Types.InputMaybe<Types.CustomerFilterInput>;
@@ -436,6 +436,7 @@ export type GetShipmentQuery = { __typename?: 'Query', shipment?: { __typename?:
436
436
 
437
437
  export type GetDeliveryJobTrackingQueryVariables = Types.Exact<{
438
438
  deliveryJobId: Types.Scalars['ID']['input'];
439
+ pagination?: Types.InputMaybe<Types.PaginationInput>;
439
440
  }>;
440
441
 
441
442
 
@@ -1837,10 +1838,15 @@ export const GetMovethewheelsDashboardDocument = gql`
1837
1838
  deliveryJobStats {
1838
1839
  total
1839
1840
  pending
1841
+ confirmed
1842
+ assigned
1843
+ pickedUp
1840
1844
  inTransit
1845
+ outForDelivery
1841
1846
  delivered
1842
1847
  cancelled
1843
1848
  failed
1849
+ returned
1844
1850
  }
1845
1851
  fleetStats {
1846
1852
  totalVehicles
@@ -2809,8 +2815,8 @@ export type GetShipmentLazyQueryHookResult = ReturnType<typeof useGetShipmentLaz
2809
2815
  export type GetShipmentSuspenseQueryHookResult = ReturnType<typeof useGetShipmentSuspenseQuery>;
2810
2816
  export type GetShipmentQueryResult = Apollo.QueryResult<Types.GetShipmentQuery, Types.GetShipmentQueryVariables>;
2811
2817
  export const GetDeliveryJobTrackingDocument = gql`
2812
- query GetDeliveryJobTracking($deliveryJobId: ID!) {
2813
- deliveryJobTracking(deliveryJobId: $deliveryJobId) {
2818
+ query GetDeliveryJobTracking($deliveryJobId: ID!, $pagination: PaginationInput) {
2819
+ deliveryJobTracking(deliveryJobId: $deliveryJobId, pagination: $pagination) {
2814
2820
  id
2815
2821
  shipmentId
2816
2822
  eventType
@@ -2834,6 +2840,7 @@ export const GetDeliveryJobTrackingDocument = gql`
2834
2840
  * const { data, loading, error } = useGetDeliveryJobTrackingQuery({
2835
2841
  * variables: {
2836
2842
  * deliveryJobId: // value for 'deliveryJobId'
2843
+ * pagination: // value for 'pagination'
2837
2844
  * },
2838
2845
  * });
2839
2846
  */
@@ -0,0 +1,235 @@
1
+ /**
2
+ * OrderItemRow — one order-item line in CreateOrderPage's "Shipment contents" section.
3
+ *
4
+ * Its own component (not inlined in a `.map()`) because it needs one
5
+ * `useProduct`/`useProductStockLevels` hook call per row to resolve the selected
6
+ * catalog item's label and stock hint independently of the shared, search-filtered
7
+ * products list passed down from the page.
8
+ */
9
+ import type { ChangeEvent } from 'react';
10
+ import { Trash2 } from 'lucide-react';
11
+ import { Button, Input, Label } from './ui';
12
+ import { EntityCombobox } from './EntityCombobox';
13
+ import { useProduct, useProductStockLevels } from '../hooks/useInventory';
14
+ import type { Dimensions, OrderItem, Product } from '../types';
15
+
16
+ interface OrderItemRowProps {
17
+ item: OrderItem;
18
+ index: number;
19
+ products: Product[];
20
+ productsLoading: boolean;
21
+ onUpdate: (index: number, field: string, value: unknown) => void;
22
+ onRemove: (index: number) => void;
23
+ }
24
+
25
+ function stockHintText(totalAvailable: number, levels: { warehouseName: string }[]): string {
26
+ if (totalAvailable <= 0) {
27
+ return 'No stock recorded for this item.';
28
+ }
29
+ if (levels.length === 1) {
30
+ return `${totalAvailable.toLocaleString()} available at ${levels[0].warehouseName}`;
31
+ }
32
+ return `${totalAvailable.toLocaleString()} available across ${levels.length} warehouses`;
33
+ }
34
+
35
+ export function OrderItemRow({
36
+ item,
37
+ index,
38
+ products,
39
+ productsLoading,
40
+ onUpdate,
41
+ onRemove,
42
+ }: OrderItemRowProps) {
43
+ // Resolved independently of the (search-filtered) `products` list so the
44
+ // trigger still shows the right label once a pick scrolls out of the
45
+ // current search results.
46
+ const { product: selectedProduct, isLoading: selectedProductLoading } = useProduct(
47
+ item.productId || undefined
48
+ );
49
+ const {
50
+ levels,
51
+ totalAvailable,
52
+ isLoading: stockLoading,
53
+ } = useProductStockLevels(item.productId || undefined);
54
+
55
+ const dimensions =
56
+ item.dimensions ?? ({ length: 0, width: 0, height: 0, unit: 'cm' } satisfies Dimensions);
57
+
58
+ const selectProduct = (productId: string) => {
59
+ if (!productId) {
60
+ onUpdate(index, 'productId', '');
61
+ return;
62
+ }
63
+ const product = products.find((candidate) => candidate.id === productId);
64
+ if (!product) return;
65
+
66
+ onUpdate(index, 'productId', product.id);
67
+ onUpdate(index, 'name', product.name);
68
+ onUpdate(index, 'sku', product.sku);
69
+ onUpdate(index, 'weight', product.weight);
70
+ onUpdate(index, 'value', product.value);
71
+ onUpdate(index, 'dimensions', product.dimensions);
72
+ // Quantity is never set from the catalog — there is no per-unit default
73
+ // quantity on a cargo item; it belongs to this specific order line.
74
+ };
75
+
76
+ const updateDimension = (field: keyof Omit<Dimensions, 'unit'>, value: string) => {
77
+ onUpdate(index, 'dimensions', {
78
+ ...dimensions,
79
+ [field]: Number.parseFloat(value) || 0,
80
+ });
81
+ };
82
+
83
+ return (
84
+ <div
85
+ className="rounded-card border border-border-subtle bg-bg-surface p-4 shadow-elevation-1 sm:p-5"
86
+ aria-labelledby={`order-item-${item.id}-title`}
87
+ >
88
+ <div className="mb-4 flex items-start justify-between gap-4">
89
+ <div>
90
+ <h3
91
+ id={`order-item-${item.id}-title`}
92
+ className="text-sm font-semibold text-text-primary"
93
+ >
94
+ Item {index + 1}
95
+ </h3>
96
+ <p className="mt-1 text-xs text-text-secondary">
97
+ Select a catalog item to prefill details, or enter a one-off shipment manually.
98
+ </p>
99
+ </div>
100
+ <Button
101
+ type="button"
102
+ variant="ghost"
103
+ size="icon"
104
+ className="shrink-0 text-status-error-text"
105
+ onClick={() => onRemove(index)}
106
+ aria-label={`Remove item ${index + 1}`}
107
+ title={`Remove item ${index + 1}`}
108
+ >
109
+ <Trash2 size={16} aria-hidden="true" />
110
+ </Button>
111
+ </div>
112
+
113
+ <div className="space-y-4">
114
+ <div className="rounded-card border border-border-subtle bg-bg-sunken/40 p-3 sm:p-4">
115
+ <Label htmlFor={`item-product-${item.id}`}>Catalog item (optional)</Label>
116
+ <EntityCombobox
117
+ id={`item-product-${item.id}`}
118
+ value={item.productId}
119
+ onChange={selectProduct}
120
+ options={products}
121
+ loading={productsLoading}
122
+ getId={(product) => product.id}
123
+ getLabel={(product) => `${product.name} (${product.sku})`}
124
+ renderOption={(product) => (
125
+ <>
126
+ {product.name} <span className="text-text-tertiary">({product.sku})</span>
127
+ </>
128
+ )}
129
+ selectedOption={selectedProduct}
130
+ selectedLoading={selectedProductLoading}
131
+ placeholder="Pick from your product catalog"
132
+ emptyText="No products found."
133
+ noneLabel="Enter details manually"
134
+ />
135
+ {item.productId && !stockLoading && (
136
+ <p className="mt-2 text-xs text-text-secondary" role="status">
137
+ {stockHintText(totalAvailable, levels)}
138
+ </p>
139
+ )}
140
+ </div>
141
+
142
+ <div className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-12">
143
+ <div className="space-y-2 lg:col-span-4">
144
+ <Label htmlFor={`item-name-${item.id}`}>Item name</Label>
145
+ <Input
146
+ id={`item-name-${item.id}`}
147
+ value={item.name}
148
+ onChange={(e: ChangeEvent<HTMLInputElement>) =>
149
+ onUpdate(index, 'name', e.target.value)
150
+ }
151
+ placeholder="Product name"
152
+ />
153
+ </div>
154
+ <div className="space-y-2 lg:col-span-3">
155
+ <Label htmlFor={`item-sku-${item.id}`}>SKU</Label>
156
+ <Input
157
+ id={`item-sku-${item.id}`}
158
+ value={item.sku}
159
+ onChange={(e: ChangeEvent<HTMLInputElement>) =>
160
+ onUpdate(index, 'sku', e.target.value)
161
+ }
162
+ placeholder="SKU-001"
163
+ />
164
+ </div>
165
+ <div className="space-y-2 lg:col-span-2">
166
+ <Label htmlFor={`item-quantity-${item.id}`}>Quantity</Label>
167
+ <Input
168
+ id={`item-quantity-${item.id}`}
169
+ type="number"
170
+ min="1"
171
+ value={item.quantity}
172
+ onChange={(e: ChangeEvent<HTMLInputElement>) =>
173
+ onUpdate(index, 'quantity', parseInt(e.target.value, 10) || 1)
174
+ }
175
+ />
176
+ </div>
177
+ <div className="space-y-2 lg:col-span-2">
178
+ <Label htmlFor={`item-weight-${item.id}`}>Weight (kg)</Label>
179
+ <Input
180
+ id={`item-weight-${item.id}`}
181
+ type="number"
182
+ min="0"
183
+ step="0.1"
184
+ value={item.weight}
185
+ onChange={(e: ChangeEvent<HTMLInputElement>) =>
186
+ onUpdate(index, 'weight', parseFloat(e.target.value) || 0)
187
+ }
188
+ />
189
+ </div>
190
+ <div className="space-y-2 lg:col-span-1">
191
+ <Label htmlFor={`item-value-${item.id}`}>Value ($)</Label>
192
+ <Input
193
+ id={`item-value-${item.id}`}
194
+ type="number"
195
+ min="0"
196
+ step="0.01"
197
+ value={item.value}
198
+ onChange={(e: ChangeEvent<HTMLInputElement>) =>
199
+ onUpdate(index, 'value', parseFloat(e.target.value) || 0)
200
+ }
201
+ />
202
+ </div>
203
+ </div>
204
+
205
+ <fieldset className="rounded-card border border-border-subtle bg-bg-sunken/40 p-3 sm:p-4">
206
+ <legend className="px-1 text-xs font-medium text-text-secondary">
207
+ Dimensions ({dimensions.unit})
208
+ </legend>
209
+ <div className="grid grid-cols-1 gap-4 sm:grid-cols-3">
210
+ {(['length', 'width', 'height'] as const).map((field) => (
211
+ <div key={field} className="space-y-2">
212
+ <Label htmlFor={`item-${field}-${item.id}`}>
213
+ {field.charAt(0).toUpperCase() + field.slice(1)}
214
+ </Label>
215
+ <Input
216
+ id={`item-${field}-${item.id}`}
217
+ type="number"
218
+ min="0"
219
+ step="0.1"
220
+ value={dimensions[field]}
221
+ onChange={(e: ChangeEvent<HTMLInputElement>) =>
222
+ updateDimension(field, e.target.value)
223
+ }
224
+ />
225
+ </div>
226
+ ))}
227
+ </div>
228
+ <p className="mt-2 text-xs text-text-secondary">
229
+ Catalog selections prefill these measurements, and you can adjust them for this order.
230
+ </p>
231
+ </fieldset>
232
+ </div>
233
+ </div>
234
+ );
235
+ }
@@ -46,6 +46,7 @@ export const ORDER_STATUS_COLORS: Record<string, StatusColor> = {
46
46
  in_transit: INFO,
47
47
  out_for_delivery: WARNING,
48
48
  delivered: SUCCESS,
49
+ returned: WARNING,
49
50
  cancelled: ERROR,
50
51
  failed: ERROR,
51
52
  };
@@ -155,10 +155,14 @@ const ORDER_STATS_QUERY = `
155
155
  deliveryJobStats {
156
156
  total
157
157
  pending
158
+ confirmed
159
+ assigned
158
160
  inTransit
161
+ outForDelivery
159
162
  delivered
160
163
  cancelled
161
164
  failed
165
+ returned
162
166
  }
163
167
  financeMetrics {
164
168
  revenue {
@@ -221,6 +225,8 @@ export function mapOrderStatus(status: string): OrderStatus {
221
225
  return 'out_for_delivery';
222
226
  case 'DELIVERED':
223
227
  return 'delivered';
228
+ case 'RETURNED':
229
+ return 'returned';
224
230
  case 'CANCELLED':
225
231
  return 'cancelled';
226
232
  default:
@@ -263,6 +269,8 @@ function toApiStatus(status: OrderStatus): string {
263
269
  return 'OUT_FOR_DELIVERY';
264
270
  case 'delivered':
265
271
  return 'DELIVERED';
272
+ case 'returned':
273
+ return 'RETURNED';
266
274
  case 'cancelled':
267
275
  return 'CANCELLED';
268
276
  default:
@@ -593,6 +601,7 @@ export function useOrderStats() {
593
601
  in_transit: 0,
594
602
  out_for_delivery: 0,
595
603
  delivered: 0,
604
+ returned: 0,
596
605
  cancelled: 0,
597
606
  failed: 0,
598
607
  },
@@ -621,10 +630,14 @@ export function useOrderStats() {
621
630
  deliveryJobStats: {
622
631
  total: number;
623
632
  pending: number;
633
+ confirmed: number;
634
+ assigned: number;
624
635
  inTransit: number;
636
+ outForDelivery: number;
625
637
  delivered: number;
626
638
  cancelled: number;
627
639
  failed: number;
640
+ returned: number;
628
641
  };
629
642
  financeMetrics: {
630
643
  revenue: {
@@ -638,12 +651,13 @@ export function useOrderStats() {
638
651
  total: data.deliveryJobStats.total,
639
652
  byStatus: {
640
653
  pending: data.deliveryJobStats.pending,
641
- confirmed: 0,
642
- assigned: 0,
654
+ confirmed: data.deliveryJobStats.confirmed,
655
+ assigned: data.deliveryJobStats.assigned,
643
656
  picked_up: 0,
644
657
  in_transit: data.deliveryJobStats.inTransit,
645
- out_for_delivery: 0,
658
+ out_for_delivery: data.deliveryJobStats.outForDelivery,
646
659
  delivered: data.deliveryJobStats.delivered,
660
+ returned: data.deliveryJobStats.returned,
647
661
  cancelled: data.deliveryJobStats.cancelled,
648
662
  failed: data.deliveryJobStats.failed,
649
663
  },