@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.
- package/dist/{AIAssistantPage-AvsMbunM.js → AIAssistantPage-C3ovZ0Fh.js} +100 -73
- package/dist/AnalyticsPage-O8SpY-V4.js +215 -0
- package/dist/{CreateOrderPage-CjlnEtk3.js → CreateOrderPage-CB1_-_mN.js} +33 -35
- package/dist/{CustomerDetailsPage-I6cdDHUi.js → CustomerDetailsPage-BaeKEI1D.js} +42 -44
- package/dist/{CustomersPage-CGGfD4ki.js → CustomersPage-DRaLuHMC.js} +26 -28
- package/dist/{DashboardPage-D2HRqwg9.js → DashboardPage-DPjhNfKB.js} +85 -83
- package/dist/{DataTable-CD9ZhpLB.js → DataTable-DmciIKKM.js} +3 -3
- package/dist/{DriverDetailsPage-rg_pGNuP.js → DriverDetailsPage-CSSQTOzn.js} +52 -54
- package/dist/{DriversPage-DKiynISH.js → DriversPage-C2Bk0w4Q.js} +24 -26
- package/dist/{FinancePage-Vg8WPs38.js → FinancePage-lmp0Sdsw.js} +9 -9
- package/dist/FleetPage-DZn5pIqd.js +365 -0
- package/dist/ImportExportPage-BIUJZc7O.js +290 -0
- package/dist/InventoryPage-Cxh1zYJd.js +253 -0
- package/dist/{LiveTrackingPage-DQ1q4Id3.js → LiveTrackingPage-BY1yAkIt.js} +60 -56
- package/dist/MarketplacePage-RMuL17EA.js +247 -0
- package/dist/{MetricCard-B7yMyvMX.js → MetricCard-7aYgzZHK.js} +1 -1
- package/dist/{OrderDetailsPage-CFASgsYX.js → OrderDetailsPage-C2Mf4KSy.js} +83 -85
- package/dist/{OrdersListPage-BHeT8S5o.js → OrdersListPage-BrPBtnhp.js} +31 -33
- package/dist/{PageLayout-D3N8MCTm.js → PageLayout-D4LDHJJq.js} +483 -422
- package/dist/{ProductDetailsPage-CFGcbaHo.js → ProductDetailsPage-CezzzZcj.js} +27 -29
- package/dist/{ProductsPage--xEpbQ8A.js → ProductsPage-I5DilIoY.js} +21 -23
- package/dist/ReportsPage-BDXNmUng.js +284 -0
- package/dist/RouteDetailsPage-BcomL20M.js +263 -0
- package/dist/RoutesPage-lqX9ylHP.js +144 -0
- package/dist/SettingsPage-CQVIOaNs.js +421 -0
- package/dist/StatusBadge-NpTeHVlw.js +152 -0
- package/dist/{TrackingPage-CcRc_dva.js → TrackingPage-BXrjz7V8.js} +59 -61
- package/dist/{VehicleDetailsPage-3YDMPsPs.js → VehicleDetailsPage-DZjLcJgr.js} +40 -42
- package/dist/{VehiclesPage-UlxPWP9K.js → VehiclesPage-6W39XT5W.js} +19 -21
- package/dist/{WarehouseDetailsPage-Db7KeN-d.js → WarehouseDetailsPage-C10pgRGW.js} +38 -40
- package/dist/{WarehousesPage-CuTO-OPp.js → WarehousesPage-CHvBqNaQ.js} +15 -17
- package/dist/{arrow-left-CBpZKkpq.js → arrow-left-n48Y-Wis.js} +1 -1
- package/dist/{box-CAaiElsb.js → box-D9Pal4HM.js} +1 -1
- package/dist/{chart-column-D1Ym15-S.js → chart-column-BFIJysDR.js} +1 -1
- package/dist/{chevron-right-BIFGi0UA.js → chevron-right-EMIIyyJ1.js} +1 -1
- package/dist/{circle-alert-DjmNo5_L.js → circle-alert-iY24lSlp.js} +1 -1
- package/dist/{circle-check-big-CjuPVo3_.js → circle-check-big-w0TrLKMF.js} +1 -1
- package/dist/{clock-BjkdnddD.js → clock-DJZtdl8u.js} +1 -1
- package/dist/{dollar-sign-IB4O0wuQ.js → dollar-sign-CgKQRz_e.js} +1 -1
- package/dist/{download-Dpa9vqoG.js → download-DRXxoiQR.js} +1 -1
- package/dist/{file-text-C8Uioy11.js → file-text-DWWY6_f5.js} +1 -1
- package/dist/{filter-Da5OZaHI.js → filter-DloLC2eE.js} +1 -1
- package/dist/index.js +119 -111
- package/dist/{map-_Per4sT9.js → map-CkJaULb2.js} +1 -1
- package/dist/{map-pin-BSaf5IDL.js → map-pin-BGiCvyCs.js} +1 -1
- package/dist/{navigation-DaTbrhw2.js → navigation-D36prnAn.js} +1 -1
- package/dist/{package-BE7U0wQE.js → package-BChUzpJS.js} +1 -1
- package/dist/{phone-DV4StZiA.js → phone-Ac9s3mo_.js} +1 -1
- package/dist/{plus-Dl1rLeJR.js → plus-Z76q-FZm.js} +1 -1
- package/dist/{refresh-cw-CWfERAit.js → refresh-cw-DKt6f5T6.js} +1 -1
- package/dist/{save-XH8vydOn.js → save-CojywNhG.js} +1 -1
- package/dist/{search-OxoIiL_C.js → search-CDGefqb3.js} +1 -1
- package/dist/{square-pen-hLIbFQFV.js → square-pen-CopE9GmB.js} +1 -1
- package/dist/{star-YsVV3MNQ.js → star-nHlLjDKR.js} +1 -1
- package/dist/{store-RCBl7HB-.js → store-BR9Voi5E.js} +1 -1
- package/dist/{trending-up-C-85AwGT.js → trending-up-B7W-GL6P.js} +1 -1
- package/dist/{triangle-alert-BOhfGgmh.js → triangle-alert-BonHFFNz.js} +1 -1
- package/dist/{truck-CtDjNFbi.js → truck-DrlfSpRC.js} +1 -1
- package/dist/{useAnalytics-ph7eTIK6.js → useAnalytics-CgdzUfY_.js} +40 -40
- package/dist/useAssistant-D4v3oxD3.js +84 -0
- package/dist/{useCustomers-bS3a4ytk.js → useCustomers-QrlozybF.js} +24 -24
- package/dist/{useFleet-BdETplNE.js → useFleet-DiAC-IiH.js} +70 -67
- package/dist/useImportExport-BLMnAKCC.js +55 -0
- package/dist/{useInventory-Dwn18FPz.js → useInventory-DKoks37x.js} +33 -33
- package/dist/{useOrders-D_3_hGMp.js → useOrders-D3UZVy8Y.js} +42 -42
- package/dist/useReports-Puq4DCH9.js +78 -0
- package/dist/{useRoutes-v4aBaS-E.js → useRoutes-Do57LvRb.js} +31 -31
- package/dist/useSettings-CkDH_-gM.js +81 -0
- package/dist/{useTracking-De2KIUNu.js → useTracking-GJify4HN.js} +22 -22
- package/dist/{user-Dz5TJZFs.js → user-CjSoudeu.js} +1 -1
- package/dist/{users-g2N0eL-Z.js → users-BAM4gHTn.js} +1 -1
- package/dist/{warehouse-D3WWDeTX.js → warehouse-C7MSr47B.js} +1 -1
- package/dist/{wrench-CmxY1n4I.js → wrench-CANXWxF-.js} +1 -1
- package/package.json +1 -1
- package/src/movethewheels/MoveTheWheelsRoutes.tsx +27 -3
- package/src/movethewheels/components/StatusBadge.tsx +18 -37
- package/src/movethewheels/constants/index.ts +67 -26
- package/src/movethewheels/hooks/index.ts +12 -0
- package/src/movethewheels/hooks/useAnalytics.ts +20 -6
- package/src/movethewheels/hooks/useAssistant.ts +74 -0
- package/src/movethewheels/hooks/useFleet.ts +63 -59
- package/src/movethewheels/hooks/useImportExport.ts +101 -0
- package/src/movethewheels/hooks/useReports.ts +158 -0
- package/src/movethewheels/hooks/useSettings.ts +120 -0
- package/src/movethewheels/pages/AIAssistantPage.tsx +53 -15
- package/src/movethewheels/pages/AnalyticsPage.tsx +67 -64
- package/src/movethewheels/pages/CustomerDetailsPage.tsx +1 -1
- package/src/movethewheels/pages/CustomersPage.tsx +1 -1
- package/src/movethewheels/pages/DashboardPage.tsx +42 -22
- package/src/movethewheels/pages/DriverDetailsPage.tsx +1 -1
- package/src/movethewheels/pages/DriversPage.tsx +1 -1
- package/src/movethewheels/pages/FleetPage.tsx +96 -38
- package/src/movethewheels/pages/ImportExportPage.tsx +174 -68
- package/src/movethewheels/pages/InventoryPage.tsx +66 -28
- package/src/movethewheels/pages/LiveTrackingPage.tsx +12 -3
- package/src/movethewheels/pages/MarketplacePage.tsx +87 -45
- package/src/movethewheels/pages/OrderDetailsPage.tsx +2 -2
- package/src/movethewheels/pages/ReportsPage.tsx +194 -90
- package/src/movethewheels/pages/RouteDetailsPage.tsx +5 -11
- package/src/movethewheels/pages/RoutesPage.tsx +4 -9
- package/src/movethewheels/pages/SettingsPage.tsx +234 -56
- package/src/movethewheels/pages/TrackingPage.tsx +6 -6
- package/src/movethewheels/providers/MoveTheWheelsProvider.tsx +7 -6
- package/src/movethewheels/store/movethewheelsStore.ts +1 -1
- package/dist/AnalyticsPage-CCLfHgQZ.js +0 -212
- package/dist/FleetPage-DddXt8r8.js +0 -332
- package/dist/ImportExportPage-BW6qO3kc.js +0 -248
- package/dist/InventoryPage-rMGuaJN-.js +0 -222
- package/dist/MarketplacePage-DKrXAv62.js +0 -219
- package/dist/ReportsPage-Bb7_90rK.js +0 -235
- package/dist/RouteDetailsPage-CVtDVb1N.js +0 -282
- package/dist/RoutesPage-s57PtgcQ.js +0 -163
- package/dist/SettingsPage-BYNCBOpN.js +0 -298
- package/dist/StatusBadge-CijYQXtb.js +0 -209
- package/dist/graphqlClient-CdJyR_ed.js +0 -55
- package/dist/navigation-BgnOfsVd.js +0 -6
- package/dist/route-Dp9JiN_E.js +0 -51
- package/dist/settings-3Rw5NwFF.js +0 -28
- package/src/movethewheels/constants/mockData.ts +0 -1342
- /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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
{
|
|
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
|
|
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
|
-
<
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
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
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
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-
|
|
311
|
+
? 'bg-status-error-bg-subtle'
|
|
312
312
|
: alert.severity === 'high'
|
|
313
|
-
? 'bg-
|
|
314
|
-
: 'bg-
|
|
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-
|
|
321
|
+
? 'text-status-error-text'
|
|
322
322
|
: alert.severity === 'high'
|
|
323
|
-
? 'text-
|
|
324
|
-
: 'text-
|
|
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
|
-
//
|
|
58
|
-
//
|
|
59
|
-
|
|
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
|
-
|
|
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
|
|
5
|
+
* Server/entity data comes from hooks that query the live backend via GraphQL.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import { create } from 'zustand';
|