@burdenoff/microfe-movethewheels 2026.1030.1 → 2026.1031.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/ExportStatusBanner.js +53 -0
- package/dist/components/ShipmentRiskDialog.js +228 -0
- package/dist/components/TrackingAlertList.js +149 -0
- package/dist/generated/tracking-quality-operations.d.ts +1070 -2
- package/dist/generated/wspace-types.d.ts +2895 -50
- package/dist/hooks/useEntityExport.js +85 -0
- package/dist/hooks/useShipmentRisk.js +72 -0
- package/dist/hooks/useTracking.js +96 -82
- package/dist/movethewheels/components/ExportStatusBanner.d.ts +7 -0
- package/dist/movethewheels/components/ShipmentRiskDialog.d.ts +20 -0
- package/dist/movethewheels/components/TrackingAlertList.d.ts +14 -0
- package/dist/movethewheels/hooks/useEntityExport.d.ts +32 -0
- package/dist/movethewheels/hooks/useShipmentRisk.d.ts +41 -0
- package/dist/movethewheels/types/index.d.ts +15 -1
- package/dist/movethewheels/utils/entityExport.d.ts +64 -0
- package/dist/movethewheels/utils/trackingAlerts.d.ts +36 -0
- package/dist/pages/CustomersPage.js +64 -58
- package/dist/pages/DashboardPage.js +147 -151
- package/dist/pages/DriversPage.js +113 -107
- package/dist/pages/LiveTrackingPage.js +169 -169
- package/dist/pages/OrdersListPage.js +85 -79
- package/dist/pages/ProductsPage.js +63 -57
- package/dist/pages/VehiclesPage.js +91 -85
- package/dist/utils/entityExport.js +54 -0
- package/dist/utils/trackingAlerts.js +86 -0
- package/package.json +4 -2
- package/src/generated/tracking-quality-operations.ts +1409 -13
- package/src/generated/wspace-types.ts +3248 -51
- package/src/movethewheels/components/ExportStatusBanner.tsx +78 -0
- package/src/movethewheels/components/ShipmentRiskDialog.tsx +315 -0
- package/src/movethewheels/components/TrackingAlertList.tsx +188 -0
- package/src/movethewheels/hooks/useEntityExport.ts +130 -0
- package/src/movethewheels/hooks/useShipmentRisk.ts +195 -0
- package/src/movethewheels/hooks/useTracking.ts +66 -22
- package/src/movethewheels/pages/CustomersPage.tsx +11 -2
- package/src/movethewheels/pages/DashboardPage.tsx +10 -20
- package/src/movethewheels/pages/DriversPage.tsx +11 -2
- package/src/movethewheels/pages/LiveTrackingPage.tsx +20 -22
- package/src/movethewheels/pages/OrdersListPage.tsx +13 -2
- package/src/movethewheels/pages/ProductsPage.tsx +11 -2
- package/src/movethewheels/pages/VehiclesPage.tsx +11 -2
- package/src/movethewheels/types/index.ts +16 -1
- package/src/movethewheels/utils/entityExport.ts +140 -0
- package/src/movethewheels/utils/trackingAlerts.ts +151 -0
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Entity export — starts a MoveTheWheels export job and waits for its archive.
|
|
3
|
+
*
|
|
4
|
+
* `exportMtwEntities` hands the request to wspace-export-svc, which builds a
|
|
5
|
+
* ZIP of the workspace's records in the background. The job is read back with
|
|
6
|
+
* `getExportJob` until it reports COMPLETED (with a presigned `downloadUrl`),
|
|
7
|
+
* FAILED/CANCELLED/EXPIRED, or the wait budget runs out. A job that is still
|
|
8
|
+
* running when the budget is spent keeps going server side and stays
|
|
9
|
+
* downloadable from the shared Exports page.
|
|
10
|
+
*
|
|
11
|
+
* Kept free of React so the polling contract can be unit-tested.
|
|
12
|
+
*/
|
|
13
|
+
/** List pages that offer an export, mapped to wspace-export-svc entity types. */
|
|
14
|
+
export declare const MTW_EXPORT_ENTITY_TYPES: {
|
|
15
|
+
readonly customers: "movethewheels_customers";
|
|
16
|
+
readonly drivers: "movethewheels_drivers";
|
|
17
|
+
readonly orders: "movethewheels_delivery_jobs";
|
|
18
|
+
readonly products: "movethewheels_cargo_items";
|
|
19
|
+
readonly vehicles: "movethewheels_vehicles";
|
|
20
|
+
};
|
|
21
|
+
export type MtwExportEntity = keyof typeof MTW_EXPORT_ENTITY_TYPES;
|
|
22
|
+
export declare const START_MTW_EXPORT_MUTATION = "\n mutation StartMovethewheelsEntityExport($input: ExportMtwEntitiesInput!) {\n exportMtwEntities(input: $input) {\n id\n status\n downloadUrl\n }\n }\n";
|
|
23
|
+
export declare const GET_MTW_EXPORT_JOB_QUERY = "\n query GetMovethewheelsExportJob($id: ID!) {\n getExportJob(id: $id) {\n id\n status\n progress\n downloadUrl\n errorMessage\n }\n }\n";
|
|
24
|
+
export interface ExportJobSnapshot {
|
|
25
|
+
id: string;
|
|
26
|
+
status: string;
|
|
27
|
+
progress?: number | null;
|
|
28
|
+
downloadUrl?: string | null;
|
|
29
|
+
errorMessage?: string | null;
|
|
30
|
+
}
|
|
31
|
+
export type ExportJobOutcome = {
|
|
32
|
+
kind: 'ready';
|
|
33
|
+
jobId: string;
|
|
34
|
+
downloadUrl: string;
|
|
35
|
+
} | {
|
|
36
|
+
kind: 'failed';
|
|
37
|
+
jobId: string;
|
|
38
|
+
message: string;
|
|
39
|
+
} | {
|
|
40
|
+
kind: 'pending';
|
|
41
|
+
jobId: string;
|
|
42
|
+
};
|
|
43
|
+
/** Classify one job snapshot; `null` means keep waiting. */
|
|
44
|
+
export declare function classifyExportJob(job: ExportJobSnapshot): ExportJobOutcome | null;
|
|
45
|
+
export interface WaitForExportOptions {
|
|
46
|
+
/** Delay between reads of the job. */
|
|
47
|
+
intervalMs?: number;
|
|
48
|
+
/** Total time to wait before reporting the job as still pending. */
|
|
49
|
+
timeoutMs?: number;
|
|
50
|
+
/** Injectable for tests. */
|
|
51
|
+
sleep?: (ms: number) => Promise<void>;
|
|
52
|
+
/** Injectable for tests. */
|
|
53
|
+
now?: () => number;
|
|
54
|
+
/** Stops waiting (e.g. the page unmounted); resolves as pending. */
|
|
55
|
+
signal?: AbortSignal;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Read the job until it settles or the wait budget is spent. `initial` is the
|
|
59
|
+
* snapshot returned by the start mutation, so an already-finished job resolves
|
|
60
|
+
* without another round trip.
|
|
61
|
+
*/
|
|
62
|
+
export declare function waitForExportJob(initial: ExportJobSnapshot, fetchJob: (id: string) => Promise<ExportJobSnapshot | null>, { intervalMs, timeoutMs, sleep, now, signal, }?: WaitForExportOptions): Promise<ExportJobOutcome>;
|
|
63
|
+
/** Start the archive download in the current tab without navigating away. */
|
|
64
|
+
export declare function triggerUrlDownload(url: string): void;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { TrackingAlert, TrackingAlertSeverity } from '../types';
|
|
2
|
+
export type RiskSignalCode = 'LEG_SLIP' | 'OPEN_LEG_OVERDUE' | 'ROUTE_DURATION_OVERRUN' | 'EXCESS_DWELL' | 'TRACKING_STALE' | 'VEHICLE_UNAVAILABLE' | 'CARRIER_LATE_HISTORY';
|
|
3
|
+
export type TrackingAlertKind = 'DELAYED' | 'AT_RISK';
|
|
4
|
+
export interface ApiTrackingAlert {
|
|
5
|
+
id: string;
|
|
6
|
+
shipmentId: string;
|
|
7
|
+
shipmentNumber: string;
|
|
8
|
+
alertType: string;
|
|
9
|
+
message: string;
|
|
10
|
+
riskScore?: number | null;
|
|
11
|
+
promisedDelivery?: string | null;
|
|
12
|
+
predictedDelivery?: string | null;
|
|
13
|
+
signals?: RiskSignalCode[] | null;
|
|
14
|
+
acknowledged: boolean;
|
|
15
|
+
acknowledgedAt?: string | null;
|
|
16
|
+
}
|
|
17
|
+
/** Risk score at or above which an AT_RISK alert is shown as high severity (matches the backend HIGH level). */
|
|
18
|
+
export declare const HIGH_RISK_SCORE = 70;
|
|
19
|
+
/**
|
|
20
|
+
* DELAYED is always high: the promise is already broken. AT_RISK follows the
|
|
21
|
+
* forecast's risk score; the backend only raises AT_RISK at MEDIUM or above,
|
|
22
|
+
* so anything below the HIGH band is medium.
|
|
23
|
+
*/
|
|
24
|
+
export declare function trackingAlertSeverity(alertType: string, riskScore: number | null | undefined): TrackingAlertSeverity;
|
|
25
|
+
export declare function mapTrackingAlert(alert: ApiTrackingAlert): TrackingAlert;
|
|
26
|
+
export declare function sortTrackingAlerts(alerts: TrackingAlert[]): TrackingAlert[];
|
|
27
|
+
/** Translation key + English fallback for each risk signal code. */
|
|
28
|
+
export declare const RISK_SIGNAL_LABELS: Record<RiskSignalCode, {
|
|
29
|
+
key: string;
|
|
30
|
+
fallback: string;
|
|
31
|
+
}>;
|
|
32
|
+
export declare function riskSignalLabel(code: string, t: (key: string, fallback: string) => string): string;
|
|
33
|
+
/** Minutes between the promise and the predicted delivery, or null when unknown / not late. */
|
|
34
|
+
export declare function predictedSlipMinutes(alert: Pick<TrackingAlert, 'promisedDelivery' | 'predictedDelivery'>): number | null;
|
|
35
|
+
/** "45 min", "3 h 20 min", "2 d 4 h" — compact duration for alert cards. */
|
|
36
|
+
export declare function formatSlip(minutes: number): string;
|
|
@@ -14,80 +14,86 @@ import { TypeBadge as g } from "../components/StatusBadge.js";
|
|
|
14
14
|
import { DataTable as _ } from "../components/DataTable.js";
|
|
15
15
|
import { formatDate as v } from "../utils/formatters.js";
|
|
16
16
|
import { useDebouncedValue as y } from "../hooks/useDebouncedValue.js";
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
17
|
+
import { useEntityExport as b } from "../hooks/useEntityExport.js";
|
|
18
|
+
import { ExportStatusBanner as x } from "../components/ExportStatusBanner.js";
|
|
19
|
+
import { useState as S } from "react";
|
|
20
|
+
import { jsx as C, jsxs as w } from "react/jsx-runtime";
|
|
21
|
+
import { CTAOverflowMenu as T, PagePurpose as E } from "@burdenoff/fe-libs/ui";
|
|
20
22
|
//#region src/movethewheels/pages/CustomersPage.tsx
|
|
21
|
-
function
|
|
22
|
-
let { basePath:
|
|
23
|
-
limit:
|
|
24
|
-
offset:
|
|
25
|
-
filters: { search:
|
|
26
|
-
}), { stats:
|
|
27
|
-
return /* @__PURE__ */
|
|
23
|
+
function D() {
|
|
24
|
+
let { basePath: D, navigate: O } = s(), { state: k, isExporting: A, startExport: j, dismiss: M } = b("customers", "customer"), [N, P] = S(0), [F, I] = S(25), [L, R] = S(""), z = y(L), { customers: B, totalCustomers: V, hasMore: H, isLoading: U, error: W, refetch: G } = l({
|
|
25
|
+
limit: F,
|
|
26
|
+
offset: N * F,
|
|
27
|
+
filters: { search: z }
|
|
28
|
+
}), { stats: K } = c(), q = (e) => O?.(o(D, e));
|
|
29
|
+
return /* @__PURE__ */ w(p, {
|
|
28
30
|
title: "Customers",
|
|
29
|
-
subtitle: `${
|
|
30
|
-
icon: /* @__PURE__ */
|
|
31
|
-
headerActions: /* @__PURE__ */
|
|
31
|
+
subtitle: `${K.total} customers`,
|
|
32
|
+
icon: /* @__PURE__ */ C(a, { size: 24 }),
|
|
33
|
+
headerActions: /* @__PURE__ */ C(T, {
|
|
32
34
|
primary: {
|
|
33
35
|
label: "Add Customer",
|
|
34
|
-
icon: /* @__PURE__ */
|
|
35
|
-
onSelect: () =>
|
|
36
|
+
icon: /* @__PURE__ */ C(n, { size: 16 }),
|
|
37
|
+
onSelect: () => q("customers/new")
|
|
36
38
|
},
|
|
37
39
|
actions: [{
|
|
38
40
|
label: "Refresh",
|
|
39
|
-
icon: /* @__PURE__ */
|
|
40
|
-
onSelect: () =>
|
|
41
|
+
icon: /* @__PURE__ */ C(r, { size: 16 }),
|
|
42
|
+
onSelect: () => G()
|
|
41
43
|
}, {
|
|
42
|
-
label: "Export",
|
|
43
|
-
icon: /* @__PURE__ */
|
|
44
|
-
onSelect:
|
|
44
|
+
label: A ? "Exporting…" : "Export",
|
|
45
|
+
icon: /* @__PURE__ */ C(t, { size: 16 }),
|
|
46
|
+
onSelect: j
|
|
45
47
|
}]
|
|
46
48
|
}),
|
|
47
49
|
children: [
|
|
48
|
-
/* @__PURE__ */
|
|
49
|
-
icon: /* @__PURE__ */
|
|
50
|
+
/* @__PURE__ */ C(E, {
|
|
51
|
+
icon: /* @__PURE__ */ C(a, { size: 18 }),
|
|
50
52
|
children: "The businesses and individuals you deliver for. Keep contact details, locations and satisfaction ratings here, and open a customer to see their order history before booking the next shipment."
|
|
51
53
|
}),
|
|
52
|
-
/* @__PURE__ */
|
|
54
|
+
/* @__PURE__ */ C(x, {
|
|
55
|
+
state: k,
|
|
56
|
+
onDismiss: M
|
|
57
|
+
}),
|
|
58
|
+
/* @__PURE__ */ w("div", { children: [/* @__PURE__ */ C("p", {
|
|
53
59
|
className: "mb-3 text-sm font-medium text-text-secondary",
|
|
54
60
|
children: "Customer base at a glance"
|
|
55
|
-
}), /* @__PURE__ */
|
|
61
|
+
}), /* @__PURE__ */ w(h, {
|
|
56
62
|
columns: 3,
|
|
57
63
|
children: [
|
|
58
|
-
/* @__PURE__ */
|
|
64
|
+
/* @__PURE__ */ C(m, {
|
|
59
65
|
label: "Total customers",
|
|
60
|
-
value:
|
|
61
|
-
icon: /* @__PURE__ */
|
|
66
|
+
value: K.total,
|
|
67
|
+
icon: /* @__PURE__ */ C(a, { size: 20 }),
|
|
62
68
|
accent: "brand"
|
|
63
69
|
}),
|
|
64
|
-
/* @__PURE__ */
|
|
70
|
+
/* @__PURE__ */ C(m, {
|
|
65
71
|
label: "Avg. rating",
|
|
66
|
-
value:
|
|
67
|
-
icon: /* @__PURE__ */
|
|
72
|
+
value: K.avgRating > 0 ? K.avgRating.toFixed(1) : "—",
|
|
73
|
+
icon: /* @__PURE__ */ C(i, { size: 20 }),
|
|
68
74
|
accent: "success",
|
|
69
75
|
subtitle: "Across all customers"
|
|
70
76
|
}),
|
|
71
|
-
/* @__PURE__ */
|
|
77
|
+
/* @__PURE__ */ C(m, {
|
|
72
78
|
label: "Business accounts",
|
|
73
|
-
value:
|
|
74
|
-
icon: /* @__PURE__ */
|
|
79
|
+
value: K.b2b,
|
|
80
|
+
icon: /* @__PURE__ */ C(e, { size: 20 }),
|
|
75
81
|
accent: "blue",
|
|
76
82
|
subtitle: "B2B relationships"
|
|
77
83
|
})
|
|
78
84
|
]
|
|
79
85
|
})] }),
|
|
80
|
-
/* @__PURE__ */
|
|
86
|
+
/* @__PURE__ */ C(_, {
|
|
81
87
|
columns: [
|
|
82
88
|
{
|
|
83
89
|
id: "customer",
|
|
84
90
|
header: "Customer",
|
|
85
|
-
cell: (e) => /* @__PURE__ */
|
|
91
|
+
cell: (e) => /* @__PURE__ */ w("div", {
|
|
86
92
|
className: "flex items-center gap-3",
|
|
87
|
-
children: [/* @__PURE__ */
|
|
93
|
+
children: [/* @__PURE__ */ C(u, { children: /* @__PURE__ */ C(d, { children: e.name.split(" ").map((e) => e[0]).join("") }) }), /* @__PURE__ */ w("div", { children: [/* @__PURE__ */ C("p", {
|
|
88
94
|
className: "font-medium",
|
|
89
95
|
children: e.name
|
|
90
|
-
}), /* @__PURE__ */
|
|
96
|
+
}), /* @__PURE__ */ C("p", {
|
|
91
97
|
className: "text-sm text-text-secondary",
|
|
92
98
|
children: e.email
|
|
93
99
|
})] })]
|
|
@@ -97,7 +103,7 @@ function T() {
|
|
|
97
103
|
id: "type",
|
|
98
104
|
header: "Type",
|
|
99
105
|
accessorKey: "type",
|
|
100
|
-
cell: (e) => /* @__PURE__ */
|
|
106
|
+
cell: (e) => /* @__PURE__ */ C(g, { type: e.type })
|
|
101
107
|
},
|
|
102
108
|
{
|
|
103
109
|
id: "company",
|
|
@@ -118,12 +124,12 @@ function T() {
|
|
|
118
124
|
{
|
|
119
125
|
id: "rating",
|
|
120
126
|
header: "Rating",
|
|
121
|
-
cell: (e) => /* @__PURE__ */
|
|
127
|
+
cell: (e) => /* @__PURE__ */ w("div", {
|
|
122
128
|
className: "flex items-center gap-1",
|
|
123
|
-
children: [/* @__PURE__ */
|
|
129
|
+
children: [/* @__PURE__ */ C(i, {
|
|
124
130
|
size: 14,
|
|
125
131
|
className: "text-status-warning-text fill-current"
|
|
126
|
-
}), /* @__PURE__ */
|
|
132
|
+
}), /* @__PURE__ */ C("span", { children: e.rating === void 0 ? "Not provided" : e.rating.toFixed(1) })]
|
|
127
133
|
})
|
|
128
134
|
},
|
|
129
135
|
{
|
|
@@ -134,10 +140,10 @@ function T() {
|
|
|
134
140
|
cell: (e) => v(e.createdAt, "short")
|
|
135
141
|
}
|
|
136
142
|
],
|
|
137
|
-
data:
|
|
138
|
-
isLoading:
|
|
139
|
-
error:
|
|
140
|
-
onRetry:
|
|
143
|
+
data: B,
|
|
144
|
+
isLoading: U,
|
|
145
|
+
error: W,
|
|
146
|
+
onRetry: G,
|
|
141
147
|
searchable: !0,
|
|
142
148
|
searchPlaceholder: "Search customers...",
|
|
143
149
|
searchFields: [
|
|
@@ -145,26 +151,26 @@ function T() {
|
|
|
145
151
|
"email",
|
|
146
152
|
"company"
|
|
147
153
|
],
|
|
148
|
-
searchQuery:
|
|
154
|
+
searchQuery: L,
|
|
149
155
|
onSearchQueryChange: (e) => {
|
|
150
|
-
|
|
156
|
+
R(e), P(0);
|
|
151
157
|
},
|
|
152
|
-
onRowClick: (e) =>
|
|
158
|
+
onRowClick: (e) => q(`customers/${e.id}`),
|
|
153
159
|
getRowId: (e) => e.id,
|
|
154
|
-
pageIndex:
|
|
155
|
-
pageSize:
|
|
156
|
-
onPageChange:
|
|
160
|
+
pageIndex: N,
|
|
161
|
+
pageSize: F,
|
|
162
|
+
onPageChange: P,
|
|
157
163
|
onPageSizeChange: (e) => {
|
|
158
|
-
|
|
164
|
+
I(e), P(0);
|
|
159
165
|
},
|
|
160
|
-
totalItems:
|
|
161
|
-
hasNextPage:
|
|
166
|
+
totalItems: V,
|
|
167
|
+
hasNextPage: H,
|
|
162
168
|
emptyIllustration: "empty-people",
|
|
163
169
|
emptyTitle: "No customers yet",
|
|
164
170
|
emptyDescription: "Add your first customer to start booking deliveries for them.",
|
|
165
|
-
emptyAction: /* @__PURE__ */
|
|
166
|
-
onClick: () =>
|
|
167
|
-
children: [/* @__PURE__ */
|
|
171
|
+
emptyAction: /* @__PURE__ */ w(f, {
|
|
172
|
+
onClick: () => q("customers/new"),
|
|
173
|
+
children: [/* @__PURE__ */ C(n, {
|
|
168
174
|
size: 16,
|
|
169
175
|
className: "mr-1.5"
|
|
170
176
|
}), "Add Customer"]
|
|
@@ -174,4 +180,4 @@ function T() {
|
|
|
174
180
|
});
|
|
175
181
|
}
|
|
176
182
|
//#endregion
|
|
177
|
-
export {
|
|
183
|
+
export { D as default };
|