Skip to content

Commit 452026f

Browse files
committed
feat: improve wallets page
1 parent 4c387f9 commit 452026f

File tree

16 files changed

+402
-335
lines changed

16 files changed

+402
-335
lines changed

app/page.tsx

Lines changed: 41 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,28 +3,64 @@
33
import { PageContainer } from '@/components/Layout/PageContainer';
44
import { TransactionActivityPanel, PendingTransactions } from '@/components/Dashboard';
55
import { TopAssets } from '@/components/Assets/TopAssets';
6+
import { useRouter } from 'next/navigation';
67

78
export default function DashboardPage() {
9+
const router = useRouter();
10+
811
const handleViewAllTransactions = () => {
9-
// Navigate to transactions page
10-
console.log('Navigate to transactions page');
12+
router.push('/transactions');
1113
};
1214

1315
const handleViewAllAssets = () => {
14-
// Navigate to assets page
15-
console.log('Navigate to assets page');
16+
router.push('/assets');
1617
};
1718

1819
const handleAssetClick = (asset: any) => {
1920
// Handle asset click
2021
console.log('Asset clicked:', asset);
2122
};
2223

24+
const handleSend = () => {
25+
console.log('Send action clicked');
26+
// Navigate to send page or open send modal
27+
};
28+
29+
const handleSwap = () => {
30+
console.log('Swap action clicked');
31+
// Navigate to swap page or open swap modal
32+
};
33+
34+
const handleBuy = () => {
35+
console.log('Buy action clicked');
36+
// Navigate to buy page or open buy modal
37+
};
38+
39+
const handleReceive = () => {
40+
console.log('Receive action clicked');
41+
// Show receive address or QR code
42+
};
43+
44+
const handleManageWallets = () => {
45+
router.push('/wallets');
46+
};
47+
48+
const handleSettings = () => {
49+
router.push('/settings');
50+
};
51+
2352
return (
2453
<PageContainer>
2554
<div className='space-y-6'>
2655
{/* Transaction Activity Panel - Full Width */}
27-
<TransactionActivityPanel totalBalance='$18,625.00' balanceChange='+$2,345.67' isPositiveChange={true} />
56+
<TransactionActivityPanel
57+
onSend={handleSend}
58+
onSwap={handleSwap}
59+
onBuy={handleBuy}
60+
onReceive={handleReceive}
61+
onManageWallets={handleManageWallets}
62+
onSettings={handleSettings}
63+
/>
2864

2965
{/* Two Column Layout */}
3066
<div className='grid gap-6 md:grid-cols-2'>

app/wallets/[safeAddress]/page.tsx

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { PageContainer } from '@/components/Layout/PageContainer';
2+
3+
export default function WalletPage() {
4+
return (
5+
<PageContainer>
6+
<div>Wallet</div>
7+
</PageContainer>
8+
);
9+
}

app/wallets/page.tsx

Lines changed: 2 additions & 123 deletions
Original file line numberDiff line numberDiff line change
@@ -1,133 +1,12 @@
11
'use client';
22

3-
import { useState, useEffect } from 'react';
4-
import { Button } from '@/components/ui/button';
5-
import { Plus, RefreshCw } from 'lucide-react';
6-
import { useRouter } from 'next/navigation';
7-
import { useExtendedSafeApiKit, useOwnerSafeAddress } from '@/providers/SafeProvider';
8-
import { useWallet } from '@/hooks/useWallet';
9-
import { useConnectWallet } from '@web3-onboard/react';
10-
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip';
113
import { PageContainer } from '@/components/Layout/PageContainer';
12-
import { WalletStats, WalletCard, WalletSkeletonGrid, EmptyWalletState } from '@/components/Wallets';
13-
import { SafeWallet } from '@/types/wallets';
4+
import { WalletsPanel } from '@/components/Wallets/WalletsPanel';
145

156
export default function WalletsPage() {
16-
const router = useRouter();
17-
const { account } = useWallet();
18-
const [{ wallet }] = useConnectWallet();
19-
const ownerSafeAddresses = useOwnerSafeAddress();
20-
const extendedSafeApiKit = useExtendedSafeApiKit();
21-
22-
const [safeWallets, setSafeWallets] = useState<SafeWallet[]>([]);
23-
const [isLoading, setIsLoading] = useState(true);
24-
const [isRefreshing, setIsRefreshing] = useState(false);
25-
26-
// Load real Safe wallets from API
27-
useEffect(() => {
28-
loadSafeWallets();
29-
}, [account, extendedSafeApiKit, ownerSafeAddresses]);
30-
31-
const loadSafeWallets = async (isRefresh = false) => {
32-
if (!account || !extendedSafeApiKit) {
33-
setIsLoading(false);
34-
return;
35-
}
36-
37-
try {
38-
if (isRefresh) {
39-
setIsRefreshing(true);
40-
} else {
41-
setIsLoading(true);
42-
}
43-
44-
// Get Safe addresses from the API
45-
let safeAddresses: string[] = [];
46-
47-
try {
48-
const response = await extendedSafeApiKit.getApiKit().getSafesByOwner(account);
49-
safeAddresses = response.safes || [];
50-
} catch (error) {
51-
console.error('Failed to get user safes:', error);
52-
// Fallback to ownerSafeAddresses from context if API fails
53-
safeAddresses = ownerSafeAddresses;
54-
}
55-
56-
if (safeAddresses.length === 0) {
57-
setSafeWallets([]);
58-
setIsLoading(false);
59-
return;
60-
}
61-
62-
// Fetch detailed information for each Safe
63-
const safeWalletsData: SafeWallet[] = [];
64-
65-
setSafeWallets(safeWalletsData);
66-
} catch (error) {
67-
console.error('Failed to load Safe wallets:', error);
68-
setSafeWallets([]);
69-
} finally {
70-
if (isRefresh) {
71-
setIsRefreshing(false);
72-
} else {
73-
setIsLoading(false);
74-
}
75-
}
76-
};
77-
787
return (
798
<PageContainer>
80-
<TooltipProvider>
81-
<div className='space-y-8'>
82-
{/* Enhanced Header */}
83-
<div className='flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4'>
84-
<div className='space-y-1'>
85-
<h1 className='text-3xl font-bold tracking-tight'>Safe Wallets</h1>
86-
<p className='text-muted-foreground'>
87-
Manage your multi-signature wallets and secure your digital assets
88-
</p>
89-
</div>
90-
<div className='flex gap-3'>
91-
<Tooltip>
92-
<TooltipTrigger asChild>
93-
<Button
94-
variant='outline'
95-
onClick={() => loadSafeWallets(true)}
96-
disabled={isRefreshing}
97-
className='flex items-center gap-2'
98-
>
99-
<RefreshCw className={`h-4 w-4 ${isRefreshing ? 'animate-spin' : ''}`} />
100-
<span className='hidden sm:inline'>Refresh</span>
101-
</Button>
102-
</TooltipTrigger>
103-
<TooltipContent>
104-
<p>Refresh wallet data</p>
105-
</TooltipContent>
106-
</Tooltip>
107-
<Button onClick={() => router.push('/wallets/create')} className='flex items-center gap-2' size='default'>
108-
<Plus className='h-4 w-4' />
109-
Create New Safe
110-
</Button>
111-
</div>
112-
</div>
113-
114-
{/* Enhanced Stats Section */}
115-
<WalletStats safeWallets={safeWallets} />
116-
117-
{/* Enhanced Safe Wallets Grid */}
118-
{isLoading ? (
119-
<WalletSkeletonGrid />
120-
) : safeWallets.length === 0 ? (
121-
<EmptyWalletState />
122-
) : (
123-
<div className='grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6'>
124-
{safeWallets.map((safe, index) => (
125-
<WalletCard key={safe.address} safe={safe} index={index} />
126-
))}
127-
</div>
128-
)}
129-
</div>
130-
</TooltipProvider>
9+
<WalletsPanel />
13110
</PageContainer>
13211
);
13312
}

components/Breadcrumb/AppBreadcrumb.tsx

Lines changed: 81 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import Link from 'next/link';
55
import { usePathname } from 'next/navigation';
66
import {
77
Breadcrumb,
8-
BreadcrumbEllipsis,
98
BreadcrumbItem,
109
BreadcrumbLink,
1110
BreadcrumbList,
@@ -23,7 +22,6 @@ const routeConfig: Record<string, { label: string }> = {
2322
'/settings': { label: 'Settings' },
2423
};
2524

26-
// Get all parent paths for a given path
2725
function getPathSegments(pathname: string): string[] {
2826
const segments = pathname.split('/').filter(Boolean);
2927
const paths = ['/'];
@@ -37,41 +35,99 @@ function getPathSegments(pathname: string): string[] {
3735
return paths;
3836
}
3937

40-
export function AppBreadcrumb() {
41-
const pathname = usePathname();
38+
function isHexAddress(segment: string): boolean {
39+
return /^0x[a-fA-F0-9]{40}$/.test(segment);
40+
}
4241

43-
const pathSegments = getPathSegments(pathname);
42+
function getLastSegment(path: string): string {
43+
const parts = path.split('/').filter(Boolean);
44+
return parts[parts.length - 1] || '';
45+
}
4446

45-
const configuredPaths = pathSegments.filter((path) => routeConfig[path]);
47+
export function AppBreadcrumb() {
48+
const pathname = usePathname();
4649

4750
if (pathname === '/') {
4851
return null;
4952
}
5053

54+
const segments = getPathSegments(pathname);
55+
56+
// Build breadcrumb entries including dynamic segments
57+
const items = segments.map((path, index) => {
58+
const isLast = index === segments.length - 1;
59+
60+
// Static config match
61+
if (routeConfig[path]) {
62+
return {
63+
key: path,
64+
label: routeConfig[path].label,
65+
href: path,
66+
isLast,
67+
clickable: !isLast,
68+
};
69+
}
70+
71+
// Dynamic wallet address: /wallets/:address
72+
const parts = path.split('/').filter(Boolean);
73+
if (parts.length >= 2 && parts[0] === 'wallets') {
74+
const last = parts[parts.length - 1];
75+
const second = parts[1];
76+
77+
// /wallets/:address
78+
if (parts.length === 2 && isHexAddress(second)) {
79+
const label = second; // future: replace with ENS when available
80+
return {
81+
key: path,
82+
label,
83+
href: path,
84+
isLast,
85+
clickable: !isLast,
86+
};
87+
}
88+
89+
// /wallets/:address/edit
90+
if (parts.length === 3 && isHexAddress(second) && last === 'edit') {
91+
return {
92+
key: path,
93+
label: 'Edit',
94+
href: path,
95+
isLast,
96+
clickable: false, // last crumb not clickable
97+
};
98+
}
99+
}
100+
101+
// Fallback: show the last segment text
102+
const fallbackLabel = getLastSegment(path);
103+
return {
104+
key: path,
105+
label: fallbackLabel || path,
106+
href: path,
107+
isLast,
108+
clickable: !isLast,
109+
};
110+
});
111+
51112
return (
52113
<>
53114
<Separator orientation='vertical' className='mr-2 data-[orientation=vertical]:h-4' />
54115
<Breadcrumb>
55116
<BreadcrumbList>
56-
{configuredPaths.map((path, index) => {
57-
const config = routeConfig[path];
58-
const isLast = index === configuredPaths.length - 1;
59-
60-
return (
61-
<React.Fragment key={path}>
62-
<BreadcrumbItem>
63-
{isLast ? (
64-
<BreadcrumbPage>{config?.label || path}</BreadcrumbPage>
65-
) : (
66-
<BreadcrumbLink asChild>
67-
<Link href={path}>{config?.label || path}</Link>
68-
</BreadcrumbLink>
69-
)}
70-
</BreadcrumbItem>
71-
{!isLast && <BreadcrumbSeparator />}
72-
</React.Fragment>
73-
);
74-
})}
117+
{items.map((item) => (
118+
<React.Fragment key={item.key}>
119+
<BreadcrumbItem>
120+
{item.isLast || !item.clickable ? (
121+
<BreadcrumbPage>{item.label}</BreadcrumbPage>
122+
) : (
123+
<BreadcrumbLink asChild>
124+
<Link href={item.href}>{item.label}</Link>
125+
</BreadcrumbLink>
126+
)}
127+
</BreadcrumbItem>
128+
{!item.isLast && <BreadcrumbSeparator />}
129+
</React.Fragment>
130+
))}
75131
</BreadcrumbList>
76132
</Breadcrumb>
77133
</>

0 commit comments

Comments
 (0)