ShadcnUI Vaults

Back to Blocks

Money Transfer Dashboard

Unknown Block

UnknownComponent

Money Transfer Dashboard

Comprehensive money transfer dashboard with transaction monitoring, status tracking, and fee breakdown.

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

LICENSE

MIT License

Copyright (c) 2025 Aldhaneka

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

Shadcn Vaults Project (CC BY-NC 4.0 with Internal Use Exception)

All user-submitted components in the '/blocks' directory are licensed under the Creative Commons Attribution-NonCommercial 4.0 International License (CC BY-NC 4.0),
with the following clarification and exception:

You are free to:
- Share — copy and redistribute the material in any medium or format
- Adapt — remix, transform, and build upon the material

Under these conditions:
- Attribution — You must give appropriate credit, provide a link to the license, and indicate if changes were made.
- NonCommercial — You may NOT use the material for commercial redistribution, resale, or monetization.

🚫 You MAY NOT:
- Sell or redistribute the components individually or as part of a product (e.g. a UI kit, template marketplace, SaaS component library)
- Offer the components or derivative works in any paid tool, theme pack, or design system

✅ You MAY:
- Use the components in internal company tools, dashboards, or applications that are not sold as products
- Remix or adapt components for private or enterprise projects
- Use them in open-source non-commercial projects

This license encourages sharing, learning, and internal innovation — but prohibits using these components as a basis for monetized products.

Full license text: https://creativecommons.org/licenses/by-nc/4.0/

By submitting a component, contributors agree to these terms.

Contributors

Ramiro Godoy@milogodoy

Review Form Block

Aldhaneka@Aldhanekaa

Project Creator

For questions about licensing, please contact the project maintainers.