Preview
Full width desktop view
Code
banking-1.tsx
1"use client";
2import React, { useState } from "react";
3import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
4import { Badge } from "@/components/ui/badge";
5import { Button } from "@/components/ui/button";
6import { Input } from "@/components/ui/input";
7import {
8 Select,
9 SelectContent,
10 SelectItem,
11 SelectTrigger,
12 SelectValue,
13} from "@/components/ui/select";
14import {
15 Table,
16 TableBody,
17 TableCell,
18 TableHead,
19 TableHeader,
20 TableRow,
21} from "@/components/ui/table";
22import { Separator } from "@/components/ui/separator";
23import {
24 Search,
25 Filter,
26 Download,
27 Eye,
28 Calendar,
29 DollarSign,
30 CreditCard,
31 Building2,
32 Clock,
33 CheckCircle,
34 XCircle,
35 AlertCircle,
36} from "lucide-react";
37
38interface MoneyTransferTransaction {
39 id: string;
40 amount: number;
41 currency: string;
42 destination: {
43 name: string;
44 account: string;
45 bank?: string;
46 };
47 payment: {
48 paidAt: Date;
49 status: "completed" | "pending" | "failed";
50 processor: string;
51 fee: number;
52 };
53 disbursement: {
54 processedAt: Date | null;
55 status: "completed" | "pending" | "failed" | "processing";
56 fee: number;
57 };
58 platformFee: number;
59 totalFees: number;
60 netAmount: number;
61}
62
63const MoneyTransferDashboard: React.FC = () => {
64 const [searchTerm, setSearchTerm] = useState("");
65 const [statusFilter, setStatusFilter] = useState("all");
66 const [selectedTransaction, setSelectedTransaction] =
67 useState<MoneyTransferTransaction | null>(null);
68
69 const mockTransactions: MoneyTransferTransaction[] = [
70 {
71 id: "TXN-001",
72 amount: 1500.0,
73 currency: "USD",
74 destination: {
75 name: "John Smith",
76 account: "****1234",
77 bank: "Chase Bank",
78 },
79 payment: {
80 paidAt: new Date("2024-01-15T10:30:00"),
81 status: "completed",
82 processor: "Stripe",
83 fee: 45.0,
84 },
85 disbursement: {
86 processedAt: new Date("2024-01-15T11:15:00"),
87 status: "completed",
88 fee: 25.0,
89 },
90 platformFee: 30.0,
91 totalFees: 100.0,
92 netAmount: 1400.0,
93 },
94 {
95 id: "TXN-002",
96 amount: 750.0,
97 currency: "USD",
98 destination: {
99 name: "Maria Garcia",
100 account: "****5678",
101 bank: "Bank of America",
102 },
103 payment: {
104 paidAt: new Date("2024-01-15T14:20:00"),
105 status: "completed",
106 processor: "PayPal",
107 fee: 22.5,
108 },
109 disbursement: {
110 processedAt: null,
111 status: "processing",
112 fee: 15.0,
113 },
114 platformFee: 15.0,
115 totalFees: 52.5,
116 netAmount: 697.5,
117 },
118 {
119 id: "TXN-003",
120 amount: 2200.0,
121 currency: "USD",
122 destination: {
123 name: "David Wilson",
124 account: "****9012",
125 bank: "Wells Fargo",
126 },
127 payment: {
128 paidAt: new Date("2024-01-14T16:45:00"),
129 status: "failed",
130 processor: "Square",
131 fee: 66.0,
132 },
133 disbursement: {
134 processedAt: null,
135 status: "pending",
136 fee: 35.0,
137 },
138 platformFee: 44.0,
139 totalFees: 145.0,
140 netAmount: 2055.0,
141 },
142 ];
143
144 const getStatusBadge = (status: string) => {
145 const statusConfig = {
146 completed: {
147 variant: "default" as const,
148 icon: CheckCircle,
149 color: "text-green-600",
150 },
151 pending: {
152 variant: "secondary" as const,
153 icon: Clock,
154 color: "text-yellow-600",
155 },
156 processing: {
157 variant: "outline" as const,
158 icon: AlertCircle,
159 color: "text-blue-600",
160 },
161 failed: {
162 variant: "destructive" as const,
163 icon: XCircle,
164 color: "text-red-600",
165 },
166 };
167
168 const config = statusConfig[status as keyof typeof statusConfig];
169 const Icon = config.icon;
170
171 return (
172 <Badge variant={config.variant} className="flex items-center gap-1">
173 <Icon className="w-3 h-3" />
174 {status.charAt(0).toUpperCase() + status.slice(1)}
175 </Badge>
176 );
177 };
178
179 const formatCurrency = (amount: number, currency: string = "USD") => {
180 return new Intl.NumberFormat("en-US", {
181 style: "currency",
182 currency: currency,
183 }).format(amount);
184 };
185
186 const formatDateTime = (date: Date | null) => {
187 if (!date) return "N/A";
188 return new Intl.DateTimeFormat("en-US", {
189 year: "numeric",
190 month: "short",
191 day: "numeric",
192 hour: "2-digit",
193 minute: "2-digit",
194 }).format(date);
195 };
196
197 const filteredTransactions = mockTransactions.filter((transaction) => {
198 const matchesSearch =
199 transaction.id.toLowerCase().includes(searchTerm.toLowerCase()) ||
200 transaction.destination.name
201 .toLowerCase()
202 .includes(searchTerm.toLowerCase());
203 const matchesStatus =
204 statusFilter === "all" ||
205 transaction.payment.status === statusFilter ||
206 transaction.disbursement.status === statusFilter;
207 return matchesSearch && matchesStatus;
208 });
209
210 return (
211 <div className="min-h-screen bg-background p-6">
212 <div className="max-w-7xl mx-auto space-y-6">
213 {/* Header */}
214 <div className="flex flex-col sm:flex-row justify-between items-start sm:items-center gap-4">
215 <div>
216 <h1 className="text-3xl font-bold text-foreground">
217 Money Transfer Transactions
218 </h1>
219 <p className="text-muted-foreground">
220 Monitor and manage your transfer transactions
221 </p>
222 </div>
223 <Button className="flex items-center gap-2">
224 <Download className="w-4 h-4" />
225 Export Data
226 </Button>
227 </div>
228
229 {/* Stats Cards */}
230 <div className="grid grid-cols-1 md:grid-cols-4 gap-4">
231 <Card>
232 <CardContent className="p-6">
233 <div className="flex items-center justify-between">
234 <div>
235 <p className="text-sm font-medium text-muted-foreground">
236 Total Volume
237 </p>
238 <p className="text-2xl font-bold text-foreground">
239 $4,450.00
240 </p>
241 </div>
242 <DollarSign className="w-8 h-8 text-green-600" />
243 </div>
244 </CardContent>
245 </Card>
246
247 <Card>
248 <CardContent className="p-6">
249 <div className="flex items-center justify-between">
250 <div>
251 <p className="text-sm font-medium text-muted-foreground">
252 Platform Revenue
253 </p>
254 <p className="text-2xl font-bold text-foreground">$89.00</p>
255 </div>
256 <Building2 className="w-8 h-8 text-blue-600" />
257 </div>
258 </CardContent>
259 </Card>
260
261 <Card>
262 <CardContent className="p-6">
263 <div className="flex items-center justify-between">
264 <div>
265 <p className="text-sm font-medium text-muted-foreground">
266 Total Fees
267 </p>
268 <p className="text-2xl font-bold text-foreground">$297.50</p>
269 </div>
270 <CreditCard className="w-8 h-8 text-orange-600" />
271 </div>
272 </CardContent>
273 </Card>
274
275 <Card>
276 <CardContent className="p-6">
277 <div className="flex items-center justify-between">
278 <div>
279 <p className="text-sm font-medium text-muted-foreground">
280 Transactions
281 </p>
282 <p className="text-2xl font-bold text-foreground">3</p>
283 </div>
284 <Calendar className="w-8 h-8 text-purple-600" />
285 </div>
286 </CardContent>
287 </Card>
288 </div>
289
290 {/* Filters */}
291 <Card>
292 <CardContent className="p-6">
293 <div className="flex flex-col sm:flex-row gap-4">
294 <div className="relative flex-1">
295 <Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-muted-foreground w-4 h-4" />
296 <Input
297 placeholder="Search by transaction ID or recipient name..."
298 value={searchTerm}
299 onChange={(e) => setSearchTerm(e.target.value)}
300 className="pl-10"
301 />
302 </div>
303 <Select value={statusFilter} onValueChange={setStatusFilter}>
304 <SelectTrigger className="w-full sm:w-48">
305 <Filter className="w-4 h-4 mr-2" />
306 <SelectValue placeholder="Filter by status" />
307 </SelectTrigger>
308 <SelectContent>
309 <SelectItem value="all">All Status</SelectItem>
310 <SelectItem value="completed">Completed</SelectItem>
311 <SelectItem value="pending">Pending</SelectItem>
312 <SelectItem value="processing">Processing</SelectItem>
313 <SelectItem value="failed">Failed</SelectItem>
314 </SelectContent>
315 </Select>
316 </div>
317 </CardContent>
318 </Card>
319
320 {/* Transactions Table */}
321 <Card>
322 <CardHeader>
323 <CardTitle>Transaction Details</CardTitle>
324 </CardHeader>
325 <CardContent>
326 <div className="overflow-x-auto">
327 <Table>
328 <TableHeader>
329 <TableRow>
330 <TableHead>Transaction ID</TableHead>
331 <TableHead>Destination</TableHead>
332 <TableHead>Amount</TableHead>
333 <TableHead>Payment Status</TableHead>
334 <TableHead>Disbursement Status</TableHead>
335 <TableHead>Platform Fee</TableHead>
336 <TableHead>Actions</TableHead>
337 </TableRow>
338 </TableHeader>
339 <TableBody>
340 {filteredTransactions.map((transaction) => (
341 <TableRow key={transaction.id}>
342 <TableCell className="font-medium">
343 {transaction.id}
344 </TableCell>
345 <TableCell>
346 <div>
347 <p className="font-medium">
348 {transaction.destination.name}
349 </p>
350 <p className="text-sm text-muted-foreground">
351 {transaction.destination.account} •{" "}
352 {transaction.destination.bank}
353 </p>
354 </div>
355 </TableCell>
356 <TableCell>
357 <div>
358 <p className="font-medium">
359 {formatCurrency(transaction.amount)}
360 </p>
361 <p className="text-sm text-muted-foreground">
362 Net: {formatCurrency(transaction.netAmount)}
363 </p>
364 </div>
365 </TableCell>
366 <TableCell>
367 <div className="space-y-1">
368 {getStatusBadge(transaction.payment.status)}
369 <p className="text-xs text-muted-foreground">
370 {formatDateTime(transaction.payment.paidAt)}
371 </p>
372 </div>
373 </TableCell>
374 <TableCell>
375 <div className="space-y-1">
376 {getStatusBadge(transaction.disbursement.status)}
377 <p className="text-xs text-muted-foreground">
378 {formatDateTime(
379 transaction.disbursement.processedAt
380 )}
381 </p>
382 </div>
383 </TableCell>
384 <TableCell>
385 <p className="font-medium text-green-600">
386 {formatCurrency(transaction.platformFee)}
387 </p>
388 </TableCell>
389 <TableCell>
390 <Button
391 variant="outline"
392 size="sm"
393 onClick={() => setSelectedTransaction(transaction)}
394 className="flex items-center gap-1"
395 >
396 <Eye className="w-3 h-3" />
397 View
398 </Button>
399 </TableCell>
400 </TableRow>
401 ))}
402 </TableBody>
403 </Table>
404 </div>
405 </CardContent>
406 </Card>
407
408 {/* Transaction Detail Modal */}
409 {selectedTransaction && (
410 <Card className="fixed inset-4 z-50 bg-background border shadow-lg overflow-auto">
411 <CardHeader className="flex flex-row items-center justify-between">
412 <CardTitle>
413 Transaction Details - {selectedTransaction.id}
414 </CardTitle>
415 <Button
416 variant="outline"
417 size="sm"
418 onClick={() => setSelectedTransaction(null)}
419 >
420 Close
421 </Button>
422 </CardHeader>
423 <CardContent className="space-y-6">
424 <div className="grid grid-cols-1 md:grid-cols-2 gap-6">
425 {/* Payment Information */}
426 <div className="space-y-4">
427 <h3 className="text-lg font-semibold flex items-center gap-2">
428 <CreditCard className="w-5 h-5" />
429 Payment Information
430 </h3>
431 <div className="space-y-3">
432 <div className="flex justify-between">
433 <span className="text-muted-foreground">Status:</span>
434 {getStatusBadge(selectedTransaction.payment.status)}
435 </div>
436 <div className="flex justify-between">
437 <span className="text-muted-foreground">Paid At:</span>
438 <span>
439 {formatDateTime(selectedTransaction.payment.paidAt)}
440 </span>
441 </div>
442 <div className="flex justify-between">
443 <span className="text-muted-foreground">Processor:</span>
444 <span>{selectedTransaction.payment.processor}</span>
445 </div>
446 <div className="flex justify-between">
447 <span className="text-muted-foreground">
448 Payment Fee:
449 </span>
450 <span>
451 {formatCurrency(selectedTransaction.payment.fee)}
452 </span>
453 </div>
454 </div>
455 </div>
456
457 {/* Disbursement Information */}
458 <div className="space-y-4">
459 <h3 className="text-lg font-semibold flex items-center gap-2">
460 <Building2 className="w-5 h-5" />
461 Disbursement Information
462 </h3>
463 <div className="space-y-3">
464 <div className="flex justify-between">
465 <span className="text-muted-foreground">Status:</span>
466 {getStatusBadge(selectedTransaction.disbursement.status)}
467 </div>
468 <div className="flex justify-between">
469 <span className="text-muted-foreground">
470 Processed At:
471 </span>
472 <span>
473 {formatDateTime(
474 selectedTransaction.disbursement.processedAt
475 )}
476 </span>
477 </div>
478 <div className="flex justify-between">
479 <span className="text-muted-foreground">
480 Disbursement Fee:
481 </span>
482 <span>
483 {formatCurrency(selectedTransaction.disbursement.fee)}
484 </span>
485 </div>
486 </div>
487 </div>
488 </div>
489
490 <Separator />
491
492 {/* Financial Breakdown */}
493 <div className="space-y-4">
494 <h3 className="text-lg font-semibold flex items-center gap-2">
495 <DollarSign className="w-5 h-5" />
496 Financial Breakdown
497 </h3>
498 <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
499 <div className="space-y-3">
500 <div className="flex justify-between">
501 <span className="text-muted-foreground">
502 Transfer Amount:
503 </span>
504 <span className="font-medium">
505 {formatCurrency(selectedTransaction.amount)}
506 </span>
507 </div>
508 <div className="flex justify-between">
509 <span className="text-muted-foreground">
510 Platform Fee:
511 </span>
512 <span className="text-green-600 font-medium">
513 {formatCurrency(selectedTransaction.platformFee)}
514 </span>
515 </div>
516 <div className="flex justify-between">
517 <span className="text-muted-foreground">
518 Payment Fee:
519 </span>
520 <span className="text-red-600">
521 {formatCurrency(selectedTransaction.payment.fee)}
522 </span>
523 </div>
524 </div>
525 <div className="space-y-3">
526 <div className="flex justify-between">
527 <span className="text-muted-foreground">
528 Disbursement Fee:
529 </span>
530 <span className="text-red-600">
531 {formatCurrency(selectedTransaction.disbursement.fee)}
532 </span>
533 </div>
534 <div className="flex justify-between">
535 <span className="text-muted-foreground">Total Fees:</span>
536 <span className="text-red-600 font-medium">
537 {formatCurrency(selectedTransaction.totalFees)}
538 </span>
539 </div>
540 <div className="flex justify-between border-t pt-2">
541 <span className="font-medium">Net Amount:</span>
542 <span className="font-bold text-lg">
543 {formatCurrency(selectedTransaction.netAmount)}
544 </span>
545 </div>
546 </div>
547 </div>
548 </div>
549
550 <Separator />
551
552 {/* Destination Information */}
553 <div className="space-y-4">
554 <h3 className="text-lg font-semibold">Destination Details</h3>
555 <div className="space-y-3">
556 <div className="flex justify-between">
557 <span className="text-muted-foreground">
558 Recipient Name:
559 </span>
560 <span>{selectedTransaction.destination.name}</span>
561 </div>
562 <div className="flex justify-between">
563 <span className="text-muted-foreground">Account:</span>
564 <span>{selectedTransaction.destination.account}</span>
565 </div>
566 <div className="flex justify-between">
567 <span className="text-muted-foreground">Bank:</span>
568 <span>{selectedTransaction.destination.bank}</span>
569 </div>
570 </div>
571 </div>
572 </CardContent>
573 </Card>
574 )}
575 </div>
576 </div>
577 );
578};
579
580export default MoneyTransferDashboard;
Dependencies
External Libraries
lucide-reactreact
Shadcn/UI Components
badgebuttoncardinputselectseparatortable