@burdenoff/microfe-movethewheels 2026.626.2 → 2026.703.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 (120) hide show
  1. package/dist/{AIAssistantPage-AvsMbunM.js → AIAssistantPage-C3ovZ0Fh.js} +100 -73
  2. package/dist/AnalyticsPage-O8SpY-V4.js +215 -0
  3. package/dist/{CreateOrderPage-CjlnEtk3.js → CreateOrderPage-CB1_-_mN.js} +33 -35
  4. package/dist/{CustomerDetailsPage-I6cdDHUi.js → CustomerDetailsPage-BaeKEI1D.js} +42 -44
  5. package/dist/{CustomersPage-CGGfD4ki.js → CustomersPage-DRaLuHMC.js} +26 -28
  6. package/dist/{DashboardPage-D2HRqwg9.js → DashboardPage-DPjhNfKB.js} +85 -83
  7. package/dist/{DataTable-CD9ZhpLB.js → DataTable-DmciIKKM.js} +3 -3
  8. package/dist/{DriverDetailsPage-rg_pGNuP.js → DriverDetailsPage-CSSQTOzn.js} +52 -54
  9. package/dist/{DriversPage-DKiynISH.js → DriversPage-C2Bk0w4Q.js} +24 -26
  10. package/dist/{FinancePage-Vg8WPs38.js → FinancePage-lmp0Sdsw.js} +9 -9
  11. package/dist/FleetPage-DZn5pIqd.js +365 -0
  12. package/dist/ImportExportPage-BIUJZc7O.js +290 -0
  13. package/dist/InventoryPage-Cxh1zYJd.js +253 -0
  14. package/dist/{LiveTrackingPage-DQ1q4Id3.js → LiveTrackingPage-BY1yAkIt.js} +60 -56
  15. package/dist/MarketplacePage-RMuL17EA.js +247 -0
  16. package/dist/{MetricCard-B7yMyvMX.js → MetricCard-7aYgzZHK.js} +1 -1
  17. package/dist/{OrderDetailsPage-CFASgsYX.js → OrderDetailsPage-C2Mf4KSy.js} +83 -85
  18. package/dist/{OrdersListPage-BHeT8S5o.js → OrdersListPage-BrPBtnhp.js} +31 -33
  19. package/dist/{PageLayout-D3N8MCTm.js → PageLayout-D4LDHJJq.js} +483 -422
  20. package/dist/{ProductDetailsPage-CFGcbaHo.js → ProductDetailsPage-CezzzZcj.js} +27 -29
  21. package/dist/{ProductsPage--xEpbQ8A.js → ProductsPage-I5DilIoY.js} +21 -23
  22. package/dist/ReportsPage-BDXNmUng.js +284 -0
  23. package/dist/RouteDetailsPage-BcomL20M.js +263 -0
  24. package/dist/RoutesPage-lqX9ylHP.js +144 -0
  25. package/dist/SettingsPage-CQVIOaNs.js +421 -0
  26. package/dist/StatusBadge-NpTeHVlw.js +152 -0
  27. package/dist/{TrackingPage-CcRc_dva.js → TrackingPage-BXrjz7V8.js} +59 -61
  28. package/dist/{VehicleDetailsPage-3YDMPsPs.js → VehicleDetailsPage-DZjLcJgr.js} +40 -42
  29. package/dist/{VehiclesPage-UlxPWP9K.js → VehiclesPage-6W39XT5W.js} +19 -21
  30. package/dist/{WarehouseDetailsPage-Db7KeN-d.js → WarehouseDetailsPage-C10pgRGW.js} +38 -40
  31. package/dist/{WarehousesPage-CuTO-OPp.js → WarehousesPage-CHvBqNaQ.js} +15 -17
  32. package/dist/{arrow-left-CBpZKkpq.js → arrow-left-n48Y-Wis.js} +1 -1
  33. package/dist/{box-CAaiElsb.js → box-D9Pal4HM.js} +1 -1
  34. package/dist/{chart-column-D1Ym15-S.js → chart-column-BFIJysDR.js} +1 -1
  35. package/dist/{chevron-right-BIFGi0UA.js → chevron-right-EMIIyyJ1.js} +1 -1
  36. package/dist/{circle-alert-DjmNo5_L.js → circle-alert-iY24lSlp.js} +1 -1
  37. package/dist/{circle-check-big-CjuPVo3_.js → circle-check-big-w0TrLKMF.js} +1 -1
  38. package/dist/{clock-BjkdnddD.js → clock-DJZtdl8u.js} +1 -1
  39. package/dist/{dollar-sign-IB4O0wuQ.js → dollar-sign-CgKQRz_e.js} +1 -1
  40. package/dist/{download-Dpa9vqoG.js → download-DRXxoiQR.js} +1 -1
  41. package/dist/{file-text-C8Uioy11.js → file-text-DWWY6_f5.js} +1 -1
  42. package/dist/{filter-Da5OZaHI.js → filter-DloLC2eE.js} +1 -1
  43. package/dist/index.js +119 -111
  44. package/dist/{map-_Per4sT9.js → map-CkJaULb2.js} +1 -1
  45. package/dist/{map-pin-BSaf5IDL.js → map-pin-BGiCvyCs.js} +1 -1
  46. package/dist/{navigation-DaTbrhw2.js → navigation-D36prnAn.js} +1 -1
  47. package/dist/{package-BE7U0wQE.js → package-BChUzpJS.js} +1 -1
  48. package/dist/{phone-DV4StZiA.js → phone-Ac9s3mo_.js} +1 -1
  49. package/dist/{plus-Dl1rLeJR.js → plus-Z76q-FZm.js} +1 -1
  50. package/dist/{refresh-cw-CWfERAit.js → refresh-cw-DKt6f5T6.js} +1 -1
  51. package/dist/{save-XH8vydOn.js → save-CojywNhG.js} +1 -1
  52. package/dist/{search-OxoIiL_C.js → search-CDGefqb3.js} +1 -1
  53. package/dist/{square-pen-hLIbFQFV.js → square-pen-CopE9GmB.js} +1 -1
  54. package/dist/{star-YsVV3MNQ.js → star-nHlLjDKR.js} +1 -1
  55. package/dist/{store-RCBl7HB-.js → store-BR9Voi5E.js} +1 -1
  56. package/dist/{trending-up-C-85AwGT.js → trending-up-B7W-GL6P.js} +1 -1
  57. package/dist/{triangle-alert-BOhfGgmh.js → triangle-alert-BonHFFNz.js} +1 -1
  58. package/dist/{truck-CtDjNFbi.js → truck-DrlfSpRC.js} +1 -1
  59. package/dist/{useAnalytics-ph7eTIK6.js → useAnalytics-CgdzUfY_.js} +40 -40
  60. package/dist/useAssistant-D4v3oxD3.js +84 -0
  61. package/dist/{useCustomers-bS3a4ytk.js → useCustomers-QrlozybF.js} +24 -24
  62. package/dist/{useFleet-BdETplNE.js → useFleet-DiAC-IiH.js} +70 -67
  63. package/dist/useImportExport-BLMnAKCC.js +55 -0
  64. package/dist/{useInventory-Dwn18FPz.js → useInventory-DKoks37x.js} +33 -33
  65. package/dist/{useOrders-D_3_hGMp.js → useOrders-D3UZVy8Y.js} +42 -42
  66. package/dist/useReports-Puq4DCH9.js +78 -0
  67. package/dist/{useRoutes-v4aBaS-E.js → useRoutes-Do57LvRb.js} +31 -31
  68. package/dist/useSettings-CkDH_-gM.js +81 -0
  69. package/dist/{useTracking-De2KIUNu.js → useTracking-GJify4HN.js} +22 -22
  70. package/dist/{user-Dz5TJZFs.js → user-CjSoudeu.js} +1 -1
  71. package/dist/{users-g2N0eL-Z.js → users-BAM4gHTn.js} +1 -1
  72. package/dist/{warehouse-D3WWDeTX.js → warehouse-C7MSr47B.js} +1 -1
  73. package/dist/{wrench-CmxY1n4I.js → wrench-CANXWxF-.js} +1 -1
  74. package/package.json +1 -1
  75. package/src/movethewheels/MoveTheWheelsRoutes.tsx +27 -3
  76. package/src/movethewheels/components/StatusBadge.tsx +18 -37
  77. package/src/movethewheels/constants/index.ts +67 -26
  78. package/src/movethewheels/hooks/index.ts +12 -0
  79. package/src/movethewheels/hooks/useAnalytics.ts +20 -6
  80. package/src/movethewheels/hooks/useAssistant.ts +74 -0
  81. package/src/movethewheels/hooks/useFleet.ts +63 -59
  82. package/src/movethewheels/hooks/useImportExport.ts +101 -0
  83. package/src/movethewheels/hooks/useReports.ts +158 -0
  84. package/src/movethewheels/hooks/useSettings.ts +120 -0
  85. package/src/movethewheels/pages/AIAssistantPage.tsx +53 -15
  86. package/src/movethewheels/pages/AnalyticsPage.tsx +67 -64
  87. package/src/movethewheels/pages/CustomerDetailsPage.tsx +1 -1
  88. package/src/movethewheels/pages/CustomersPage.tsx +1 -1
  89. package/src/movethewheels/pages/DashboardPage.tsx +42 -22
  90. package/src/movethewheels/pages/DriverDetailsPage.tsx +1 -1
  91. package/src/movethewheels/pages/DriversPage.tsx +1 -1
  92. package/src/movethewheels/pages/FleetPage.tsx +96 -38
  93. package/src/movethewheels/pages/ImportExportPage.tsx +174 -68
  94. package/src/movethewheels/pages/InventoryPage.tsx +66 -28
  95. package/src/movethewheels/pages/LiveTrackingPage.tsx +12 -3
  96. package/src/movethewheels/pages/MarketplacePage.tsx +87 -45
  97. package/src/movethewheels/pages/OrderDetailsPage.tsx +2 -2
  98. package/src/movethewheels/pages/ReportsPage.tsx +194 -90
  99. package/src/movethewheels/pages/RouteDetailsPage.tsx +5 -11
  100. package/src/movethewheels/pages/RoutesPage.tsx +4 -9
  101. package/src/movethewheels/pages/SettingsPage.tsx +234 -56
  102. package/src/movethewheels/pages/TrackingPage.tsx +6 -6
  103. package/src/movethewheels/providers/MoveTheWheelsProvider.tsx +7 -6
  104. package/src/movethewheels/store/movethewheelsStore.ts +1 -1
  105. package/dist/AnalyticsPage-CCLfHgQZ.js +0 -212
  106. package/dist/FleetPage-DddXt8r8.js +0 -332
  107. package/dist/ImportExportPage-BW6qO3kc.js +0 -248
  108. package/dist/InventoryPage-rMGuaJN-.js +0 -222
  109. package/dist/MarketplacePage-DKrXAv62.js +0 -219
  110. package/dist/ReportsPage-Bb7_90rK.js +0 -235
  111. package/dist/RouteDetailsPage-CVtDVb1N.js +0 -282
  112. package/dist/RoutesPage-s57PtgcQ.js +0 -163
  113. package/dist/SettingsPage-BYNCBOpN.js +0 -298
  114. package/dist/StatusBadge-CijYQXtb.js +0 -209
  115. package/dist/graphqlClient-CdJyR_ed.js +0 -55
  116. package/dist/navigation-BgnOfsVd.js +0 -6
  117. package/dist/route-Dp9JiN_E.js +0 -51
  118. package/dist/settings-3Rw5NwFF.js +0 -28
  119. package/src/movethewheels/constants/mockData.ts +0 -1342
  120. /package/dist/{formatters-_vJlC-47.js → formatters-DhKZ-eA1.js} +0 -0
@@ -1,8 +1,12 @@
1
1
  /**
2
2
  * Settings Page
3
+ *
4
+ * Loads persisted module settings (Query.moduleSettings.preferences) and saves
5
+ * changes via updateModuleSettings. Integrations read "Not configured" until a
6
+ * real connection is recorded — no fabricated "Connected" statuses.
3
7
  */
4
8
 
5
- import { useState } from 'react';
9
+ import { useEffect, useMemo, useState } from 'react';
6
10
  import { Settings, User, Bell, Shield, Globe, Webhook, Save } from 'lucide-react';
7
11
  import { PageLayout } from '../components/PageLayout';
8
12
  import {
@@ -26,9 +30,116 @@ import {
26
30
  TabsContent,
27
31
  } from '../components/ui';
28
32
  import { EmphasisPanel, PagePurpose, NextSteps } from '@burdenoff/fe-libs/ui';
33
+ import { useModuleSettings, type ModulePreferences } from '../hooks/useSettings';
34
+
35
+ interface CompanyPrefs {
36
+ name: string;
37
+ email: string;
38
+ phone: string;
39
+ website: string;
40
+ }
41
+ interface LocalizationPrefs {
42
+ timezone: string;
43
+ currency: string;
44
+ language: string;
45
+ }
46
+ interface NotificationPrefs {
47
+ orderUpdates: boolean;
48
+ deliveryAlerts: boolean;
49
+ systemNotifications: boolean;
50
+ weeklyReports: boolean;
51
+ }
52
+ interface SettingsForm {
53
+ company: CompanyPrefs;
54
+ localization: LocalizationPrefs;
55
+ notifications: NotificationPrefs;
56
+ twoFactorEnabled: boolean;
57
+ integrations: Record<string, boolean>;
58
+ }
59
+
60
+ const INTEGRATIONS = [
61
+ { key: 'googleMaps', name: 'Google Maps' },
62
+ { key: 'stripe', name: 'Stripe' },
63
+ { key: 'twilio', name: 'Twilio' },
64
+ ];
65
+
66
+ const NOTIFICATION_ITEMS: { key: keyof NotificationPrefs; label: string; desc: string }[] = [
67
+ {
68
+ key: 'orderUpdates',
69
+ label: 'Order updates',
70
+ desc: 'Receive notifications for order status changes',
71
+ },
72
+ { key: 'deliveryAlerts', label: 'Delivery alerts', desc: 'Get notified about delivery issues' },
73
+ {
74
+ key: 'systemNotifications',
75
+ label: 'System notifications',
76
+ desc: 'Important system updates and maintenance',
77
+ },
78
+ { key: 'weeklyReports', label: 'Weekly reports', desc: 'Receive weekly performance summary' },
79
+ ];
80
+
81
+ function toForm(preferences: ModulePreferences): SettingsForm {
82
+ const company = (preferences.company as Partial<CompanyPrefs> | undefined) ?? {};
83
+ const localization = (preferences.localization as Partial<LocalizationPrefs> | undefined) ?? {};
84
+ const notifications = (preferences.notifications as Partial<NotificationPrefs> | undefined) ?? {};
85
+ const security = (preferences.security as { twoFactorEnabled?: boolean } | undefined) ?? {};
86
+ const integrations = (preferences.integrations as Record<string, boolean> | undefined) ?? {};
87
+ return {
88
+ company: {
89
+ name: company.name ?? '',
90
+ email: company.email ?? '',
91
+ phone: company.phone ?? '',
92
+ website: company.website ?? '',
93
+ },
94
+ localization: {
95
+ timezone: localization.timezone ?? 'utc',
96
+ currency: localization.currency ?? 'usd',
97
+ language: localization.language ?? 'en',
98
+ },
99
+ notifications: {
100
+ orderUpdates: notifications.orderUpdates ?? true,
101
+ deliveryAlerts: notifications.deliveryAlerts ?? true,
102
+ systemNotifications: notifications.systemNotifications ?? true,
103
+ weeklyReports: notifications.weeklyReports ?? false,
104
+ },
105
+ twoFactorEnabled: security.twoFactorEnabled ?? false,
106
+ integrations,
107
+ };
108
+ }
29
109
 
30
110
  export default function SettingsPage() {
111
+ const { preferences, isLoading, isSaving, error, savePreferences } = useModuleSettings();
31
112
  const [tab, setTab] = useState('general');
113
+ const [form, setForm] = useState<SettingsForm>(() => toForm({}));
114
+ const [saved, setSaved] = useState(false);
115
+
116
+ const loadedForm = useMemo(() => toForm(preferences), [preferences]);
117
+ useEffect(() => {
118
+ setForm(loadedForm);
119
+ }, [loadedForm]);
120
+
121
+ const patchCompany = (patch: Partial<CompanyPrefs>) =>
122
+ setForm((prev) => ({ ...prev, company: { ...prev.company, ...patch } }));
123
+ const patchLocalization = (patch: Partial<LocalizationPrefs>) =>
124
+ setForm((prev) => ({ ...prev, localization: { ...prev.localization, ...patch } }));
125
+ const toggleNotification = (key: keyof NotificationPrefs, value: boolean) =>
126
+ setForm((prev) => ({ ...prev, notifications: { ...prev.notifications, [key]: value } }));
127
+
128
+ const handleSave = async () => {
129
+ setSaved(false);
130
+ try {
131
+ await savePreferences({
132
+ company: form.company,
133
+ localization: form.localization,
134
+ notifications: form.notifications,
135
+ security: { twoFactorEnabled: form.twoFactorEnabled },
136
+ integrations: form.integrations,
137
+ });
138
+ setSaved(true);
139
+ } catch {
140
+ // error surfaced via the hook's `error`
141
+ }
142
+ };
32
143
 
33
144
  return (
34
145
  <PageLayout
@@ -43,6 +154,19 @@ export default function SettingsPage() {
43
154
  expect.
44
155
  </PagePurpose>
45
156
 
157
+ {(saved || error) && (
158
+ <div
159
+ role="status"
160
+ className={`rounded-lg p-3 text-sm ${
161
+ error
162
+ ? 'bg-status-error-bg-subtle text-status-error-text'
163
+ : 'bg-status-success-bg-subtle text-status-success-text'
164
+ }`}
165
+ >
166
+ {error ? error.message : 'Settings saved.'}
167
+ </div>
168
+ )}
169
+
46
170
  <NextSteps
47
171
  storageKey="movethewheels-settings"
48
172
  steps={[
@@ -101,19 +225,40 @@ export default function SettingsPage() {
101
225
  <div className="grid grid-cols-2 gap-4">
102
226
  <div>
103
227
  <Label>Company Name</Label>
104
- <Input defaultValue="QuickLogistics Pro" />
228
+ <Input
229
+ value={form.company.name}
230
+ placeholder="Your company name"
231
+ disabled={isLoading}
232
+ onChange={(e) => patchCompany({ name: e.target.value })}
233
+ />
105
234
  </div>
106
235
  <div>
107
236
  <Label>Contact Email</Label>
108
- <Input defaultValue="contact@quicklogistics.com" />
237
+ <Input
238
+ type="email"
239
+ value={form.company.email}
240
+ placeholder="contact@example.com"
241
+ disabled={isLoading}
242
+ onChange={(e) => patchCompany({ email: e.target.value })}
243
+ />
109
244
  </div>
110
245
  <div>
111
246
  <Label>Phone</Label>
112
- <Input defaultValue="+1-555-QUICK" />
247
+ <Input
248
+ value={form.company.phone}
249
+ placeholder="+1-555-0100"
250
+ disabled={isLoading}
251
+ onChange={(e) => patchCompany({ phone: e.target.value })}
252
+ />
113
253
  </div>
114
254
  <div>
115
255
  <Label>Website</Label>
116
- <Input defaultValue="https://quicklogistics.com" />
256
+ <Input
257
+ value={form.company.website}
258
+ placeholder="https://example.com"
259
+ disabled={isLoading}
260
+ onChange={(e) => patchCompany({ website: e.target.value })}
261
+ />
117
262
  </div>
118
263
  </div>
119
264
  </EmphasisPanel>
@@ -128,7 +273,10 @@ export default function SettingsPage() {
128
273
  <div className="grid grid-cols-3 gap-4">
129
274
  <div>
130
275
  <Label>Timezone</Label>
131
- <Select defaultValue="pst">
276
+ <Select
277
+ value={form.localization.timezone}
278
+ onValueChange={(value) => patchLocalization({ timezone: value })}
279
+ >
132
280
  <SelectTrigger>
133
281
  <SelectValue />
134
282
  </SelectTrigger>
@@ -141,7 +289,10 @@ export default function SettingsPage() {
141
289
  </div>
142
290
  <div>
143
291
  <Label>Currency</Label>
144
- <Select defaultValue="usd">
292
+ <Select
293
+ value={form.localization.currency}
294
+ onValueChange={(value) => patchLocalization({ currency: value })}
295
+ >
145
296
  <SelectTrigger>
146
297
  <SelectValue />
147
298
  </SelectTrigger>
@@ -154,7 +305,10 @@ export default function SettingsPage() {
154
305
  </div>
155
306
  <div>
156
307
  <Label>Language</Label>
157
- <Select defaultValue="en">
308
+ <Select
309
+ value={form.localization.language}
310
+ onValueChange={(value) => patchLocalization({ language: value })}
311
+ >
158
312
  <SelectTrigger>
159
313
  <SelectValue />
160
314
  </SelectTrigger>
@@ -176,18 +330,17 @@ export default function SettingsPage() {
176
330
  <CardTitle>Email Notifications</CardTitle>
177
331
  </CardHeader>
178
332
  <CardContent className="space-y-4">
179
- {[
180
- { label: 'Order updates', desc: 'Receive notifications for order status changes' },
181
- { label: 'Delivery alerts', desc: 'Get notified about delivery issues' },
182
- { label: 'System notifications', desc: 'Important system updates and maintenance' },
183
- { label: 'Weekly reports', desc: 'Receive weekly performance summary' },
184
- ].map((item) => (
185
- <div key={item.label} className="flex items-center justify-between">
333
+ {NOTIFICATION_ITEMS.map((item) => (
334
+ <div key={item.key} className="flex items-center justify-between">
186
335
  <div>
187
336
  <p className="font-medium">{item.label}</p>
188
337
  <p className="text-sm text-text-secondary">{item.desc}</p>
189
338
  </div>
190
- <Switch defaultChecked />
339
+ <Switch
340
+ checked={form.notifications[item.key]}
341
+ disabled={isLoading}
342
+ onCheckedChange={(value) => toggleNotification(item.key, value)}
343
+ />
191
344
  </div>
192
345
  ))}
193
346
  </CardContent>
@@ -198,23 +351,26 @@ export default function SettingsPage() {
198
351
  <Card>
199
352
  <CardHeader>
200
353
  <CardTitle>Password</CardTitle>
354
+ <CardDescription>
355
+ Passwords are managed by your workspace identity provider.
356
+ </CardDescription>
201
357
  </CardHeader>
202
358
  <CardContent className="space-y-4">
203
359
  <div>
204
360
  <Label>Current Password</Label>
205
- <Input type="password" />
361
+ <Input type="password" autoComplete="current-password" />
206
362
  </div>
207
363
  <div className="grid grid-cols-2 gap-4">
208
364
  <div>
209
365
  <Label>New Password</Label>
210
- <Input type="password" />
366
+ <Input type="password" autoComplete="new-password" />
211
367
  </div>
212
368
  <div>
213
369
  <Label>Confirm Password</Label>
214
- <Input type="password" />
370
+ <Input type="password" autoComplete="new-password" />
215
371
  </div>
216
372
  </div>
217
- <Button>Update Password</Button>
373
+ <Button variant="outline">Update Password</Button>
218
374
  </CardContent>
219
375
  </Card>
220
376
  <Card>
@@ -224,59 +380,81 @@ export default function SettingsPage() {
224
380
  </CardHeader>
225
381
  <CardContent>
226
382
  <div className="flex items-center justify-between">
227
- <div>
228
- <p>
229
- Status: <span className="text-red-500 font-medium">Disabled</span>
230
- </p>
231
- </div>
232
- <Button variant="outline">Enable 2FA</Button>
383
+ <p>
384
+ Status:{' '}
385
+ <span
386
+ className={`font-medium ${
387
+ form.twoFactorEnabled
388
+ ? 'text-status-success-text'
389
+ : 'text-status-warning-text'
390
+ }`}
391
+ >
392
+ {form.twoFactorEnabled ? 'Enabled' : 'Disabled'}
393
+ </span>
394
+ </p>
395
+ <Button
396
+ variant="outline"
397
+ disabled={isSaving}
398
+ onClick={() =>
399
+ setForm((prev) => ({ ...prev, twoFactorEnabled: !prev.twoFactorEnabled }))
400
+ }
401
+ >
402
+ {form.twoFactorEnabled ? 'Disable 2FA' : 'Enable 2FA'}
403
+ </Button>
233
404
  </div>
234
405
  </CardContent>
235
406
  </Card>
236
407
  </TabsContent>
237
408
 
238
409
  <TabsContent value="integrations" className="space-y-6">
239
- <Card>
240
- <CardHeader>
241
- <CardTitle>API Keys</CardTitle>
242
- <CardDescription>Manage your API access</CardDescription>
243
- </CardHeader>
244
- <CardContent className="space-y-4">
245
- <div className="p-4 bg-bg-sunken rounded-lg">
246
- <p className="font-medium">Production API Key</p>
247
- <code className="text-sm">pk_live_••••••••••••••••</code>
248
- </div>
249
- <Button variant="outline">Generate New Key</Button>
250
- </CardContent>
251
- </Card>
252
410
  <Card>
253
411
  <CardHeader>
254
412
  <CardTitle>Connected Services</CardTitle>
413
+ <CardDescription>
414
+ Connect the providers that power maps, payments and messaging.
415
+ </CardDescription>
255
416
  </CardHeader>
256
417
  <CardContent className="space-y-3">
257
- {[
258
- { name: 'Google Maps', status: 'Connected' },
259
- { name: 'Stripe', status: 'Connected' },
260
- { name: 'Twilio', status: 'Not Connected' },
261
- ].map((s) => (
262
- <div
263
- key={s.name}
264
- className="flex items-center justify-between p-3 border rounded-lg"
265
- >
266
- <span>{s.name}</span>
267
- <Button variant={s.status === 'Connected' ? 'outline' : 'default'} size="sm">
268
- {s.status === 'Connected' ? 'Manage' : 'Connect'}
269
- </Button>
270
- </div>
271
- ))}
418
+ {INTEGRATIONS.map((service) => {
419
+ const connected = form.integrations[service.key] === true;
420
+ return (
421
+ <div
422
+ key={service.key}
423
+ className="flex items-center justify-between p-3 border rounded-lg"
424
+ >
425
+ <div>
426
+ <span className="font-medium">{service.name}</span>
427
+ <p className="text-sm text-text-secondary">
428
+ {connected ? 'Connected' : 'Not configured'}
429
+ </p>
430
+ </div>
431
+ <Button
432
+ variant={connected ? 'outline' : 'default'}
433
+ size="sm"
434
+ disabled={isSaving}
435
+ onClick={() =>
436
+ setForm((prev) => ({
437
+ ...prev,
438
+ integrations: {
439
+ ...prev.integrations,
440
+ [service.key]: !connected,
441
+ },
442
+ }))
443
+ }
444
+ >
445
+ {connected ? 'Disconnect' : 'Connect'}
446
+ </Button>
447
+ </div>
448
+ );
449
+ })}
272
450
  </CardContent>
273
451
  </Card>
274
452
  </TabsContent>
275
453
  </Tabs>
276
454
 
277
455
  <div className="flex justify-end">
278
- <Button>
279
- <Save size={16} /> Save Changes
456
+ <Button onClick={() => void handleSave()} disabled={isSaving || isLoading}>
457
+ <Save size={16} /> {isSaving ? 'Saving…' : 'Save Changes'}
280
458
  </Button>
281
459
  </div>
282
460
  </PageLayout>
@@ -308,20 +308,20 @@ export default function TrackingPage() {
308
308
  key={alert.id}
309
309
  className={`flex items-start gap-3 p-3 rounded-lg ${
310
310
  alert.severity === 'critical'
311
- ? 'bg-red-50 dark:bg-red-900/20'
311
+ ? 'bg-status-error-bg-subtle'
312
312
  : alert.severity === 'high'
313
- ? 'bg-orange-50 dark:bg-orange-900/20'
314
- : 'bg-yellow-50 dark:bg-yellow-900/20'
313
+ ? 'bg-status-warning-bg-subtle'
314
+ : 'bg-status-warning-bg-subtle'
315
315
  }`}
316
316
  >
317
317
  <AlertCircle
318
318
  size={16}
319
319
  className={
320
320
  alert.severity === 'critical'
321
- ? 'text-red-600'
321
+ ? 'text-status-error-text'
322
322
  : alert.severity === 'high'
323
- ? 'text-orange-600'
324
- : 'text-yellow-600'
323
+ ? 'text-status-warning-text'
324
+ : 'text-status-warning-text'
325
325
  }
326
326
  />
327
327
  <div>
@@ -8,6 +8,7 @@
8
8
  import { createContext, useContext, useMemo, type FC, type PropsWithChildren } from 'react';
9
9
  import { MemoryRouter } from 'react-router-dom';
10
10
  import type { MoveTheWheelsRootProps, MoveTheWheelsContextValue } from '../types';
11
+ import { joinPath } from '../utils/navigation';
11
12
 
12
13
  const MoveTheWheelsContext = createContext<MoveTheWheelsContextValue | null>(null);
13
14
 
@@ -54,13 +55,13 @@ export function useMoveTheWheelsNavigate(): (path: string) => void {
54
55
  const { navigate, basePath } = useMoveTheWheels();
55
56
 
56
57
  return (path: string) => {
57
- // If path is absolute (starts with /), use it directly
58
- // Otherwise, prepend basePath
59
- if (path.startsWith('/')) {
58
+ // Genuine external URLs (http/https or protocol-relative) pass through
59
+ // untouched. Everything else is an in-app path and MUST be resolved against
60
+ // basePath — otherwise a leading "/" would escape the microfrontend mount.
61
+ if (/^(https?:)?\/\//i.test(path)) {
60
62
  navigate(path);
61
- } else {
62
- const cleanBase = basePath.endsWith('/') ? basePath.slice(0, -1) : basePath;
63
- navigate(`${cleanBase}/${path}`);
63
+ return;
64
64
  }
65
+ navigate(joinPath(basePath, path));
65
66
  };
66
67
  }
@@ -2,7 +2,7 @@
2
2
  * MoveTheWheels Zustand Store
3
3
  *
4
4
  * Manages UI state for the microfrontend.
5
- * Server/entity data comes from hooks with mock data (ready for GraphQL replacement).
5
+ * Server/entity data comes from hooks that query the live backend via GraphQL.
6
6
  */
7
7
 
8
8
  import { create } from 'zustand';