@burdenoff/microfe-movethewheels 2026.827.1 → 2026.830.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/MoveTheWheelsRoot.js +86 -83
- package/dist/MoveTheWheelsRoutes.js +107 -104
- package/dist/hooks/index.js +1 -0
- package/dist/hooks/useImportExport.js +22 -51
- package/dist/hooks/useReports.js +63 -73
- package/dist/hooks/useSettings.js +52 -44
- package/dist/index.js +38 -37
- package/dist/movethewheels/hooks/index.d.ts +1 -0
- package/dist/movethewheels/hooks/useImportExport.d.ts +10 -0
- package/dist/movethewheels/pages/MarketplacePage.d.ts +7 -4
- package/dist/movethewheels/shared/hooks/useTr.d.ts +16 -0
- package/dist/pages/AIAssistantPage.js +45 -43
- package/dist/pages/DashboardPage.js +6 -6
- package/dist/pages/FleetPage.js +7 -7
- package/dist/pages/InventoryPage.js +6 -6
- package/dist/pages/MarketplacePage.js +11 -432
- package/dist/pages/OrdersListPage.js +5 -5
- package/dist/pages/ReportsPage.js +120 -109
- package/dist/pages/SettingsPage.js +175 -154
- package/dist/pages/TrackingPage.js +5 -5
- package/dist/shared/hooks/useTr.js +12 -0
- package/package.json +2 -2
- package/src/movethewheels/MoveTheWheelsRoot.tsx +28 -24
- package/src/movethewheels/MoveTheWheelsRoutes.tsx +5 -2
- package/src/movethewheels/hooks/index.ts +3 -0
- package/src/movethewheels/hooks/useImportExport.ts +36 -72
- package/src/movethewheels/hooks/useReports.ts +73 -87
- package/src/movethewheels/hooks/useSettings.ts +50 -53
- package/src/movethewheels/pages/AIAssistantPage.tsx +50 -32
- package/src/movethewheels/pages/MarketplacePage.tsx +16 -539
- package/src/movethewheels/pages/ReportsPage.tsx +23 -0
- package/src/movethewheels/pages/SettingsPage.tsx +154 -70
- package/src/movethewheels/shared/hooks/useTr.ts +37 -0
- package/dist/generated/global-operations.d.ts +0 -1
- package/dist/generated/global-types.d.ts +0 -23526
- package/dist/node_modules/lucide-react/dist/esm/icons/chevron-down.js +0 -8
- package/dist/node_modules/lucide-react/dist/esm/icons/chevron-up.js +0 -8
- package/src/generated/global-operations.ts +0 -2
- package/src/generated/global-types.ts +0 -27259
|
@@ -30,6 +30,7 @@ import { MoveTheWheelsProvider } from './providers/MoveTheWheelsProvider';
|
|
|
30
30
|
import { FeatureFlagProvider, useFeatureFlag } from '@burdenoff/fe-libs/shared/feature-flags';
|
|
31
31
|
import type { NavItem, MoveTheWheelsRootProps } from './types';
|
|
32
32
|
import { MoveTheWheelsRoutes } from './MoveTheWheelsRoutes';
|
|
33
|
+
import { useTr } from './hooks';
|
|
33
34
|
|
|
34
35
|
/**
|
|
35
36
|
* Registers the MoveTheWheels sidebar nav tree with the host shell.
|
|
@@ -43,6 +44,8 @@ const NavRegistrar: FC<Pick<MoveTheWheelsRootProps, 'registerNavItems' | 'basePa
|
|
|
43
44
|
registerNavItems,
|
|
44
45
|
basePath = '/',
|
|
45
46
|
}) => {
|
|
47
|
+
const tr = useTr();
|
|
48
|
+
|
|
46
49
|
// The backend has no ServiceProvider/ServiceOffering model (dropped by
|
|
47
50
|
// migration 20260707000001_movethewheels_reuse_cleanup) — hide the
|
|
48
51
|
// Marketplace nav entry until a serviceProviders/serviceOfferings resolver
|
|
@@ -62,7 +65,7 @@ const NavRegistrar: FC<Pick<MoveTheWheelsRootProps, 'registerNavItems' | 'basePa
|
|
|
62
65
|
|
|
63
66
|
const movethewheelsNavItem: NavItem = {
|
|
64
67
|
id: 'movethewheels',
|
|
65
|
-
label: 'MoveTheWheels',
|
|
68
|
+
label: tr('movethewheels.page.title', 'MoveTheWheels'),
|
|
66
69
|
path: basePath,
|
|
67
70
|
icon: <Truck size={20} />,
|
|
68
71
|
order: 25,
|
|
@@ -70,49 +73,49 @@ const NavRegistrar: FC<Pick<MoveTheWheelsRootProps, 'registerNavItems' | 'basePa
|
|
|
70
73
|
children: [
|
|
71
74
|
{
|
|
72
75
|
id: 'mtw-dashboard',
|
|
73
|
-
label: 'Dashboard',
|
|
76
|
+
label: tr('movethewheels.root.nav.dashboard', 'Dashboard'),
|
|
74
77
|
path: basePath,
|
|
75
78
|
icon: <LayoutDashboard size={16} />,
|
|
76
79
|
order: 1,
|
|
77
80
|
},
|
|
78
81
|
{
|
|
79
82
|
id: 'mtw-orders',
|
|
80
|
-
label: 'Orders',
|
|
83
|
+
label: tr('movethewheels.root.nav.orders', 'Orders'),
|
|
81
84
|
path: joinPath(basePath, 'orders'),
|
|
82
85
|
icon: <Package size={16} />,
|
|
83
86
|
order: 2,
|
|
84
87
|
},
|
|
85
88
|
{
|
|
86
89
|
id: 'mtw-tracking',
|
|
87
|
-
label: 'Tracking',
|
|
90
|
+
label: tr('movethewheels.root.nav.tracking', 'Tracking'),
|
|
88
91
|
path: joinPath(basePath, 'tracking'),
|
|
89
92
|
icon: <Map size={16} />,
|
|
90
93
|
order: 3,
|
|
91
94
|
},
|
|
92
95
|
{
|
|
93
96
|
id: 'mtw-fleet',
|
|
94
|
-
label: 'Fleet',
|
|
97
|
+
label: tr('movethewheels.root.nav.fleet', 'Fleet'),
|
|
95
98
|
path: joinPath(basePath, 'fleet'),
|
|
96
99
|
icon: <Truck size={16} />,
|
|
97
100
|
order: 4,
|
|
98
101
|
children: [
|
|
99
102
|
{
|
|
100
103
|
id: 'mtw-fleet-overview',
|
|
101
|
-
label: 'Overview',
|
|
104
|
+
label: tr('movethewheels.root.nav.overview', 'Overview'),
|
|
102
105
|
path: joinPath(basePath, 'fleet'),
|
|
103
106
|
icon: <LayoutDashboard size={14} />,
|
|
104
107
|
order: 1,
|
|
105
108
|
},
|
|
106
109
|
{
|
|
107
110
|
id: 'mtw-vehicles',
|
|
108
|
-
label: 'Vehicles',
|
|
111
|
+
label: tr('movethewheels.root.nav.vehicles', 'Vehicles'),
|
|
109
112
|
path: joinPath(basePath, 'fleet/vehicles'),
|
|
110
113
|
icon: <Car size={14} />,
|
|
111
114
|
order: 2,
|
|
112
115
|
},
|
|
113
116
|
{
|
|
114
117
|
id: 'mtw-drivers',
|
|
115
|
-
label: 'Drivers',
|
|
118
|
+
label: tr('movethewheels.root.nav.drivers', 'Drivers'),
|
|
116
119
|
path: joinPath(basePath, 'fleet/drivers'),
|
|
117
120
|
icon: <UserCheck size={14} />,
|
|
118
121
|
order: 3,
|
|
@@ -121,28 +124,28 @@ const NavRegistrar: FC<Pick<MoveTheWheelsRootProps, 'registerNavItems' | 'basePa
|
|
|
121
124
|
},
|
|
122
125
|
{
|
|
123
126
|
id: 'mtw-inventory',
|
|
124
|
-
label: 'Inventory',
|
|
127
|
+
label: tr('movethewheels.root.nav.inventory', 'Inventory'),
|
|
125
128
|
path: joinPath(basePath, 'inventory'),
|
|
126
129
|
icon: <Warehouse size={16} />,
|
|
127
130
|
order: 5,
|
|
128
131
|
children: [
|
|
129
132
|
{
|
|
130
133
|
id: 'mtw-inventory-overview',
|
|
131
|
-
label: 'Overview',
|
|
134
|
+
label: tr('movethewheels.root.nav.overview', 'Overview'),
|
|
132
135
|
path: joinPath(basePath, 'inventory'),
|
|
133
136
|
icon: <LayoutDashboard size={14} />,
|
|
134
137
|
order: 1,
|
|
135
138
|
},
|
|
136
139
|
{
|
|
137
140
|
id: 'mtw-products',
|
|
138
|
-
label: 'Products',
|
|
141
|
+
label: tr('movethewheels.root.nav.products', 'Products'),
|
|
139
142
|
path: joinPath(basePath, 'inventory/products'),
|
|
140
143
|
icon: <Boxes size={14} />,
|
|
141
144
|
order: 2,
|
|
142
145
|
},
|
|
143
146
|
{
|
|
144
147
|
id: 'mtw-warehouses',
|
|
145
|
-
label: 'Warehouses',
|
|
148
|
+
label: tr('movethewheels.root.nav.warehouses', 'Warehouses'),
|
|
146
149
|
path: joinPath(basePath, 'inventory/warehouses'),
|
|
147
150
|
icon: <Warehouse size={14} />,
|
|
148
151
|
order: 3,
|
|
@@ -151,28 +154,28 @@ const NavRegistrar: FC<Pick<MoveTheWheelsRootProps, 'registerNavItems' | 'basePa
|
|
|
151
154
|
},
|
|
152
155
|
{
|
|
153
156
|
id: 'mtw-customers',
|
|
154
|
-
label: 'Customers',
|
|
157
|
+
label: tr('movethewheels.root.nav.customers', 'Customers'),
|
|
155
158
|
path: joinPath(basePath, 'customers'),
|
|
156
159
|
icon: <Users size={16} />,
|
|
157
160
|
order: 6,
|
|
158
161
|
},
|
|
159
162
|
{
|
|
160
163
|
id: 'mtw-routes',
|
|
161
|
-
label: 'Routes',
|
|
164
|
+
label: tr('movethewheels.root.nav.routes', 'Routes'),
|
|
162
165
|
path: joinPath(basePath, 'routes'),
|
|
163
166
|
icon: <Route size={16} />,
|
|
164
167
|
order: 7,
|
|
165
168
|
},
|
|
166
169
|
{
|
|
167
170
|
id: 'mtw-analytics',
|
|
168
|
-
label: 'Analytics',
|
|
171
|
+
label: tr('movethewheels.root.nav.analytics', 'Analytics'),
|
|
169
172
|
path: joinPath(basePath, 'analytics'),
|
|
170
173
|
icon: <BarChart3 size={16} />,
|
|
171
174
|
order: 8,
|
|
172
175
|
},
|
|
173
176
|
{
|
|
174
177
|
id: 'mtw-finance',
|
|
175
|
-
label: 'Finance',
|
|
178
|
+
label: tr('movethewheels.root.nav.finance', 'Finance'),
|
|
176
179
|
path: joinPath(basePath, 'finance'),
|
|
177
180
|
icon: <DollarSign size={16} />,
|
|
178
181
|
order: 9,
|
|
@@ -183,7 +186,7 @@ const NavRegistrar: FC<Pick<MoveTheWheelsRootProps, 'registerNavItems' | 'basePa
|
|
|
183
186
|
? [
|
|
184
187
|
{
|
|
185
188
|
id: 'mtw-marketplace',
|
|
186
|
-
label: 'Marketplace',
|
|
189
|
+
label: tr('movethewheels.root.nav.marketplace', 'Marketplace'),
|
|
187
190
|
path: joinPath(basePath, 'marketplace'),
|
|
188
191
|
icon: <Store size={16} />,
|
|
189
192
|
order: 10,
|
|
@@ -192,28 +195,28 @@ const NavRegistrar: FC<Pick<MoveTheWheelsRootProps, 'registerNavItems' | 'basePa
|
|
|
192
195
|
: []),
|
|
193
196
|
{
|
|
194
197
|
id: 'mtw-import-export',
|
|
195
|
-
label: 'Import/Export',
|
|
198
|
+
label: tr('movethewheels.root.nav.importExport', 'Import/Export'),
|
|
196
199
|
path: joinPath(basePath, 'import-export'),
|
|
197
200
|
icon: <Globe size={16} />,
|
|
198
201
|
order: 11,
|
|
199
202
|
},
|
|
200
203
|
{
|
|
201
204
|
id: 'mtw-reports',
|
|
202
|
-
label: 'Reports',
|
|
205
|
+
label: tr('movethewheels.root.nav.reports', 'Reports'),
|
|
203
206
|
path: joinPath(basePath, 'reports'),
|
|
204
207
|
icon: <FileText size={16} />,
|
|
205
208
|
order: 12,
|
|
206
209
|
},
|
|
207
210
|
{
|
|
208
211
|
id: 'mtw-ai-assistant',
|
|
209
|
-
label: 'AI Assistant',
|
|
212
|
+
label: tr('movethewheels.root.nav.aiAssistant', 'AI Assistant'),
|
|
210
213
|
path: joinPath(basePath, 'ai-assistant'),
|
|
211
214
|
icon: <Bot size={16} />,
|
|
212
215
|
order: 13,
|
|
213
216
|
},
|
|
214
217
|
{
|
|
215
218
|
id: 'mtw-settings',
|
|
216
|
-
label: 'Settings',
|
|
219
|
+
label: tr('movethewheels.root.nav.settings', 'Settings'),
|
|
217
220
|
path: joinPath(basePath, 'settings'),
|
|
218
221
|
icon: <Settings size={16} />,
|
|
219
222
|
order: 99,
|
|
@@ -226,7 +229,7 @@ const NavRegistrar: FC<Pick<MoveTheWheelsRootProps, 'registerNavItems' | 'basePa
|
|
|
226
229
|
|
|
227
230
|
// Return cleanup function to unregister on unmount
|
|
228
231
|
return cleanup;
|
|
229
|
-
}, [registerNavItems, basePath, marketplaceEnabled]);
|
|
232
|
+
}, [registerNavItems, basePath, marketplaceEnabled, tr]);
|
|
230
233
|
|
|
231
234
|
return null;
|
|
232
235
|
};
|
|
@@ -254,6 +257,7 @@ const NavRegistrar: FC<Pick<MoveTheWheelsRootProps, 'registerNavItems' | 'basePa
|
|
|
254
257
|
*/
|
|
255
258
|
export const MoveTheWheelsRoot: FC<MoveTheWheelsRootProps> = (props) => {
|
|
256
259
|
const { registerNavItems, registerFooterItems, basePath = '/' } = props;
|
|
260
|
+
const tr = useTr();
|
|
257
261
|
|
|
258
262
|
// Register footer items
|
|
259
263
|
useEffect(() => {
|
|
@@ -262,7 +266,7 @@ export const MoveTheWheelsRoot: FC<MoveTheWheelsRootProps> = (props) => {
|
|
|
262
266
|
const footerItems = [
|
|
263
267
|
{
|
|
264
268
|
id: 'mtw-status',
|
|
265
|
-
label: 'Logistics Ready',
|
|
269
|
+
label: tr('movethewheels.root.footer.status', 'Logistics Ready'),
|
|
266
270
|
type: 'status' as const,
|
|
267
271
|
order: 20,
|
|
268
272
|
section: 'left' as const,
|
|
@@ -271,7 +275,7 @@ export const MoveTheWheelsRoot: FC<MoveTheWheelsRootProps> = (props) => {
|
|
|
271
275
|
|
|
272
276
|
const cleanup = registerFooterItems(footerItems);
|
|
273
277
|
return cleanup;
|
|
274
|
-
}, [registerFooterItems]);
|
|
278
|
+
}, [registerFooterItems, tr]);
|
|
275
279
|
|
|
276
280
|
return (
|
|
277
281
|
<FeatureFlagProvider workspaceId={null} gateway="global" defaultEnabled={true}>
|
|
@@ -9,6 +9,7 @@ import { Routes, Route, useNavigate } from 'react-router';
|
|
|
9
9
|
import { lazy, Suspense } from 'react';
|
|
10
10
|
import { NotFoundPage } from '@burdenoff/fe-libs/shared/pages';
|
|
11
11
|
import { useMoveTheWheels } from './providers/MoveTheWheelsProvider';
|
|
12
|
+
import { useTr } from './hooks';
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
15
|
* Catch-all 404 rendered inside the content area (previously a redirect to ""
|
|
@@ -18,9 +19,10 @@ import { useMoveTheWheels } from './providers/MoveTheWheelsProvider';
|
|
|
18
19
|
function MoveTheWheelsNotFound() {
|
|
19
20
|
const { navigate, basePath } = useMoveTheWheels();
|
|
20
21
|
const routerNavigate = useNavigate();
|
|
22
|
+
const tr = useTr();
|
|
21
23
|
return (
|
|
22
24
|
<NotFoundPage
|
|
23
|
-
homeLabel=
|
|
25
|
+
homeLabel={tr('movethewheels.root.notFound.homeLabel', 'Back to MoveTheWheels')}
|
|
24
26
|
onGoHome={() => {
|
|
25
27
|
if (navigate) {
|
|
26
28
|
navigate(basePath || '/');
|
|
@@ -34,11 +36,12 @@ function MoveTheWheelsNotFound() {
|
|
|
34
36
|
|
|
35
37
|
// Loading fallback
|
|
36
38
|
function PageLoader() {
|
|
39
|
+
const tr = useTr();
|
|
37
40
|
return (
|
|
38
41
|
<div className="flex items-center justify-center min-h-[400px]">
|
|
39
42
|
<div className="flex flex-col items-center gap-3">
|
|
40
43
|
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-primary"></div>
|
|
41
|
-
<p className="text-sm text-text-secondary">Loading
|
|
44
|
+
<p className="text-sm text-text-secondary">{tr('movethewheels.root.loading', 'Loading...')}</p>
|
|
42
45
|
</div>
|
|
43
46
|
</div>
|
|
44
47
|
);
|
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
import { useCallback,
|
|
2
|
-
import { useMoveTheWheels } from '../providers/MoveTheWheelsProvider';
|
|
3
|
-
import { executeGraphQL } from '../utils/graphqlClient';
|
|
1
|
+
import { useCallback, useState } from 'react';
|
|
4
2
|
|
|
5
3
|
export interface ImportExportResult {
|
|
6
4
|
success: boolean;
|
|
@@ -8,26 +6,6 @@ export interface ImportExportResult {
|
|
|
8
6
|
payload?: Record<string, unknown> | null;
|
|
9
7
|
}
|
|
10
8
|
|
|
11
|
-
const EXPORT_DATA_MUTATION = `
|
|
12
|
-
mutation MovethewheelsExportData($input: MovethewheelsExportDataInput!) {
|
|
13
|
-
exportData(input: $input) {
|
|
14
|
-
success
|
|
15
|
-
message
|
|
16
|
-
payload
|
|
17
|
-
}
|
|
18
|
-
}
|
|
19
|
-
`;
|
|
20
|
-
|
|
21
|
-
const IMPORT_DATA_MUTATION = `
|
|
22
|
-
mutation MovethewheelsImportData($input: MovethewheelsImportDataInput!) {
|
|
23
|
-
importData(input: $input) {
|
|
24
|
-
success
|
|
25
|
-
message
|
|
26
|
-
payload
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
`;
|
|
30
|
-
|
|
31
9
|
interface UseImportExportResult {
|
|
32
10
|
exportData: (resource: string, format?: string) => Promise<ImportExportResult>;
|
|
33
11
|
importData: (resource: string, payload: Record<string, unknown>) => Promise<ImportExportResult>;
|
|
@@ -35,66 +13,52 @@ interface UseImportExportResult {
|
|
|
35
13
|
error: Error | null;
|
|
36
14
|
}
|
|
37
15
|
|
|
16
|
+
/**
|
|
17
|
+
* Import/export surface withdrawn from wspace-movethewheels-svc by migration
|
|
18
|
+
* 20260707000001_movethewheels_reuse_cleanup. The service now exposes
|
|
19
|
+
* `exportMtwEntities` (async job, returns MtwExportJob) but no import
|
|
20
|
+
* counterpart, so the old synchronous exportData/importData mutations are no
|
|
21
|
+
* longer resolvable. This hook keeps the same contract for consumers but
|
|
22
|
+
* rejects calls with a clear message until a replacement import/export flow is
|
|
23
|
+
* designed. The original operations are preserved in
|
|
24
|
+
* src/operations/wspace/movethewheels/mutations/marketplace-reports.withdrawn.graphql.
|
|
25
|
+
*/
|
|
38
26
|
export function useImportExport(): UseImportExportResult {
|
|
39
|
-
const { apiGatewayUrl, authToken, workspaceToken, workspaceId, tenantId, currentUser } =
|
|
40
|
-
useMoveTheWheels();
|
|
41
27
|
const [isBusy, setIsBusy] = useState(false);
|
|
42
28
|
const [error, setError] = useState<Error | null>(null);
|
|
43
29
|
|
|
44
|
-
const
|
|
45
|
-
()
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
30
|
+
const withBusy = useCallback(async <T>(fn: () => Promise<T>): Promise<T> => {
|
|
31
|
+
setIsBusy(true);
|
|
32
|
+
setError(null);
|
|
33
|
+
try {
|
|
34
|
+
return await fn();
|
|
35
|
+
} catch (err) {
|
|
36
|
+
const normalized = err instanceof Error ? err : new Error('Import/export failed');
|
|
37
|
+
setError(normalized);
|
|
38
|
+
throw normalized;
|
|
39
|
+
} finally {
|
|
40
|
+
setIsBusy(false);
|
|
41
|
+
}
|
|
42
|
+
}, []);
|
|
55
43
|
|
|
56
44
|
const exportData = useCallback(
|
|
57
|
-
async (resource: string,
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
const data = await executeGraphQL<{ exportData: ImportExportResult }>(
|
|
62
|
-
graphQLOptions,
|
|
63
|
-
EXPORT_DATA_MUTATION,
|
|
64
|
-
{ input: { resource, format } }
|
|
45
|
+
async (resource: string, _format?: string): Promise<ImportExportResult> =>
|
|
46
|
+
withBusy(async () => {
|
|
47
|
+
throw new Error(
|
|
48
|
+
`Export for "${resource}" is not available: the import/export surface was withdrawn from the service.`
|
|
65
49
|
);
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
const normalized = fetchError instanceof Error ? fetchError : new Error('Export failed');
|
|
69
|
-
setError(normalized);
|
|
70
|
-
throw normalized;
|
|
71
|
-
} finally {
|
|
72
|
-
setIsBusy(false);
|
|
73
|
-
}
|
|
74
|
-
},
|
|
75
|
-
[graphQLOptions]
|
|
50
|
+
}),
|
|
51
|
+
[withBusy]
|
|
76
52
|
);
|
|
77
53
|
|
|
78
54
|
const importData = useCallback(
|
|
79
|
-
async (resource: string,
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
const data = await executeGraphQL<{ importData: ImportExportResult }>(
|
|
84
|
-
graphQLOptions,
|
|
85
|
-
IMPORT_DATA_MUTATION,
|
|
86
|
-
{ input: { resource, payload } }
|
|
55
|
+
async (resource: string, _payload: Record<string, unknown>): Promise<ImportExportResult> =>
|
|
56
|
+
withBusy(async () => {
|
|
57
|
+
throw new Error(
|
|
58
|
+
`Import for "${resource}" is not available: the import surface was withdrawn from the service.`
|
|
87
59
|
);
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
const normalized = fetchError instanceof Error ? fetchError : new Error('Import failed');
|
|
91
|
-
setError(normalized);
|
|
92
|
-
throw normalized;
|
|
93
|
-
} finally {
|
|
94
|
-
setIsBusy(false);
|
|
95
|
-
}
|
|
96
|
-
},
|
|
97
|
-
[graphQLOptions]
|
|
60
|
+
}),
|
|
61
|
+
[withBusy]
|
|
98
62
|
);
|
|
99
63
|
|
|
100
64
|
return { exportData, importData, isBusy, error };
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { useCallback, useEffect,
|
|
1
|
+
import { useCallback, useEffect, useState } from 'react';
|
|
2
2
|
import { useMoveTheWheels } from '../providers/MoveTheWheelsProvider';
|
|
3
|
-
import { executeGraphQL } from '../utils/graphqlClient';
|
|
4
3
|
|
|
5
4
|
export interface MovethewheelsReport {
|
|
6
5
|
id: string;
|
|
@@ -20,46 +19,6 @@ export interface GeneratedReport {
|
|
|
20
19
|
report: MovethewheelsReport;
|
|
21
20
|
}
|
|
22
21
|
|
|
23
|
-
const REPORT_FIELDS = `
|
|
24
|
-
id
|
|
25
|
-
name
|
|
26
|
-
description
|
|
27
|
-
type
|
|
28
|
-
status
|
|
29
|
-
schedule
|
|
30
|
-
lastGenerated
|
|
31
|
-
createdAt
|
|
32
|
-
updatedAt
|
|
33
|
-
`;
|
|
34
|
-
|
|
35
|
-
const LIST_REPORTS_QUERY = `
|
|
36
|
-
query MovethewheelsReports($filter: MovethewheelsReportFilterInput, $pagination: PaginationInput) {
|
|
37
|
-
movethewheelsReports(filter: $filter, pagination: $pagination) {
|
|
38
|
-
${REPORT_FIELDS}
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
`;
|
|
42
|
-
|
|
43
|
-
const CREATE_REPORT_MUTATION = `
|
|
44
|
-
mutation CreateMovethewheelsReport($input: CreateMovethewheelsReportInput!) {
|
|
45
|
-
createMovethewheelsReport(input: $input) {
|
|
46
|
-
${REPORT_FIELDS}
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
`;
|
|
50
|
-
|
|
51
|
-
const GENERATE_REPORT_MUTATION = `
|
|
52
|
-
mutation GenerateMovethewheelsReport($input: GenerateMovethewheelsReportInput!) {
|
|
53
|
-
generateMovethewheelsReport(input: $input) {
|
|
54
|
-
generatedAt
|
|
55
|
-
result
|
|
56
|
-
report {
|
|
57
|
-
${REPORT_FIELDS}
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
`;
|
|
62
|
-
|
|
63
22
|
export interface CreateReportInput {
|
|
64
23
|
name: string;
|
|
65
24
|
type: string;
|
|
@@ -87,44 +46,60 @@ interface UseReportsResult {
|
|
|
87
46
|
) => Promise<GeneratedReport>;
|
|
88
47
|
}
|
|
89
48
|
|
|
49
|
+
function newId(): string {
|
|
50
|
+
if (typeof crypto !== 'undefined' && 'randomUUID' in crypto) {
|
|
51
|
+
return crypto.randomUUID();
|
|
52
|
+
}
|
|
53
|
+
return `${Date.now()}-${Math.random().toString(36).slice(2)}`;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function storageKey(workspaceId: string | undefined): string {
|
|
57
|
+
return `movethewheels:reports:${workspaceId ?? 'default'}`;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function readReports(workspaceId: string | undefined): MovethewheelsReport[] {
|
|
61
|
+
if (typeof window === 'undefined') return [];
|
|
62
|
+
try {
|
|
63
|
+
const raw = window.localStorage.getItem(storageKey(workspaceId));
|
|
64
|
+
if (!raw) return [];
|
|
65
|
+
const parsed = JSON.parse(raw) as MovethewheelsReport[];
|
|
66
|
+
return Array.isArray(parsed) ? parsed : [];
|
|
67
|
+
} catch {
|
|
68
|
+
return [];
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function writeReports(workspaceId: string | undefined, reports: MovethewheelsReport[]): void {
|
|
73
|
+
if (typeof window === 'undefined') return;
|
|
74
|
+
try {
|
|
75
|
+
window.localStorage.setItem(storageKey(workspaceId), JSON.stringify(reports));
|
|
76
|
+
} catch {
|
|
77
|
+
// Ignore storage errors.
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
90
81
|
export function useReports(options: UseReportsOptions = {}): UseReportsResult {
|
|
91
82
|
const { limit = 100, offset = 0 } = options;
|
|
92
|
-
const {
|
|
93
|
-
useMoveTheWheels();
|
|
83
|
+
const { workspaceId } = useMoveTheWheels();
|
|
94
84
|
const [reports, setReports] = useState<MovethewheelsReport[]>([]);
|
|
95
85
|
const [hasMore, setHasMore] = useState(false);
|
|
96
86
|
const [isLoading, setIsLoading] = useState(false);
|
|
97
87
|
const [error, setError] = useState<Error | null>(null);
|
|
98
88
|
|
|
99
|
-
const
|
|
100
|
-
() => ({
|
|
101
|
-
apiGatewayUrl,
|
|
102
|
-
authToken,
|
|
103
|
-
workspaceToken,
|
|
104
|
-
workspaceId,
|
|
105
|
-
tenantId,
|
|
106
|
-
actorId: currentUser?.id,
|
|
107
|
-
}),
|
|
108
|
-
[apiGatewayUrl, authToken, workspaceToken, workspaceId, tenantId, currentUser?.id]
|
|
109
|
-
);
|
|
110
|
-
|
|
111
|
-
const load = useCallback(async () => {
|
|
89
|
+
const load = useCallback(() => {
|
|
112
90
|
setIsLoading(true);
|
|
113
91
|
setError(null);
|
|
114
92
|
try {
|
|
115
|
-
const
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
setReports(data.movethewheelsReports);
|
|
122
|
-
} catch (fetchError) {
|
|
123
|
-
setError(fetchError instanceof Error ? fetchError : new Error('Failed to load reports'));
|
|
93
|
+
const all = readReports(workspaceId);
|
|
94
|
+
const page = all.slice(offset, offset + limit);
|
|
95
|
+
setHasMore(page.length === limit);
|
|
96
|
+
setReports(page);
|
|
97
|
+
} catch (loadError) {
|
|
98
|
+
setError(loadError instanceof Error ? loadError : new Error('Failed to load reports'));
|
|
124
99
|
} finally {
|
|
125
100
|
setIsLoading(false);
|
|
126
101
|
}
|
|
127
|
-
}, [
|
|
102
|
+
}, [workspaceId, limit, offset]);
|
|
128
103
|
|
|
129
104
|
useEffect(() => {
|
|
130
105
|
void load();
|
|
@@ -136,33 +111,44 @@ export function useReports(options: UseReportsOptions = {}): UseReportsResult {
|
|
|
136
111
|
|
|
137
112
|
const createReport = useCallback(
|
|
138
113
|
async (input: CreateReportInput): Promise<MovethewheelsReport> => {
|
|
139
|
-
const
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
114
|
+
const now = new Date().toISOString();
|
|
115
|
+
const created: MovethewheelsReport = {
|
|
116
|
+
id: newId(),
|
|
117
|
+
name: input.name,
|
|
118
|
+
description: input.description ?? null,
|
|
119
|
+
type: input.type,
|
|
120
|
+
status: input.status ?? 'ready',
|
|
121
|
+
schedule: input.schedule ?? null,
|
|
122
|
+
lastGenerated: null,
|
|
123
|
+
createdAt: now,
|
|
124
|
+
updatedAt: now,
|
|
125
|
+
};
|
|
126
|
+
setReports((previous) => {
|
|
127
|
+
const next = [created, ...previous];
|
|
128
|
+
writeReports(workspaceId, next);
|
|
129
|
+
return next;
|
|
130
|
+
});
|
|
146
131
|
return created;
|
|
147
132
|
},
|
|
148
|
-
[
|
|
133
|
+
[workspaceId]
|
|
149
134
|
);
|
|
150
135
|
|
|
151
136
|
const generateReport = useCallback(
|
|
152
|
-
async (
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
GENERATE_REPORT_MUTATION,
|
|
156
|
-
{ input: { reportId, parameters } }
|
|
157
|
-
);
|
|
158
|
-
const generated = data.generateMovethewheelsReport;
|
|
159
|
-
setReports((previous) =>
|
|
160
|
-
previous.map((item) => (item.id === reportId ? generated.report : item))
|
|
137
|
+
async (_reportId: string, _parameters?: Record<string, unknown>): Promise<GeneratedReport> => {
|
|
138
|
+
throw new Error(
|
|
139
|
+
'Report generation is not available: the movethewheelsReports surface was withdrawn from the service.'
|
|
161
140
|
);
|
|
162
|
-
return generated;
|
|
163
141
|
},
|
|
164
|
-
[
|
|
142
|
+
[]
|
|
165
143
|
);
|
|
166
144
|
|
|
167
|
-
return {
|
|
145
|
+
return {
|
|
146
|
+
reports,
|
|
147
|
+
hasMore,
|
|
148
|
+
isLoading,
|
|
149
|
+
error,
|
|
150
|
+
refetch,
|
|
151
|
+
createReport,
|
|
152
|
+
generateReport,
|
|
153
|
+
};
|
|
168
154
|
}
|