create-bluecopa-react-app 1.0.16 → 1.0.18

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.
@@ -0,0 +1,493 @@
1
+ // Note: These statement hooks require @bluecopa/react version 0.1.16 or higher
2
+ // Update your package.json to use the latest version that includes statement hooks
3
+ import {
4
+ useGetStatementData,
5
+ useGetViewsBySheetId,
6
+ useGetRunsByViewId,
7
+ useGetViewById,
8
+ useGetRunResultById,
9
+ useCreateStatementRun,
10
+ } from "@bluecopa/react";
11
+ import { AppSidebar } from "~/components/app-sidebar";
12
+ import { SiteHeader } from "~/components/site-header";
13
+ import { SidebarInset, SidebarProvider } from "~/components/ui/sidebar";
14
+ import { Badge } from "~/components/ui/badge";
15
+ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "~/components/ui/card";
16
+ import {
17
+ Table,
18
+ TableBody,
19
+ TableCell,
20
+ TableHead,
21
+ TableHeader,
22
+ TableRow,
23
+ } from "~/components/ui/table";
24
+ import { Tabs, TabsContent, TabsList, TabsTrigger } from "~/components/ui/tabs";
25
+ import { Skeleton } from "~/components/ui/skeleton";
26
+ import { Button } from "~/components/ui/button";
27
+ import { useState } from "react";
28
+ import { toast } from "sonner";
29
+
30
+ export default function StatementsPage() {
31
+ // Example 1: Statement without viewId (uses default view)
32
+ const statementId1 = "MDYK9CU8NR1S41AFAOEX";
33
+
34
+ // Example 2: Statement with specific viewId
35
+ const statementId2 = "MGIZ7GD08NQJ71SK8RA7";
36
+ const viewId2 = "MGJ1ZF0JN53B0V0YH10Z";
37
+
38
+ const [selectedStatement, setSelectedStatement] = useState<"example1" | "example2">("example1");
39
+ const currentStatementId = selectedStatement === "example1" ? statementId1 : statementId2;
40
+ const currentViewId = selectedStatement === "example2" ? viewId2 : undefined;
41
+
42
+ // Fetch statement data
43
+ const {
44
+ data: statementData,
45
+ isLoading: statementDataLoading,
46
+ error: statementDataError,
47
+ refetch: refetchStatementData,
48
+ } = useGetStatementData(currentStatementId, currentViewId, undefined, {
49
+ enabled: !!currentStatementId,
50
+ });
51
+
52
+ // Fetch views by sheet ID (if we had sheetId, but we'll use this for demonstration)
53
+ // Note: In real usage, you'd get sheetId from the workbook first
54
+ const [selectedViewId, setSelectedViewId] = useState<string | undefined>(currentViewId);
55
+
56
+ // Fetch runs by view ID (if we have a viewId)
57
+ const {
58
+ data: runsData,
59
+ isLoading: runsLoading,
60
+ error: runsError,
61
+ } = useGetRunsByViewId(selectedViewId, {
62
+ enabled: !!selectedViewId,
63
+ });
64
+
65
+ // Fetch specific view details
66
+ const {
67
+ data: viewData,
68
+ isLoading: viewLoading,
69
+ error: viewError,
70
+ } = useGetViewById(selectedViewId, {
71
+ enabled: !!selectedViewId,
72
+ });
73
+
74
+ // Mutation hook for creating a new run
75
+ const createRunMutation = useCreateStatementRun({
76
+ onSuccess: (data: { runId: string }) => {
77
+ toast.success(`New run created successfully! Run ID: ${data.runId}`);
78
+ // Refetch statement data and runs after creating a new run
79
+ refetchStatementData();
80
+ },
81
+ onError: (error: Error) => {
82
+ toast.error(`Failed to create run: ${error.message}`);
83
+ },
84
+ });
85
+
86
+ const handleCreateRun = () => {
87
+ if (!currentStatementId) {
88
+ toast.error("Please select a statement first");
89
+ return;
90
+ }
91
+
92
+ createRunMutation.mutate({
93
+ statementId: currentStatementId,
94
+ viewId: currentViewId,
95
+ options: {
96
+ name: `Run ${new Date().toLocaleString()}`,
97
+ },
98
+ });
99
+ };
100
+
101
+ // Extract data from statement result
102
+ const statementRows = statementData?.data || [];
103
+ const statementErrors = statementData?.error || { lineErrors: [], comparisonErrors: {} };
104
+
105
+ return (
106
+ <SidebarProvider
107
+ style={
108
+ {
109
+ "--sidebar-width": "calc(var(--spacing) * 72)",
110
+ "--header-height": "calc(var(--spacing) * 12)",
111
+ } as React.CSSProperties
112
+ }
113
+ >
114
+ <AppSidebar variant="inset" />
115
+ <SidebarInset>
116
+ <SiteHeader />
117
+ <div className="flex flex-1 flex-col">
118
+ <div className="@container/main flex flex-1 flex-col gap-2">
119
+ <div className="flex flex-col gap-4 py-4 md:gap-6 md:py-6">
120
+ {/* Header Section */}
121
+ <div className="px-4 lg:px-6">
122
+ <div className="flex items-center justify-between">
123
+ <div>
124
+ <h1 className="text-3xl font-bold tracking-tight">Statement Viewer</h1>
125
+ <p className="text-muted-foreground">
126
+ View and manage statement data using BlueCopa statement hooks
127
+ </p>
128
+ </div>
129
+ <div className="flex gap-2">
130
+ <Button
131
+ variant={selectedStatement === "example1" ? "default" : "outline"}
132
+ onClick={() => setSelectedStatement("example1")}
133
+ >
134
+ Example 1
135
+ </Button>
136
+ <Button
137
+ variant={selectedStatement === "example2" ? "default" : "outline"}
138
+ onClick={() => setSelectedStatement("example2")}
139
+ >
140
+ Example 2
141
+ </Button>
142
+ <Button
143
+ onClick={handleCreateRun}
144
+ disabled={createRunMutation.isPending || !currentStatementId}
145
+ >
146
+ {createRunMutation.isPending ? "Creating..." : "Create New Run"}
147
+ </Button>
148
+ </div>
149
+ </div>
150
+ </div>
151
+
152
+ {/* Statement Info Cards */}
153
+ <div className="grid gap-4 px-4 md:grid-cols-3 lg:px-6">
154
+ <Card>
155
+ <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
156
+ <CardTitle className="text-sm font-medium">Statement ID</CardTitle>
157
+ </CardHeader>
158
+ <CardContent>
159
+ <div className="text-lg font-mono text-xs break-all">
160
+ {currentStatementId}
161
+ </div>
162
+ </CardContent>
163
+ </Card>
164
+ <Card>
165
+ <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
166
+ <CardTitle className="text-sm font-medium">View ID</CardTitle>
167
+ </CardHeader>
168
+ <CardContent>
169
+ <div className="text-lg font-mono text-xs break-all">
170
+ {currentViewId || "Default View"}
171
+ </div>
172
+ </CardContent>
173
+ </Card>
174
+ <Card>
175
+ <CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
176
+ <CardTitle className="text-sm font-medium">Data Rows</CardTitle>
177
+ </CardHeader>
178
+ <CardContent>
179
+ <div className="text-2xl font-bold">
180
+ {statementDataLoading ? (
181
+ <Skeleton className="h-8 w-16" />
182
+ ) : (
183
+ statementRows.length
184
+ )}
185
+ </div>
186
+ <p className="text-xs text-muted-foreground">
187
+ {statementDataError ? "Error loading data" : "Total rows"}
188
+ </p>
189
+ </CardContent>
190
+ </Card>
191
+ </div>
192
+
193
+ {/* Tabs for different views */}
194
+ <div className="px-4 lg:px-6">
195
+ <Tabs defaultValue="data" className="w-full">
196
+ <TabsList>
197
+ <TabsTrigger value="data">
198
+ Statement Data
199
+ {statementDataLoading && <Skeleton className="ml-2 h-4 w-8" />}
200
+ </TabsTrigger>
201
+ <TabsTrigger value="runs">
202
+ Runs
203
+ {runsLoading && <Skeleton className="ml-2 h-4 w-8" />}
204
+ </TabsTrigger>
205
+ <TabsTrigger value="view">
206
+ View Details
207
+ {viewLoading && <Skeleton className="ml-2 h-4 w-8" />}
208
+ </TabsTrigger>
209
+ </TabsList>
210
+
211
+ {/* Statement Data Tab */}
212
+ <TabsContent value="data" className="space-y-4">
213
+ <Card>
214
+ <CardHeader>
215
+ <CardTitle>Statement Data</CardTitle>
216
+ <CardDescription>
217
+ {statementDataError
218
+ ? `Error: ${statementDataError.message}`
219
+ : `Viewing data for statement ${currentStatementId}`}
220
+ </CardDescription>
221
+ </CardHeader>
222
+ <CardContent>
223
+ {statementDataLoading ? (
224
+ <div className="space-y-2">
225
+ {[...Array(5)].map((_, i) => (
226
+ <Skeleton key={i} className="h-12 w-full" />
227
+ ))}
228
+ </div>
229
+ ) : statementDataError ? (
230
+ <div className="text-center py-8 text-destructive">
231
+ <p className="font-medium">Failed to load statement data</p>
232
+ <p className="text-sm text-muted-foreground mt-2">
233
+ {statementDataError.message}
234
+ </p>
235
+ <Button
236
+ onClick={() => refetchStatementData()}
237
+ className="mt-4"
238
+ variant="outline"
239
+ >
240
+ Retry
241
+ </Button>
242
+ </div>
243
+ ) : statementRows.length === 0 ? (
244
+ <div className="text-center py-8 text-muted-foreground">
245
+ No data available
246
+ </div>
247
+ ) : (
248
+ <div className="rounded-md border overflow-x-auto">
249
+ <Table>
250
+ <TableHeader>
251
+ <TableRow>
252
+ {Object.keys(statementRows[0] || {}).map((key) => (
253
+ <TableHead key={key} className="capitalize">
254
+ {key.replace(/_/g, " ")}
255
+ </TableHead>
256
+ ))}
257
+ </TableRow>
258
+ </TableHeader>
259
+ <TableBody>
260
+ {statementRows.slice(0, 100).map((row: any, index: number) => (
261
+ <TableRow key={index}>
262
+ {Object.keys(statementRows[0] || {}).map((key) => (
263
+ <TableCell key={key} className="font-mono text-xs">
264
+ {typeof row[key] === "object"
265
+ ? JSON.stringify(row[key])
266
+ : String(row[key] || "-")}
267
+ </TableCell>
268
+ ))}
269
+ </TableRow>
270
+ ))}
271
+ </TableBody>
272
+ </Table>
273
+ {statementRows.length > 100 && (
274
+ <div className="p-4 text-center text-sm text-muted-foreground">
275
+ Showing first 100 of {statementRows.length} rows
276
+ </div>
277
+ )}
278
+ </div>
279
+ )}
280
+ </CardContent>
281
+ </Card>
282
+
283
+ {/* Errors Section */}
284
+ {statementErrors.lineErrors?.length > 0 && (
285
+ <Card>
286
+ <CardHeader>
287
+ <CardTitle className="text-destructive">Data Errors</CardTitle>
288
+ <CardDescription>
289
+ {statementErrors.lineErrors.length} error(s) found
290
+ </CardDescription>
291
+ </CardHeader>
292
+ <CardContent>
293
+ <div className="space-y-2">
294
+ {statementErrors.lineErrors.map((error: any, index: number) => (
295
+ <div
296
+ key={index}
297
+ className="p-3 border border-destructive/50 rounded-md bg-destructive/5"
298
+ >
299
+ <p className="text-sm font-medium text-destructive">
300
+ Line {error.line || index + 1}
301
+ </p>
302
+ <p className="text-xs text-muted-foreground mt-1">
303
+ {error.message || JSON.stringify(error)}
304
+ </p>
305
+ </div>
306
+ ))}
307
+ </div>
308
+ </CardContent>
309
+ </Card>
310
+ )}
311
+ </TabsContent>
312
+
313
+ {/* Runs Tab */}
314
+ <TabsContent value="runs" className="space-y-4">
315
+ <Card>
316
+ <CardHeader>
317
+ <CardTitle>Statement Runs</CardTitle>
318
+ <CardDescription>
319
+ {runsError
320
+ ? `Error: ${runsError.message}`
321
+ : selectedViewId
322
+ ? `Viewing runs for view ${selectedViewId}`
323
+ : "Select a view to see runs"}
324
+ </CardDescription>
325
+ </CardHeader>
326
+ <CardContent>
327
+ {!selectedViewId ? (
328
+ <div className="text-center py-8 text-muted-foreground">
329
+ No view selected. Select Example 2 to see runs for a specific view.
330
+ </div>
331
+ ) : runsLoading ? (
332
+ <div className="space-y-2">
333
+ {[...Array(3)].map((_, i) => (
334
+ <Skeleton key={i} className="h-12 w-full" />
335
+ ))}
336
+ </div>
337
+ ) : runsError ? (
338
+ <div className="text-center py-8 text-destructive">
339
+ <p className="font-medium">Failed to load runs</p>
340
+ <p className="text-sm text-muted-foreground mt-2">
341
+ {runsError.message}
342
+ </p>
343
+ </div>
344
+ ) : !runsData || runsData.length === 0 ? (
345
+ <div className="text-center py-8 text-muted-foreground">
346
+ No runs found for this view
347
+ </div>
348
+ ) : (
349
+ <div className="rounded-md border overflow-x-auto">
350
+ <Table>
351
+ <TableHeader>
352
+ <TableRow>
353
+ <TableHead>Run ID</TableHead>
354
+ <TableHead>Name</TableHead>
355
+ <TableHead>Status</TableHead>
356
+ <TableHead>Created Date</TableHead>
357
+ <TableHead>Last Modified</TableHead>
358
+ </TableRow>
359
+ </TableHeader>
360
+ <TableBody>
361
+ {runsData.map((run: any) => (
362
+ <TableRow key={run.id}>
363
+ <TableCell className="font-mono text-xs">
364
+ {run.id || "N/A"}
365
+ </TableCell>
366
+ <TableCell className="font-medium">
367
+ {run.name || "Unnamed Run"}
368
+ </TableCell>
369
+ <TableCell>
370
+ <Badge
371
+ variant={
372
+ run.status === "SUCCEEDED"
373
+ ? "default"
374
+ : run.status === "FAILED"
375
+ ? "destructive"
376
+ : "secondary"
377
+ }
378
+ >
379
+ {run.status || "UNKNOWN"}
380
+ </Badge>
381
+ </TableCell>
382
+ <TableCell>
383
+ {run.createdDate
384
+ ? new Date(run.createdDate).toLocaleString()
385
+ : "N/A"}
386
+ </TableCell>
387
+ <TableCell>
388
+ {run.lastModifiedDate
389
+ ? new Date(run.lastModifiedDate).toLocaleString()
390
+ : "N/A"}
391
+ </TableCell>
392
+ </TableRow>
393
+ ))}
394
+ </TableBody>
395
+ </Table>
396
+ </div>
397
+ )}
398
+ </CardContent>
399
+ </Card>
400
+ </TabsContent>
401
+
402
+ {/* View Details Tab */}
403
+ <TabsContent value="view" className="space-y-4">
404
+ <Card>
405
+ <CardHeader>
406
+ <CardTitle>View Details</CardTitle>
407
+ <CardDescription>
408
+ {viewError
409
+ ? `Error: ${viewError.message}`
410
+ : selectedViewId
411
+ ? `Details for view ${selectedViewId}`
412
+ : "Select a view to see details"}
413
+ </CardDescription>
414
+ </CardHeader>
415
+ <CardContent>
416
+ {!selectedViewId ? (
417
+ <div className="text-center py-8 text-muted-foreground">
418
+ No view selected. Select Example 2 to see view details.
419
+ </div>
420
+ ) : viewLoading ? (
421
+ <div className="space-y-2">
422
+ {[...Array(5)].map((_, i) => (
423
+ <Skeleton key={i} className="h-8 w-full" />
424
+ ))}
425
+ </div>
426
+ ) : viewError ? (
427
+ <div className="text-center py-8 text-destructive">
428
+ <p className="font-medium">Failed to load view details</p>
429
+ <p className="text-sm text-muted-foreground mt-2">
430
+ {viewError.message}
431
+ </p>
432
+ </div>
433
+ ) : !viewData ? (
434
+ <div className="text-center py-8 text-muted-foreground">
435
+ No view data available
436
+ </div>
437
+ ) : (
438
+ <div className="space-y-4">
439
+ <div className="grid gap-4 md:grid-cols-2">
440
+ <div>
441
+ <p className="text-sm font-medium text-muted-foreground">View ID</p>
442
+ <p className="text-sm font-mono break-all">{viewData.id || "N/A"}</p>
443
+ </div>
444
+ <div>
445
+ <p className="text-sm font-medium text-muted-foreground">Name</p>
446
+ <p className="text-sm">{viewData.name || "N/A"}</p>
447
+ </div>
448
+ <div>
449
+ <p className="text-sm font-medium text-muted-foreground">
450
+ Created Date
451
+ </p>
452
+ <p className="text-sm">
453
+ {viewData.createdDate
454
+ ? new Date(viewData.createdDate).toLocaleString()
455
+ : "N/A"}
456
+ </p>
457
+ </div>
458
+ <div>
459
+ <p className="text-sm font-medium text-muted-foreground">
460
+ Last Modified
461
+ </p>
462
+ <p className="text-sm">
463
+ {viewData.lastModifiedDate
464
+ ? new Date(viewData.lastModifiedDate).toLocaleString()
465
+ : "N/A"}
466
+ </p>
467
+ </div>
468
+ </div>
469
+ {viewData.customFields && (
470
+ <div>
471
+ <p className="text-sm font-medium text-muted-foreground mb-2">
472
+ Custom Fields
473
+ </p>
474
+ <pre className="p-4 bg-muted rounded-md text-xs overflow-x-auto">
475
+ {JSON.stringify(viewData.customFields, null, 2)}
476
+ </pre>
477
+ </div>
478
+ )}
479
+ </div>
480
+ )}
481
+ </CardContent>
482
+ </Card>
483
+ </TabsContent>
484
+ </Tabs>
485
+ </div>
486
+ </div>
487
+ </div>
488
+ </div>
489
+ </SidebarInset>
490
+ </SidebarProvider>
491
+ );
492
+ }
493
+
@@ -4,6 +4,7 @@ import Dashboard from "./routes/dashboard";
4
4
  import Comments from "./routes/comments";
5
5
  import Websocket from "./routes/websocket";
6
6
  import Payments from "./routes/payments";
7
+ import Statements from "./routes/statements";
7
8
 
8
9
  export default function RouteConfig() {
9
10
  return (
@@ -12,6 +13,7 @@ export default function RouteConfig() {
12
13
  <Route path="/comments" element={<Comments />} />
13
14
  <Route path="/websocket" element={<Websocket />} />
14
15
  <Route path="/payments" element={<Payments />} />
16
+ <Route path="/statements" element={<Statements />} />
15
17
  <Route path="*" element={<Navigate to="/" replace />} />
16
18
  </Routes>
17
19
  );
@@ -19,7 +19,6 @@
19
19
  "hooks": "~/hooks"
20
20
  },
21
21
  "registries": {
22
- "@shadcn": "https://ui.shadcn.com/r/index.json",
23
- "@bluecopa-ui": "https://shadcn-ui-henna.vercel.app/r/registry.json"
22
+ "@bluecopa-ui": "https://blui.vercel.app/r/{name}.json"
24
23
  }
25
24
  }
@@ -1,4 +1,4 @@
1
- System.register(['./__federation_fn_import-CzfA7kmP.js', './client-DqBeHjYu.js'], (function (exports, module) {
1
+ System.register(['./__federation_fn_import-CzfA7kmP.js', './client-uh-HfYnI.js'], (function (exports, module) {
2
2
  'use strict';
3
3
  var importShared, clientExports, jsxRuntimeExports, MemoryRouter, BrowserRouter, App;
4
4
  return {