create-bluecopa-react-app 1.0.11 → 1.0.13
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/bin/create-bluecopa-react-app.js +1 -1
- package/package.json +1 -1
- package/templates/latest/.dockerignore +5 -1
- package/templates/latest/Agent.md +621 -0
- package/templates/latest/Dockerfile +2 -2
- package/templates/latest/app/app.tsx +3 -1
- package/templates/latest/app/components/app-sidebar.tsx +14 -16
- package/templates/latest/app/components/nav-main.tsx +6 -22
- package/templates/latest/app/data/mock-payments.json +122 -0
- package/templates/latest/app/data/mock-transactions.json +128 -0
- package/templates/latest/app/routes/comments.tsx +552 -0
- package/templates/latest/app/routes/{home.tsx → dashboard.tsx} +1 -1
- package/templates/latest/app/routes/payments.tsx +342 -0
- package/templates/latest/app/routes/websocket.tsx +450 -0
- package/templates/latest/app/routes.tsx +8 -5
- package/templates/latest/dist/assets/{__federation_expose_App-C8_sl1dD.js → __federation_expose_App-BIH7hwj_.js} +12 -2
- package/templates/latest/dist/assets/{home-DhyEFlEc.js → client-CsvW46cT.js} +18530 -983
- package/templates/latest/dist/assets/{index-DkyIpbj3.js → index-CFECuPSy.js} +1 -1
- package/templates/latest/dist/assets/remoteEntry.css +155 -23
- package/templates/latest/dist/assets/remoteEntry.js +1 -1
- package/templates/latest/dist/favicon.ico +0 -0
- package/templates/latest/dist/index.html +2 -2
- package/templates/latest/package-lock.json +203 -203
- package/templates/latest/package.json +1 -1
- package/templates/latest/public/favicon.ico +0 -0
- package/templates/latest/dist/assets/client-Hh38T4k9.js +0 -12660
- package/templates/latest/dist/avatars/shadcn.svg +0 -6
- package/templates/latest/public/avatars/shadcn.svg +0 -6
- /package/templates/latest/app/{dashboard → data}/data.json +0 -0
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
import { useDataset } from "@bluecopa/react";
|
|
2
|
+
import { AppSidebar } from "~/components/app-sidebar";
|
|
3
|
+
import { SiteHeader } from "~/components/site-header";
|
|
4
|
+
import { SidebarInset, SidebarProvider } from "~/components/ui/sidebar";
|
|
5
|
+
import { Badge } from "~/components/ui/badge";
|
|
6
|
+
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "~/components/ui/card";
|
|
7
|
+
import {
|
|
8
|
+
Table,
|
|
9
|
+
TableBody,
|
|
10
|
+
TableCell,
|
|
11
|
+
TableHead,
|
|
12
|
+
TableHeader,
|
|
13
|
+
TableRow,
|
|
14
|
+
} from "~/components/ui/table";
|
|
15
|
+
import { Tabs, TabsContent, TabsList, TabsTrigger } from "~/components/ui/tabs";
|
|
16
|
+
import { Skeleton } from "~/components/ui/skeleton";
|
|
17
|
+
import mockTransactions from "~/data/mock-transactions.json";
|
|
18
|
+
import mockPayments from "~/data/mock-payments.json";
|
|
19
|
+
|
|
20
|
+
export default function PaymentsPage() {
|
|
21
|
+
// Fetch customers dataset (bank transactions)
|
|
22
|
+
const {
|
|
23
|
+
data: customersData,
|
|
24
|
+
isLoading: customersLoading,
|
|
25
|
+
error: customersError
|
|
26
|
+
} = useDataset("0Uz7OdcTzoofrHA9vPDb_mt940_a_tr_tr610012300123100101128200_pc000018202_2023050302584600720230502_225853_55320230502_225911_654_csv");
|
|
27
|
+
|
|
28
|
+
// Fetch invoices dataset (payments received)
|
|
29
|
+
const {
|
|
30
|
+
data: invoicesData,
|
|
31
|
+
isLoading: invoicesLoading,
|
|
32
|
+
error: invoicesError
|
|
33
|
+
} = useDataset("0Uz7OOjMvou8FWTdMYov_home_depot__us__20230501_112012951_parsed_csv");
|
|
34
|
+
|
|
35
|
+
// Use mock data as fallback if there's an error or no data
|
|
36
|
+
const transactions = customersError || !customersData?.data
|
|
37
|
+
? mockTransactions
|
|
38
|
+
: customersData.data;
|
|
39
|
+
|
|
40
|
+
const invoices = invoicesError || !invoicesData?.data
|
|
41
|
+
? mockPayments
|
|
42
|
+
: invoicesData.data;
|
|
43
|
+
|
|
44
|
+
// Calculate payment statistics from invoices
|
|
45
|
+
const totalInvoices = invoices.length;
|
|
46
|
+
const totalTransactions = transactions.length;
|
|
47
|
+
|
|
48
|
+
// Calculate totals from invoices (payments received)
|
|
49
|
+
const totalPaymentAmount = invoices.reduce((sum: number, inv: any) => sum + (Number(inv.paymentamount) || 0), 0);
|
|
50
|
+
const totalGrossAmount = invoices.reduce((sum: number, inv: any) => sum + (Number(inv.grossamount) || 0), 0);
|
|
51
|
+
const totalNetAmount = invoices.reduce((sum: number, inv: any) => sum + (Number(inv.netamountpaid) || 0), 0);
|
|
52
|
+
const totalDeductions = invoices.reduce((sum: number, inv: any) => sum + (Number(inv.deductionamount) || 0), 0);
|
|
53
|
+
|
|
54
|
+
// Calculate totals from transactions
|
|
55
|
+
const totalTransactionAmount = transactions.reduce((sum: number, txn: any) => sum + (Number(txn.amount) || 0), 0);
|
|
56
|
+
const creditTransactions = transactions.filter((txn: any) => txn.debit_credit_mark === "C").length;
|
|
57
|
+
const debitTransactions = transactions.filter((txn: any) => txn.debit_credit_mark === "D").length;
|
|
58
|
+
|
|
59
|
+
return (
|
|
60
|
+
<SidebarProvider
|
|
61
|
+
style={
|
|
62
|
+
{
|
|
63
|
+
"--sidebar-width": "calc(var(--spacing) * 72)",
|
|
64
|
+
"--header-height": "calc(var(--spacing) * 12)",
|
|
65
|
+
} as React.CSSProperties
|
|
66
|
+
}
|
|
67
|
+
>
|
|
68
|
+
<AppSidebar variant="inset" />
|
|
69
|
+
<SidebarInset>
|
|
70
|
+
<SiteHeader />
|
|
71
|
+
<div className="flex flex-1 flex-col">
|
|
72
|
+
<div className="@container/main flex flex-1 flex-col gap-2">
|
|
73
|
+
<div className="flex flex-col gap-4 py-4 md:gap-6 md:py-6">
|
|
74
|
+
{/* Stats Cards */}
|
|
75
|
+
<div className="grid gap-4 px-4 md:grid-cols-2 lg:grid-cols-4 lg:px-6">
|
|
76
|
+
<Card>
|
|
77
|
+
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
|
78
|
+
<CardTitle className="text-sm font-medium">
|
|
79
|
+
Total Payments Received
|
|
80
|
+
</CardTitle>
|
|
81
|
+
</CardHeader>
|
|
82
|
+
<CardContent>
|
|
83
|
+
<div className="text-2xl font-bold">
|
|
84
|
+
${totalPaymentAmount.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}
|
|
85
|
+
</div>
|
|
86
|
+
<p className="text-xs text-muted-foreground">
|
|
87
|
+
From {totalInvoices} payments
|
|
88
|
+
</p>
|
|
89
|
+
</CardContent>
|
|
90
|
+
</Card>
|
|
91
|
+
<Card>
|
|
92
|
+
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
|
93
|
+
<CardTitle className="text-sm font-medium">
|
|
94
|
+
Net Amount
|
|
95
|
+
</CardTitle>
|
|
96
|
+
</CardHeader>
|
|
97
|
+
<CardContent>
|
|
98
|
+
<div className="text-2xl font-bold">
|
|
99
|
+
${totalNetAmount.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}
|
|
100
|
+
</div>
|
|
101
|
+
<p className="text-xs text-muted-foreground">
|
|
102
|
+
After deductions
|
|
103
|
+
</p>
|
|
104
|
+
</CardContent>
|
|
105
|
+
</Card>
|
|
106
|
+
<Card>
|
|
107
|
+
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
|
108
|
+
<CardTitle className="text-sm font-medium">
|
|
109
|
+
Total Deductions
|
|
110
|
+
</CardTitle>
|
|
111
|
+
</CardHeader>
|
|
112
|
+
<CardContent>
|
|
113
|
+
<div className="text-2xl font-bold text-orange-600">
|
|
114
|
+
${totalDeductions.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}
|
|
115
|
+
</div>
|
|
116
|
+
<p className="text-xs text-muted-foreground">
|
|
117
|
+
From {totalInvoices} payments
|
|
118
|
+
</p>
|
|
119
|
+
</CardContent>
|
|
120
|
+
</Card>
|
|
121
|
+
<Card>
|
|
122
|
+
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
|
|
123
|
+
<CardTitle className="text-sm font-medium">
|
|
124
|
+
Transactions
|
|
125
|
+
</CardTitle>
|
|
126
|
+
</CardHeader>
|
|
127
|
+
<CardContent>
|
|
128
|
+
<div className="text-2xl font-bold">
|
|
129
|
+
{totalTransactions}
|
|
130
|
+
</div>
|
|
131
|
+
<p className="text-xs text-muted-foreground">
|
|
132
|
+
{creditTransactions} credits / {debitTransactions} debits
|
|
133
|
+
</p>
|
|
134
|
+
</CardContent>
|
|
135
|
+
</Card>
|
|
136
|
+
</div>
|
|
137
|
+
|
|
138
|
+
{/* Tabs for Invoices and Transactions */}
|
|
139
|
+
<div className="px-4 lg:px-6">
|
|
140
|
+
<Tabs defaultValue="payments" className="w-full">
|
|
141
|
+
<TabsList>
|
|
142
|
+
<TabsTrigger value="payments">
|
|
143
|
+
Payments Received
|
|
144
|
+
{invoicesLoading && <Skeleton className="ml-2 h-4 w-8" />}
|
|
145
|
+
</TabsTrigger>
|
|
146
|
+
<TabsTrigger value="transactions">
|
|
147
|
+
Bank Transactions
|
|
148
|
+
{customersLoading && <Skeleton className="ml-2 h-4 w-8" />}
|
|
149
|
+
</TabsTrigger>
|
|
150
|
+
</TabsList>
|
|
151
|
+
|
|
152
|
+
{/* Payments Tab */}
|
|
153
|
+
<TabsContent value="payments" className="space-y-4">
|
|
154
|
+
<Card>
|
|
155
|
+
<CardHeader>
|
|
156
|
+
<CardTitle>Payments Received</CardTitle>
|
|
157
|
+
<CardDescription>
|
|
158
|
+
{invoicesError
|
|
159
|
+
? "Using mock data (dataset unavailable)"
|
|
160
|
+
: "View all customer payments and deductions"}
|
|
161
|
+
</CardDescription>
|
|
162
|
+
</CardHeader>
|
|
163
|
+
<CardContent>
|
|
164
|
+
{invoicesLoading ? (
|
|
165
|
+
<div className="space-y-2">
|
|
166
|
+
{[...Array(5)].map((_, i) => (
|
|
167
|
+
<Skeleton key={i} className="h-12 w-full" />
|
|
168
|
+
))}
|
|
169
|
+
</div>
|
|
170
|
+
) : invoices.length === 0 ? (
|
|
171
|
+
<div className="text-center py-8 text-muted-foreground">
|
|
172
|
+
No payments found
|
|
173
|
+
</div>
|
|
174
|
+
) : (
|
|
175
|
+
<div className="rounded-md border overflow-x-auto">
|
|
176
|
+
<Table>
|
|
177
|
+
<TableHeader>
|
|
178
|
+
<TableRow>
|
|
179
|
+
<TableHead>Payer Name</TableHead>
|
|
180
|
+
<TableHead>Payer ID</TableHead>
|
|
181
|
+
<TableHead>Payment Amount</TableHead>
|
|
182
|
+
<TableHead>Gross Amount</TableHead>
|
|
183
|
+
<TableHead>Net Paid</TableHead>
|
|
184
|
+
<TableHead>Deduction</TableHead>
|
|
185
|
+
<TableHead>Effective Date</TableHead>
|
|
186
|
+
<TableHead>ACH Trace #</TableHead>
|
|
187
|
+
<TableHead>PO Number</TableHead>
|
|
188
|
+
</TableRow>
|
|
189
|
+
</TableHeader>
|
|
190
|
+
<TableBody>
|
|
191
|
+
{invoices.map((payment: any, index: number) => (
|
|
192
|
+
<TableRow key={payment.achtracenumber || index}>
|
|
193
|
+
<TableCell className="font-medium">
|
|
194
|
+
{payment.payername || "N/A"}
|
|
195
|
+
</TableCell>
|
|
196
|
+
<TableCell>
|
|
197
|
+
{payment.payerid || "N/A"}
|
|
198
|
+
</TableCell>
|
|
199
|
+
<TableCell>
|
|
200
|
+
${Number(payment.paymentamount || 0).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}
|
|
201
|
+
</TableCell>
|
|
202
|
+
<TableCell>
|
|
203
|
+
${Number(payment.grossamount || 0).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}
|
|
204
|
+
</TableCell>
|
|
205
|
+
<TableCell>
|
|
206
|
+
${Number(payment.netamountpaid || 0).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}
|
|
207
|
+
</TableCell>
|
|
208
|
+
<TableCell>
|
|
209
|
+
{payment.deductionamount > 0 ? (
|
|
210
|
+
<div className="flex flex-col">
|
|
211
|
+
<span className="text-orange-600 font-medium">
|
|
212
|
+
${Number(payment.deductionamount || 0).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}
|
|
213
|
+
</span>
|
|
214
|
+
{payment.deductionreasontext && (
|
|
215
|
+
<span className="text-xs text-muted-foreground">
|
|
216
|
+
{payment.deductionreasontext}
|
|
217
|
+
</span>
|
|
218
|
+
)}
|
|
219
|
+
</div>
|
|
220
|
+
) : (
|
|
221
|
+
<span className="text-muted-foreground">-</span>
|
|
222
|
+
)}
|
|
223
|
+
</TableCell>
|
|
224
|
+
<TableCell>
|
|
225
|
+
{payment.paymenteffectivedate || "N/A"}
|
|
226
|
+
</TableCell>
|
|
227
|
+
<TableCell className="font-mono text-xs">
|
|
228
|
+
{payment.achtracenumber || "N/A"}
|
|
229
|
+
</TableCell>
|
|
230
|
+
<TableCell>
|
|
231
|
+
{payment.purchaseordernumber || "N/A"}
|
|
232
|
+
</TableCell>
|
|
233
|
+
</TableRow>
|
|
234
|
+
))}
|
|
235
|
+
</TableBody>
|
|
236
|
+
</Table>
|
|
237
|
+
</div>
|
|
238
|
+
)}
|
|
239
|
+
</CardContent>
|
|
240
|
+
</Card>
|
|
241
|
+
</TabsContent>
|
|
242
|
+
|
|
243
|
+
{/* Transactions Tab */}
|
|
244
|
+
<TabsContent value="transactions" className="space-y-4">
|
|
245
|
+
<Card>
|
|
246
|
+
<CardHeader>
|
|
247
|
+
<CardTitle>Bank Transactions</CardTitle>
|
|
248
|
+
<CardDescription>
|
|
249
|
+
{customersError
|
|
250
|
+
? "Using mock data (dataset unavailable)"
|
|
251
|
+
: "View all bank account transactions and statements"}
|
|
252
|
+
</CardDescription>
|
|
253
|
+
</CardHeader>
|
|
254
|
+
<CardContent>
|
|
255
|
+
{customersLoading ? (
|
|
256
|
+
<div className="space-y-2">
|
|
257
|
+
{[...Array(5)].map((_, i) => (
|
|
258
|
+
<Skeleton key={i} className="h-12 w-full" />
|
|
259
|
+
))}
|
|
260
|
+
</div>
|
|
261
|
+
) : transactions.length === 0 ? (
|
|
262
|
+
<div className="text-center py-8 text-muted-foreground">
|
|
263
|
+
No transactions found
|
|
264
|
+
</div>
|
|
265
|
+
) : (
|
|
266
|
+
<div className="rounded-md border overflow-x-auto">
|
|
267
|
+
<Table>
|
|
268
|
+
<TableHeader>
|
|
269
|
+
<TableRow>
|
|
270
|
+
<TableHead>Transaction Ref</TableHead>
|
|
271
|
+
<TableHead>Value Date</TableHead>
|
|
272
|
+
<TableHead>Type</TableHead>
|
|
273
|
+
<TableHead>Description</TableHead>
|
|
274
|
+
<TableHead>Debit/Credit</TableHead>
|
|
275
|
+
<TableHead>Amount</TableHead>
|
|
276
|
+
<TableHead>Balance</TableHead>
|
|
277
|
+
<TableHead>Customer Ref</TableHead>
|
|
278
|
+
<TableHead>Bank Ref</TableHead>
|
|
279
|
+
</TableRow>
|
|
280
|
+
</TableHeader>
|
|
281
|
+
<TableBody>
|
|
282
|
+
{transactions.map((txn: any, index: number) => (
|
|
283
|
+
<TableRow key={txn.transaction_reference || index}>
|
|
284
|
+
<TableCell className="font-medium font-mono text-xs">
|
|
285
|
+
{txn.transaction_reference || "N/A"}
|
|
286
|
+
</TableCell>
|
|
287
|
+
<TableCell>
|
|
288
|
+
{txn.value_date || "N/A"}
|
|
289
|
+
</TableCell>
|
|
290
|
+
<TableCell>
|
|
291
|
+
<Badge variant="outline">
|
|
292
|
+
{txn.transaction_type || "N/A"}
|
|
293
|
+
</Badge>
|
|
294
|
+
</TableCell>
|
|
295
|
+
<TableCell className="max-w-xs truncate">
|
|
296
|
+
{txn.description || "N/A"}
|
|
297
|
+
</TableCell>
|
|
298
|
+
<TableCell>
|
|
299
|
+
<Badge
|
|
300
|
+
variant={
|
|
301
|
+
txn.debit_credit_mark === "C"
|
|
302
|
+
? "default"
|
|
303
|
+
: txn.debit_credit_mark === "D"
|
|
304
|
+
? "secondary"
|
|
305
|
+
: "outline"
|
|
306
|
+
}
|
|
307
|
+
>
|
|
308
|
+
{txn.debit_credit_mark === "C" ? "Credit" : txn.debit_credit_mark === "D" ? "Debit" : "N/A"}
|
|
309
|
+
</Badge>
|
|
310
|
+
</TableCell>
|
|
311
|
+
<TableCell className="font-medium">
|
|
312
|
+
{txn.debit_credit_mark === "D" && "-"}
|
|
313
|
+
${Number(txn.amount || 0).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}
|
|
314
|
+
</TableCell>
|
|
315
|
+
<TableCell>
|
|
316
|
+
${Number(txn.closing_balance_amount || 0).toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}
|
|
317
|
+
</TableCell>
|
|
318
|
+
<TableCell className="font-mono text-xs">
|
|
319
|
+
{txn.customer_reference || "-"}
|
|
320
|
+
</TableCell>
|
|
321
|
+
<TableCell className="font-mono text-xs">
|
|
322
|
+
{txn.bank_reference || "-"}
|
|
323
|
+
</TableCell>
|
|
324
|
+
</TableRow>
|
|
325
|
+
))}
|
|
326
|
+
</TableBody>
|
|
327
|
+
</Table>
|
|
328
|
+
</div>
|
|
329
|
+
)}
|
|
330
|
+
</CardContent>
|
|
331
|
+
</Card>
|
|
332
|
+
</TabsContent>
|
|
333
|
+
</Tabs>
|
|
334
|
+
</div>
|
|
335
|
+
</div>
|
|
336
|
+
</div>
|
|
337
|
+
</div>
|
|
338
|
+
</SidebarInset>
|
|
339
|
+
</SidebarProvider>
|
|
340
|
+
);
|
|
341
|
+
}
|
|
342
|
+
|