UNPKG

bigblocks

Version:

Complete Bitcoin UI component library - authentication, social, wallet, market, inscriptions, and blockchain interactions for React

15 lines 11.4 kB
{ "name": "wallet-components-walletoverview", "type": "registry:component", "dependencies": [], "devDependencies": [], "registryDependencies": [], "files": [ { "path": "components/wallet/components/WalletOverview.tsx", "type": "registry:component", "content": "import {\n\tArrowDownIcon,\n\tArrowUpIcon,\n\tGearIcon,\n\tPlusIcon,\n} from \"@radix-ui/react-icons\";\nimport { useBitcoinAuth } from \"../../../hooks/useBitcoinAuth.js\";\nimport { SPACING, responsive } from \"../../../lib/layout-constants.js\";\nimport { cn } from \"../../../lib/utils.js\";\nimport { Badge } from \"../../ui/badge.js\";\nimport { Button } from \"../../ui/button.js\";\nimport { Card, CardContent } from \"../../ui/card.js\";\nimport { Separator } from \"../../ui/separator.js\";\nimport { Skeleton } from \"../../ui/skeleton.js\";\nimport type { BSVBalance } from \"../types/wallet.js\";\nimport {\n\tcalculatePercentageChange,\n\tformatBSV,\n\tformatCurrency,\n\tformatPercentage,\n} from \"../utils/currency.js\";\n\nexport interface WalletOverviewProps {\n\tbalance?: BSVBalance;\n\tshowValues?: boolean;\n\tcurrency?: \"BSV\" | \"USD\";\n\texchangeRate?: number;\n\tpreviousBalance?: number;\n\tloading?: boolean;\n\tonSend?: () => void;\n\tonReceive?: () => void;\n\tonBuy?: () => void;\n\tonSettings?: () => void;\n\tclassName?: string;\n}\n\nexport function WalletOverview({\n\tbalance,\n\tshowValues = true,\n\tcurrency = \"USD\",\n\texchangeRate = 50, // Default BSV/USD rate\n\tpreviousBalance,\n\tloading = false,\n\tonSend,\n\tonReceive,\n\tonBuy,\n\tonSettings,\n\tclassName,\n}: WalletOverviewProps) {\n\tconst { user, isAuthenticated } = useBitcoinAuth();\n\n\tif (!isAuthenticated || !user) {\n\t\treturn (\n\t\t\t<Card\n\t\t\t\tclassName={cn(\n\t\t\t\t\tclassName,\n\t\t\t\t\t\"w-full\",\n\t\t\t\t\t`max-w-[${responsive.maxWidth.cardLarge.sm}]`,\n\t\t\t\t)}\n\t\t\t>\n\t\t\t\t<CardContent\n\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\"flex flex-col items-center\",\n\t\t\t\t\t\t`gap-${responsive.gap.loose}`,\n\t\t\t\t\t\t`py-${responsive.spacing.sectionPadding}`,\n\t\t\t\t\t)}\n\t\t\t\t>\n\t\t\t\t\t<h3 className=\"text-2xl font-bold\">Wallet</h3>\n\t\t\t\t\t<p className=\"text-sm text-muted-foreground\">\n\t\t\t\t\t\tConnect your wallet to view balance\n\t\t\t\t\t</p>\n\t\t\t\t\t<p className=\"text-sm text-muted-foreground\">\n\t\t\t\t\t\tSign in with Bitcoin to get started\n\t\t\t\t\t</p>\n\t\t\t\t</CardContent>\n\t\t\t</Card>\n\t\t);\n\t}\n\n\tif (loading || !balance) {\n\t\treturn (\n\t\t\t<Card\n\t\t\t\tclassName={cn(\n\t\t\t\t\tclassName,\n\t\t\t\t\t\"w-full\",\n\t\t\t\t\t`max-w-[${responsive.maxWidth.cardLarge.sm}]`,\n\t\t\t\t)}\n\t\t\t>\n\t\t\t\t<CardContent\n\t\t\t\t\tclassName={cn(\"flex flex-col\", `gap-${responsive.gap.loose}`)}\n\t\t\t\t>\n\t\t\t\t\t<div className=\"flex justify-between items-center\">\n\t\t\t\t\t\t<h3 className=\"text-2xl font-bold\">Wallet</h3>\n\t\t\t\t\t\t{onSettings && (\n\t\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\t\t\tsize=\"sm\"\n\t\t\t\t\t\t\t\tonClick={onSettings}\n\t\t\t\t\t\t\t\tclassName=\"h-8 w-8 p-0\"\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<GearIcon className=\"w-4 h-4\" />\n\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t)}\n\t\t\t\t\t</div>\n\n\t\t\t\t\t{/* Loading skeleton */}\n\t\t\t\t\t<div\n\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\"flex flex-col items-center\",\n\t\t\t\t\t\t\t`gap-${responsive.gap.tight}`,\n\t\t\t\t\t\t)}\n\t\t\t\t\t>\n\t\t\t\t\t\t<Skeleton className=\"w-32 h-8\" />\n\t\t\t\t\t\t<Skeleton className=\"w-24 h-4\" />\n\t\t\t\t\t</div>\n\n\t\t\t\t\t<div\n\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\"grid\",\n\t\t\t\t\t\t\t`grid-cols-${responsive.gridColumns.threeColumn}`,\n\t\t\t\t\t\t\t`gap-${responsive.gap.tight}`,\n\t\t\t\t\t\t)}\n\t\t\t\t\t>\n\t\t\t\t\t\t{Array.from({ length: 3 }).map((_, i) => (\n\t\t\t\t\t\t\t<Skeleton\n\t\t\t\t\t\t\t\tkey={`wallet-overview-skeleton-${i + 1}`}\n\t\t\t\t\t\t\t\tclassName=\"h-10\"\n\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t))}\n\t\t\t\t\t</div>\n\t\t\t\t</CardContent>\n\t\t\t</Card>\n\t\t);\n\t}\n\n\tconst totalSatoshis = balance.confirmed + balance.unconfirmed;\n\tconst totalBSV = totalSatoshis / 100000000;\n\tconst fiatValue = showValues ? totalBSV * exchangeRate : 0;\n\n\t// Calculate percentage change if previous balance is provided\n\tconst percentageChange = previousBalance\n\t\t? calculatePercentageChange(previousBalance, totalSatoshis)\n\t\t: null;\n\tconst changeData = percentageChange\n\t\t? formatPercentage(percentageChange)\n\t\t: null;\n\n\treturn (\n\t\t<Card\n\t\t\tclassName={cn(\n\t\t\t\tclassName,\n\t\t\t\t\"w-full\",\n\t\t\t\t`max-w-[${responsive.maxWidth.cardLarge.sm}]`,\n\t\t\t)}\n\t\t>\n\t\t\t<CardContent\n\t\t\t\tclassName={cn(\"flex flex-col\", `gap-${responsive.gap.loose}`)}\n\t\t\t>\n\t\t\t\t{/* Header */}\n\t\t\t\t<div className=\"flex justify-between items-center\">\n\t\t\t\t\t<h3 className=\"text-2xl font-bold\">Wallet</h3>\n\t\t\t\t\t{onSettings && (\n\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\tvariant=\"ghost\"\n\t\t\t\t\t\t\tsize=\"sm\"\n\t\t\t\t\t\t\tonClick={onSettings}\n\t\t\t\t\t\t\tclassName=\"h-8 w-8 p-0\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t<GearIcon className=\"w-4 h-4\" />\n\t\t\t\t\t\t</Button>\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\n\t\t\t\t{/* Main Balance */}\n\t\t\t\t<div\n\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\"flex flex-col items-center\",\n\t\t\t\t\t\t`gap-${responsive.gap.tight}`,\n\t\t\t\t\t)}\n\t\t\t\t>\n\t\t\t\t\t{showValues ? (\n\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t<p className=\"text-3xl sm:text-4xl font-bold\">\n\t\t\t\t\t\t\t\t{currency === \"BSV\"\n\t\t\t\t\t\t\t\t\t? formatBSV(totalSatoshis, 8)\n\t\t\t\t\t\t\t\t\t: formatCurrency(fiatValue, currency)}\n\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t<div\n\t\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\t\"flex items-center\",\n\t\t\t\t\t\t\t\t\t`gap-${responsive.gap.tight}`,\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t<p className=\"text-sm text-muted-foreground\">\n\t\t\t\t\t\t\t\t\t{currency === \"BSV\"\n\t\t\t\t\t\t\t\t\t\t? formatCurrency(fiatValue, \"USD\")\n\t\t\t\t\t\t\t\t\t\t: formatBSV(totalSatoshis, 8)}\n\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t{changeData && (\n\t\t\t\t\t\t\t\t\t<Badge\n\t\t\t\t\t\t\t\t\t\tvariant=\"secondary\"\n\t\t\t\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\t\t\t\"text-xs\",\n\t\t\t\t\t\t\t\t\t\t\tchangeData.color === \"green\"\n\t\t\t\t\t\t\t\t\t\t\t\t? \"bg-green-100 text-green-700\"\n\t\t\t\t\t\t\t\t\t\t\t\t: changeData.color === \"red\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t? \"bg-red-100 text-red-700\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t: \"\",\n\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t{changeData.text}\n\t\t\t\t\t\t\t\t\t</Badge>\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</>\n\t\t\t\t\t) : (\n\t\t\t\t\t\t<>\n\t\t\t\t\t\t\t<p className=\"text-3xl sm:text-4xl font-bold\">••••••</p>\n\t\t\t\t\t\t\t<p className=\"text-sm text-muted-foreground\">Balance hidden</p>\n\t\t\t\t\t\t</>\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\n\t\t\t\t{/* Balance Breakdown */}\n\t\t\t\t{showValues && (\n\t\t\t\t\t<>\n\t\t\t\t\t\t<Separator className=\"my-4\" />\n\t\t\t\t\t\t<dl className=\"space-y-2\">\n\t\t\t\t\t\t\t<div className=\"flex justify-between items-center\">\n\t\t\t\t\t\t\t\t<dt\n\t\t\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\t\t\"text-sm text-muted-foreground\",\n\t\t\t\t\t\t\t\t\t\t`min-w-[${SPACING.XXL}px]`,\n\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\tConfirmed\n\t\t\t\t\t\t\t\t</dt>\n\t\t\t\t\t\t\t\t<dd className=\"text-sm font-medium\">\n\t\t\t\t\t\t\t\t\t{formatBSV(balance.confirmed)} BSV\n\t\t\t\t\t\t\t\t</dd>\n\t\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t\t{balance.unconfirmed > 0 && (\n\t\t\t\t\t\t\t\t<div className=\"flex justify-between items-center\">\n\t\t\t\t\t\t\t\t\t<dt\n\t\t\t\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\t\t\t\"text-sm text-muted-foreground\",\n\t\t\t\t\t\t\t\t\t\t\t`min-w-[${SPACING.XXL}px]`,\n\t\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\tPending\n\t\t\t\t\t\t\t\t\t</dt>\n\t\t\t\t\t\t\t\t\t<dd className=\"text-sm font-medium text-orange-600\">\n\t\t\t\t\t\t\t\t\t\t{formatBSV(balance.unconfirmed)} BSV\n\t\t\t\t\t\t\t\t\t</dd>\n\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t</dl>\n\t\t\t\t\t</>\n\t\t\t\t)}\n\n\t\t\t\t{/* Action Buttons */}\n\t\t\t\t<div\n\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\"grid\",\n\t\t\t\t\t\t`grid-cols-${responsive.gridColumns.threeColumn}`,\n\t\t\t\t\t\t`gap-${responsive.gap.tight}`,\n\t\t\t\t\t)}\n\t\t\t\t>\n\t\t\t\t\t<Button\n\t\t\t\t\t\tvariant=\"secondary\"\n\t\t\t\t\t\tsize=\"sm\"\n\t\t\t\t\t\tonClick={onSend}\n\t\t\t\t\t\tdisabled={totalSatoshis === 0}\n\t\t\t\t\t>\n\t\t\t\t\t\t<ArrowUpIcon className=\"w-4 h-4 mr-1\" />\n\t\t\t\t\t\tSend\n\t\t\t\t\t</Button>\n\n\t\t\t\t\t<Button variant=\"secondary\" size=\"sm\" onClick={onReceive}>\n\t\t\t\t\t\t<ArrowDownIcon className=\"w-4 h-4 mr-1\" />\n\t\t\t\t\t\tReceive\n\t\t\t\t\t</Button>\n\n\t\t\t\t\t<Button variant=\"secondary\" size=\"sm\" onClick={onBuy}>\n\t\t\t\t\t\t<PlusIcon className=\"w-4 h-4 mr-1\" />\n\t\t\t\t\t\tBuy\n\t\t\t\t\t</Button>\n\t\t\t\t</div>\n\n\t\t\t\t{/* Address Info */}\n\t\t\t\t<Separator className=\"my-4\" />\n\t\t\t\t<dl className=\"space-y-2\">\n\t\t\t\t\t<div className=\"flex justify-between items-center\">\n\t\t\t\t\t\t<dt\n\t\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\t\"text-sm text-muted-foreground\",\n\t\t\t\t\t\t\t\t`min-w-[${SPACING.XXL}px]`,\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\tAddress\n\t\t\t\t\t\t</dt>\n\t\t\t\t\t\t<dd className=\"text-xs sm:text-sm font-mono text-muted-foreground\">\n\t\t\t\t\t\t\t{user.address.slice(0, 12)}...{user.address.slice(-8)}\n\t\t\t\t\t\t</dd>\n\t\t\t\t\t</div>\n\t\t\t\t</dl>\n\t\t\t</CardContent>\n\t\t</Card>\n\t);\n}\n\n// Simplified version for compact display\nexport function CompactWalletOverview({\n\tbalance,\n\tshowValues = true,\n\tcurrency = \"USD\",\n\texchangeRate = 50,\n\tclassName,\n}: Pick<\n\tWalletOverviewProps,\n\t\"balance\" | \"showValues\" | \"currency\" | \"exchangeRate\" | \"className\"\n>) {\n\tconst { isAuthenticated } = useBitcoinAuth();\n\n\tif (!isAuthenticated || !balance) {\n\t\treturn (\n\t\t\t<div\n\t\t\t\tclassName={cn(\n\t\t\t\t\t\"flex items-center\",\n\t\t\t\t\t`gap-${responsive.gap.tight}`,\n\t\t\t\t\tclassName,\n\t\t\t\t)}\n\t\t\t>\n\t\t\t\t<p className=\"text-sm text-muted-foreground\">No wallet connected</p>\n\t\t\t</div>\n\t\t);\n\t}\n\n\tconst totalSatoshis = balance.confirmed + balance.unconfirmed;\n\tconst totalBSV = totalSatoshis / 100000000;\n\tconst fiatValue = totalBSV * exchangeRate;\n\n\treturn (\n\t\t<div\n\t\t\tclassName={cn(\n\t\t\t\t\"flex items-center\",\n\t\t\t\t`gap-${responsive.gap.tight}`,\n\t\t\t\tclassName,\n\t\t\t)}\n\t\t>\n\t\t\t{showValues ? (\n\t\t\t\t<>\n\t\t\t\t\t<p className=\"text-base font-medium\">\n\t\t\t\t\t\t{currency === \"BSV\"\n\t\t\t\t\t\t\t? formatBSV(totalSatoshis, 4)\n\t\t\t\t\t\t\t: formatCurrency(fiatValue, currency)}\n\t\t\t\t\t</p>\n\t\t\t\t\t{balance.unconfirmed > 0 && (\n\t\t\t\t\t\t<Badge\n\t\t\t\t\t\t\tvariant=\"secondary\"\n\t\t\t\t\t\t\tclassName=\"text-xs bg-orange-100 text-orange-700\"\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t+{formatBSV(balance.unconfirmed, 4)} pending\n\t\t\t\t\t\t</Badge>\n\t\t\t\t\t)}\n\t\t\t\t</>\n\t\t\t) : (\n\t\t\t\t<p className=\"text-base font-medium\">••••••</p>\n\t\t\t)}\n\t\t</div>\n\t);\n}\n", "target": "<%- config.aliases.components %>/walletoverview.tsx" } ] }