@burdenoff/microfe-movethewheels 2026.1101.3 → 2026.1102.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.
Files changed (29) hide show
  1. package/dist/components/CustomerDeliveryUpdatesFields.js +57 -0
  2. package/dist/components/CustomerNotificationSettingsCard.js +368 -0
  3. package/dist/components/UpcomingDemandPanel.js +75 -77
  4. package/dist/generated/tracking-quality-operations.d.ts +275 -0
  5. package/dist/generated/wspace-operations.d.ts +3 -0
  6. package/dist/hooks/useCustomers.js +9 -3
  7. package/dist/hooks/useDemandForecast.js +1 -1
  8. package/dist/movethewheels/components/CustomerDeliveryUpdatesFields.d.ts +12 -0
  9. package/dist/movethewheels/components/CustomerNotificationSettingsCard.d.ts +3 -0
  10. package/dist/movethewheels/hooks/useDemandForecast.d.ts +3 -0
  11. package/dist/movethewheels/types/index.d.ts +4 -0
  12. package/dist/movethewheels/utils/customerNotifications.d.ts +66 -0
  13. package/dist/node_modules/lucide-react/dist/esm/icons/bell-ring.js +22 -0
  14. package/dist/pages/CustomerDetailsPage.js +279 -231
  15. package/dist/pages/SettingsPage.js +242 -238
  16. package/dist/utils/customerNotifications.js +83 -0
  17. package/package.json +3 -2
  18. package/src/generated/tracking-quality-operations.ts +304 -0
  19. package/src/generated/wspace-operations.ts +6 -6
  20. package/src/movethewheels/components/CustomerDeliveryUpdatesFields.tsx +67 -0
  21. package/src/movethewheels/components/CustomerNotificationSettingsCard.tsx +465 -0
  22. package/src/movethewheels/components/UpcomingDemandPanel.tsx +71 -29
  23. package/src/movethewheels/hooks/useCustomers.ts +17 -0
  24. package/src/movethewheels/hooks/useDemandForecast.ts +7 -0
  25. package/src/movethewheels/pages/CustomerDetailsPage.tsx +39 -0
  26. package/src/movethewheels/pages/SettingsPage.tsx +221 -208
  27. package/src/movethewheels/types/index.ts +4 -0
  28. package/src/movethewheels/utils/customerNotifications.ts +209 -0
  29. package/src/operations/wspace/movethewheels/queries/forecast.graphql +3 -0
@@ -0,0 +1,83 @@
1
+ //#region src/movethewheels/utils/customerNotifications.ts
2
+ var e = [
3
+ {
4
+ value: "BOOKED",
5
+ label: "Booked",
6
+ description: "A shipment is created for the customer"
7
+ },
8
+ {
9
+ value: "PICKED_UP",
10
+ label: "Picked up",
11
+ description: "The shipment is collected"
12
+ },
13
+ {
14
+ value: "OUT_FOR_DELIVERY",
15
+ label: "Out for delivery",
16
+ description: "Tracking says the shipment is on its last leg"
17
+ },
18
+ {
19
+ value: "DELIVERED",
20
+ label: "Delivered",
21
+ description: "The shipment is delivered"
22
+ },
23
+ {
24
+ value: "DELAYED",
25
+ label: "Running late",
26
+ description: "The risk monitor expects it to miss the promised delivery (once per promise)"
27
+ },
28
+ {
29
+ value: "FAILED",
30
+ label: "Delivery failed",
31
+ description: "Delivery could not be completed"
32
+ }
33
+ ];
34
+ function t(t) {
35
+ return e.find((e) => e.value === t)?.label ?? t;
36
+ }
37
+ var n = {
38
+ PENDING: "Sending",
39
+ SENT: "Sent",
40
+ FAILED: "Failed",
41
+ SKIPPED: "Not sent"
42
+ };
43
+ function r(t = "") {
44
+ return {
45
+ workspaceId: t,
46
+ enabled: !1,
47
+ enabledAt: null,
48
+ emailEnabled: !0,
49
+ smsEnabled: !1,
50
+ events: e.map((e) => e.value),
51
+ isCustomized: !1,
52
+ updatedAt: null,
53
+ updatedBy: null
54
+ };
55
+ }
56
+ function i(t) {
57
+ let n = new Set(t);
58
+ return e.map((e) => e.value).filter((e) => n.has(e));
59
+ }
60
+ function a(e, t, n) {
61
+ let r = new Set(e);
62
+ return n ? r.add(t) : r.delete(t), i([...r]);
63
+ }
64
+ function o(e, t) {
65
+ let n = {};
66
+ t.enabled !== e.enabled && (n.enabled = t.enabled), t.emailEnabled !== e.emailEnabled && (n.emailEnabled = t.emailEnabled), t.smsEnabled !== e.smsEnabled && (n.smsEnabled = t.smsEnabled);
67
+ let r = i(t.events), a = i(e.events);
68
+ return r.join(",") !== a.join(",") && (n.events = r), n;
69
+ }
70
+ function s(e) {
71
+ return e.enabled ? !e.emailEnabled && !e.smsEnabled ? "Choose email, SMS or both." : e.events.length === 0 ? "Choose at least one event to notify customers about." : null : null;
72
+ }
73
+ var c = "\n workspaceId\n enabled\n enabledAt\n emailEnabled\n smsEnabled\n events\n isCustomized\n updatedAt\n updatedBy\n", l = `
74
+ query MtwCustomerNotificationSettings {
75
+ mtwCustomerNotificationSettings {${c}}
76
+ }
77
+ `, u = `
78
+ mutation UpdateMtwCustomerNotificationSettings($input: UpdateMtwCustomerNotificationSettingsInput!) {
79
+ updateMtwCustomerNotificationSettings(input: $input) {${c}}
80
+ }
81
+ `, d = "\n query MtwCustomerNotifications(\n $filter: MtwCustomerNotificationFilterInput\n $pagination: PaginationInput\n ) {\n mtwCustomerNotifications(filter: $filter, pagination: $pagination) {\n total\n hasMore\n items {\n id\n shipmentId\n shipmentNumber\n customerId\n event\n channel\n status\n recipient\n subject\n attempts\n lastError\n sentAt\n createdAt\n }\n }\n }\n", f = "\n query MtwCustomerNotificationPreviews {\n mtwCustomerNotificationPreviews {\n event\n subject\n emailText\n smsText\n }\n }\n";
82
+ //#endregion
83
+ export { e as CUSTOMER_NOTIFICATION_EVENTS, d as MTW_CUSTOMER_NOTIFICATIONS_QUERY, f as MTW_CUSTOMER_NOTIFICATION_PREVIEWS_QUERY, l as MTW_CUSTOMER_NOTIFICATION_SETTINGS_QUERY, n as STATUS_LABELS, u as UPDATE_MTW_CUSTOMER_NOTIFICATION_SETTINGS_MUTATION, o as changedNotificationSettings, r as defaultNotificationSettings, t as eventLabel, s as notificationDraftError, a as toggleEvent };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@burdenoff/microfe-movethewheels",
3
- "version": "2026.1101.3",
3
+ "version": "2026.1102.1",
4
4
  "description": "Burdenoff MoveTheWheels Microfrontend - Logistics & Supply Chain Management Platform",
5
5
  "type": "module",
6
6
  "files": [
@@ -37,7 +37,7 @@
37
37
  "format": "prettier --write \"src/**/*.{ts,tsx}\"",
38
38
  "type:check": "bash scripts/build-limits.sh tsc --noEmit",
39
39
  "validate:schema-drift": "graphql-schema-drift",
40
- "sanity": "bash scripts/build-limits.sh --gate bash -c 'bun run codegen && bun run validate:schema-drift && bun run lint:sanity && bun run format && bun run type:check && bun run verify:live-logistics-contract && bun run verify:empty-reuse-retry && bun run verify:empty-reuse-recovery && bun run verify:trip-utilization-input && bun run verify:trip-draft-context && bun run verify:stock-binding-input && bun run verify:stock-observation-input && bun run verify:pending-pick-input && bun run verify:pick-review-ack && bun run verify:driver-license-expiry && bun run verify:tracking-alerts && bun run verify:freight-input && bun run verify:freight-recovery && bun run verify:freight-commercial && bun run verify:freight-invoice && bun run verify:procedure-input && bun run verify:consolidation && bun run verify:multimodal-single-attempt && bun run verify:workspace-predispatch && bun run verify:tracking-quality-outcomes && bun run verify:shipment-status-authority && bun run verify:free-time-permissions && bun run verify:core-codegen && bun run verify:order-vehicle-hold && bun run verify:pallet-preview && bun run verify:entity-export && bun run verify:entity-import && bun run verify:module-settings && bun run verify:carrier-integrations && bun run verify:driver-day && bun run verify:consumed-fuel-preview && bun run build && bun run build-storybook'",
40
+ "sanity": "bash scripts/build-limits.sh --gate bash -c 'bun run codegen && bun run validate:schema-drift && bun run lint:sanity && bun run format && bun run type:check && bun run verify:live-logistics-contract && bun run verify:empty-reuse-retry && bun run verify:empty-reuse-recovery && bun run verify:trip-utilization-input && bun run verify:trip-draft-context && bun run verify:stock-binding-input && bun run verify:stock-observation-input && bun run verify:pending-pick-input && bun run verify:pick-review-ack && bun run verify:driver-license-expiry && bun run verify:tracking-alerts && bun run verify:freight-input && bun run verify:freight-recovery && bun run verify:freight-commercial && bun run verify:freight-invoice && bun run verify:procedure-input && bun run verify:consolidation && bun run verify:multimodal-single-attempt && bun run verify:workspace-predispatch && bun run verify:tracking-quality-outcomes && bun run verify:shipment-status-authority && bun run verify:free-time-permissions && bun run verify:core-codegen && bun run verify:order-vehicle-hold && bun run verify:pallet-preview && bun run verify:entity-export && bun run verify:entity-import && bun run verify:module-settings && bun run verify:carrier-integrations && bun run verify:driver-day && bun run verify:customer-notifications && bun run verify:consumed-fuel-preview && bun run build && bun run build-storybook'",
41
41
  "storybook": "storybook dev -p 6006",
42
42
  "build-storybook": "storybook build",
43
43
  "test:storybook": "vitest run --project=storybook",
@@ -77,6 +77,7 @@
77
77
  "verify:module-settings": "bun test scripts/check-module-settings.test.ts",
78
78
  "verify:driver-day": "bun test scripts/verify-driver-day.test.ts && bun scripts/verify-acknowledged-pending-delivery.mjs",
79
79
  "verify:carrier-integrations": "bun test scripts/check-carrier-integrations.test.ts",
80
+ "verify:customer-notifications": "bun test scripts/check-customer-notifications.test.ts",
80
81
  "verify:pallet-preview": "bun test scripts/check-pallet-preview.test.ts && bun scripts/verify-pallet-preview-hook.mjs",
81
82
  "verify:consumed-fuel-preview": "bun test scripts/check-consumed-fuel-preview.test.ts && bun scripts/verify-consumed-fuel-preview-hook.mjs && bun scripts/verify-consumed-fuel-preview-component.mjs"
82
83
  },
@@ -128,6 +128,8 @@ export type CreateCustomerInput = {
128
128
  context?: InputMaybe<ContextInput>;
129
129
  email?: InputMaybe<Scalars['String']['input']>;
130
130
  name: Scalars['String']['input'];
131
+ notifyByEmail?: InputMaybe<Scalars['Boolean']['input']>;
132
+ notifyBySms?: InputMaybe<Scalars['Boolean']['input']>;
131
133
  phone?: InputMaybe<Scalars['String']['input']>;
132
134
  rating?: InputMaybe<Scalars['Float']['input']>;
133
135
  taxId?: InputMaybe<Scalars['String']['input']>;
@@ -1115,6 +1117,10 @@ export type MtwCustomer = {
1115
1117
  email?: Maybe<Scalars['String']['output']>;
1116
1118
  id: Scalars['ID']['output'];
1117
1119
  name: Scalars['String']['output'];
1120
+ /** Send this customer delivery notifications by email (BOFF-4402). The workspace must also switch customer notifications on. */
1121
+ notifyByEmail: Scalars['Boolean']['output'];
1122
+ /** Send this customer delivery notifications by SMS. Needs a phone number with its country code. */
1123
+ notifyBySms: Scalars['Boolean']['output'];
1118
1124
  onBehalfOf: Scalars['String']['output'];
1119
1125
  phone?: Maybe<Scalars['String']['output']>;
1120
1126
  rating?: Maybe<Scalars['Float']['output']>;
@@ -1125,6 +1131,101 @@ export type MtwCustomer = {
1125
1131
  workspaceId: Scalars['ID']['output'];
1126
1132
  };
1127
1133
 
1134
+ /** One notification sent (or attempted) to a shipment's customer. */
1135
+ export type MtwCustomerNotification = {
1136
+ __typename?: 'MtwCustomerNotification';
1137
+ attempts: Scalars['Int']['output'];
1138
+ channel: MtwCustomerNotificationChannel;
1139
+ createdAt: Scalars['DateTime']['output'];
1140
+ customerId?: Maybe<Scalars['ID']['output']>;
1141
+ event: MtwCustomerNotificationEvent;
1142
+ id: Scalars['ID']['output'];
1143
+ lastError?: Maybe<Scalars['String']['output']>;
1144
+ /** Masked address, e.g. j***@example.com. */
1145
+ recipient?: Maybe<Scalars['String']['output']>;
1146
+ sentAt?: Maybe<Scalars['DateTime']['output']>;
1147
+ shipmentId: Scalars['ID']['output'];
1148
+ shipmentNumber?: Maybe<Scalars['String']['output']>;
1149
+ status: MtwCustomerNotificationStatus;
1150
+ subject?: Maybe<Scalars['String']['output']>;
1151
+ updatedAt: Scalars['DateTime']['output'];
1152
+ };
1153
+
1154
+ /** How a customer is reached. */
1155
+ export type MtwCustomerNotificationChannel =
1156
+ | 'EMAIL'
1157
+ | 'SMS';
1158
+
1159
+ /** A shipment lifecycle moment a customer can be told about (BOFF-4402). */
1160
+ export type MtwCustomerNotificationEvent =
1161
+ /** The shipment was created for the customer. */
1162
+ | 'BOOKED'
1163
+ /** The risk monitor expects the shipment to miss its promised delivery. Sent once per promise. */
1164
+ | 'DELAYED'
1165
+ /** The shipment was delivered. */
1166
+ | 'DELIVERED'
1167
+ /** Delivery could not be completed. */
1168
+ | 'FAILED'
1169
+ /** The latest tracking event says the shipment is out for delivery. */
1170
+ | 'OUT_FOR_DELIVERY'
1171
+ /** The shipment was collected. */
1172
+ | 'PICKED_UP';
1173
+
1174
+ export type MtwCustomerNotificationFilterInput = {
1175
+ event?: InputMaybe<MtwCustomerNotificationEvent>;
1176
+ shipmentId?: InputMaybe<Scalars['ID']['input']>;
1177
+ status?: InputMaybe<MtwCustomerNotificationStatus>;
1178
+ };
1179
+
1180
+ /** A page of customer notifications, newest first. */
1181
+ export type MtwCustomerNotificationPage = {
1182
+ __typename?: 'MtwCustomerNotificationPage';
1183
+ hasMore: Scalars['Boolean']['output'];
1184
+ items: Array<MtwCustomerNotification>;
1185
+ total: Scalars['Int']['output'];
1186
+ };
1187
+
1188
+ /** What a customer notification for an event looks like, rendered with sample data. */
1189
+ export type MtwCustomerNotificationPreview = {
1190
+ __typename?: 'MtwCustomerNotificationPreview';
1191
+ emailText: Scalars['String']['output'];
1192
+ event: MtwCustomerNotificationEvent;
1193
+ smsText: Scalars['String']['output'];
1194
+ subject: Scalars['String']['output'];
1195
+ };
1196
+
1197
+ /**
1198
+ * Whether and how this workspace tells its customers about their shipments.
1199
+ * Off until a workspace admin switches it on; only shipment changes made after
1200
+ * that moment notify, so switching on never mails customers about old shipments.
1201
+ */
1202
+ export type MtwCustomerNotificationSettings = {
1203
+ __typename?: 'MtwCustomerNotificationSettings';
1204
+ emailEnabled: Scalars['Boolean']['output'];
1205
+ enabled: Scalars['Boolean']['output'];
1206
+ /** When notifications were last switched on. */
1207
+ enabledAt?: Maybe<Scalars['DateTime']['output']>;
1208
+ /** Events that notify customers. */
1209
+ events: Array<MtwCustomerNotificationEvent>;
1210
+ /** False until the workspace saves these settings. */
1211
+ isCustomized: Scalars['Boolean']['output'];
1212
+ smsEnabled: Scalars['Boolean']['output'];
1213
+ updatedAt?: Maybe<Scalars['DateTime']['output']>;
1214
+ updatedBy?: Maybe<Scalars['ID']['output']>;
1215
+ workspaceId: Scalars['ID']['output'];
1216
+ };
1217
+
1218
+ /** Where a customer notification stands. */
1219
+ export type MtwCustomerNotificationStatus =
1220
+ /** Not accepted; retried by a later evaluation up to three times. */
1221
+ | 'FAILED'
1222
+ /** Claimed and being sent. */
1223
+ | 'PENDING'
1224
+ /** Accepted by the platform notification service. */
1225
+ | 'SENT'
1226
+ /** Not sent because the customer has no usable address for the channel. */
1227
+ | 'SKIPPED';
1228
+
1128
1229
  export type MtwCustomerStats = {
1129
1230
  __typename?: 'MtwCustomerStats';
1130
1231
  /** Unavailable because customers have no lifecycle status field. */
@@ -3562,6 +3663,9 @@ export type MtwRiskForecast = {
3562
3663
  evaluatedAt: Scalars['DateTime']['output'];
3563
3664
  /** Forecasting method, e.g. DETERMINISTIC_V1. */
3564
3665
  method: Scalars['String']['output'];
3666
+ /** AI narration of this forecast. Null when AI is disabled, credits are spent, or none was generated yet. */
3667
+ narration?: Maybe<MtwRiskNarration>;
3668
+ narrationStatus: MtwRiskNarrationStatus;
3565
3669
  predictedDeliveryEarliest?: Maybe<Scalars['DateTime']['output']>;
3566
3670
  predictedDeliveryLatest?: Maybe<Scalars['DateTime']['output']>;
3567
3671
  predictedSlipMinutes: Scalars['Int']['output'];
@@ -3583,6 +3687,42 @@ export type MtwRiskLevel =
3583
3687
  | 'MEDIUM'
3584
3688
  | 'NONE';
3585
3689
 
3690
+ /**
3691
+ * Plain-language explanation of a risk forecast, written by a model from the
3692
+ * forecast's computed features. It never changes the forecast's numbers.
3693
+ */
3694
+ export type MtwRiskNarration = {
3695
+ __typename?: 'MtwRiskNarration';
3696
+ /** Suggested message to the end customer re-promising delivery, when one is warranted. */
3697
+ customerMessage?: Maybe<Scalars['String']['output']>;
3698
+ /** Why the shipment is predicted to slip, in two or three sentences. */
3699
+ explanation: Scalars['String']['output'];
3700
+ generatedAt: Scalars['DateTime']['output'];
3701
+ id: Scalars['ID']['output'];
3702
+ /** Language the narration was written in. */
3703
+ locale: Scalars['String']['output'];
3704
+ /** Provider-qualified model that wrote it, e.g. openai/gpt-4o-mini. */
3705
+ model: Scalars['String']['output'];
3706
+ /** interactive, scheduled or event. */
3707
+ trigger: Scalars['String']['output'];
3708
+ };
3709
+
3710
+ export type MtwRiskNarrationStatus =
3711
+ /** AI narration is switched off for this environment. */
3712
+ | 'AI_DISABLED'
3713
+ /** A narration for the forecast as it stands now is attached. */
3714
+ | 'AVAILABLE'
3715
+ /** The workspace has spent its MoveTheWheels AI credits; no model call was made. */
3716
+ | 'CREDITS_EXHAUSTED'
3717
+ /** The workspace reached its daily narration cap; no model call was made. */
3718
+ | 'DAILY_CAP_REACHED'
3719
+ /** The forecast has no risk signals, so there is nothing to explain. */
3720
+ | 'NOT_NEEDED'
3721
+ /** No narration has been generated for this forecast yet; call mtwNarrateShipmentRiskForecast. */
3722
+ | 'NOT_REQUESTED'
3723
+ /** The model could not produce a narration this time; the forecast is unchanged. */
3724
+ | 'UNAVAILABLE';
3725
+
3586
3726
  export type MtwRiskRecommendedAction =
3587
3727
  | 'MONITOR'
3588
3728
  | 'NONE'
@@ -3612,6 +3752,51 @@ export type MtwRiskSignalCode =
3612
3752
  | 'TRACKING_STALE'
3613
3753
  | 'VEHICLE_UNAVAILABLE';
3614
3754
 
3755
+ export type MtwRiskSweep = {
3756
+ __typename?: 'MtwRiskSweep';
3757
+ atRisk: Scalars['Int']['output'];
3758
+ /**
3759
+ * True when this call was a redelivery of an earlier scheduler fire with the
3760
+ * same idempotency key; the original sweep is returned and nothing ran again.
3761
+ */
3762
+ duplicate: Scalars['Boolean']['output'];
3763
+ durationMs?: Maybe<Scalars['Int']['output']>;
3764
+ episodesClosed: Scalars['Int']['output'];
3765
+ episodesOpened: Scalars['Int']['output'];
3766
+ error?: Maybe<Scalars['String']['output']>;
3767
+ finishedAt?: Maybe<Scalars['DateTime']['output']>;
3768
+ id: Scalars['ID']['output'];
3769
+ notificationsSent: Scalars['Int']['output'];
3770
+ scheduledFor?: Maybe<Scalars['DateTime']['output']>;
3771
+ shipmentsEvaluated: Scalars['Int']['output'];
3772
+ /** KILL_SWITCH when the monitor is switched off. */
3773
+ skippedReason?: Maybe<Scalars['String']['output']>;
3774
+ startedAt: Scalars['DateTime']['output'];
3775
+ /** RUNNING, COMPLETED, SKIPPED or FAILED. */
3776
+ status: Scalars['String']['output'];
3777
+ /** SCHEDULED (fired by wspace-scheduler-svc) or MANUAL. */
3778
+ trigger: Scalars['String']['output'];
3779
+ workspaceId: Scalars['ID']['output'];
3780
+ };
3781
+
3782
+ /**
3783
+ * The workspace's cadence job in wspace-scheduler-svc. A workspace admin can pause
3784
+ * it on the shared Schedules screen; the monitor leaves a paused job alone.
3785
+ */
3786
+ export type MtwRiskSweepSchedule = {
3787
+ __typename?: 'MtwRiskSweepSchedule';
3788
+ cronExpression: Scalars['String']['output'];
3789
+ id: Scalars['ID']['output'];
3790
+ lastError?: Maybe<Scalars['String']['output']>;
3791
+ lastSweepAt?: Maybe<Scalars['DateTime']['output']>;
3792
+ schedulerJobId?: Maybe<Scalars['ID']['output']>;
3793
+ /** PENDING, ACTIVE or FAILED. */
3794
+ status: Scalars['String']['output'];
3795
+ timezone: Scalars['String']['output'];
3796
+ updatedAt: Scalars['DateTime']['output'];
3797
+ workspaceId: Scalars['ID']['output'];
3798
+ };
3799
+
3615
3800
  export type MtwRoute = {
3616
3801
  __typename?: 'MtwRoute';
3617
3802
  actualDistance?: Maybe<Scalars['Float']['output']>;
@@ -3650,6 +3835,16 @@ export type MtwRouteStats = {
3650
3835
  totalDuration: Scalars['Float']['output'];
3651
3836
  };
3652
3837
 
3838
+ /**
3839
+ * Set by wspace-scheduler-svc at fire time: idempotencyKey is
3840
+ * "<scheduleId>:<scheduledTime>" and triggerData carries scheduledTime, firedAt,
3841
+ * timezone, scheduleId and eventId. Ignored for on-demand runs by a person.
3842
+ */
3843
+ export type MtwRunRiskSweepInput = {
3844
+ idempotencyKey?: InputMaybe<Scalars['String']['input']>;
3845
+ triggerData?: InputMaybe<Scalars['JSON']['input']>;
3846
+ };
3847
+
3653
3848
  /** Whether the provider currently has capacity for an offering. */
3654
3849
  export type MtwServiceAvailability =
3655
3850
  | 'AVAILABLE'
@@ -3871,6 +4066,40 @@ export type MtwShipmentLeg = {
3871
4066
  workspaceId: Scalars['ID']['output'];
3872
4067
  };
3873
4068
 
4069
+ /**
4070
+ * The current (or last) risk episode of one shipment. An episode opens when the
4071
+ * forecast first reports the shipment at risk, notifies once, and closes when the
4072
+ * forecast recovers, the promise passes or the shipment finishes.
4073
+ */
4074
+ export type MtwShipmentRiskEpisode = {
4075
+ __typename?: 'MtwShipmentRiskEpisode';
4076
+ /** RECOVERED, BREACHED, COMPLETED or REMOVED. */
4077
+ closeReason?: Maybe<Scalars['String']['output']>;
4078
+ closedAt?: Maybe<Scalars['DateTime']['output']>;
4079
+ /** How many episodes this shipment has had. */
4080
+ episodeCount: Scalars['Int']['output'];
4081
+ id: Scalars['ID']['output'];
4082
+ lastEvaluatedAt: Scalars['DateTime']['output'];
4083
+ lastNotificationError?: Maybe<Scalars['String']['output']>;
4084
+ /** EVENT or SWEEP: what evaluated the shipment last. */
4085
+ lastTrigger: Scalars['String']['output'];
4086
+ notificationAttempts: Scalars['Int']['output'];
4087
+ notificationsSent: Scalars['Int']['output'];
4088
+ /** When the episode's notification reached at least one recipient. */
4089
+ notifiedAt?: Maybe<Scalars['DateTime']['output']>;
4090
+ openedAt: Scalars['DateTime']['output'];
4091
+ predictedSlipMinutes: Scalars['Int']['output'];
4092
+ /** The promised delivery the episode was raised against. */
4093
+ promisedDelivery?: Maybe<Scalars['DateTime']['output']>;
4094
+ recommendedAction?: Maybe<Scalars['String']['output']>;
4095
+ riskLevel: Scalars['String']['output'];
4096
+ riskScore: Scalars['Int']['output'];
4097
+ shipmentId: Scalars['ID']['output'];
4098
+ signals: Array<Scalars['String']['output']>;
4099
+ /** OPEN or CLOSED. */
4100
+ state: Scalars['String']['output'];
4101
+ };
4102
+
3874
4103
  export type MtwShipmentStatusDecision = {
3875
4104
  __typename?: 'MtwShipmentStatusDecision';
3876
4105
  authorityEpoch: Scalars['Int']['output'];
@@ -4254,6 +4483,8 @@ export type MtwTrackingAlert = {
4254
4483
  /** Stable alert id, `<shipmentId>:<alertType>`. */
4255
4484
  id: Scalars['ID']['output'];
4256
4485
  message: Scalars['String']['output'];
4486
+ /** AI narration of the forecast behind an AT_RISK alert, when one was generated for its current signals. */
4487
+ narration?: Maybe<MtwRiskNarration>;
4257
4488
  predictedDelivery?: Maybe<Scalars['DateTime']['output']>;
4258
4489
  /** The shipment's promised delivery (estimatedDelivery) the alert was raised against. */
4259
4490
  promisedDelivery?: Maybe<Scalars['DateTime']['output']>;
@@ -4652,6 +4883,24 @@ export type Mutation = {
4652
4883
  importMtwDrivers: MtwImportResult;
4653
4884
  importMtwVehicles: MtwImportResult;
4654
4885
  importMtwWarehouses: MtwImportResult;
4886
+ /**
4887
+ * Register, verify or repair the workspace's risk sweep with the platform
4888
+ * scheduler. Idempotent; leaves a job an operator paused alone.
4889
+ */
4890
+ mtwEnsureRiskSweepSchedule?: Maybe<MtwRiskSweepSchedule>;
4891
+ /**
4892
+ * Generate (or reuse) the AI narration for a shipment's current risk forecast
4893
+ * and return the forecast with it attached. With AI disabled, credits spent or
4894
+ * the daily cap reached, this returns the deterministic forecast unchanged and
4895
+ * makes no model call; `narrationStatus` says why.
4896
+ */
4897
+ mtwNarrateShipmentRiskForecast: MtwRiskForecast;
4898
+ /**
4899
+ * Re-forecast every in-flight shipment of the workspace and notify for any that
4900
+ * started slipping. Fired by wspace-scheduler-svc as this service's own service
4901
+ * actor; a workspace user may also run it on demand.
4902
+ */
4903
+ mtwRunRiskSweep: MtwRiskSweep;
4655
4904
  openMtwContainerCycle: MtwContainerCycle;
4656
4905
  openMtwFreeTimeClock: MtwFreeTimeClock;
4657
4906
  optimizeRoute: MtwRoute;
@@ -4697,6 +4946,8 @@ export type Mutation = {
4697
4946
  updateDriverStatus: MtwDriver;
4698
4947
  updateIntegration: MtwIntegration;
4699
4948
  updateInventoryItem: MtwInventoryItem;
4949
+ /** Switch customer notifications on or off and choose channels and events. */
4950
+ updateMtwCustomerNotificationSettings: MtwCustomerNotificationSettings;
4700
4951
  /** Save the workspace's MoveTheWheels operational defaults. Every field is validated. */
4701
4952
  updateMtwModuleSettings: MtwModuleSettings;
4702
4953
  updateRoute: MtwRoute;
@@ -4947,6 +5198,16 @@ export type MutationImportMtwWarehousesArgs = {
4947
5198
  };
4948
5199
 
4949
5200
 
5201
+ export type MutationMtwNarrateShipmentRiskForecastArgs = {
5202
+ shipmentId: Scalars['ID']['input'];
5203
+ };
5204
+
5205
+
5206
+ export type MutationMtwRunRiskSweepArgs = {
5207
+ input?: InputMaybe<MtwRunRiskSweepInput>;
5208
+ };
5209
+
5210
+
4950
5211
  export type MutationOpenMtwContainerCycleArgs = {
4951
5212
  input: OpenMtwContainerCycleInput;
4952
5213
  };
@@ -5182,6 +5443,11 @@ export type MutationUpdateInventoryItemArgs = {
5182
5443
  };
5183
5444
 
5184
5445
 
5446
+ export type MutationUpdateMtwCustomerNotificationSettingsArgs = {
5447
+ input: UpdateMtwCustomerNotificationSettingsInput;
5448
+ };
5449
+
5450
+
5185
5451
  export type MutationUpdateMtwModuleSettingsArgs = {
5186
5452
  input: UpdateMtwModuleSettingsInput;
5187
5453
  };
@@ -5400,6 +5666,12 @@ export type Query = {
5400
5666
  mtwContainerReleaseDecisionPage: MtwContainerReleaseDecisionPage;
5401
5667
  mtwContainerRepairPage: MtwContainerRepairPage;
5402
5668
  mtwContainers: Array<MtwContainer>;
5669
+ /** Sample messages for each event, signed with this workspace's name. */
5670
+ mtwCustomerNotificationPreviews: Array<MtwCustomerNotificationPreview>;
5671
+ /** This workspace's customer notification settings; off when none were saved. */
5672
+ mtwCustomerNotificationSettings: MtwCustomerNotificationSettings;
5673
+ /** Customer notifications sent or attempted in this workspace, newest first. */
5674
+ mtwCustomerNotifications: MtwCustomerNotificationPage;
5403
5675
  /** Exact immutable instruction, including a proposal's historical owner; not a current-eligibility claim. */
5404
5676
  mtwEmptyReturnInstruction: MtwEmptyReturnInstruction;
5405
5677
  mtwEmptyReturnInstructionPage: MtwEmptyReturnInstructionPage;
@@ -5439,6 +5711,12 @@ export type Query = {
5439
5711
  mtwReuseProposalPage: MtwReuseProposalPage;
5440
5712
  mtwReuseReservationPage: MtwReuseReservationPage;
5441
5713
  mtwReuseReservationTransitionPage: MtwReuseReservationTransitionPage;
5714
+ /** The workspace's risk-sweep registration with the platform scheduler, if any. */
5715
+ mtwRiskSweepSchedule?: Maybe<MtwRiskSweepSchedule>;
5716
+ /** Recent risk sweeps of the workspace, newest first. */
5717
+ mtwRiskSweeps: Array<MtwRiskSweep>;
5718
+ /** The shipment's current or last risk episode, or null if it has never been at risk. */
5719
+ mtwShipmentRiskEpisode?: Maybe<MtwShipmentRiskEpisode>;
5442
5720
  mtwShipmentRiskForecast: MtwRiskForecast;
5443
5721
  mtwStockBindingAssessment: MtwStockBindingAssessment;
5444
5722
  mtwStockBindingAssessmentPage: MtwStockBindingAssessmentPage;
@@ -5773,6 +6051,12 @@ export type QueryMtwContainersArgs = {
5773
6051
  };
5774
6052
 
5775
6053
 
6054
+ export type QueryMtwCustomerNotificationsArgs = {
6055
+ filter?: InputMaybe<MtwCustomerNotificationFilterInput>;
6056
+ pagination?: InputMaybe<PaginationInput>;
6057
+ };
6058
+
6059
+
5776
6060
  export type QueryMtwEmptyReturnInstructionArgs = {
5777
6061
  id: Scalars['ID']['input'];
5778
6062
  };
@@ -5940,6 +6224,16 @@ export type QueryMtwReuseReservationTransitionPageArgs = {
5940
6224
  };
5941
6225
 
5942
6226
 
6227
+ export type QueryMtwRiskSweepsArgs = {
6228
+ pagination?: InputMaybe<PaginationInput>;
6229
+ };
6230
+
6231
+
6232
+ export type QueryMtwShipmentRiskEpisodeArgs = {
6233
+ shipmentId: Scalars['ID']['input'];
6234
+ };
6235
+
6236
+
5943
6237
  export type QueryMtwShipmentRiskForecastArgs = {
5944
6238
  shipmentId: Scalars['ID']['input'];
5945
6239
  };
@@ -6617,6 +6911,8 @@ export type UpdateCustomerInput = {
6617
6911
  context?: InputMaybe<ContextInput>;
6618
6912
  email?: InputMaybe<Scalars['String']['input']>;
6619
6913
  name?: InputMaybe<Scalars['String']['input']>;
6914
+ notifyByEmail?: InputMaybe<Scalars['Boolean']['input']>;
6915
+ notifyBySms?: InputMaybe<Scalars['Boolean']['input']>;
6620
6916
  phone?: InputMaybe<Scalars['String']['input']>;
6621
6917
  rating?: InputMaybe<Scalars['Float']['input']>;
6622
6918
  taxId?: InputMaybe<Scalars['String']['input']>;
@@ -6695,6 +6991,14 @@ export type UpdateInventoryItemInput = {
6695
6991
  unitCost?: InputMaybe<Scalars['Float']['input']>;
6696
6992
  };
6697
6993
 
6994
+ /** Changes to the customer notification settings. Omitted fields keep their value. */
6995
+ export type UpdateMtwCustomerNotificationSettingsInput = {
6996
+ emailEnabled?: InputMaybe<Scalars['Boolean']['input']>;
6997
+ enabled?: InputMaybe<Scalars['Boolean']['input']>;
6998
+ events?: InputMaybe<Array<MtwCustomerNotificationEvent>>;
6999
+ smsEnabled?: InputMaybe<Scalars['Boolean']['input']>;
7000
+ };
7001
+
6698
7002
  /**
6699
7003
  * Changes to the workspace's MoveTheWheels defaults. Omitted fields keep their
6700
7004
  * current value. `timezone: null` (or an empty string) clears the time zone so