@burdenoff/microfe-movethewheels 2026.1101.2 → 2026.1101.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.
@@ -2,178 +2,176 @@ import { Calendar as e } from "../node_modules/lucide-react/dist/esm/icons/calen
2
2
  import { CircleAlert as t } from "../node_modules/lucide-react/dist/esm/icons/circle-alert.js";
3
3
  import { TrendingDown as n } from "../node_modules/lucide-react/dist/esm/icons/trending-down.js";
4
4
  import { TrendingUp as r } from "../node_modules/lucide-react/dist/esm/icons/trending-up.js";
5
- import { Card as i, CardContent as a, CardHeader as o, CardTitle as s, Skeleton as c } from "./ui.js";
6
- import { jsx as l, jsxs as u } from "react/jsx-runtime";
5
+ import { formatCurrency as i } from "../utils/formatters.js";
6
+ import { Card as a, CardContent as o, CardHeader as s, CardTitle as c, Skeleton as l } from "./ui.js";
7
+ import { jsx as u, jsxs as d } from "react/jsx-runtime";
7
8
  //#region src/movethewheels/components/UpcomingDemandPanel.tsx
8
- var d = (e) => {
9
+ var f = (e) => {
9
10
  let t = new Date(e);
10
11
  return Number.isNaN(t.getTime()) ? e : t.toLocaleDateString(void 0, {
11
12
  month: "short",
12
13
  day: "numeric"
13
14
  });
14
- }, f = (e, t) => [...e.filter((e) => e.periodStart === t)].sort((e, t) => t.predictedVolume - e.predictedVolume).slice(0, 3), p = (e, t) => [...e.filter((e) => e.periodStart === t)].sort((e, t) => t.predictedRevenue - e.predictedRevenue).slice(0, 3), m = (e, t) => e.filter((e) => e.periodStart === t).reduce((e, t) => e + t.predictedVolume, 0), h = (e) => e <= 0 ? "Not enough history" : `${Math.round(e * 100)}% confidence`;
15
- function g({ laneForecast: n, customerForecast: r, laneVariance: g, customerVariance: y, isLoading: b, error: x, onRetry: S }) {
16
- if (x && !b) return /* @__PURE__ */ l(i, {
15
+ }, p = (e, t) => e.filter((e) => e.periodStart === t && e.confidence > 0), m = (e, t) => [...p(e, t)].sort((e, t) => t.predictedVolume - e.predictedVolume || t.predictedRevenue - e.predictedRevenue).slice(0, 3), h = (e, t) => [...p(e, t)].sort((e, t) => t.predictedRevenue - e.predictedRevenue).slice(0, 3), g = (e, t) => p(e, t).reduce((e, t) => e + t.predictedVolume, 0), _ = (e, t = /* @__PURE__ */ new Date()) => {
16
+ let n = [...new Set(e.map((e) => e.periodStart))].sort((e, t) => new Date(e).getTime() - new Date(t).getTime());
17
+ return n.find((e) => new Date(e).getTime() > t.getTime()) ?? n.at(-1) ?? null;
18
+ }, v = (e, t) => e === 0 && t > 0 ? "<1 job" : e === 1 ? "1 job" : `${e} jobs`, y = (e) => e <= 0 ? "Not enough history" : `${Math.round(e * 100)}% confidence`;
19
+ function b({ laneForecast: n, customerForecast: r, laneVariance: i, customerVariance: v, isLoading: b, error: C, onRetry: w }) {
20
+ if (C && !b) return /* @__PURE__ */ u(a, {
17
21
  className: "border-status-error-border",
18
- children: /* @__PURE__ */ u(a, {
22
+ children: /* @__PURE__ */ d(o, {
19
23
  className: "p-6 text-center space-y-3",
20
24
  children: [
21
- /* @__PURE__ */ l("p", {
25
+ /* @__PURE__ */ u("p", {
22
26
  className: "text-status-error-text font-medium",
23
27
  children: "Couldn't load demand forecast"
24
28
  }),
25
- /* @__PURE__ */ l("p", {
29
+ /* @__PURE__ */ u("p", {
26
30
  className: "text-sm text-text-secondary",
27
- children: x.message
31
+ children: C.message
28
32
  }),
29
- /* @__PURE__ */ l("button", {
33
+ /* @__PURE__ */ u("button", {
30
34
  type: "button",
31
- onClick: S,
35
+ onClick: w,
32
36
  className: "text-sm font-medium text-primary hover:underline",
33
37
  children: "Retry"
34
38
  })
35
39
  ]
36
40
  })
37
41
  });
38
- if (b) return /* @__PURE__ */ u(i, { children: [/* @__PURE__ */ l(o, { children: /* @__PURE__ */ l(s, { children: "Upcoming demand" }) }), /* @__PURE__ */ u(a, {
42
+ if (b) return /* @__PURE__ */ d(a, { children: [/* @__PURE__ */ u(s, { children: /* @__PURE__ */ u(c, { children: "Upcoming demand" }) }), /* @__PURE__ */ d(o, {
39
43
  className: "space-y-4",
40
44
  children: [
41
- /* @__PURE__ */ l(c, { className: "h-16 w-full" }),
42
- /* @__PURE__ */ l(c, { className: "h-16 w-full" }),
43
- /* @__PURE__ */ l(c, { className: "h-16 w-full" })
45
+ /* @__PURE__ */ u(l, { className: "h-16 w-full" }),
46
+ /* @__PURE__ */ u(l, { className: "h-16 w-full" }),
47
+ /* @__PURE__ */ u(l, { className: "h-16 w-full" })
44
48
  ]
45
49
  })] });
46
- let C = n[0]?.periodStart ?? r[0]?.periodStart ?? null;
47
- if (!C || n.length === 0) return /* @__PURE__ */ u(i, { children: [/* @__PURE__ */ l(o, { children: /* @__PURE__ */ u(s, {
50
+ let T = _([...n, ...r]), E = T !== null && (p(n, T).length > 0 || p(r, T).length > 0);
51
+ if (!T || !E) return /* @__PURE__ */ d(a, { children: [/* @__PURE__ */ u(s, { children: /* @__PURE__ */ d(c, {
48
52
  className: "flex items-center gap-2",
49
- children: [/* @__PURE__ */ l(e, {
53
+ children: [/* @__PURE__ */ u(e, {
50
54
  size: 18,
51
55
  "aria-hidden": "true"
52
56
  }), "Upcoming demand"]
53
- }) }), /* @__PURE__ */ l(a, { children: /* @__PURE__ */ u("div", {
57
+ }) }), /* @__PURE__ */ u(o, { children: /* @__PURE__ */ d("div", {
54
58
  className: "flex items-start gap-2 rounded-lg border border-border/60 bg-muted/40 p-4 text-sm text-text-secondary",
55
- children: [/* @__PURE__ */ l(t, {
59
+ children: [/* @__PURE__ */ u(t, {
56
60
  size: 16,
57
61
  "aria-hidden": "true",
58
62
  className: "mt-0.5 shrink-0"
59
- }), /* @__PURE__ */ l("p", { children: "Not enough history to forecast next week yet. Once you have at least 4 weeks of completed orders, this panel will surface demand by lane and customer." })]
63
+ }), /* @__PURE__ */ u("p", { children: "Not enough history to forecast next week yet. Once a lane or customer has at least 4 weeks of delivery jobs, this panel will surface its demand." })]
60
64
  }) })] });
61
- let w = f(n, C), T = p(n, C), E = f(r, C), D = m(n, C), O = [...g, ...y].sort((e, t) => Math.abs(t.volumeVariance) - Math.abs(e.volumeVariance)).slice(0, 3);
62
- return /* @__PURE__ */ u(i, { children: [/* @__PURE__ */ l(o, { children: /* @__PURE__ */ u(s, {
65
+ let D = m(n, T), O = h(n, T), k = m(r, T), A = p(n, T).length > 0, j = g(A ? n : r, T), M = A ? "Predicted jobs (all lanes)" : "Predicted jobs (all customers)", N = A ? D[0] : k[0], P = [...i, ...v].sort((e, t) => Math.abs(t.volumeVariance) - Math.abs(e.volumeVariance)).slice(0, 3);
66
+ return /* @__PURE__ */ d(a, { children: [/* @__PURE__ */ u(s, { children: /* @__PURE__ */ d(c, {
63
67
  className: "flex items-center gap-2",
64
68
  children: [
65
- /* @__PURE__ */ l(e, {
69
+ /* @__PURE__ */ u(e, {
66
70
  size: 18,
67
71
  "aria-hidden": "true"
68
72
  }),
69
73
  "Upcoming demand — week of ",
70
- d(C)
74
+ f(T)
71
75
  ]
72
- }) }), /* @__PURE__ */ u(a, {
76
+ }) }), /* @__PURE__ */ d(o, {
73
77
  className: "space-y-4",
74
- children: [/* @__PURE__ */ u("div", {
78
+ children: [/* @__PURE__ */ d("div", {
75
79
  className: "flex items-baseline justify-between rounded-lg border border-border/60 bg-muted/40 px-4 py-3",
76
- children: [/* @__PURE__ */ u("div", { children: [/* @__PURE__ */ l("p", {
80
+ children: [/* @__PURE__ */ d("div", { children: [/* @__PURE__ */ u("p", {
77
81
  className: "text-xs uppercase tracking-wide text-text-secondary",
78
- children: "Predicted jobs (all lanes)"
79
- }), /* @__PURE__ */ l("p", {
82
+ children: M
83
+ }), /* @__PURE__ */ u("p", {
80
84
  className: "text-2xl font-bold",
81
- children: D
82
- })] }), /* @__PURE__ */ l("p", {
85
+ children: j
86
+ })] }), /* @__PURE__ */ u("p", {
83
87
  className: "text-xs text-text-secondary",
84
- children: w[0] ? h(w[0].confidence) : ""
88
+ children: N ? y(N.confidence) : ""
85
89
  })]
86
- }), /* @__PURE__ */ u("div", {
90
+ }), /* @__PURE__ */ d("div", {
87
91
  className: "grid grid-cols-1 md:grid-cols-2 gap-4",
88
92
  children: [
89
- /* @__PURE__ */ l(_, {
93
+ /* @__PURE__ */ u(x, {
90
94
  title: "Top lanes (volume)",
91
- rows: w
95
+ rows: D
92
96
  }),
93
- /* @__PURE__ */ l(_, {
97
+ /* @__PURE__ */ u(x, {
94
98
  title: "Top lanes (revenue)",
95
- rows: T
99
+ rows: O
96
100
  }),
97
- /* @__PURE__ */ l(_, {
101
+ /* @__PURE__ */ u(x, {
98
102
  title: "Top customers (volume)",
99
- rows: E
103
+ rows: k
100
104
  }),
101
- /* @__PURE__ */ l(v, { rows: O })
105
+ /* @__PURE__ */ u(S, { rows: P })
102
106
  ]
103
107
  })]
104
108
  })] });
105
109
  }
106
- function _({ title: e, rows: t }) {
107
- return t.length === 0 ? /* @__PURE__ */ u("div", {
110
+ function x({ title: e, rows: t }) {
111
+ return t.length === 0 ? /* @__PURE__ */ d("div", {
108
112
  className: "rounded-lg border border-border/60 p-4",
109
- children: [/* @__PURE__ */ l("p", {
113
+ children: [/* @__PURE__ */ u("p", {
110
114
  className: "text-sm font-medium mb-2",
111
115
  children: e
112
- }), /* @__PURE__ */ l("p", {
116
+ }), /* @__PURE__ */ u("p", {
113
117
  className: "text-xs text-text-secondary",
114
118
  children: "No forecasts yet for this week."
115
119
  })]
116
- }) : /* @__PURE__ */ u("div", {
120
+ }) : /* @__PURE__ */ d("div", {
117
121
  className: "rounded-lg border border-border/60 p-4",
118
- children: [/* @__PURE__ */ l("p", {
122
+ children: [/* @__PURE__ */ u("p", {
119
123
  className: "text-sm font-medium mb-3",
120
124
  children: e
121
- }), /* @__PURE__ */ l("ul", {
125
+ }), /* @__PURE__ */ u("ul", {
122
126
  className: "space-y-2",
123
- children: t.map((e) => /* @__PURE__ */ u("li", {
127
+ children: t.map((e) => /* @__PURE__ */ d("li", {
124
128
  className: "flex items-center justify-between text-sm",
125
- children: [/* @__PURE__ */ l("span", {
129
+ children: [/* @__PURE__ */ u("span", {
126
130
  className: "truncate font-medium",
127
- children: e.scopeKey
128
- }), /* @__PURE__ */ u("span", {
131
+ title: e.scopeLabel,
132
+ children: e.scopeLabel
133
+ }), /* @__PURE__ */ d("span", {
129
134
  className: "ml-2 shrink-0 text-text-secondary",
130
135
  children: [
131
- e.predictedVolume,
132
- " jobs ·",
136
+ v(e.predictedVolume, e.predictedRevenue),
137
+ " ·",
133
138
  " ",
134
- new Intl.NumberFormat(void 0, {
135
- style: "currency",
136
- currency: "USD",
137
- maximumFractionDigits: 0
138
- }).format(e.predictedRevenue)
139
+ i(e.predictedRevenue)
139
140
  ]
140
141
  })]
141
142
  }, `${e.scopeType}-${e.scopeKey}`))
142
143
  })]
143
144
  });
144
145
  }
145
- function v({ rows: e }) {
146
- return /* @__PURE__ */ u("div", {
146
+ function S({ rows: e }) {
147
+ return /* @__PURE__ */ d("div", {
147
148
  className: "rounded-lg border border-border/60 p-4",
148
- children: [/* @__PURE__ */ l("p", {
149
+ children: [/* @__PURE__ */ u("p", {
149
150
  className: "text-sm font-medium mb-3",
150
151
  children: "Recent forecast variance"
151
- }), e.length === 0 ? /* @__PURE__ */ l("p", {
152
+ }), e.length === 0 ? /* @__PURE__ */ u("p", {
152
153
  className: "text-xs text-text-secondary",
153
154
  children: "No closed periods to compare yet."
154
- }) : /* @__PURE__ */ l("ul", {
155
+ }) : /* @__PURE__ */ u("ul", {
155
156
  className: "space-y-2",
156
157
  children: e.map((e) => {
157
158
  let t = e.volumeVariance > 0, i = t ? r : n, a = t ? "text-status-success-text" : "text-status-error-text";
158
- return /* @__PURE__ */ u("li", {
159
+ return /* @__PURE__ */ d("li", {
159
160
  className: "flex items-center justify-between text-sm",
160
- children: [/* @__PURE__ */ u("span", {
161
+ children: [/* @__PURE__ */ d("span", {
161
162
  className: "flex items-center gap-2 truncate",
162
- children: [/* @__PURE__ */ l(i, {
163
+ children: [/* @__PURE__ */ u(i, {
163
164
  size: 14,
164
165
  className: a,
165
166
  "aria-hidden": "true"
166
- }), /* @__PURE__ */ l("span", {
167
+ }), /* @__PURE__ */ u("span", {
167
168
  className: "truncate font-medium",
168
- children: e.scopeKey
169
+ title: e.scopeLabel,
170
+ children: e.scopeLabel
169
171
  })]
170
- }), /* @__PURE__ */ u("span", {
172
+ }), /* @__PURE__ */ d("span", {
171
173
  className: `ml-2 shrink-0 ${a}`,
172
- children: [
173
- t ? "+" : "",
174
- e.volumeVariance,
175
- " jobs"
176
- ]
174
+ children: [t ? "+" : "", Math.abs(e.volumeVariance) === 1 ? `${e.volumeVariance} job` : `${e.volumeVariance} jobs`]
177
175
  })]
178
176
  }, `${e.scopeType}-${e.scopeKey}-${e.periodStart}`);
179
177
  })
@@ -181,4 +179,4 @@ function v({ rows: e }) {
181
179
  });
182
180
  }
183
181
  //#endregion
184
- export { g as UpcomingDemandPanel };
182
+ export { b as UpcomingDemandPanel };
@@ -1141,6 +1141,7 @@ export type GetMovethewheelsDemandForecastByLaneQuery = {
1141
1141
  demandForecastByLane: Array<{
1142
1142
  scopeType: Types.DemandForecastScope;
1143
1143
  scopeKey: string;
1144
+ scopeLabel: string;
1144
1145
  periodStart: string;
1145
1146
  periodEnd: string;
1146
1147
  predictedVolume: number;
@@ -1157,6 +1158,7 @@ export type GetMovethewheelsDemandForecastByCustomerQuery = {
1157
1158
  demandForecastByCustomer: Array<{
1158
1159
  scopeType: Types.DemandForecastScope;
1159
1160
  scopeKey: string;
1161
+ scopeLabel: string;
1160
1162
  periodStart: string;
1161
1163
  periodEnd: string;
1162
1164
  predictedVolume: number;
@@ -1174,6 +1176,7 @@ export type GetMovethewheelsDemandForecastVarianceQuery = {
1174
1176
  demandForecastVariance: Array<{
1175
1177
  scopeType: Types.DemandForecastScope;
1176
1178
  scopeKey: string;
1179
+ scopeLabel: string;
1177
1180
  periodStart: string;
1178
1181
  periodEnd: string;
1179
1182
  predictedVolume: number;
@@ -2,7 +2,7 @@ import { useMoveTheWheels as e } from "../providers/MoveTheWheelsProvider.js";
2
2
  import { executeGraphQL as t } from "../utils/graphqlClient.js";
3
3
  import { useCallback as n, useEffect as r, useMemo as i, useState as a } from "react";
4
4
  //#region src/movethewheels/hooks/useDemandForecast.ts
5
- var o = "\n query GetMovethewheelsDemandForecast($weeks: Int!, $varianceLimit: Int!) {\n demandForecastByLane(weeks: $weeks) {\n scopeType\n scopeKey\n periodStart\n periodEnd\n predictedVolume\n predictedRevenue\n actualVolume\n actualRevenue\n confidence\n }\n demandForecastByCustomer(weeks: $weeks) {\n scopeType\n scopeKey\n periodStart\n periodEnd\n predictedVolume\n predictedRevenue\n actualVolume\n actualRevenue\n confidence\n }\n laneVariance: demandForecastVariance(scopeType: LANE, limit: $varianceLimit) {\n scopeType\n scopeKey\n periodStart\n periodEnd\n predictedVolume\n actualVolume\n volumeVariance\n predictedRevenue\n actualRevenue\n revenueVariance\n }\n customerVariance: demandForecastVariance(scopeType: CUSTOMER, limit: $varianceLimit) {\n scopeType\n scopeKey\n periodStart\n periodEnd\n predictedVolume\n actualVolume\n volumeVariance\n predictedRevenue\n actualRevenue\n revenueVariance\n }\n }\n";
5
+ var o = "\n query GetMovethewheelsDemandForecast($weeks: Int!, $varianceLimit: Int!) {\n demandForecastByLane(weeks: $weeks) {\n scopeType\n scopeKey\n scopeLabel\n periodStart\n periodEnd\n predictedVolume\n predictedRevenue\n actualVolume\n actualRevenue\n confidence\n }\n demandForecastByCustomer(weeks: $weeks) {\n scopeType\n scopeKey\n scopeLabel\n periodStart\n periodEnd\n predictedVolume\n predictedRevenue\n actualVolume\n actualRevenue\n confidence\n }\n laneVariance: demandForecastVariance(scopeType: LANE, limit: $varianceLimit) {\n scopeType\n scopeKey\n scopeLabel\n periodStart\n periodEnd\n predictedVolume\n actualVolume\n volumeVariance\n predictedRevenue\n actualRevenue\n revenueVariance\n }\n customerVariance: demandForecastVariance(scopeType: CUSTOMER, limit: $varianceLimit) {\n scopeType\n scopeKey\n scopeLabel\n periodStart\n periodEnd\n predictedVolume\n actualVolume\n volumeVariance\n predictedRevenue\n actualRevenue\n revenueVariance\n }\n }\n";
6
6
  function s(s = 4, c = 8) {
7
7
  let { apiGatewayUrl: l, authToken: u, workspaceToken: d, workspaceId: f, tenantId: p, currentUser: m } = e(), [h, g] = a([]), [_, v] = a([]), [y, b] = a([]), [x, S] = a([]), [C, w] = a(!1), [T, E] = a(null), D = i(() => ({
8
8
  apiGatewayUrl: l,
@@ -6,6 +6,8 @@
6
6
  export interface ForecastPoint {
7
7
  scopeType: 'LANE' | 'CUSTOMER';
8
8
  scopeKey: string;
9
+ /** Customer name or `Origin → Destination`; falls back to the key. */
10
+ scopeLabel: string;
9
11
  periodStart: string;
10
12
  periodEnd: string;
11
13
  predictedVolume: number;
@@ -17,6 +19,7 @@ export interface ForecastPoint {
17
19
  export interface ForecastVariance {
18
20
  scopeType: 'LANE' | 'CUSTOMER';
19
21
  scopeKey: string;
22
+ scopeLabel: string;
20
23
  periodStart: string;
21
24
  periodEnd: string;
22
25
  predictedVolume: number;
@@ -29,3 +29,9 @@ export declare function timezoneOffsetFor(date: string): number;
29
29
  * days. Null when the runtime cannot report one; the fixed offset is then used.
30
30
  */
31
31
  export declare function deviceTimeZone(): string | null;
32
+ /**
33
+ * A calendar label for a YYYY-MM-DD day, e.g. "Sunday, November 1, 2026".
34
+ * Formatted as a pure calendar date (no time, no zone shift) so the header
35
+ * always names the day the driver picked.
36
+ */
37
+ export declare function formatLocalDay(date: string): string;
@@ -10,20 +10,20 @@ import { Phone as c } from "../node_modules/lucide-react/dist/esm/icons/phone.js
10
10
  import { RefreshCw as l } from "../node_modules/lucide-react/dist/esm/icons/refresh-cw.js";
11
11
  import { Route as u } from "../node_modules/lucide-react/dist/esm/icons/route.js";
12
12
  import { TriangleAlert as d } from "../node_modules/lucide-react/dist/esm/icons/triangle-alert.js";
13
- import { Truck as ee } from "../node_modules/lucide-react/dist/esm/icons/truck.js";
14
- import { WifiOff as te } from "../node_modules/lucide-react/dist/esm/icons/wifi-off.js";
15
- import { useMoveTheWheels as ne } from "../providers/MoveTheWheelsProvider.js";
16
- import { formatAddress as f, formatDate as p, formatTime as m } from "../utils/formatters.js";
17
- import { mapOrderPriority as h } from "../hooks/useOrders.js";
18
- import { localDateString as g, phoneHref as _, routeNavigationUrl as v, shiftLocalDate as y, stopNavigationUrl as b } from "../utils/driverNavigation.js";
19
- import { useMyDeliveryDay as re, useOnlineStatus as x } from "../hooks/useDriverDay.js";
20
- import { Badge as S, Button as C, Card as w, CardContent as T, Input as ie, Progress as ae } from "../components/ui.js";
21
- import { PageLayout as E } from "../components/PageLayout.js";
22
- import { PriorityBadge as D } from "../components/StatusBadge.js";
23
- import { FormError as O } from "../components/FormError.js";
24
- import { Notice as k, useNotice as A } from "../components/Notice.js";
13
+ import { Truck as f } from "../node_modules/lucide-react/dist/esm/icons/truck.js";
14
+ import { WifiOff as ee } from "../node_modules/lucide-react/dist/esm/icons/wifi-off.js";
15
+ import { useMoveTheWheels as te } from "../providers/MoveTheWheelsProvider.js";
16
+ import { formatAddress as p, formatDate as m, formatTime as h } from "../utils/formatters.js";
17
+ import { mapOrderPriority as g } from "../hooks/useOrders.js";
18
+ import { formatLocalDay as _, localDateString as v, phoneHref as y, routeNavigationUrl as ne, shiftLocalDate as b, stopNavigationUrl as x } from "../utils/driverNavigation.js";
19
+ import { useMyDeliveryDay as re, useOnlineStatus as ie } from "../hooks/useDriverDay.js";
20
+ import { Badge as S, Button as C, Card as w, CardContent as T, Input as E, Progress as D } from "../components/ui.js";
21
+ import { PageLayout as O } from "../components/PageLayout.js";
22
+ import { PriorityBadge as k } from "../components/StatusBadge.js";
23
+ import { FormError as A } from "../components/FormError.js";
24
+ import { Notice as j, useNotice as ae } from "../components/Notice.js";
25
25
  import { EpodCaptureForm as oe } from "../components/EpodCaptureForm.js";
26
- import { useEffect as se, useMemo as j, useRef as M, useState as N } from "react";
26
+ import { useEffect as se, useMemo as M, useRef as ce, useState as N } from "react";
27
27
  import { Fragment as P, jsx as F, jsxs as I } from "react/jsx-runtime";
28
28
  //#region src/movethewheels/pages/DeliveryCapturePage.tsx
29
29
  var L = {
@@ -96,7 +96,7 @@ function W({ stop: e, onOpen: t, onCapture: n, offline: r }) {
96
96
  }),
97
97
  e.deliveryAddress && /* @__PURE__ */ F("p", {
98
98
  className: "mt-1 text-sm text-text-secondary",
99
- children: f(e.deliveryAddress)
99
+ children: p(e.deliveryAddress)
100
100
  }),
101
101
  /* @__PURE__ */ I("p", {
102
102
  className: "mt-1 flex flex-wrap items-center gap-x-3 gap-y-0.5 text-xs text-text-secondary",
@@ -106,7 +106,7 @@ function W({ stop: e, onOpen: t, onCapture: n, offline: r }) {
106
106
  children: [
107
107
  /* @__PURE__ */ F(a, { size: 12 }),
108
108
  " ",
109
- m(e.scheduledDelivery)
109
+ h(e.scheduledDelivery)
110
110
  ]
111
111
  }),
112
112
  /* @__PURE__ */ I("span", {
@@ -139,8 +139,8 @@ function W({ stop: e, onOpen: t, onCapture: n, offline: r }) {
139
139
  ]
140
140
  }) });
141
141
  }
142
- function G({ stop: t, onBack: n, onCapture: r, offline: a }) {
143
- let s = b(t.deliveryAddress), l = _(t.customerPhone);
142
+ function le({ stop: t, onBack: n, onCapture: r, offline: a }) {
143
+ let s = x(t.deliveryAddress), l = y(t.customerPhone);
144
144
  return /* @__PURE__ */ I("div", {
145
145
  className: "space-y-4",
146
146
  children: [/* @__PURE__ */ I(C, {
@@ -161,8 +161,8 @@ function G({ stop: t, onBack: n, onCapture: r, offline: a }) {
161
161
  children: ["Stop ", t.sequence]
162
162
  }),
163
163
  /* @__PURE__ */ F(H, { state: t.state }),
164
- /* @__PURE__ */ F(D, {
165
- priority: h(t.priority),
164
+ /* @__PURE__ */ F(k, {
165
+ priority: g(t.priority),
166
166
  size: "sm"
167
167
  }),
168
168
  t.overdue && /* @__PURE__ */ F(S, {
@@ -179,7 +179,7 @@ function G({ stop: t, onBack: n, onCapture: r, offline: a }) {
179
179
  children: "Deliver to"
180
180
  }), /* @__PURE__ */ F("dd", {
181
181
  className: "font-medium",
182
- children: t.deliveryAddress ? f(t.deliveryAddress) : "No address on file"
182
+ children: t.deliveryAddress ? p(t.deliveryAddress) : "No address on file"
183
183
  })] }),
184
184
  t.customerName && /* @__PURE__ */ I("div", { children: [/* @__PURE__ */ F("dt", {
185
185
  className: "text-text-secondary",
@@ -200,7 +200,7 @@ function G({ stop: t, onBack: n, onCapture: r, offline: a }) {
200
200
  children: "Scheduled"
201
201
  }), /* @__PURE__ */ F("dd", {
202
202
  className: "font-medium",
203
- children: p(t.scheduledDelivery, "full")
203
+ children: m(t.scheduledDelivery, "full")
204
204
  })] }),
205
205
  t.routeName && /* @__PURE__ */ I("div", { children: [/* @__PURE__ */ F("dt", {
206
206
  className: "text-text-secondary",
@@ -221,7 +221,7 @@ function G({ stop: t, onBack: n, onCapture: r, offline: a }) {
221
221
  children: "Proof of delivery captured"
222
222
  }), /* @__PURE__ */ F("dd", {
223
223
  className: "font-medium",
224
- children: p(t.deliveredAt, "full")
224
+ children: m(t.deliveredAt, "full")
225
225
  })] })
226
226
  ]
227
227
  }),
@@ -257,46 +257,46 @@ function G({ stop: t, onBack: n, onCapture: r, offline: a }) {
257
257
  }) })]
258
258
  });
259
259
  }
260
- function K() {
261
- let { workspaceId: e, tenantId: a, currentUser: o, apiGatewayUrl: s } = ne(), c = x(), m = !c, [h, _] = N(() => g(/* @__PURE__ */ new Date())), [b, S] = N("all"), [D, L] = N({ kind: "list" }), { day: R, isLoading: H, error: U, refetch: K, markCaptured: ce } = re(h), { message: le, notify: ue } = A(), q = g(/* @__PURE__ */ new Date()), J = M(!1);
260
+ function G() {
261
+ let { workspaceId: e, tenantId: a, currentUser: o, apiGatewayUrl: s } = te(), c = ie(), m = !c, [h, g] = N(() => v(/* @__PURE__ */ new Date())), [y, x] = N("all"), [S, k] = N({ kind: "list" }), { day: L, isLoading: R, error: H, refetch: U, markCaptured: G } = re(h), { message: ue, notify: K } = ae(), q = v(/* @__PURE__ */ new Date()), J = ce(!1);
262
262
  se(() => {
263
- c ? J.current && (J.current = !1, K()) : J.current = !0;
264
- }, [c, K]);
265
- let Y = j(() => R?.stops ?? [], [R]), X = j(() => Y.filter((e) => b === "all" ? !0 : b === "completed" ? z(e) : !z(e)), [Y, b]), Z = j(() => v(Y.filter((e) => !z(e)).map((e) => e.deliveryAddress)), [Y]), Q = D.kind === "list" ? null : Y.find((e) => e.jobId === D.jobId) ?? null, de = (e) => {
266
- ce(e), ue("Proof of delivery captured — delivery marked complete."), L({ kind: "list" }), K();
263
+ c ? J.current && (J.current = !1, U()) : J.current = !0;
264
+ }, [c, U]);
265
+ let Y = M(() => L?.stops ?? [], [L]), X = M(() => Y.filter((e) => y === "all" ? !0 : y === "completed" ? z(e) : !z(e)), [Y, y]), Z = M(() => ne(Y.filter((e) => !z(e)).map((e) => e.deliveryAddress)), [Y]), Q = S.kind === "list" ? null : Y.find((e) => e.jobId === S.jobId) ?? null, de = (e) => {
266
+ G(e), K("Proof of delivery captured — delivery marked complete."), k({ kind: "list" }), U();
267
267
  }, $ = (e) => {
268
- /^\d{4}-\d{2}-\d{2}$/.test(e) && (_(e), L({ kind: "list" }));
269
- }, fe = R && R.totalStops > 0 ? R.completedStops / R.totalStops * 100 : 0;
270
- return /* @__PURE__ */ F(E, {
268
+ /^\d{4}-\d{2}-\d{2}$/.test(e) && (g(e), k({ kind: "list" }));
269
+ }, fe = L && L.totalStops > 0 ? L.completedStops / L.totalStops * 100 : 0;
270
+ return /* @__PURE__ */ F(O, {
271
271
  title: "My Deliveries",
272
272
  subtitle: "Your stops for the day, in order — navigate, then capture proof of delivery",
273
- icon: /* @__PURE__ */ F(ee, { size: 24 }),
273
+ icon: /* @__PURE__ */ F(f, { size: 24 }),
274
274
  maxWidth: "2xl",
275
275
  children: /* @__PURE__ */ I("div", {
276
276
  className: "space-y-4",
277
277
  children: [
278
- /* @__PURE__ */ F(k, { message: le }),
278
+ /* @__PURE__ */ F(j, { message: ue }),
279
279
  m && /* @__PURE__ */ I("div", {
280
280
  role: "status",
281
281
  className: "flex items-start gap-2 rounded-md border border-border bg-surface-secondary p-3 text-sm",
282
- children: [/* @__PURE__ */ F(te, {
282
+ children: [/* @__PURE__ */ F(ee, {
283
283
  size: 16,
284
284
  className: "mt-0.5 shrink-0"
285
285
  }), /* @__PURE__ */ F("span", { children: "You are offline. Your last loaded stops stay available; reconnect to capture proof of delivery." })]
286
286
  }),
287
- Q && D.kind === "capture" && Q.awaitingCapture ? /* @__PURE__ */ F(oe, {
287
+ Q && S.kind === "capture" && Q.awaitingCapture ? /* @__PURE__ */ F(oe, {
288
288
  job: B(Q),
289
289
  offline: m,
290
290
  onCaptured: de,
291
- onCancel: () => L({
291
+ onCancel: () => k({
292
292
  kind: "stop",
293
293
  jobId: Q.jobId
294
294
  })
295
- }, `${e}:${a}:${o?.id}:${s}:${Q.jobId}`) : Q ? /* @__PURE__ */ F(G, {
295
+ }, `${e}:${a}:${o?.id}:${s}:${Q.jobId}`) : Q ? /* @__PURE__ */ F(le, {
296
296
  stop: Q,
297
297
  offline: m,
298
- onBack: () => L({ kind: "list" }),
299
- onCapture: () => L({
298
+ onBack: () => k({ kind: "list" }),
299
+ onCapture: () => k({
300
300
  kind: "capture",
301
301
  jobId: Q.jobId
302
302
  })
@@ -309,7 +309,7 @@ function K() {
309
309
  variant: "outline",
310
310
  size: "sm",
311
311
  "aria-label": "Previous day",
312
- onClick: () => $(y(h, -1)),
312
+ onClick: () => $(b(h, -1)),
313
313
  children: /* @__PURE__ */ F(n, { size: 16 })
314
314
  }),
315
315
  /* @__PURE__ */ I(C, {
@@ -324,10 +324,10 @@ function K() {
324
324
  variant: "outline",
325
325
  size: "sm",
326
326
  "aria-label": "Next day",
327
- onClick: () => $(y(h, 1)),
327
+ onClick: () => $(b(h, 1)),
328
328
  children: /* @__PURE__ */ F(r, { size: 16 })
329
329
  }),
330
- /* @__PURE__ */ F(ie, {
330
+ /* @__PURE__ */ F(E, {
331
331
  type: "date",
332
332
  "aria-label": "Delivery date",
333
333
  value: h,
@@ -339,20 +339,20 @@ function K() {
339
339
  variant: "ghost",
340
340
  size: "sm",
341
341
  "aria-label": "Refresh stops",
342
- disabled: H || m,
343
- onClick: () => void K(),
342
+ disabled: R || m,
343
+ onClick: () => void U(),
344
344
  children: /* @__PURE__ */ F(l, {
345
345
  size: 16,
346
- className: H ? "animate-spin" : void 0
346
+ className: R ? "animate-spin" : void 0
347
347
  })
348
348
  })
349
349
  ]
350
350
  }),
351
- U && /* @__PURE__ */ F(O, { message: U.message }),
352
- H && !R ? /* @__PURE__ */ F("p", {
351
+ H && /* @__PURE__ */ F(A, { message: H.message }),
352
+ R && !L ? /* @__PURE__ */ F("p", {
353
353
  className: "py-8 text-center text-text-secondary",
354
354
  children: "Loading your deliveries…"
355
- }) : R && R.driverId === null ? /* @__PURE__ */ I("div", {
355
+ }) : L && L.driverId === null ? /* @__PURE__ */ I("div", {
356
356
  className: "py-12 text-center",
357
357
  children: [/* @__PURE__ */ F(d, {
358
358
  size: 40,
@@ -361,7 +361,7 @@ function K() {
361
361
  className: "text-text-secondary",
362
362
  children: "Your account is not linked to a driver in this workspace. Ask your dispatcher to link your user on the driver profile."
363
363
  })]
364
- }) : R ? /* @__PURE__ */ I(P, { children: [
364
+ }) : L ? /* @__PURE__ */ I(P, { children: [
365
365
  /* @__PURE__ */ F(w, { children: /* @__PURE__ */ I(T, {
366
366
  className: "space-y-3 py-4",
367
367
  children: [
@@ -370,35 +370,35 @@ function K() {
370
370
  children: [/* @__PURE__ */ I("p", {
371
371
  className: "font-medium",
372
372
  children: [
373
- R.completedStops,
373
+ L.completedStops,
374
374
  " of ",
375
- R.totalStops,
375
+ L.totalStops,
376
376
  " stops done",
377
- R.driverName ? ` · ${R.driverName}` : ""
377
+ L.driverName ? ` · ${L.driverName}` : ""
378
378
  ]
379
379
  }), /* @__PURE__ */ F("p", {
380
380
  className: "text-sm text-text-secondary",
381
- children: p(/* @__PURE__ */ new Date(`${h}T12:00:00`), "full")
381
+ children: _(h)
382
382
  })]
383
383
  }),
384
- /* @__PURE__ */ F(ae, {
384
+ /* @__PURE__ */ F(D, {
385
385
  value: fe,
386
386
  "aria-label": "Stops completed"
387
387
  }),
388
- R.nextStop && /* @__PURE__ */ I("div", {
388
+ L.nextStop && /* @__PURE__ */ I("div", {
389
389
  className: "rounded-md border border-border p-3",
390
390
  children: [
391
391
  /* @__PURE__ */ I("p", {
392
392
  className: "text-xs uppercase tracking-wide text-text-secondary",
393
- children: ["Next stop · Stop ", R.nextStop.sequence]
393
+ children: ["Next stop · Stop ", L.nextStop.sequence]
394
394
  }),
395
395
  /* @__PURE__ */ F("p", {
396
396
  className: "mt-1 font-medium",
397
- children: R.nextStop.deliveryAddress ? f(R.nextStop.deliveryAddress) : R.nextStop.deliveryJobNumber
397
+ children: L.nextStop.deliveryAddress ? p(L.nextStop.deliveryAddress) : L.nextStop.deliveryJobNumber
398
398
  }),
399
- R.nextStop.customerName && /* @__PURE__ */ I("p", {
399
+ L.nextStop.customerName && /* @__PURE__ */ I("p", {
400
400
  className: "text-sm text-text-secondary",
401
- children: ["Recipient: ", R.nextStop.customerName]
401
+ children: ["Recipient: ", L.nextStop.customerName]
402
402
  }),
403
403
  /* @__PURE__ */ I("div", {
404
404
  className: "mt-3 grid gap-2 sm:grid-cols-2",
@@ -411,8 +411,8 @@ function K() {
411
411
  type: "button",
412
412
  variant: "outline",
413
413
  onClick: () => {
414
- let e = R.nextStop;
415
- e && L({
414
+ let e = L.nextStop;
415
+ e && k({
416
416
  kind: "stop",
417
417
  jobId: e.jobId
418
418
  });
@@ -429,16 +429,16 @@ function K() {
429
429
  "aria-label": "Filter stops",
430
430
  className: "flex gap-2",
431
431
  children: [
432
- ["all", `All stops (${R.totalStops})`],
433
- ["remaining", `Remaining (${R.remainingStops})`],
434
- ["completed", `Done (${R.completedStops})`]
432
+ ["all", `All stops (${L.totalStops})`],
433
+ ["remaining", `Remaining (${L.remainingStops})`],
434
+ ["completed", `Done (${L.completedStops})`]
435
435
  ].map(([e, t]) => /* @__PURE__ */ F(C, {
436
436
  type: "button",
437
437
  role: "tab",
438
- "aria-selected": b === e,
438
+ "aria-selected": y === e,
439
439
  size: "sm",
440
- variant: b === e ? "default" : "outline",
441
- onClick: () => S(e),
440
+ variant: y === e ? "default" : "outline",
441
+ onClick: () => x(e),
442
442
  children: t
443
443
  }, e))
444
444
  }),
@@ -447,11 +447,11 @@ function K() {
447
447
  children: X.map((e) => /* @__PURE__ */ F(W, {
448
448
  stop: e,
449
449
  offline: m,
450
- onOpen: () => L({
450
+ onOpen: () => k({
451
451
  kind: "stop",
452
452
  jobId: e.jobId
453
453
  }),
454
- onCapture: () => L({
454
+ onCapture: () => k({
455
455
  kind: "capture",
456
456
  jobId: e.jobId
457
457
  })
@@ -463,7 +463,7 @@ function K() {
463
463
  className: "mx-auto mb-3 text-text-secondary opacity-50"
464
464
  }), /* @__PURE__ */ F("p", {
465
465
  className: "text-text-secondary",
466
- children: R.totalStops === 0 ? "No deliveries scheduled for this day." : b === "completed" ? "No stops completed yet." : "All stops done for this day."
466
+ children: L.totalStops === 0 ? "No deliveries scheduled for this day." : y === "completed" ? "No stops completed yet." : "All stops done for this day."
467
467
  })]
468
468
  })
469
469
  ] }) : null
@@ -473,4 +473,4 @@ function K() {
473
473
  });
474
474
  }
475
475
  //#endregion
476
- export { K as default };
476
+ export { G as default };
@@ -47,5 +47,15 @@ function l() {
47
47
  return null;
48
48
  }
49
49
  }
50
+ function u(e) {
51
+ let [t, n, r] = e.split("-").map(Number);
52
+ return new Intl.DateTimeFormat("en-US", {
53
+ weekday: "long",
54
+ year: "numeric",
55
+ month: "long",
56
+ day: "numeric",
57
+ timeZone: "UTC"
58
+ }).format(new Date(Date.UTC(t, n - 1, r)));
59
+ }
50
60
  //#endregion
51
- export { l as deviceTimeZone, o as localDateString, a as phoneHref, i as routeNavigationUrl, s as shiftLocalDate, r as stopNavigationUrl, c as timezoneOffsetFor };
61
+ export { l as deviceTimeZone, u as formatLocalDay, o as localDateString, a as phoneHref, i as routeNavigationUrl, s as shiftLocalDate, r as stopNavigationUrl, c as timezoneOffsetFor };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@burdenoff/microfe-movethewheels",
3
- "version": "2026.1101.2",
3
+ "version": "2026.1101.4",
4
4
  "description": "Burdenoff MoveTheWheels Microfrontend - Logistics & Supply Chain Management Platform",
5
5
  "type": "module",
6
6
  "files": [
@@ -397,14 +397,14 @@ export type GetMovethewheelsDemandForecastByLaneQueryVariables = Exact<{
397
397
  }>;
398
398
 
399
399
 
400
- export type GetMovethewheelsDemandForecastByLaneQuery = { demandForecastByLane: Array<{ scopeType: Types.DemandForecastScope, scopeKey: string, periodStart: string, periodEnd: string, predictedVolume: number, predictedRevenue: number, actualVolume: number | null, actualRevenue: number | null, confidence: number }> };
400
+ export type GetMovethewheelsDemandForecastByLaneQuery = { demandForecastByLane: Array<{ scopeType: Types.DemandForecastScope, scopeKey: string, scopeLabel: string, periodStart: string, periodEnd: string, predictedVolume: number, predictedRevenue: number, actualVolume: number | null, actualRevenue: number | null, confidence: number }> };
401
401
 
402
402
  export type GetMovethewheelsDemandForecastByCustomerQueryVariables = Exact<{
403
403
  weeks: number;
404
404
  }>;
405
405
 
406
406
 
407
- export type GetMovethewheelsDemandForecastByCustomerQuery = { demandForecastByCustomer: Array<{ scopeType: Types.DemandForecastScope, scopeKey: string, periodStart: string, periodEnd: string, predictedVolume: number, predictedRevenue: number, actualVolume: number | null, actualRevenue: number | null, confidence: number }> };
407
+ export type GetMovethewheelsDemandForecastByCustomerQuery = { demandForecastByCustomer: Array<{ scopeType: Types.DemandForecastScope, scopeKey: string, scopeLabel: string, periodStart: string, periodEnd: string, predictedVolume: number, predictedRevenue: number, actualVolume: number | null, actualRevenue: number | null, confidence: number }> };
408
408
 
409
409
  export type GetMovethewheelsDemandForecastVarianceQueryVariables = Exact<{
410
410
  scopeType: Types.DemandForecastScope;
@@ -412,7 +412,7 @@ export type GetMovethewheelsDemandForecastVarianceQueryVariables = Exact<{
412
412
  }>;
413
413
 
414
414
 
415
- export type GetMovethewheelsDemandForecastVarianceQuery = { demandForecastVariance: Array<{ scopeType: Types.DemandForecastScope, scopeKey: string, periodStart: string, periodEnd: string, predictedVolume: number, actualVolume: number, volumeVariance: number, predictedRevenue: number, actualRevenue: number, revenueVariance: number }> };
415
+ export type GetMovethewheelsDemandForecastVarianceQuery = { demandForecastVariance: Array<{ scopeType: Types.DemandForecastScope, scopeKey: string, scopeLabel: string, periodStart: string, periodEnd: string, predictedVolume: number, actualVolume: number, volumeVariance: number, predictedRevenue: number, actualRevenue: number, revenueVariance: number }> };
416
416
 
417
417
  export type ListInventoryItemsQueryVariables = Exact<{
418
418
  filter?: Types.InventoryFilterInput | null | undefined;
@@ -549,9 +549,9 @@ export const ListDriversDocument = {"kind":"Document","definitions":[{"kind":"Op
549
549
  export const GetDriverDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetDriver"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"driver"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"DriverCore"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"DriverCore"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"MtwDriver"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"employeeId"}},{"kind":"Field","name":{"kind":"Name","value":"firstName"}},{"kind":"Field","name":{"kind":"Name","value":"lastName"}},{"kind":"Field","name":{"kind":"Name","value":"phone"}},{"kind":"Field","name":{"kind":"Name","value":"email"}},{"kind":"Field","name":{"kind":"Name","value":"licenseNumber"}},{"kind":"Field","name":{"kind":"Name","value":"licenseClass"}},{"kind":"Field","name":{"kind":"Name","value":"licenseExpiry"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"currentLocation"}},{"kind":"Field","name":{"kind":"Name","value":"rating"}},{"kind":"Field","name":{"kind":"Name","value":"userId"}},{"kind":"Field","name":{"kind":"Name","value":"vehicleId"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]} as unknown as DocumentNode<GetDriverQuery, GetDriverQueryVariables>;
550
550
  export const ListWarehousesDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"ListWarehouses"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"filter"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"WarehouseFilterInput"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"pagination"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"PaginationInput"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"warehouses"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"Variable","name":{"kind":"Name","value":"filter"}}},{"kind":"Argument","name":{"kind":"Name","value":"pagination"},"value":{"kind":"Variable","name":{"kind":"Name","value":"pagination"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"WarehouseCore"}},{"kind":"Field","name":{"kind":"Name","value":"locations"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"locationCode"}},{"kind":"Field","name":{"kind":"Name","value":"zone"}},{"kind":"Field","name":{"kind":"Name","value":"aisle"}},{"kind":"Field","name":{"kind":"Name","value":"rack"}},{"kind":"Field","name":{"kind":"Name","value":"shelf"}},{"kind":"Field","name":{"kind":"Name","value":"bin"}},{"kind":"Field","name":{"kind":"Name","value":"locationType"}},{"kind":"Field","name":{"kind":"Name","value":"capacity"}},{"kind":"Field","name":{"kind":"Name","value":"status"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WarehouseCore"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"MtwWarehouse"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"code"}},{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"address"}},{"kind":"Field","name":{"kind":"Name","value":"contactInfo"}},{"kind":"Field","name":{"kind":"Name","value":"operatingHours"}},{"kind":"Field","name":{"kind":"Name","value":"capabilities"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"totalCapacity"}},{"kind":"Field","name":{"kind":"Name","value":"usedCapacity"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]} as unknown as DocumentNode<ListWarehousesQuery, ListWarehousesQueryVariables>;
551
551
  export const GetWarehouseDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetWarehouse"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"ID"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"warehouse"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"Variable","name":{"kind":"Name","value":"id"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"WarehouseCore"}},{"kind":"Field","name":{"kind":"Name","value":"locations"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"locationCode"}},{"kind":"Field","name":{"kind":"Name","value":"zone"}},{"kind":"Field","name":{"kind":"Name","value":"aisle"}},{"kind":"Field","name":{"kind":"Name","value":"rack"}},{"kind":"Field","name":{"kind":"Name","value":"shelf"}},{"kind":"Field","name":{"kind":"Name","value":"bin"}},{"kind":"Field","name":{"kind":"Name","value":"locationType"}},{"kind":"Field","name":{"kind":"Name","value":"capacity"}},{"kind":"Field","name":{"kind":"Name","value":"status"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"WarehouseCore"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"MtwWarehouse"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"code"}},{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"address"}},{"kind":"Field","name":{"kind":"Name","value":"contactInfo"}},{"kind":"Field","name":{"kind":"Name","value":"operatingHours"}},{"kind":"Field","name":{"kind":"Name","value":"capabilities"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"totalCapacity"}},{"kind":"Field","name":{"kind":"Name","value":"usedCapacity"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]} as unknown as DocumentNode<GetWarehouseQuery, GetWarehouseQueryVariables>;
552
- export const GetMovethewheelsDemandForecastByLaneDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetMovethewheelsDemandForecastByLane"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"weeks"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"demandForecastByLane"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"weeks"},"value":{"kind":"Variable","name":{"kind":"Name","value":"weeks"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"scopeType"}},{"kind":"Field","name":{"kind":"Name","value":"scopeKey"}},{"kind":"Field","name":{"kind":"Name","value":"periodStart"}},{"kind":"Field","name":{"kind":"Name","value":"periodEnd"}},{"kind":"Field","name":{"kind":"Name","value":"predictedVolume"}},{"kind":"Field","name":{"kind":"Name","value":"predictedRevenue"}},{"kind":"Field","name":{"kind":"Name","value":"actualVolume"}},{"kind":"Field","name":{"kind":"Name","value":"actualRevenue"}},{"kind":"Field","name":{"kind":"Name","value":"confidence"}}]}}]}}]} as unknown as DocumentNode<GetMovethewheelsDemandForecastByLaneQuery, GetMovethewheelsDemandForecastByLaneQueryVariables>;
553
- export const GetMovethewheelsDemandForecastByCustomerDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetMovethewheelsDemandForecastByCustomer"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"weeks"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"demandForecastByCustomer"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"weeks"},"value":{"kind":"Variable","name":{"kind":"Name","value":"weeks"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"scopeType"}},{"kind":"Field","name":{"kind":"Name","value":"scopeKey"}},{"kind":"Field","name":{"kind":"Name","value":"periodStart"}},{"kind":"Field","name":{"kind":"Name","value":"periodEnd"}},{"kind":"Field","name":{"kind":"Name","value":"predictedVolume"}},{"kind":"Field","name":{"kind":"Name","value":"predictedRevenue"}},{"kind":"Field","name":{"kind":"Name","value":"actualVolume"}},{"kind":"Field","name":{"kind":"Name","value":"actualRevenue"}},{"kind":"Field","name":{"kind":"Name","value":"confidence"}}]}}]}}]} as unknown as DocumentNode<GetMovethewheelsDemandForecastByCustomerQuery, GetMovethewheelsDemandForecastByCustomerQueryVariables>;
554
- export const GetMovethewheelsDemandForecastVarianceDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetMovethewheelsDemandForecastVariance"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"scopeType"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"DemandForecastScope"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"limit"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"demandForecastVariance"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"scopeType"},"value":{"kind":"Variable","name":{"kind":"Name","value":"scopeType"}}},{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"Variable","name":{"kind":"Name","value":"limit"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"scopeType"}},{"kind":"Field","name":{"kind":"Name","value":"scopeKey"}},{"kind":"Field","name":{"kind":"Name","value":"periodStart"}},{"kind":"Field","name":{"kind":"Name","value":"periodEnd"}},{"kind":"Field","name":{"kind":"Name","value":"predictedVolume"}},{"kind":"Field","name":{"kind":"Name","value":"actualVolume"}},{"kind":"Field","name":{"kind":"Name","value":"volumeVariance"}},{"kind":"Field","name":{"kind":"Name","value":"predictedRevenue"}},{"kind":"Field","name":{"kind":"Name","value":"actualRevenue"}},{"kind":"Field","name":{"kind":"Name","value":"revenueVariance"}}]}}]}}]} as unknown as DocumentNode<GetMovethewheelsDemandForecastVarianceQuery, GetMovethewheelsDemandForecastVarianceQueryVariables>;
552
+ export const GetMovethewheelsDemandForecastByLaneDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetMovethewheelsDemandForecastByLane"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"weeks"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"demandForecastByLane"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"weeks"},"value":{"kind":"Variable","name":{"kind":"Name","value":"weeks"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"scopeType"}},{"kind":"Field","name":{"kind":"Name","value":"scopeKey"}},{"kind":"Field","name":{"kind":"Name","value":"scopeLabel"}},{"kind":"Field","name":{"kind":"Name","value":"periodStart"}},{"kind":"Field","name":{"kind":"Name","value":"periodEnd"}},{"kind":"Field","name":{"kind":"Name","value":"predictedVolume"}},{"kind":"Field","name":{"kind":"Name","value":"predictedRevenue"}},{"kind":"Field","name":{"kind":"Name","value":"actualVolume"}},{"kind":"Field","name":{"kind":"Name","value":"actualRevenue"}},{"kind":"Field","name":{"kind":"Name","value":"confidence"}}]}}]}}]} as unknown as DocumentNode<GetMovethewheelsDemandForecastByLaneQuery, GetMovethewheelsDemandForecastByLaneQueryVariables>;
553
+ export const GetMovethewheelsDemandForecastByCustomerDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetMovethewheelsDemandForecastByCustomer"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"weeks"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"demandForecastByCustomer"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"weeks"},"value":{"kind":"Variable","name":{"kind":"Name","value":"weeks"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"scopeType"}},{"kind":"Field","name":{"kind":"Name","value":"scopeKey"}},{"kind":"Field","name":{"kind":"Name","value":"scopeLabel"}},{"kind":"Field","name":{"kind":"Name","value":"periodStart"}},{"kind":"Field","name":{"kind":"Name","value":"periodEnd"}},{"kind":"Field","name":{"kind":"Name","value":"predictedVolume"}},{"kind":"Field","name":{"kind":"Name","value":"predictedRevenue"}},{"kind":"Field","name":{"kind":"Name","value":"actualVolume"}},{"kind":"Field","name":{"kind":"Name","value":"actualRevenue"}},{"kind":"Field","name":{"kind":"Name","value":"confidence"}}]}}]}}]} as unknown as DocumentNode<GetMovethewheelsDemandForecastByCustomerQuery, GetMovethewheelsDemandForecastByCustomerQueryVariables>;
554
+ export const GetMovethewheelsDemandForecastVarianceDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetMovethewheelsDemandForecastVariance"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"scopeType"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"DemandForecastScope"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"limit"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"demandForecastVariance"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"scopeType"},"value":{"kind":"Variable","name":{"kind":"Name","value":"scopeType"}}},{"kind":"Argument","name":{"kind":"Name","value":"limit"},"value":{"kind":"Variable","name":{"kind":"Name","value":"limit"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"scopeType"}},{"kind":"Field","name":{"kind":"Name","value":"scopeKey"}},{"kind":"Field","name":{"kind":"Name","value":"scopeLabel"}},{"kind":"Field","name":{"kind":"Name","value":"periodStart"}},{"kind":"Field","name":{"kind":"Name","value":"periodEnd"}},{"kind":"Field","name":{"kind":"Name","value":"predictedVolume"}},{"kind":"Field","name":{"kind":"Name","value":"actualVolume"}},{"kind":"Field","name":{"kind":"Name","value":"volumeVariance"}},{"kind":"Field","name":{"kind":"Name","value":"predictedRevenue"}},{"kind":"Field","name":{"kind":"Name","value":"actualRevenue"}},{"kind":"Field","name":{"kind":"Name","value":"revenueVariance"}}]}}]}}]} as unknown as DocumentNode<GetMovethewheelsDemandForecastVarianceQuery, GetMovethewheelsDemandForecastVarianceQueryVariables>;
555
555
  export const ListInventoryItemsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"ListInventoryItems"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"filter"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"InventoryFilterInput"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"pagination"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"PaginationInput"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"inventoryItems"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"Variable","name":{"kind":"Name","value":"filter"}}},{"kind":"Argument","name":{"kind":"Name","value":"pagination"},"value":{"kind":"Variable","name":{"kind":"Name","value":"pagination"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"InventoryItemCore"}},{"kind":"Field","name":{"kind":"Name","value":"cargoItem"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"sku"}},{"kind":"Field","name":{"kind":"Name","value":"category"}}]}},{"kind":"Field","name":{"kind":"Name","value":"warehouse"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"code"}},{"kind":"Field","name":{"kind":"Name","value":"type"}}]}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"InventoryItemCore"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"MtwInventoryItem"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"cargoItemId"}},{"kind":"Field","name":{"kind":"Name","value":"warehouseId"}},{"kind":"Field","name":{"kind":"Name","value":"locationId"}},{"kind":"Field","name":{"kind":"Name","value":"onHand"}},{"kind":"Field","name":{"kind":"Name","value":"available"}},{"kind":"Field","name":{"kind":"Name","value":"allocated"}},{"kind":"Field","name":{"kind":"Name","value":"damaged"}},{"kind":"Field","name":{"kind":"Name","value":"unitCost"}},{"kind":"Field","name":{"kind":"Name","value":"reorderPoint"}},{"kind":"Field","name":{"kind":"Name","value":"safetyStock"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]} as unknown as DocumentNode<ListInventoryItemsQuery, ListInventoryItemsQueryVariables>;
556
556
  export const GetInventoryStatsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"GetInventoryStats"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"inventoryStats"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"totalOnHand"}},{"kind":"Field","name":{"kind":"Name","value":"totalAvailable"}},{"kind":"Field","name":{"kind":"Name","value":"totalAllocated"}},{"kind":"Field","name":{"kind":"Name","value":"totalDamaged"}},{"kind":"Field","name":{"kind":"Name","value":"lowStockCount"}}]}}]}}]} as unknown as DocumentNode<GetInventoryStatsQuery, GetInventoryStatsQueryVariables>;
557
557
  export const ListRoutesDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"ListRoutes"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"filter"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"RouteFilterInput"}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"pagination"}},"type":{"kind":"NamedType","name":{"kind":"Name","value":"PaginationInput"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"routes"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"Variable","name":{"kind":"Name","value":"filter"}}},{"kind":"Argument","name":{"kind":"Name","value":"pagination"},"value":{"kind":"Variable","name":{"kind":"Name","value":"pagination"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"RouteCore"}}]}}]}},{"kind":"FragmentDefinition","name":{"kind":"Name","value":"RouteCore"},"typeCondition":{"kind":"NamedType","name":{"kind":"Name","value":"MtwRoute"}},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"id"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"status"}},{"kind":"Field","name":{"kind":"Name","value":"shipmentId"}},{"kind":"Field","name":{"kind":"Name","value":"vehicleId"}},{"kind":"Field","name":{"kind":"Name","value":"driverId"}},{"kind":"Field","name":{"kind":"Name","value":"waypoints"}},{"kind":"Field","name":{"kind":"Name","value":"estimatedDistance"}},{"kind":"Field","name":{"kind":"Name","value":"estimatedDuration"}},{"kind":"Field","name":{"kind":"Name","value":"actualDistance"}},{"kind":"Field","name":{"kind":"Name","value":"actualDuration"}},{"kind":"Field","name":{"kind":"Name","value":"fuelCost"}},{"kind":"Field","name":{"kind":"Name","value":"createdAt"}},{"kind":"Field","name":{"kind":"Name","value":"updatedAt"}}]}}]} as unknown as DocumentNode<ListRoutesQuery, ListRoutesQueryVariables>;
@@ -4,11 +4,17 @@
4
4
  * Surfaces next-week demand forecasts by lane / customer alongside the
5
5
  * most recent closed-period variance (BOFF-4393). Pure read-side view
6
6
  * over `wspace-movethewheels-svc`'s DemandForecast surface.
7
+ *
8
+ * "Next week" is the first forecast week that has not begun yet: the current
9
+ * week's rows are kept server-side for the closed-period variance, but they
10
+ * are not upcoming demand. Cold-start rows (confidence 0, fewer than 4 weeks
11
+ * of history for that lane or customer) are not shown as forecasts.
7
12
  */
8
13
 
9
14
  import { Calendar, TrendingUp, TrendingDown, AlertCircle } from 'lucide-react';
10
15
 
11
16
  import { Card, CardContent, CardHeader, CardTitle, Skeleton } from './ui';
17
+ import { formatCurrency } from '../utils/formatters';
12
18
 
13
19
  import type { ForecastPoint, ForecastVariance } from '../hooks/useDemandForecast';
14
20
 
@@ -28,20 +34,41 @@ const formatWeek = (iso: string): string => {
28
34
  return date.toLocaleDateString(undefined, { month: 'short', day: 'numeric' });
29
35
  };
30
36
 
31
- const topByVolume = (rows: ForecastPoint[], weekStart: string): ForecastPoint[] => {
32
- const filtered = rows.filter((row) => row.periodStart === weekStart);
33
- return [...filtered].sort((a, b) => b.predictedVolume - a.predictedVolume).slice(0, 3);
34
- };
37
+ /** Rows for one week that rest on enough history to be a forecast. */
38
+ const forecastsFor = (rows: ForecastPoint[], weekStart: string): ForecastPoint[] =>
39
+ rows.filter((row) => row.periodStart === weekStart && row.confidence > 0);
35
40
 
36
- const topByRevenue = (rows: ForecastPoint[], weekStart: string): ForecastPoint[] => {
37
- const filtered = rows.filter((row) => row.periodStart === weekStart);
38
- return [...filtered].sort((a, b) => b.predictedRevenue - a.predictedRevenue).slice(0, 3);
39
- };
41
+ const topByVolume = (rows: ForecastPoint[], weekStart: string): ForecastPoint[] =>
42
+ [...forecastsFor(rows, weekStart)]
43
+ .sort(
44
+ (a, b) => b.predictedVolume - a.predictedVolume || b.predictedRevenue - a.predictedRevenue
45
+ )
46
+ .slice(0, 3);
47
+
48
+ const topByRevenue = (rows: ForecastPoint[], weekStart: string): ForecastPoint[] =>
49
+ [...forecastsFor(rows, weekStart)]
50
+ .sort((a, b) => b.predictedRevenue - a.predictedRevenue)
51
+ .slice(0, 3);
40
52
 
41
53
  const totalVolume = (rows: ForecastPoint[], weekStart: string): number =>
42
- rows
43
- .filter((row) => row.periodStart === weekStart)
44
- .reduce((sum, row) => sum + row.predictedVolume, 0);
54
+ forecastsFor(rows, weekStart).reduce((sum, row) => sum + row.predictedVolume, 0);
55
+
56
+ /**
57
+ * The first forecast week that has not started yet, else the latest week on
58
+ * offer (rows arrive sorted by period).
59
+ */
60
+ const upcomingWeekStart = (rows: ForecastPoint[], now: Date = new Date()): string | null => {
61
+ const starts = [...new Set(rows.map((row) => row.periodStart))].sort(
62
+ (a, b) => new Date(a).getTime() - new Date(b).getTime()
63
+ );
64
+ return starts.find((start) => new Date(start).getTime() > now.getTime()) ?? starts.at(-1) ?? null;
65
+ };
66
+
67
+ /** `3 jobs`, `1 job`, or `<1 job` when demand is real but under one a week. */
68
+ const formatJobs = (volume: number, revenue: number): string => {
69
+ if (volume === 0 && revenue > 0) return '<1 job';
70
+ return volume === 1 ? '1 job' : `${volume} jobs`;
71
+ };
45
72
 
46
73
  const formatConfidence = (confidence: number): string => {
47
74
  if (confidence <= 0) return 'Not enough history';
@@ -90,9 +117,13 @@ export function UpcomingDemandPanel({
90
117
  );
91
118
  }
92
119
 
93
- const nextWeekStart = laneForecast[0]?.periodStart ?? customerForecast[0]?.periodStart ?? null;
120
+ const nextWeekStart = upcomingWeekStart([...laneForecast, ...customerForecast]);
121
+ const hasForecast =
122
+ nextWeekStart !== null &&
123
+ (forecastsFor(laneForecast, nextWeekStart).length > 0 ||
124
+ forecastsFor(customerForecast, nextWeekStart).length > 0);
94
125
 
95
- if (!nextWeekStart || laneForecast.length === 0) {
126
+ if (!nextWeekStart || !hasForecast) {
96
127
  return (
97
128
  <Card>
98
129
  <CardHeader>
@@ -105,8 +136,8 @@ export function UpcomingDemandPanel({
105
136
  <div className="flex items-start gap-2 rounded-lg border border-border/60 bg-muted/40 p-4 text-sm text-text-secondary">
106
137
  <AlertCircle size={16} aria-hidden="true" className="mt-0.5 shrink-0" />
107
138
  <p>
108
- Not enough history to forecast next week yet. Once you have at least 4 weeks of
109
- completed orders, this panel will surface demand by lane and customer.
139
+ Not enough history to forecast next week yet. Once a lane or customer has at least 4
140
+ weeks of delivery jobs, this panel will surface its demand.
110
141
  </p>
111
142
  </div>
112
143
  </CardContent>
@@ -117,7 +148,18 @@ export function UpcomingDemandPanel({
117
148
  const topLanes = topByVolume(laneForecast, nextWeekStart);
118
149
  const topLanesByRevenue = topByRevenue(laneForecast, nextWeekStart);
119
150
  const topCustomers = topByVolume(customerForecast, nextWeekStart);
120
- const total = totalVolume(laneForecast, nextWeekStart);
151
+ // The headline total comes from lane forecasts when any lane clears the
152
+ // history threshold. A customer can have enough history while each of its
153
+ // lanes does not, so fall back to the customer total rather than show a
154
+ // lane total of 0 beside valid customer forecasts.
155
+ const hasLaneForecast = forecastsFor(laneForecast, nextWeekStart).length > 0;
156
+ const total = hasLaneForecast
157
+ ? totalVolume(laneForecast, nextWeekStart)
158
+ : totalVolume(customerForecast, nextWeekStart);
159
+ const totalLabel = hasLaneForecast
160
+ ? 'Predicted jobs (all lanes)'
161
+ : 'Predicted jobs (all customers)';
162
+ const headlineRow = hasLaneForecast ? topLanes[0] : topCustomers[0];
121
163
 
122
164
  // Most-recent variance row per scope key — show the lane that overshot /
123
165
  // undershot the most in absolute volume terms.
@@ -136,13 +178,11 @@ export function UpcomingDemandPanel({
136
178
  <CardContent className="space-y-4">
137
179
  <div className="flex items-baseline justify-between rounded-lg border border-border/60 bg-muted/40 px-4 py-3">
138
180
  <div>
139
- <p className="text-xs uppercase tracking-wide text-text-secondary">
140
- Predicted jobs (all lanes)
141
- </p>
181
+ <p className="text-xs uppercase tracking-wide text-text-secondary">{totalLabel}</p>
142
182
  <p className="text-2xl font-bold">{total}</p>
143
183
  </div>
144
184
  <p className="text-xs text-text-secondary">
145
- {topLanes[0] ? formatConfidence(topLanes[0].confidence) : ''}
185
+ {headlineRow ? formatConfidence(headlineRow.confidence) : ''}
146
186
  </p>
147
187
  </div>
148
188
 
@@ -180,14 +220,12 @@ function ForecastList({ title, rows }: ForecastListProps) {
180
220
  key={`${row.scopeType}-${row.scopeKey}`}
181
221
  className="flex items-center justify-between text-sm"
182
222
  >
183
- <span className="truncate font-medium">{row.scopeKey}</span>
223
+ <span className="truncate font-medium" title={row.scopeLabel}>
224
+ {row.scopeLabel}
225
+ </span>
184
226
  <span className="ml-2 shrink-0 text-text-secondary">
185
- {row.predictedVolume} jobs ·{' '}
186
- {new Intl.NumberFormat(undefined, {
187
- style: 'currency',
188
- currency: 'USD',
189
- maximumFractionDigits: 0,
190
- }).format(row.predictedRevenue)}
227
+ {formatJobs(row.predictedVolume, row.predictedRevenue)} ·{' '}
228
+ {formatCurrency(row.predictedRevenue)}
191
229
  </span>
192
230
  </li>
193
231
  ))}
@@ -219,11 +257,15 @@ function VarianceList({ rows }: VarianceListProps) {
219
257
  >
220
258
  <span className="flex items-center gap-2 truncate">
221
259
  <Icon size={14} className={tone} aria-hidden="true" />
222
- <span className="truncate font-medium">{row.scopeKey}</span>
260
+ <span className="truncate font-medium" title={row.scopeLabel}>
261
+ {row.scopeLabel}
262
+ </span>
223
263
  </span>
224
264
  <span className={`ml-2 shrink-0 ${tone}`}>
225
265
  {isOvershoot ? '+' : ''}
226
- {row.volumeVariance} jobs
266
+ {Math.abs(row.volumeVariance) === 1
267
+ ? `${row.volumeVariance} job`
268
+ : `${row.volumeVariance} jobs`}
227
269
  </span>
228
270
  </li>
229
271
  );
@@ -12,6 +12,8 @@ import { executeGraphQL } from '../utils/graphqlClient';
12
12
  export interface ForecastPoint {
13
13
  scopeType: 'LANE' | 'CUSTOMER';
14
14
  scopeKey: string;
15
+ /** Customer name or `Origin → Destination`; falls back to the key. */
16
+ scopeLabel: string;
15
17
  periodStart: string;
16
18
  periodEnd: string;
17
19
  predictedVolume: number;
@@ -24,6 +26,7 @@ export interface ForecastPoint {
24
26
  export interface ForecastVariance {
25
27
  scopeType: 'LANE' | 'CUSTOMER';
26
28
  scopeKey: string;
29
+ scopeLabel: string;
27
30
  periodStart: string;
28
31
  periodEnd: string;
29
32
  predictedVolume: number;
@@ -49,6 +52,7 @@ const FORECAST_QUERY = `
49
52
  demandForecastByLane(weeks: $weeks) {
50
53
  scopeType
51
54
  scopeKey
55
+ scopeLabel
52
56
  periodStart
53
57
  periodEnd
54
58
  predictedVolume
@@ -60,6 +64,7 @@ const FORECAST_QUERY = `
60
64
  demandForecastByCustomer(weeks: $weeks) {
61
65
  scopeType
62
66
  scopeKey
67
+ scopeLabel
63
68
  periodStart
64
69
  periodEnd
65
70
  predictedVolume
@@ -71,6 +76,7 @@ const FORECAST_QUERY = `
71
76
  laneVariance: demandForecastVariance(scopeType: LANE, limit: $varianceLimit) {
72
77
  scopeType
73
78
  scopeKey
79
+ scopeLabel
74
80
  periodStart
75
81
  periodEnd
76
82
  predictedVolume
@@ -83,6 +89,7 @@ const FORECAST_QUERY = `
83
89
  customerVariance: demandForecastVariance(scopeType: CUSTOMER, limit: $varianceLimit) {
84
90
  scopeType
85
91
  scopeKey
92
+ scopeLabel
86
93
  periodStart
87
94
  periodEnd
88
95
  predictedVolume
@@ -48,6 +48,7 @@ import {
48
48
  type DriverStopState,
49
49
  } from '../hooks/useDriverDay';
50
50
  import {
51
+ formatLocalDay,
51
52
  localDateString,
52
53
  phoneHref,
53
54
  routeNavigationUrl,
@@ -445,9 +446,7 @@ export default function DeliveryCapturePage() {
445
446
  {day.completedStops} of {day.totalStops} stops done
446
447
  {day.driverName ? ` · ${day.driverName}` : ''}
447
448
  </p>
448
- <p className="text-sm text-text-secondary">
449
- {formatDate(new Date(`${date}T12:00:00`), 'full')}
450
- </p>
449
+ <p className="text-sm text-text-secondary">{formatLocalDay(date)}</p>
451
450
  </div>
452
451
  <Progress value={progress} aria-label="Stops completed" />
453
452
  {day.nextStop && (
@@ -101,3 +101,19 @@ export function deviceTimeZone(): string | null {
101
101
  return null;
102
102
  }
103
103
  }
104
+
105
+ /**
106
+ * A calendar label for a YYYY-MM-DD day, e.g. "Sunday, November 1, 2026".
107
+ * Formatted as a pure calendar date (no time, no zone shift) so the header
108
+ * always names the day the driver picked.
109
+ */
110
+ export function formatLocalDay(date: string): string {
111
+ const [year, month, day] = date.split('-').map(Number);
112
+ return new Intl.DateTimeFormat('en-US', {
113
+ weekday: 'long',
114
+ year: 'numeric',
115
+ month: 'long',
116
+ day: 'numeric',
117
+ timeZone: 'UTC',
118
+ }).format(new Date(Date.UTC(year, month - 1, day)));
119
+ }
@@ -7,6 +7,7 @@ query GetMovethewheelsDemandForecastByLane($weeks: Int!) {
7
7
  demandForecastByLane(weeks: $weeks) {
8
8
  scopeType
9
9
  scopeKey
10
+ scopeLabel
10
11
  periodStart
11
12
  periodEnd
12
13
  predictedVolume
@@ -21,6 +22,7 @@ query GetMovethewheelsDemandForecastByCustomer($weeks: Int!) {
21
22
  demandForecastByCustomer(weeks: $weeks) {
22
23
  scopeType
23
24
  scopeKey
25
+ scopeLabel
24
26
  periodStart
25
27
  periodEnd
26
28
  predictedVolume
@@ -35,6 +37,7 @@ query GetMovethewheelsDemandForecastVariance($scopeType: DemandForecastScope!, $
35
37
  demandForecastVariance(scopeType: $scopeType, limit: $limit) {
36
38
  scopeType
37
39
  scopeKey
40
+ scopeLabel
38
41
  periodStart
39
42
  periodEnd
40
43
  predictedVolume