synor/apps/desktop-wallet/src/components/Layout.tsx
Gulshan Yadav f08eb965c2 a
2026-02-02 15:00:13 +05:30

268 lines
8.4 KiB
TypeScript

import { Outlet, NavLink } from 'react-router-dom';
import {
LayoutDashboard,
Send,
Download,
History,
Settings,
Lock,
Wifi,
WifiOff,
Server,
Hammer,
FileCode2,
Coins,
Image,
PiggyBank,
ArrowLeftRight,
Users,
Globe,
Usb,
Shield,
BarChart3,
QrCode,
HardDrive,
Cloud,
Globe2,
Cpu,
Database,
EyeOff,
GitBranch,
Vote,
Layers,
Eye,
ListPlus,
Activity,
Timer,
ShieldCheck,
// Phase 7-16 icons
UserX,
Shuffle,
ArrowUpDown,
TrendingUp,
PieChart,
Bell,
Terminal,
Wrench,
Puzzle,
} from 'lucide-react';
import { useState } from 'react';
import { useWalletStore } from '../store/wallet';
import { useNodeStore } from '../store/node';
import { useMiningStore, formatHashrate } from '../store/mining';
import { NotificationsBell } from './NotificationsPanel';
import { WalletSelector } from './WalletSelector';
import { CreateWalletModal } from './CreateWalletModal';
import { ImportWalletModal } from './ImportWalletModal';
const navItems = [
{ to: '/dashboard', label: 'Dashboard', icon: LayoutDashboard },
{ to: '/send', label: 'Send', icon: Send },
{ to: '/batch-send', label: 'Batch Send', icon: ListPlus },
{ to: '/receive', label: 'Receive', icon: Download },
{ to: '/history', label: 'History', icon: History },
];
const defiNavItems = [
{ to: '/staking', label: 'Staking', icon: PiggyBank },
{ to: '/swap', label: 'Swap', icon: ArrowLeftRight },
{ to: '/market', label: 'Market', icon: BarChart3 },
];
const advancedNavItems = [
{ to: '/node', label: 'Node', icon: Server },
{ to: '/mining', label: 'Mining', icon: Hammer },
{ to: '/contracts', label: 'Contracts', icon: FileCode2 },
{ to: '/tokens', label: 'Tokens', icon: Coins },
{ to: '/nfts', label: 'NFTs', icon: Image },
];
const infrastructureNavItems = [
{ to: '/storage', label: 'Storage', icon: Cloud },
{ to: '/hosting', label: 'Hosting', icon: Globe2 },
{ to: '/compute', label: 'Compute', icon: Cpu },
{ to: '/database', label: 'Database', icon: Database },
];
const privacyBridgeNavItems = [
{ to: '/privacy', label: 'Privacy', icon: EyeOff },
{ to: '/bridge', label: 'Bridge', icon: GitBranch },
];
const governanceNavItems = [
{ to: '/governance', label: 'Governance', icon: Vote },
{ to: '/zk', label: 'ZK-Rollup', icon: Layers },
];
const toolsNavItems = [
{ to: '/watch-only', label: 'Watch-Only', icon: Eye },
{ to: '/fee-analytics', label: 'Fee Analytics', icon: Activity },
{ to: '/vaults', label: 'Time Vaults', icon: Timer },
{ to: '/recovery', label: 'Recovery', icon: ShieldCheck },
{ to: '/decoy', label: 'Decoy Wallets', icon: UserX },
{ to: '/mixer', label: 'Mixer', icon: Shuffle },
{ to: '/limit-orders', label: 'Limit Orders', icon: ArrowUpDown },
{ to: '/yield', label: 'Yield', icon: TrendingUp },
{ to: '/portfolio', label: 'Portfolio', icon: PieChart },
{ to: '/alerts', label: 'Alerts', icon: Bell },
{ to: '/cli', label: 'CLI', icon: Terminal },
{ to: '/rpc-profiles', label: 'RPC Profiles', icon: Server },
{ to: '/tx-builder', label: 'Tx Builder', icon: Wrench },
{ to: '/plugins', label: 'Plugins', icon: Puzzle },
{ to: '/dapps', label: 'DApps', icon: Globe },
{ to: '/addressbook', label: 'Address Book', icon: Users },
{ to: '/multisig', label: 'Multi-sig', icon: Shield },
{ to: '/hardware', label: 'Hardware', icon: Usb },
{ to: '/qr', label: 'QR Code', icon: QrCode },
{ to: '/backup', label: 'Backup', icon: HardDrive },
{ to: '/settings', label: 'Settings', icon: Settings },
];
export default function Layout() {
const { lockWallet, balance } = useWalletStore();
const nodeStatus = useNodeStore((state) => state.status);
const miningStatus = useMiningStore((state) => state.status);
// Modal state for multi-wallet management
const [showCreateModal, setShowCreateModal] = useState(false);
const [showImportModal, setShowImportModal] = useState(false);
const handleLock = async () => {
await lockWallet();
};
const renderNavSection = (
items: typeof navItems,
title?: string
) => (
<>
{title && (
<div className="pt-4 pb-2">
<p className="px-4 text-xs text-gray-600 uppercase tracking-wider">{title}</p>
</div>
)}
{items.map(({ to, label, icon: Icon }) => (
<NavLink
key={to}
to={to}
className={({ isActive }) =>
`flex items-center justify-between px-4 py-2.5 rounded-lg transition-colors ${
isActive
? 'bg-synor-600 text-white'
: 'text-gray-400 hover:text-white hover:bg-gray-800'
}`
}
>
<div className="flex items-center gap-3">
<Icon size={18} />
{label}
</div>
{/* Status indicators */}
{to === '/node' && nodeStatus.isConnected && (
<span className="w-2 h-2 rounded-full bg-green-400" />
)}
{to === '/mining' && miningStatus.isMining && (
<span className="text-xs text-synor-400">
{formatHashrate(miningStatus.hashrate)}
</span>
)}
</NavLink>
))}
</>
);
return (
<div className="flex h-full">
{/* Sidebar */}
<aside className="w-56 bg-gray-900 border-r border-gray-800 flex flex-col">
{/* Wallet Selector */}
<div className="p-3 border-b border-gray-800">
<WalletSelector
onCreateWallet={() => setShowCreateModal(true)}
onImportWallet={() => setShowImportModal(true)}
/>
</div>
{/* Balance display */}
<div className="p-4 border-b border-gray-800">
<p className="text-xs text-gray-500 uppercase tracking-wider mb-1">Balance</p>
<p className="text-xl font-bold text-white">
{balance?.balanceHuman || '0 SYN'}
</p>
{balance?.pending ? (
<p className="text-xs text-gray-500 mt-1">
+ {(balance.pending / 100_000_000).toFixed(8)} pending
</p>
) : null}
</div>
{/* Navigation */}
<nav className="flex-1 p-3 space-y-0.5 overflow-y-auto">
{renderNavSection(navItems)}
{renderNavSection(defiNavItems, 'DeFi')}
{renderNavSection(advancedNavItems, 'Advanced')}
{renderNavSection(infrastructureNavItems, 'Infrastructure')}
{renderNavSection(privacyBridgeNavItems, 'Privacy & Bridge')}
{renderNavSection(governanceNavItems, 'Governance')}
{renderNavSection(toolsNavItems, 'Tools')}
</nav>
{/* Footer */}
<div className="p-3 border-t border-gray-800 space-y-2">
{/* Notifications */}
<div className="flex items-center justify-between px-4 py-2">
<span className="text-sm text-gray-400">Notifications</span>
<NotificationsBell />
</div>
{/* Node status */}
<div className="flex items-center gap-2 px-4 py-2 text-sm">
{nodeStatus.isConnected ? (
<>
<Wifi size={14} className="text-green-400" />
<span className="text-gray-400 text-xs">
{nodeStatus.network || 'Connected'}
{nodeStatus.isSyncing && ' (Syncing)'}
</span>
</>
) : (
<>
<WifiOff size={14} className="text-red-400" />
<span className="text-gray-400 text-xs">Disconnected</span>
</>
)}
</div>
{/* Block height */}
{nodeStatus.isConnected && nodeStatus.blockHeight > 0 && (
<div className="px-4 text-xs text-gray-500">
Block #{nodeStatus.blockHeight.toLocaleString()}
</div>
)}
{/* Lock button */}
<button
onClick={handleLock}
className="w-full flex items-center justify-center gap-2 px-4 py-2 rounded-lg bg-gray-800 hover:bg-gray-700 text-gray-300 hover:text-white transition-colors text-sm"
>
<Lock size={14} />
Lock Wallet
</button>
</div>
</aside>
{/* Main content */}
<main className="flex-1 overflow-auto p-6">
<Outlet />
</main>
{/* Multi-wallet Modals */}
{showCreateModal && (
<CreateWalletModal onClose={() => setShowCreateModal(false)} />
)}
{showImportModal && (
<ImportWalletModal onClose={() => setShowImportModal(false)} />
)}
</div>
);
}