@burdenoff/microfe-movethewheels 2026.1010.2 → 2026.1011.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.
@@ -92,6 +92,14 @@ const NavRegistrar: FC<Pick<MoveTheWheelsRootProps, 'registerNavItems' | 'basePa
92
92
  path: joinPath(basePath, 'tracking'),
93
93
  icon: <Map size={16} />,
94
94
  order: 3,
95
+ children: [
96
+ {
97
+ id: 'mtw-tracking-quality',
98
+ label: tr('movethewheels.root.nav.trackingQuality', 'Tracking event quality'),
99
+ path: joinPath(basePath, 'tracking/event-quality'),
100
+ icon: <Map size={16} />,
101
+ },
102
+ ],
95
103
  },
96
104
  {
97
105
  id: 'mtw-consolidation',
@@ -55,6 +55,7 @@ const OrdersListPage = lazy(() => import('./pages/OrdersListPage'));
55
55
  const OrderDetailsPage = lazy(() => import('./pages/OrderDetailsPage'));
56
56
  const CreateOrderPage = lazy(() => import('./pages/CreateOrderPage'));
57
57
  const TrackingPage = lazy(() => import('./pages/TrackingPage'));
58
+ const TrackingEventQualityPage = lazy(() => import('./pages/TrackingEventQualityPage'));
58
59
  const LiveTrackingPage = lazy(() => import('./pages/LiveTrackingPage'));
59
60
  const FleetPage = lazy(() => import('./pages/FleetPage'));
60
61
  const VehiclesPage = lazy(() => import('./pages/VehiclesPage'));
@@ -108,6 +109,7 @@ export function MoveTheWheelsRoutes() {
108
109
  {/* Tracking */}
109
110
  <Route path="tracking" element={<TrackingPage />} />
110
111
  <Route path="tracking/live" element={<LiveTrackingPage />} />
112
+ <Route path="tracking/event-quality" element={<TrackingEventQualityPage />} />
111
113
 
112
114
  {/* Driver ePOD capture (BOFF-4399) */}
113
115
  <Route path="deliveries/mine" element={<DeliveryCapturePage />} />
@@ -0,0 +1,68 @@
1
+ import {
2
+ TrackingQualityIntegrationsDocument,
3
+ TrackingQualityDeliveriesDocument,
4
+ TrackingQualityShipmentDocument,
5
+ } from '../../generated/tracking-quality-operations';
6
+ export {
7
+ TrackingQualityIntegrationsDocument as QualityIntegrationsDocument,
8
+ TrackingQualityDeliveriesDocument as QualityDeliveriesDocument,
9
+ TrackingQualityShipmentDocument as QualityShipmentDocument,
10
+ };
11
+
12
+ export interface TrackingUpdateOutcome {
13
+ index: number;
14
+ externalEventId: string | null;
15
+ shipmentReference: string;
16
+ shipmentId: string | null;
17
+ status: 'ACCEPTED' | 'DUPLICATE' | 'CONFLICT' | 'REJECTED';
18
+ errorCode: string | null;
19
+ }
20
+
21
+ const statuses = new Set(['ACCEPTED', 'DUPLICATE', 'CONFLICT', 'REJECTED']);
22
+ const codes = new Set([
23
+ 'KEY_CONFLICT',
24
+ 'BUDGET_EXHAUSTED',
25
+ 'INTEGRATION_UNAVAILABLE',
26
+ 'SHIPMENT_UNAVAILABLE',
27
+ 'UPDATE_REJECTED',
28
+ ]);
29
+ const bounded = (value: unknown, max: number): value is string =>
30
+ typeof value === 'string' && value.length <= max;
31
+ const nullable = (value: unknown, max: number): value is string | null =>
32
+ value === null || bounded(value, max);
33
+
34
+ /** JSON scalar decoding fails closed; legacy delivery history has no outcomes. */
35
+ export function trackingOutcomes(value: unknown): TrackingUpdateOutcome[] | null {
36
+ if (!Array.isArray(value) || value.length > 5) return null;
37
+ const result: TrackingUpdateOutcome[] = [];
38
+ for (const item of value) {
39
+ if (typeof item !== 'object' || item === null || Array.isArray(item)) return null;
40
+ const row: Record<string, unknown> = item;
41
+ if (
42
+ !Number.isInteger(row.index) ||
43
+ typeof row.index !== 'number' ||
44
+ row.index !== result.length ||
45
+ row.index < 0 ||
46
+ row.index > 4 ||
47
+ !nullable(row.externalEventId, 255) ||
48
+ !bounded(row.shipmentReference, 255) ||
49
+ !nullable(row.shipmentId, 255) ||
50
+ typeof row.status !== 'string' ||
51
+ !statuses.has(row.status) ||
52
+ (row.status === 'CONFLICT' && row.shipmentId !== null) ||
53
+ !(row.errorCode === null || (typeof row.errorCode === 'string' && codes.has(row.errorCode)))
54
+ ) {
55
+ return null;
56
+ }
57
+ // The finite status/code guards justify narrowing the untyped JSON scalar.
58
+ result.push({
59
+ index: row.index,
60
+ externalEventId: row.externalEventId,
61
+ shipmentReference: row.shipmentReference,
62
+ shipmentId: row.shipmentId,
63
+ status: row.status as TrackingUpdateOutcome['status'],
64
+ errorCode: row.errorCode,
65
+ });
66
+ }
67
+ return result;
68
+ }
@@ -0,0 +1,239 @@
1
+ import { useState } from 'react';
2
+ import { RadioTower } from 'lucide-react';
3
+ import { PageLayout } from '../components/PageLayout';
4
+ import { Button } from '../components/ui';
5
+ import { reuseCard, reuseSelect, ReuseState } from '../components/ReuseControls';
6
+ import { useConditionQuery } from '../hooks/useContainerCondition';
7
+ import { useMoveTheWheels } from '../providers/MoveTheWheelsProvider';
8
+ import {
9
+ QualityIntegrationsDocument,
10
+ QualityDeliveriesDocument,
11
+ QualityShipmentDocument,
12
+ trackingOutcomes,
13
+ } from '../hooks/useTrackingEventQuality';
14
+ import type {
15
+ TrackingQualityIntegrationsQuery,
16
+ TrackingQualityDeliveriesQuery,
17
+ TrackingQualityShipmentQuery,
18
+ } from '../../generated/tracking-quality-operations';
19
+
20
+ const LIMIT = 20;
21
+ function Pager({
22
+ offset,
23
+ length,
24
+ busy,
25
+ onChange,
26
+ }: {
27
+ offset: number;
28
+ length: number | undefined;
29
+ busy: boolean;
30
+ onChange: (offset: number) => void;
31
+ }) {
32
+ return (
33
+ <div className="flex flex-wrap items-center gap-3">
34
+ <Button
35
+ variant="outline"
36
+ disabled={busy || offset === 0}
37
+ onClick={() => onChange(Math.max(0, offset - LIMIT))}
38
+ >
39
+ Previous page
40
+ </Button>
41
+ <span>{length === undefined ? 'Page unavailable' : `${length} records on this page`}</span>
42
+ <Button
43
+ variant="outline"
44
+ disabled={busy || length !== LIMIT || offset >= 9980}
45
+ onClick={() => onChange(offset + LIMIT)}
46
+ >
47
+ Next page
48
+ </Button>
49
+ </div>
50
+ );
51
+ }
52
+
53
+ export default function TrackingEventQualityPage() {
54
+ const { workspaceId, tenantId, currentUser, apiGatewayUrl } = useMoveTheWheels();
55
+ return (
56
+ <PageLayout
57
+ title="Tracking event quality"
58
+ subtitle="Review delivery results before investigating a shipment."
59
+ icon={<RadioTower aria-hidden="true" />}
60
+ >
61
+ <QualityWorkbench key={`${workspaceId}:${tenantId}:${currentUser?.id}:${apiGatewayUrl}`} />
62
+ </PageLayout>
63
+ );
64
+ }
65
+ function QualityWorkbench() {
66
+ const [integrationOffset, setIntegrationOffset] = useState(0);
67
+ const [selected, setSelected] = useState('');
68
+ const [offset, setOffset] = useState(0);
69
+ const [revision, setRevision] = useState(0);
70
+ const [shipmentId, setShipmentId] = useState<string | null>(null);
71
+ const integrations = useConditionQuery<TrackingQualityIntegrationsQuery>(
72
+ QualityIntegrationsDocument,
73
+ { pagination: { limit: LIMIT, offset: integrationOffset } },
74
+ revision
75
+ );
76
+ const deliveries = useConditionQuery<TrackingQualityDeliveriesQuery>(
77
+ QualityDeliveriesDocument,
78
+ selected ? { id: selected, pagination: { limit: LIMIT, offset } } : null,
79
+ revision
80
+ );
81
+ const options = integrations.data?.integrations;
82
+ const records = deliveries.data?.integration?.events;
83
+ return (
84
+ <div className="space-y-6">
85
+ <p>
86
+ Matching event keys and payloads create no additional tracking event. A changed payload with
87
+ the same key is a conflict. Updates without a key can create new events.
88
+ </p>
89
+ <p>
90
+ Each delivery admits at most five updates. An oversized or malformed delivery is rejected
91
+ before updates are recorded. Delivery history can include older records without item
92
+ results.
93
+ </p>
94
+ <section className={reuseCard} aria-label="Integration selection">
95
+ <ReuseState loading={integrations.loading} error={integrations.error} />
96
+ {options?.length === 0 && <p>No integrations on this page.</p>}
97
+ <label className="block">
98
+ Integration
99
+ <select
100
+ className={reuseSelect}
101
+ value={selected}
102
+ disabled={!options || integrations.loading}
103
+ onChange={(event) => {
104
+ setSelected(event.target.value);
105
+ setOffset(0);
106
+ setShipmentId(null);
107
+ }}
108
+ >
109
+ <option value="">Choose an integration</option>
110
+ {options?.map((integration) => (
111
+ <option key={integration.id} value={integration.id}>
112
+ {integration.name} · {integration.provider} · {integration.status}
113
+ </option>
114
+ ))}
115
+ </select>
116
+ </label>
117
+ <Pager
118
+ offset={integrationOffset}
119
+ length={options?.length}
120
+ busy={integrations.loading}
121
+ onChange={(next) => {
122
+ setIntegrationOffset(next);
123
+ setSelected('');
124
+ setOffset(0);
125
+ setShipmentId(null);
126
+ }}
127
+ />
128
+ </section>
129
+ {selected && (
130
+ <section className={reuseCard} aria-label="Tracking delivery history">
131
+ <div className="flex flex-wrap items-center justify-between gap-3">
132
+ <h2 className="text-lg font-semibold">Delivery history</h2>
133
+ <Button
134
+ variant="outline"
135
+ disabled={deliveries.loading}
136
+ onClick={() => setRevision((value) => value + 1)}
137
+ >
138
+ Refresh results
139
+ </Button>
140
+ </div>
141
+ <ReuseState loading={deliveries.loading} error={deliveries.error} />
142
+ {deliveries.data?.integration === null && (
143
+ <p role="status">Integration unavailable in this workspace.</p>
144
+ )}
145
+ {records?.length === 0 && <p>No deliveries on this page.</p>}
146
+ {records?.map((delivery) => {
147
+ const outcomes = trackingOutcomes(delivery.outcomes);
148
+ return (
149
+ <article key={delivery.id} className="space-y-3 rounded-lg border p-4">
150
+ <h3 className="font-semibold">
151
+ {delivery.status} · {new Date(delivery.receivedAt).toLocaleString()}
152
+ </h3>
153
+ {outcomes === null ? (
154
+ <p>Per-update results unavailable for this delivery.</p>
155
+ ) : outcomes.length === 0 ? (
156
+ <p>
157
+ No normalized updates were attempted. This is not a count of raw sender items.
158
+ </p>
159
+ ) : (
160
+ <ol className="space-y-3">
161
+ {outcomes.map((outcome) => (
162
+ <li key={outcome.index} className="break-words rounded-md bg-bg-surface p-3">
163
+ <p>
164
+ Update {outcome.index + 1}: <strong>{outcome.status}</strong>
165
+ </p>
166
+ <p>Event key: {outcome.externalEventId ?? 'Missing — not deduplicated'}</p>
167
+ <p>Shipment reference: {outcome.shipmentReference || 'Not recorded'}</p>
168
+ {outcome.errorCode && (
169
+ <p>Reason: {outcome.errorCode.replaceAll('_', ' ').toLowerCase()}</p>
170
+ )}
171
+ {outcome.status === 'CONFLICT' && (
172
+ <p>The earlier receipt stays unchanged; this update adds no event.</p>
173
+ )}
174
+ {outcome.shipmentId && (
175
+ <Button
176
+ variant="outline"
177
+ onClick={() => setShipmentId(outcome.shipmentId)}
178
+ >
179
+ View shipment
180
+ </Button>
181
+ )}
182
+ </li>
183
+ ))}
184
+ </ol>
185
+ )}
186
+ </article>
187
+ );
188
+ })}
189
+ <Pager
190
+ offset={offset}
191
+ length={records?.length}
192
+ busy={deliveries.loading}
193
+ onChange={setOffset}
194
+ />
195
+ </section>
196
+ )}
197
+ {shipmentId && (
198
+ <QualityShipment key={shipmentId} id={shipmentId} onClose={() => setShipmentId(null)} />
199
+ )}
200
+ <p className="text-sm text-text-secondary">
201
+ These results describe recorded ingestion, not physical shipment truth. Ordering, late
202
+ updates and provider corrections need separate policies.
203
+ </p>
204
+ </div>
205
+ );
206
+ }
207
+
208
+ function QualityShipment({ id, onClose }: { id: string; onClose: () => void }) {
209
+ const query = useConditionQuery<TrackingQualityShipmentQuery>(QualityShipmentDocument, { id });
210
+ return (
211
+ <section className={reuseCard} aria-label="Shipment tracking summary">
212
+ <h2 className="text-lg font-semibold">Shipment tracking summary</h2>
213
+ <Button variant="outline" onClick={onClose}>
214
+ Close shipment summary
215
+ </Button>
216
+ <ReuseState loading={query.loading} error={query.error} />
217
+ {query.data?.shipment === null && <p>Shipment unavailable in this workspace.</p>}
218
+ {query.data?.shipment && (
219
+ <>
220
+ <p>
221
+ {query.data.shipment.shipmentNumber} · {query.data.shipment.status}
222
+ </p>
223
+ <p>Tracking number: {query.data.shipment.trackingNumber ?? 'Not recorded'}</p>
224
+ <p>
225
+ {query.data.shipment.trackingEvents.length} tracking events on the first page (up to
226
+ 20).
227
+ </p>
228
+ <ul>
229
+ {query.data.shipment.trackingEvents.map((event) => (
230
+ <li key={event.id}>
231
+ {event.eventType} · {new Date(event.timestamp).toLocaleString()}
232
+ </li>
233
+ ))}
234
+ </ul>
235
+ </>
236
+ )}
237
+ </section>
238
+ );
239
+ }
@@ -0,0 +1,15 @@
1
+ query TrackingQualityIntegrations($pagination: PaginationInput!) {
2
+ integrations(pagination: $pagination) { id name provider status }
3
+ }
4
+ query TrackingQualityDeliveries($id: ID!, $pagination: PaginationInput!) {
5
+ integration(id: $id) {
6
+ id name provider status
7
+ events(pagination: $pagination) { id status receivedAt processedAt outcomes }
8
+ }
9
+ }
10
+ query TrackingQualityShipment($id: ID!) {
11
+ shipment(id: $id) {
12
+ id shipmentNumber status trackingNumber
13
+ trackingEvents(pagination: {limit: 20, offset: 0}) { id eventType timestamp }
14
+ }
15
+ }