268 lines
8.4 KiB
TypeScript
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>
|
|
);
|
|
}
|