@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.
Files changed (46) hide show
  1. package/dist/MoveTheWheelsRoot.js +126 -159
  2. package/dist/MoveTheWheelsRoutes.js +16 -32
  3. package/dist/components/CustomerPicker.js +2 -2
  4. package/dist/hooks/index.js +0 -3
  5. package/dist/index.js +27 -30
  6. package/dist/movethewheels/hooks/index.d.ts +0 -3
  7. package/dist/pages/CreateOrderPage.js +1 -1
  8. package/package.json +1 -1
  9. package/src/movethewheels/MoveTheWheelsRoot.tsx +2 -46
  10. package/src/movethewheels/MoveTheWheelsRoutes.tsx +0 -16
  11. package/src/movethewheels/components/CustomerPicker.tsx +2 -2
  12. package/src/movethewheels/hooks/index.ts +0 -9
  13. package/src/movethewheels/pages/CreateOrderPage.tsx +1 -1
  14. package/dist/hooks/useImportExport.js +0 -27
  15. package/dist/hooks/useReports.js +0 -75
  16. package/dist/hooks/useSettings.js +0 -67
  17. package/dist/movethewheels/hooks/useImportExport.d.ts +0 -23
  18. package/dist/movethewheels/hooks/useReports.d.ts +0 -39
  19. package/dist/movethewheels/hooks/useSettings.d.ts +0 -17
  20. package/dist/movethewheels/pages/ImportExportPage.d.ts +0 -8
  21. package/dist/movethewheels/pages/MarketplacePage.d.ts +0 -12
  22. package/dist/movethewheels/pages/ReportsPage.d.ts +0 -8
  23. package/dist/movethewheels/pages/SettingsPage.d.ts +0 -8
  24. package/dist/node_modules/lucide-react/dist/esm/icons/bell.js +0 -11
  25. package/dist/node_modules/lucide-react/dist/esm/icons/file-chart-column-increasing.js +0 -26
  26. package/dist/node_modules/lucide-react/dist/esm/icons/file-spreadsheet.js +0 -30
  27. package/dist/node_modules/lucide-react/dist/esm/icons/globe.js +0 -20
  28. package/dist/node_modules/lucide-react/dist/esm/icons/settings.js +0 -13
  29. package/dist/node_modules/lucide-react/dist/esm/icons/shield.js +0 -8
  30. package/dist/node_modules/lucide-react/dist/esm/icons/store.js +0 -26
  31. package/dist/node_modules/lucide-react/dist/esm/icons/webhook.js +0 -18
  32. package/dist/pages/ImportExportPage.js +0 -245
  33. package/dist/pages/MarketplacePage.js +0 -23
  34. package/dist/pages/ReportsPage.js +0 -328
  35. package/dist/pages/SettingsPage.js +0 -404
  36. package/dist/utils/download.js +0 -22
  37. package/src/movethewheels/hooks/useImportExport.ts +0 -65
  38. package/src/movethewheels/hooks/useReports.ts +0 -154
  39. package/src/movethewheels/hooks/useSettings.ts +0 -124
  40. package/src/movethewheels/pages/ImportExportPage.tsx +0 -288
  41. package/src/movethewheels/pages/MarketplacePage.tsx +0 -36
  42. package/src/movethewheels/pages/ReportsPage.tsx +0 -379
  43. package/src/movethewheels/pages/SettingsPage.tsx +0 -548
  44. package/src/operations/wspace/movethewheels/fragments/core.withdrawn.graphql +0 -59
  45. package/src/operations/wspace/movethewheels/mutations/marketplace-reports.withdrawn.graphql +0 -93
  46. package/src/operations/wspace/movethewheels/queries/marketplace-reports.withdrawn.graphql +0 -58
@@ -1,379 +0,0 @@
1
- /**
2
- * Reports Page
3
- *
4
- * Lists reports from the backend (movethewheelsReports) and lets operators
5
- * create, schedule and generate them via the real mutations. Pre-launch
6
- * workspaces start empty — no fabricated report history is shown.
7
- */
8
-
9
- import { useState } from 'react';
10
- import {
11
- FileBarChart,
12
- Download,
13
- Calendar,
14
- Filter,
15
- Clock,
16
- Plus,
17
- ChevronLeft,
18
- ChevronRight,
19
- } from 'lucide-react';
20
- import { PageLayout } from '../components/PageLayout';
21
- import {
22
- Card,
23
- CardContent,
24
- CardHeader,
25
- CardTitle,
26
- Button,
27
- Badge,
28
- Select,
29
- SelectContent,
30
- SelectItem,
31
- SelectTrigger,
32
- SelectValue,
33
- Skeleton,
34
- } from '../components/ui';
35
- import { EmphasisPanel, IllustratedEmptyState, PagePurpose } from '@burdenoff/fe-libs/ui';
36
- import { VolumeBar } from '../components/charts';
37
- import { useReports, type CreateReportInput } from '../hooks/useReports';
38
- import { useAnalytics } from '../hooks/useAnalytics';
39
- import { useDemandForecast } from '../hooks/useDemandForecast';
40
- import { UpcomingDemandPanel } from '../components/UpcomingDemandPanel';
41
- import { formatDate } from '../utils/formatters';
42
- import { downloadFile, downloadJson, recordToCsv, slugifyForFilename } from '../utils/download';
43
-
44
- const REPORT_TYPES: { value: string; label: string }[] = [
45
- { value: 'operations', label: 'Operations Summary' },
46
- { value: 'finance', label: 'Financial Report' },
47
- { value: 'fleet', label: 'Fleet Utilization' },
48
- { value: 'customers', label: 'Customer Analytics' },
49
- { value: 'inventory', label: 'Inventory Status' },
50
- ];
51
-
52
- interface Feedback {
53
- tone: 'success' | 'error';
54
- text: string;
55
- }
56
-
57
- const REPORTS_PAGE_SIZE = 10;
58
-
59
- export default function ReportsPage() {
60
- const [page, setPage] = useState(0);
61
- const { reports, hasMore, isLoading, error, refetch, createReport, generateReport } = useReports({
62
- limit: REPORTS_PAGE_SIZE,
63
- offset: page * REPORTS_PAGE_SIZE,
64
- });
65
- const { analytics } = useAnalytics();
66
- const previewData = analytics.trends.dailyOrders.slice(-7);
67
- const {
68
- laneForecast,
69
- customerForecast,
70
- laneVariance,
71
- customerVariance,
72
- isLoading: forecastLoading,
73
- error: forecastError,
74
- refetch: refetchForecast,
75
- } = useDemandForecast(4, 8);
76
- const [reportType, setReportType] = useState('operations');
77
- const [busyId, setBusyId] = useState<string | null>(null);
78
- const [creating, setCreating] = useState(false);
79
- const [feedback, setFeedback] = useState<Feedback | null>(null);
80
-
81
- const typeLabel = (value: string) =>
82
- REPORT_TYPES.find((item) => item.value === value)?.label ?? value;
83
-
84
- const handleCreate = async (schedule?: string) => {
85
- setCreating(true);
86
- setFeedback(null);
87
- const input: CreateReportInput = {
88
- name: `${typeLabel(reportType)}${schedule ? ' (Scheduled)' : ''}`,
89
- type: reportType,
90
- description: `${typeLabel(reportType)} generated from live operational data`,
91
- ...(schedule ? { schedule } : {}),
92
- };
93
- try {
94
- await createReport(input);
95
- setFeedback({
96
- tone: 'success',
97
- text: schedule ? 'Scheduled report created.' : 'Report created.',
98
- });
99
- } catch (createError) {
100
- setFeedback({
101
- tone: 'error',
102
- text: createError instanceof Error ? createError.message : 'Could not create report.',
103
- });
104
- } finally {
105
- setCreating(false);
106
- }
107
- };
108
-
109
- const handleGenerate = async (id: string) => {
110
- setBusyId(id);
111
- setFeedback(null);
112
- try {
113
- const generated = await generateReport(id);
114
- // Turn the freshly generated report into a downloadable file. Prefer a
115
- // flat CSV of the result payload; fall back to the full JSON document so a
116
- // file always lands even when the result is nested/empty.
117
- const baseName = `${slugifyForFilename(generated.report.name)}-${generated.generatedAt.slice(
118
- 0,
119
- 10
120
- )}`;
121
- const csv = recordToCsv(generated.result ?? null);
122
- if (csv) {
123
- downloadFile(`${baseName}.csv`, csv, 'text/csv');
124
- } else {
125
- downloadJson(`${baseName}.json`, generated);
126
- }
127
- setFeedback({ tone: 'success', text: 'Report generated and downloaded.' });
128
- } catch (genError) {
129
- setFeedback({
130
- tone: 'error',
131
- text: genError instanceof Error ? genError.message : 'Could not generate report.',
132
- });
133
- } finally {
134
- setBusyId(null);
135
- }
136
- };
137
-
138
- const scheduledReports = reports.filter((report) => report.schedule);
139
-
140
- return (
141
- <PageLayout
142
- title="Reports"
143
- subtitle="Generate and schedule reports"
144
- icon={<FileBarChart size={24} />}
145
- headerActions={
146
- <div className="flex gap-2">
147
- <Select value={reportType} onValueChange={setReportType}>
148
- <SelectTrigger className="w-[190px]">
149
- <SelectValue />
150
- </SelectTrigger>
151
- <SelectContent>
152
- {REPORT_TYPES.map((item) => (
153
- <SelectItem key={item.value} value={item.value}>
154
- {item.label}
155
- </SelectItem>
156
- ))}
157
- </SelectContent>
158
- </Select>
159
- <Button onClick={() => void handleCreate()} disabled={creating}>
160
- <Plus size={16} /> Create Report
161
- </Button>
162
- </div>
163
- }
164
- >
165
- <PagePurpose icon={<FileBarChart size={18} />}>
166
- Turn your operational data into shareable summaries. Generate one-off reports on orders,
167
- finance, fleet, customers or inventory, or schedule them to land in inboxes automatically —
168
- so stakeholders always have the latest numbers without logging in.
169
- </PagePurpose>
170
-
171
- {feedback && (
172
- <div
173
- role="status"
174
- className={`rounded-lg p-3 text-sm ${
175
- feedback.tone === 'success'
176
- ? 'bg-status-success-bg-subtle text-status-success-text'
177
- : 'bg-status-error-bg-subtle text-status-error-text'
178
- }`}
179
- >
180
- {feedback.text}
181
- </div>
182
- )}
183
-
184
- <div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
185
- <div className="lg:col-span-2 space-y-4">
186
- <h3 className="font-semibold">Available Reports</h3>
187
-
188
- {isLoading ? (
189
- <div className="space-y-3">
190
- {Array.from({ length: 3 }).map((_, i) => (
191
- <Skeleton key={i} className="h-20 w-full rounded-lg" />
192
- ))}
193
- </div>
194
- ) : error ? (
195
- <Card className="border-status-error-border">
196
- <CardContent className="p-6 text-center space-y-3">
197
- <p className="text-status-error-text font-medium">Couldn&apos;t load reports</p>
198
- <p className="text-sm text-text-secondary">{error.message}</p>
199
- <Button variant="outline" onClick={refetch}>
200
- Retry
201
- </Button>
202
- </CardContent>
203
- </Card>
204
- ) : reports.length === 0 ? (
205
- <Card>
206
- <CardContent className="p-0">
207
- <IllustratedEmptyState
208
- illustration="empty-data"
209
- title="No reports yet"
210
- description="Create your first report to turn live operational data into a shareable summary."
211
- action={
212
- <Button onClick={() => void handleCreate()} disabled={creating}>
213
- <Plus size={16} className="mr-1.5" /> Create Report
214
- </Button>
215
- }
216
- />
217
- </CardContent>
218
- </Card>
219
- ) : (
220
- reports.map((report) => (
221
- <Card key={report.id}>
222
- <CardContent className="p-4">
223
- <div className="flex items-center justify-between">
224
- <div className="flex items-center gap-3">
225
- <div className="w-10 h-10 rounded-lg bg-action-primary-bg/10 flex items-center justify-center">
226
- <FileBarChart size={20} className="text-primary" />
227
- </div>
228
- <div>
229
- <p className="font-medium">{report.name}</p>
230
- <p className="text-sm text-text-secondary">
231
- {report.description ?? typeLabel(report.type)}
232
- </p>
233
- </div>
234
- </div>
235
- <div className="flex items-center gap-3">
236
- <div className="text-right">
237
- <Badge variant="outline">{report.schedule ?? 'On demand'}</Badge>
238
- {report.lastGenerated && (
239
- <p className="text-xs text-text-secondary mt-1">
240
- Last: {formatDate(new Date(report.lastGenerated), 'short')}
241
- </p>
242
- )}
243
- </div>
244
- <Button
245
- variant="outline"
246
- size="sm"
247
- aria-label={`Generate ${report.name}`}
248
- disabled={busyId === report.id}
249
- onClick={() => void handleGenerate(report.id)}
250
- >
251
- <Download size={14} />
252
- </Button>
253
- </div>
254
- </div>
255
- </CardContent>
256
- </Card>
257
- ))
258
- )}
259
-
260
- {!isLoading && !error && (reports.length > 0 || page > 0) && (
261
- <div className="flex flex-wrap items-center justify-between gap-3 pt-1">
262
- <span className="text-sm text-text-secondary">
263
- Showing {reports.length === 0 ? 0 : page * REPORTS_PAGE_SIZE + 1} to{' '}
264
- {page * REPORTS_PAGE_SIZE + reports.length}
265
- </span>
266
- <div className="flex items-center gap-1">
267
- <Button
268
- variant="outline"
269
- size="icon"
270
- className="h-8 w-8"
271
- aria-label="Previous page"
272
- onClick={() => setPage((p) => Math.max(0, p - 1))}
273
- disabled={page === 0}
274
- >
275
- <ChevronLeft className="h-4 w-4" />
276
- </Button>
277
- <span className="px-3 text-sm">Page {page + 1}</span>
278
- <Button
279
- variant="outline"
280
- size="icon"
281
- className="h-8 w-8"
282
- aria-label="Next page"
283
- onClick={() => setPage((p) => p + 1)}
284
- disabled={!hasMore}
285
- >
286
- <ChevronRight className="h-4 w-4" />
287
- </Button>
288
- </div>
289
- </div>
290
- )}
291
- </div>
292
-
293
- <div className="space-y-6">
294
- {/* Demand forecast (BOFF-4393) — shared with AnalyticsPage so reports
295
- and analytics show the same planning signal. */}
296
- <UpcomingDemandPanel
297
- laneForecast={laneForecast}
298
- customerForecast={customerForecast}
299
- laneVariance={laneVariance}
300
- customerVariance={customerVariance}
301
- isLoading={forecastLoading}
302
- error={forecastError}
303
- onRetry={refetchForecast}
304
- />
305
-
306
- {/* Data preview — a real snapshot (recent order volume) of what a
307
- generated report draws from. Honest empty state when no data. */}
308
- <Card>
309
- <CardHeader>
310
- <CardTitle className="text-base">Data preview</CardTitle>
311
- </CardHeader>
312
- <CardContent>
313
- <p className="mb-2 text-xs text-text-secondary">Order volume · last 7 days</p>
314
- <VolumeBar
315
- xKey="date"
316
- height={160}
317
- showLegend={false}
318
- data={previewData}
319
- series={[{ dataKey: 'orders', name: 'Orders' }]}
320
- emptyMessage="No order data to preview yet"
321
- />
322
- </CardContent>
323
- </Card>
324
-
325
- {/* Scheduled Reports — the page's single emphasis zone */}
326
- <EmphasisPanel className="space-y-3">
327
- <h2 className="flex items-center gap-2 text-base font-semibold text-text-primary">
328
- <Clock size={18} /> Scheduled Reports
329
- </h2>
330
- {scheduledReports.length === 0 ? (
331
- <p className="text-sm text-text-secondary">
332
- No scheduled reports yet. Schedule one to have it generated automatically.
333
- </p>
334
- ) : (
335
- scheduledReports.map((report) => (
336
- <div key={report.id} className="p-3 border border-border-seam rounded-lg">
337
- <p className="font-medium">{report.name}</p>
338
- <p className="text-sm text-text-secondary">Schedule: {report.schedule}</p>
339
- </div>
340
- ))
341
- )}
342
- <Button
343
- variant="outline"
344
- className="w-full"
345
- disabled={creating}
346
- onClick={() => void handleCreate('daily')}
347
- >
348
- Schedule New Report
349
- </Button>
350
- </EmphasisPanel>
351
-
352
- <Card>
353
- <CardHeader>
354
- <CardTitle>Quick Actions</CardTitle>
355
- </CardHeader>
356
- <CardContent className="space-y-2">
357
- <Button
358
- variant="outline"
359
- className="w-full justify-start"
360
- disabled={creating}
361
- onClick={() => void handleCreate('weekly')}
362
- >
363
- <Calendar size={16} /> Schedule Report
364
- </Button>
365
- <Button
366
- variant="outline"
367
- className="w-full justify-start"
368
- disabled={creating}
369
- onClick={() => void handleCreate()}
370
- >
371
- <Filter size={16} /> Custom Report
372
- </Button>
373
- </CardContent>
374
- </Card>
375
- </div>
376
- </div>
377
- </PageLayout>
378
- );
379
- }