@burdenoff/microfe-movethewheels 2026.1026.1 → 2026.1026.3
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 +126 -159
- package/dist/MoveTheWheelsRoutes.js +16 -32
- package/dist/components/CustomerPicker.js +2 -2
- package/dist/hooks/index.js +0 -3
- package/dist/index.js +27 -30
- package/dist/movethewheels/hooks/index.d.ts +0 -3
- package/dist/pages/CreateOrderPage.js +1 -1
- package/package.json +1 -1
- package/src/movethewheels/MoveTheWheelsRoot.tsx +2 -46
- package/src/movethewheels/MoveTheWheelsRoutes.tsx +0 -16
- package/src/movethewheels/components/CustomerPicker.tsx +2 -2
- package/src/movethewheels/hooks/index.ts +0 -9
- package/src/movethewheels/pages/CreateOrderPage.tsx +1 -1
- package/dist/hooks/useImportExport.js +0 -27
- package/dist/hooks/useReports.js +0 -75
- package/dist/hooks/useSettings.js +0 -67
- package/dist/movethewheels/hooks/useImportExport.d.ts +0 -23
- package/dist/movethewheels/hooks/useReports.d.ts +0 -39
- package/dist/movethewheels/hooks/useSettings.d.ts +0 -17
- package/dist/movethewheels/pages/ImportExportPage.d.ts +0 -8
- package/dist/movethewheels/pages/MarketplacePage.d.ts +0 -12
- package/dist/movethewheels/pages/ReportsPage.d.ts +0 -8
- package/dist/movethewheels/pages/SettingsPage.d.ts +0 -8
- package/dist/node_modules/lucide-react/dist/esm/icons/bell.js +0 -11
- package/dist/node_modules/lucide-react/dist/esm/icons/file-chart-column-increasing.js +0 -26
- package/dist/node_modules/lucide-react/dist/esm/icons/file-spreadsheet.js +0 -30
- package/dist/node_modules/lucide-react/dist/esm/icons/globe.js +0 -20
- package/dist/node_modules/lucide-react/dist/esm/icons/settings.js +0 -13
- package/dist/node_modules/lucide-react/dist/esm/icons/shield.js +0 -8
- package/dist/node_modules/lucide-react/dist/esm/icons/store.js +0 -26
- package/dist/node_modules/lucide-react/dist/esm/icons/webhook.js +0 -18
- package/dist/pages/ImportExportPage.js +0 -245
- package/dist/pages/MarketplacePage.js +0 -23
- package/dist/pages/ReportsPage.js +0 -328
- package/dist/pages/SettingsPage.js +0 -404
- package/dist/utils/download.js +0 -22
- package/src/movethewheels/hooks/useImportExport.ts +0 -65
- package/src/movethewheels/hooks/useReports.ts +0 -154
- package/src/movethewheels/hooks/useSettings.ts +0 -124
- package/src/movethewheels/pages/ImportExportPage.tsx +0 -288
- package/src/movethewheels/pages/MarketplacePage.tsx +0 -36
- package/src/movethewheels/pages/ReportsPage.tsx +0 -379
- package/src/movethewheels/pages/SettingsPage.tsx +0 -548
- package/src/operations/wspace/movethewheels/fragments/core.withdrawn.graphql +0 -59
- package/src/operations/wspace/movethewheels/mutations/marketplace-reports.withdrawn.graphql +0 -93
- package/src/operations/wspace/movethewheels/queries/marketplace-reports.withdrawn.graphql +0 -58
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
import { useCallback, useEffect, useMemo, useState } from 'react';
|
|
2
|
-
import { useMoveTheWheels } from '../providers/MoveTheWheelsProvider';
|
|
3
|
-
|
|
4
|
-
export type ModulePreferences = Record<string, unknown>;
|
|
5
|
-
|
|
6
|
-
export interface ModuleSettings {
|
|
7
|
-
preferences: ModulePreferences;
|
|
8
|
-
updatedAt: string;
|
|
9
|
-
workspaceId: string;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
interface UseModuleSettingsResult {
|
|
13
|
-
settings: ModuleSettings | null;
|
|
14
|
-
preferences: ModulePreferences;
|
|
15
|
-
isLoading: boolean;
|
|
16
|
-
isSaving: boolean;
|
|
17
|
-
error: Error | null;
|
|
18
|
-
refetch: () => void;
|
|
19
|
-
savePreferences: (preferences: ModulePreferences) => Promise<ModuleSettings>;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
function readSettings(workspaceId: string): ModuleSettings | null {
|
|
23
|
-
if (typeof window === 'undefined') return null;
|
|
24
|
-
try {
|
|
25
|
-
const raw = window.localStorage.getItem(`movethewheels:moduleSettings:${workspaceId}`);
|
|
26
|
-
if (!raw) return null;
|
|
27
|
-
const parsed = JSON.parse(raw) as ModuleSettings;
|
|
28
|
-
if (parsed && typeof parsed === 'object' && 'preferences' in parsed) {
|
|
29
|
-
return parsed;
|
|
30
|
-
}
|
|
31
|
-
return null;
|
|
32
|
-
} catch {
|
|
33
|
-
return null;
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
function writeSettings(settings: ModuleSettings): void {
|
|
38
|
-
if (typeof window === 'undefined') return;
|
|
39
|
-
try {
|
|
40
|
-
window.localStorage.setItem(
|
|
41
|
-
`movethewheels:moduleSettings:${settings.workspaceId}`,
|
|
42
|
-
JSON.stringify(settings)
|
|
43
|
-
);
|
|
44
|
-
} catch {
|
|
45
|
-
// Ignore storage errors (e.g. private mode quota).
|
|
46
|
-
}
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
export function useModuleSettings(): UseModuleSettingsResult {
|
|
50
|
-
const { workspaceId } = useMoveTheWheels();
|
|
51
|
-
const [settings, setSettings] = useState<ModuleSettings | null>(null);
|
|
52
|
-
const [isLoading, setIsLoading] = useState(false);
|
|
53
|
-
const [isSaving, setIsSaving] = useState(false);
|
|
54
|
-
const [error, setError] = useState<Error | null>(null);
|
|
55
|
-
|
|
56
|
-
const load = useCallback(() => {
|
|
57
|
-
setIsLoading(true);
|
|
58
|
-
setError(null);
|
|
59
|
-
try {
|
|
60
|
-
const stored = workspaceId ? readSettings(workspaceId) : null;
|
|
61
|
-
setSettings(
|
|
62
|
-
stored ?? {
|
|
63
|
-
preferences: {},
|
|
64
|
-
updatedAt: new Date().toISOString(),
|
|
65
|
-
workspaceId: workspaceId ?? '',
|
|
66
|
-
}
|
|
67
|
-
);
|
|
68
|
-
} catch (loadError) {
|
|
69
|
-
setError(loadError instanceof Error ? loadError : new Error('Failed to load settings'));
|
|
70
|
-
} finally {
|
|
71
|
-
setIsLoading(false);
|
|
72
|
-
}
|
|
73
|
-
}, [workspaceId]);
|
|
74
|
-
|
|
75
|
-
useEffect(() => {
|
|
76
|
-
void load();
|
|
77
|
-
}, [load]);
|
|
78
|
-
|
|
79
|
-
const refetch = useCallback(() => {
|
|
80
|
-
void load();
|
|
81
|
-
}, [load]);
|
|
82
|
-
|
|
83
|
-
const savePreferences = useCallback(
|
|
84
|
-
async (preferences: ModulePreferences): Promise<ModuleSettings> => {
|
|
85
|
-
setIsSaving(true);
|
|
86
|
-
setError(null);
|
|
87
|
-
try {
|
|
88
|
-
const next: ModuleSettings = {
|
|
89
|
-
preferences,
|
|
90
|
-
updatedAt: new Date().toISOString(),
|
|
91
|
-
workspaceId: workspaceId ?? '',
|
|
92
|
-
};
|
|
93
|
-
writeSettings(next);
|
|
94
|
-
setSettings(next);
|
|
95
|
-
return next;
|
|
96
|
-
} catch (saveError) {
|
|
97
|
-
const normalized =
|
|
98
|
-
saveError instanceof Error ? saveError : new Error('Failed to save settings');
|
|
99
|
-
setError(normalized);
|
|
100
|
-
throw normalized;
|
|
101
|
-
} finally {
|
|
102
|
-
setIsSaving(false);
|
|
103
|
-
}
|
|
104
|
-
},
|
|
105
|
-
[workspaceId]
|
|
106
|
-
);
|
|
107
|
-
|
|
108
|
-
// Stabilize the preferences reference. Returning `settings?.preferences ?? {}`
|
|
109
|
-
// inline minted a fresh `{}` on every render while settings was null, which
|
|
110
|
-
// made SettingsPage's `useMemo(toForm, [preferences])` recompute every render
|
|
111
|
-
// and its `useEffect(setForm, [loadedForm])` fire in a loop → "Maximum update
|
|
112
|
-
// depth exceeded" → the page rendered blank behind the shell's error boundary.
|
|
113
|
-
const preferences = useMemo<ModulePreferences>(() => settings?.preferences ?? {}, [settings]);
|
|
114
|
-
|
|
115
|
-
return {
|
|
116
|
-
settings,
|
|
117
|
-
preferences,
|
|
118
|
-
isLoading,
|
|
119
|
-
isSaving,
|
|
120
|
-
error,
|
|
121
|
-
refetch,
|
|
122
|
-
savePreferences,
|
|
123
|
-
};
|
|
124
|
-
}
|
|
@@ -1,288 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Import/Export Page
|
|
3
|
-
*
|
|
4
|
-
* Bulk data in/out wired to the backend exportData / importData mutations.
|
|
5
|
-
* Activity is session-local (reflects actions the operator actually ran) — no
|
|
6
|
-
* fabricated history is shown; the empty state renders until something happens.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import { useRef, useState } from 'react';
|
|
10
|
-
import { Upload, Download, FileText, FileSpreadsheet, Check, X } from 'lucide-react';
|
|
11
|
-
import { PageLayout } from '../components/PageLayout';
|
|
12
|
-
import { Card, CardContent, CardHeader, CardTitle, Button, Badge } from '../components/ui';
|
|
13
|
-
import { EmphasisPanel, IllustratedEmptyState, PagePurpose } from '@burdenoff/fe-libs/ui';
|
|
14
|
-
import { useImportExport } from '../hooks/useImportExport';
|
|
15
|
-
|
|
16
|
-
interface ImportOption {
|
|
17
|
-
name: string;
|
|
18
|
-
description: string;
|
|
19
|
-
resource: string;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
interface ExportOption {
|
|
23
|
-
name: string;
|
|
24
|
-
description: string;
|
|
25
|
-
resource: string;
|
|
26
|
-
formats: string[];
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
interface ActivityEntry {
|
|
30
|
-
type: 'import' | 'export';
|
|
31
|
-
name: string;
|
|
32
|
-
at: Date;
|
|
33
|
-
success: boolean;
|
|
34
|
-
message: string;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
const importOptions: ImportOption[] = [
|
|
38
|
-
{ name: 'Orders', description: 'Import orders from CSV or JSON', resource: 'orders' },
|
|
39
|
-
{ name: 'Customers', description: 'Import customer database', resource: 'customers' },
|
|
40
|
-
{ name: 'Products', description: 'Import product catalog', resource: 'products' },
|
|
41
|
-
{ name: 'Inventory', description: 'Import inventory levels', resource: 'inventory' },
|
|
42
|
-
];
|
|
43
|
-
|
|
44
|
-
const exportOptions: ExportOption[] = [
|
|
45
|
-
{
|
|
46
|
-
name: 'Orders Report',
|
|
47
|
-
description: 'Export all orders data',
|
|
48
|
-
resource: 'orders',
|
|
49
|
-
formats: ['CSV', 'JSON'],
|
|
50
|
-
},
|
|
51
|
-
{
|
|
52
|
-
name: 'Financial Report',
|
|
53
|
-
description: 'Export revenue and payments',
|
|
54
|
-
resource: 'finance',
|
|
55
|
-
formats: ['CSV', 'JSON'],
|
|
56
|
-
},
|
|
57
|
-
{
|
|
58
|
-
name: 'Fleet Report',
|
|
59
|
-
description: 'Export vehicle and driver data',
|
|
60
|
-
resource: 'fleet',
|
|
61
|
-
formats: ['CSV', 'JSON'],
|
|
62
|
-
},
|
|
63
|
-
{
|
|
64
|
-
name: 'Analytics Report',
|
|
65
|
-
description: 'Export performance metrics',
|
|
66
|
-
resource: 'analytics',
|
|
67
|
-
formats: ['CSV', 'JSON'],
|
|
68
|
-
},
|
|
69
|
-
];
|
|
70
|
-
|
|
71
|
-
export default function ImportExportPage() {
|
|
72
|
-
const { exportData, importData, isBusy } = useImportExport();
|
|
73
|
-
const [activity, setActivity] = useState<ActivityEntry[]>([]);
|
|
74
|
-
const [pendingResource, setPendingResource] = useState<string | null>(null);
|
|
75
|
-
const fileInputRef = useRef<HTMLInputElement | null>(null);
|
|
76
|
-
const importTargetRef = useRef<ImportOption | null>(null);
|
|
77
|
-
|
|
78
|
-
const record = (entry: ActivityEntry) => setActivity((previous) => [entry, ...previous]);
|
|
79
|
-
|
|
80
|
-
const handleExport = async (option: ExportOption, format: string) => {
|
|
81
|
-
setPendingResource(`${option.resource}:${format}`);
|
|
82
|
-
try {
|
|
83
|
-
const result = await exportData(option.resource, format);
|
|
84
|
-
if (result.success && result.payload) {
|
|
85
|
-
const contents = JSON.stringify(result.payload, null, 2);
|
|
86
|
-
const blob = new Blob([contents], { type: 'application/json' });
|
|
87
|
-
const url = URL.createObjectURL(blob);
|
|
88
|
-
const link = document.createElement('a');
|
|
89
|
-
link.href = url;
|
|
90
|
-
link.download = `${option.resource}-export.${format.toLowerCase() === 'csv' ? 'csv' : 'json'}`;
|
|
91
|
-
document.body.appendChild(link);
|
|
92
|
-
link.click();
|
|
93
|
-
link.remove();
|
|
94
|
-
URL.revokeObjectURL(url);
|
|
95
|
-
}
|
|
96
|
-
record({
|
|
97
|
-
type: 'export',
|
|
98
|
-
name: `${option.name} (${format})`,
|
|
99
|
-
at: new Date(),
|
|
100
|
-
success: result.success,
|
|
101
|
-
message: result.message,
|
|
102
|
-
});
|
|
103
|
-
} catch (exportError) {
|
|
104
|
-
record({
|
|
105
|
-
type: 'export',
|
|
106
|
-
name: `${option.name} (${format})`,
|
|
107
|
-
at: new Date(),
|
|
108
|
-
success: false,
|
|
109
|
-
message: exportError instanceof Error ? exportError.message : 'Export failed',
|
|
110
|
-
});
|
|
111
|
-
} finally {
|
|
112
|
-
setPendingResource(null);
|
|
113
|
-
}
|
|
114
|
-
};
|
|
115
|
-
|
|
116
|
-
const triggerImport = (option: ImportOption) => {
|
|
117
|
-
importTargetRef.current = option;
|
|
118
|
-
fileInputRef.current?.click();
|
|
119
|
-
};
|
|
120
|
-
|
|
121
|
-
const handleFileSelected = async (event: React.ChangeEvent<HTMLInputElement>) => {
|
|
122
|
-
const file = event.target.files?.[0];
|
|
123
|
-
const option = importTargetRef.current;
|
|
124
|
-
event.target.value = '';
|
|
125
|
-
if (!file || !option) return;
|
|
126
|
-
setPendingResource(`import:${option.resource}`);
|
|
127
|
-
try {
|
|
128
|
-
const content = await file.text();
|
|
129
|
-
const result = await importData(option.resource, { filename: file.name, content });
|
|
130
|
-
record({
|
|
131
|
-
type: 'import',
|
|
132
|
-
name: `${option.name} — ${file.name}`,
|
|
133
|
-
at: new Date(),
|
|
134
|
-
success: result.success,
|
|
135
|
-
message: result.message,
|
|
136
|
-
});
|
|
137
|
-
} catch (importError) {
|
|
138
|
-
record({
|
|
139
|
-
type: 'import',
|
|
140
|
-
name: `${option.name} — ${file.name}`,
|
|
141
|
-
at: new Date(),
|
|
142
|
-
success: false,
|
|
143
|
-
message: importError instanceof Error ? importError.message : 'Import failed',
|
|
144
|
-
});
|
|
145
|
-
} finally {
|
|
146
|
-
setPendingResource(null);
|
|
147
|
-
}
|
|
148
|
-
};
|
|
149
|
-
|
|
150
|
-
return (
|
|
151
|
-
<PageLayout
|
|
152
|
-
title="Import / Export"
|
|
153
|
-
subtitle="Import and export your data"
|
|
154
|
-
icon={<FileText size={24} />}
|
|
155
|
-
>
|
|
156
|
-
<PagePurpose icon={<FileText size={18} />}>
|
|
157
|
-
Move data in and out in bulk. Import orders, customers, products or inventory from CSV/JSON
|
|
158
|
-
to onboard or migrate without re-keying, and export any dataset for accounting, BI tools or
|
|
159
|
-
backups — no manual copy-paste required.
|
|
160
|
-
</PagePurpose>
|
|
161
|
-
|
|
162
|
-
<input
|
|
163
|
-
ref={fileInputRef}
|
|
164
|
-
type="file"
|
|
165
|
-
accept=".csv,.json,application/json,text/csv"
|
|
166
|
-
className="hidden"
|
|
167
|
-
onChange={handleFileSelected}
|
|
168
|
-
/>
|
|
169
|
-
|
|
170
|
-
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
|
171
|
-
{/* Import Data — the page's single emphasis zone */}
|
|
172
|
-
<EmphasisPanel className="space-y-4">
|
|
173
|
-
<h2 className="flex items-center gap-2 text-base font-semibold text-text-primary">
|
|
174
|
-
<Upload size={18} /> Import Data
|
|
175
|
-
</h2>
|
|
176
|
-
<div className="space-y-4">
|
|
177
|
-
{importOptions.map((opt) => (
|
|
178
|
-
<div
|
|
179
|
-
key={opt.name}
|
|
180
|
-
className="flex items-center justify-between p-4 border rounded-lg hover:border-primary/50 transition-colors"
|
|
181
|
-
>
|
|
182
|
-
<div className="flex items-center gap-3">
|
|
183
|
-
<div className="w-10 h-10 rounded-lg bg-action-primary-bg/10 flex items-center justify-center">
|
|
184
|
-
<FileSpreadsheet size={20} className="text-primary" />
|
|
185
|
-
</div>
|
|
186
|
-
<div>
|
|
187
|
-
<p className="font-medium">{opt.name}</p>
|
|
188
|
-
<p className="text-sm text-text-secondary">{opt.description}</p>
|
|
189
|
-
</div>
|
|
190
|
-
</div>
|
|
191
|
-
<div className="text-right">
|
|
192
|
-
<Badge variant="outline">CSV, JSON</Badge>
|
|
193
|
-
<Button
|
|
194
|
-
variant="outline"
|
|
195
|
-
size="sm"
|
|
196
|
-
className="ml-2"
|
|
197
|
-
aria-label={`Import ${opt.name}`}
|
|
198
|
-
disabled={isBusy}
|
|
199
|
-
onClick={() => triggerImport(opt)}
|
|
200
|
-
>
|
|
201
|
-
<Upload size={14} />
|
|
202
|
-
</Button>
|
|
203
|
-
</div>
|
|
204
|
-
</div>
|
|
205
|
-
))}
|
|
206
|
-
</div>
|
|
207
|
-
</EmphasisPanel>
|
|
208
|
-
|
|
209
|
-
<Card>
|
|
210
|
-
<CardHeader>
|
|
211
|
-
<CardTitle className="flex items-center gap-2">
|
|
212
|
-
<Download size={18} /> Export Data
|
|
213
|
-
</CardTitle>
|
|
214
|
-
</CardHeader>
|
|
215
|
-
<CardContent className="space-y-4">
|
|
216
|
-
{exportOptions.map((opt) => (
|
|
217
|
-
<div
|
|
218
|
-
key={opt.name}
|
|
219
|
-
className="flex items-center justify-between p-4 border rounded-lg hover:border-primary/50 transition-colors"
|
|
220
|
-
>
|
|
221
|
-
<div>
|
|
222
|
-
<p className="font-medium">{opt.name}</p>
|
|
223
|
-
<p className="text-sm text-text-secondary">{opt.description}</p>
|
|
224
|
-
</div>
|
|
225
|
-
<div className="flex gap-1">
|
|
226
|
-
{opt.formats.map((f) => (
|
|
227
|
-
<Button
|
|
228
|
-
key={f}
|
|
229
|
-
variant="outline"
|
|
230
|
-
size="sm"
|
|
231
|
-
disabled={pendingResource === `${opt.resource}:${f}`}
|
|
232
|
-
onClick={() => void handleExport(opt, f)}
|
|
233
|
-
>
|
|
234
|
-
{f}
|
|
235
|
-
</Button>
|
|
236
|
-
))}
|
|
237
|
-
</div>
|
|
238
|
-
</div>
|
|
239
|
-
))}
|
|
240
|
-
</CardContent>
|
|
241
|
-
</Card>
|
|
242
|
-
</div>
|
|
243
|
-
|
|
244
|
-
<Card>
|
|
245
|
-
<CardHeader>
|
|
246
|
-
<CardTitle>Recent Activity</CardTitle>
|
|
247
|
-
</CardHeader>
|
|
248
|
-
<CardContent>
|
|
249
|
-
{activity.length === 0 ? (
|
|
250
|
-
<IllustratedEmptyState
|
|
251
|
-
illustration="empty-data"
|
|
252
|
-
title="No import or export activity yet"
|
|
253
|
-
description="Your recent imports and exports will show up here once you run one."
|
|
254
|
-
/>
|
|
255
|
-
) : (
|
|
256
|
-
<div className="space-y-3">
|
|
257
|
-
{activity.map((entry, i) => (
|
|
258
|
-
<div
|
|
259
|
-
key={i}
|
|
260
|
-
className="flex items-center justify-between p-3 bg-bg-sunken/50 rounded-lg"
|
|
261
|
-
>
|
|
262
|
-
<div className="flex items-center gap-3">
|
|
263
|
-
{entry.type === 'import' ? (
|
|
264
|
-
<Upload size={16} className="text-primary" />
|
|
265
|
-
) : (
|
|
266
|
-
<Download size={16} className="text-primary" />
|
|
267
|
-
)}
|
|
268
|
-
<div>
|
|
269
|
-
<p className="font-medium">{entry.name}</p>
|
|
270
|
-
<p className="text-sm text-text-secondary">
|
|
271
|
-
{entry.type === 'import' ? 'Imported' : 'Exported'} at{' '}
|
|
272
|
-
{entry.at.toLocaleTimeString()} — {entry.message}
|
|
273
|
-
</p>
|
|
274
|
-
</div>
|
|
275
|
-
</div>
|
|
276
|
-
<Badge variant={entry.success ? 'default' : 'destructive'} className="gap-1">
|
|
277
|
-
{entry.success ? <Check size={12} /> : <X size={12} />}
|
|
278
|
-
{entry.success ? 'done' : 'failed'}
|
|
279
|
-
</Badge>
|
|
280
|
-
</div>
|
|
281
|
-
))}
|
|
282
|
-
</div>
|
|
283
|
-
)}
|
|
284
|
-
</CardContent>
|
|
285
|
-
</Card>
|
|
286
|
-
</PageLayout>
|
|
287
|
-
);
|
|
288
|
-
}
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Marketplace Page
|
|
3
|
-
*
|
|
4
|
-
* The collaborative logistics marketplace (service providers / service offerings)
|
|
5
|
-
* was withdrawn from wspace-movethewheels-svc by migration
|
|
6
|
-
* 20260707000001_movethewheels_reuse_cleanup. The original query and mutation
|
|
7
|
-
* documents are preserved verbatim in
|
|
8
|
-
* src/operations/wspace/movethewheels/{queries,mutations}/marketplace-reports.withdrawn.graphql
|
|
9
|
-
* so they can be restored if the surface returns. Until then the page renders a
|
|
10
|
-
* static "coming soon" state.
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
import { Store } from 'lucide-react';
|
|
14
|
-
import { PageLayout } from '../components/PageLayout';
|
|
15
|
-
import { Card, CardContent } from '../components/ui';
|
|
16
|
-
import { IllustratedEmptyState } from '@burdenoff/fe-libs/ui';
|
|
17
|
-
|
|
18
|
-
export default function MarketplacePage() {
|
|
19
|
-
return (
|
|
20
|
-
<PageLayout
|
|
21
|
-
title="Marketplace"
|
|
22
|
-
subtitle="Find logistics service providers"
|
|
23
|
-
icon={<Store size={24} />}
|
|
24
|
-
>
|
|
25
|
-
<Card>
|
|
26
|
-
<CardContent className="p-0">
|
|
27
|
-
<IllustratedEmptyState
|
|
28
|
-
illustration="empty-people"
|
|
29
|
-
title="Marketplace is coming soon"
|
|
30
|
-
description="The collaborative logistics marketplace is not open yet. Check back after it launches to browse verified third-party service providers."
|
|
31
|
-
/>
|
|
32
|
-
</CardContent>
|
|
33
|
-
</Card>
|
|
34
|
-
</PageLayout>
|
|
35
|
-
);
|
|
36
|
-
}
|