Skip to content

Commit 5451dbb

Browse files
committed
feat: add receive funds modal with QR code and address display to dashboard
1 parent d642d4a commit 5451dbb

File tree

4 files changed

+114
-4
lines changed

4 files changed

+114
-4
lines changed

apps/web/app/dashboard/page.tsx

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import { useTokenBalances } from "@/hooks/use-token-balance"
1111
import { useTransactions } from "@/hooks/use-transactions"
1212
import { useSmartAccount } from "@/hooks/use-smart-account"
1313
import { formatUnits } from "viem"
14+
import { ReceiveModal } from "@/components/dashboard/receive-modal"
15+
import { useState } from "react"
1416

1517
// Mock data
1618
const mockTransactions = [
@@ -103,6 +105,8 @@ export default function DashboardPage() {
103105
const { balances, isLoading: balancesLoading } = useTokenBalances("0x4fff0f708c768a46050f9b96c46c265729d1a62f"); // for testing
104106
const { transactions, isLoading: transactionsLoading } = useTransactions(walletAddress, 10);
105107

108+
const [receiveModalOpen, setReceiveModalOpen] = useState(false)
109+
106110
const balanceChange = "-12.5%"
107111
const balanceChangeValue = parseFloat(balanceChange)
108112
const isPositiveChange = balanceChangeValue >= 0
@@ -142,10 +146,15 @@ export default function DashboardPage() {
142146
Send
143147
</Link>
144148
</Button>
145-
<Button size="sm" variant="secondary" className="w-full font-semibold shadow-sm bg-white/10 hover:bg-white/20 text-white border-none backdrop-blur-sm sm:h-11 sm:px-8">
146-
<ArrowDownLeft className="mr-1 sm:mr-2 size-3 sm:size-4" />
147-
Receive
148-
</Button>
149+
<Button
150+
size="sm"
151+
variant="secondary"
152+
className="w-full font-semibold shadow-sm bg-white/10 hover:bg-white/20 text-white border-none backdrop-blur-sm sm:h-11 sm:px-8"
153+
onClick={() => setReceiveModalOpen(true)}
154+
>
155+
<ArrowDownLeft className="mr-1 sm:mr-2 size-3 sm:size-4" />
156+
Receive
157+
</Button>
149158
<Button size="sm" variant="secondary" className="w-full font-semibold shadow-sm bg-white/10 hover:bg-white/20 text-white border-none backdrop-blur-sm sm:h-11 sm:px-8" asChild>
150159
<Link href="/dashboard/swap">
151160
<ArrowRightLeft className="mr-1 sm:mr-2 size-3 sm:size-4" />
@@ -333,6 +342,13 @@ export default function DashboardPage() {
333342
</Card>
334343
</TabsContent>
335344
</Tabs>
345+
346+
{/* Receive Modal */}
347+
<ReceiveModal
348+
open={receiveModalOpen}
349+
onOpenChange={setReceiveModalOpen}
350+
address={walletAddress}
351+
/>
336352
</div>
337353
)
338354
}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
"use client"
2+
3+
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from "@/components/ui/dialog"
4+
import { Button } from "@/components/ui/button"
5+
import { Badge } from "@/components/ui/badge"
6+
import { Copy, CheckCircle2, AlertTriangle } from "lucide-react"
7+
import QRCodeSVG from "react-qr-code"
8+
import { Address } from "viem"
9+
import { useState } from "react"
10+
11+
interface ReceiveModalProps {
12+
open: boolean
13+
onOpenChange: (open: boolean) => void
14+
address: Address | undefined
15+
}
16+
17+
export function ReceiveModal({ open, onOpenChange, address }: ReceiveModalProps) {
18+
const [copied, setCopied] = useState(false)
19+
20+
const handleCopy = async () => {
21+
if (!address) return
22+
23+
await navigator.clipboard.writeText(address)
24+
setCopied(true)
25+
setTimeout(() => setCopied(false), 2000)
26+
}
27+
28+
if (!address) return null
29+
30+
return (
31+
<Dialog open={open} onOpenChange={onOpenChange}>
32+
<DialogContent className="sm:max-w-md">
33+
<DialogHeader>
34+
<DialogTitle className="text-2xl font-bold">Receive Funds</DialogTitle>
35+
<DialogDescription>
36+
Scan the QR code or copy your wallet address to receive funds
37+
</DialogDescription>
38+
</DialogHeader>
39+
40+
<div className="flex flex-col items-center gap-6 py-4">
41+
{/* QR Code */}
42+
<div className="p-4 bg-white rounded-xl shadow-sm border-2 border-muted">
43+
<QRCodeSVG
44+
value={address}
45+
size={200}
46+
level="H"
47+
/>
48+
</div>
49+
50+
{/* Wallet Address */}
51+
<div className="w-full space-y-3">
52+
<div className="flex items-center justify-between gap-2 p-3 rounded-lg bg-muted/50 border">
53+
<code className="text-xs sm:text-sm font-mono break-all flex-1">
54+
{address}
55+
</code>
56+
<Button
57+
variant="ghost"
58+
size="icon"
59+
className="shrink-0 size-8"
60+
onClick={handleCopy}
61+
>
62+
{copied ? (
63+
<CheckCircle2 className="size-4 text-green-500" />
64+
) : (
65+
<Copy className="size-4" />
66+
)}
67+
</Button>
68+
</div>
69+
70+
{/* Network Warning */}
71+
<div className="flex items-start gap-2 p-3 rounded-lg bg-amber-50 dark:bg-amber-950/20 border border-amber-200 dark:border-amber-900">
72+
<AlertTriangle className="size-5 text-amber-600 dark:text-amber-500 shrink-0 mt-0.5" />
73+
<div className="space-y-1">
74+
<p className="text-sm font-semibold text-amber-900 dark:text-amber-100">
75+
Important Notice
76+
</p>
77+
<p className="text-xs text-amber-800 dark:text-amber-200">
78+
Only send assets on the <Badge variant="outline" className="text-[10px] px-1.5 py-0 font-semibold">Ethereum Network</Badge> to this address.
79+
Sending assets from other networks may result in permanent loss of funds.
80+
</p>
81+
</div>
82+
</div>
83+
</div>
84+
</div>
85+
</DialogContent>
86+
</Dialog>
87+
)
88+
}

apps/web/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
"postcss": "^8.5.6",
4141
"react": "^19.2.0",
4242
"react-dom": "^19.2.0",
43+
"react-qr-code": "^2.0.18",
4344
"sonner": "^2.0.7",
4445
"swr": "^2.3.6",
4546
"tailwind-merge": "^3.4.0",

bun.lock

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)