{
  "name": "market-components-buylistingbutton",
  "type": "registry:component",
  "dependencies": [
    "@radix-ui/react-icons"
  ],
  "devDependencies": [],
  "registryDependencies": [],
  "files": [
    {
      "path": "components/market/components/BuyListingButton.tsx",
      "type": "registry:component",
      "content": "import { Cross2Icon, InfoCircledIcon } from \"@radix-ui/react-icons\";\n// BuyListingButton Component - Purchase marketplace listings\nimport { useState } from \"react\";\nimport { useBitcoinAuth } from \"../../../hooks/useBitcoinAuth.js\";\nimport { cn } from \"../../../lib/utils.js\";\nimport { Badge } from \"../../ui/badge.js\";\nimport { Button } from \"../../ui/button.js\";\nimport {\n\tDialog,\n\tDialogClose,\n\tDialogContent,\n\tDialogTitle,\n\tDialogTrigger,\n} from \"../../ui/dialog.js\";\nimport { Separator } from \"../../ui/separator.js\";\nimport {\n\ttype WalletUser,\n\tgetWalletProps,\n} from \"../../wallet/types/extended-user.js\";\nimport { formatBSV, formatTokenAmount } from \"../../wallet/utils/currency.js\";\nimport { useBuyListing } from \"../hooks/useBuyListing.js\";\nimport type { MarketListing } from \"../types/listings.js\";\nimport { AssetType } from \"../types/market.js\";\n\nexport interface BuyListingButtonProps {\n\tlisting: MarketListing;\n\tdisabled?: boolean;\n\tloading?: boolean;\n\tclassName?: string;\n\tvariant?: \"default\" | \"secondary\" | \"outline\" | \"ghost\";\n\tsize?: \"sm\" | \"default\" | \"lg\";\n\n\t// Callbacks\n\tonSuccess?: (txid: string) => void;\n\tonError?: (error: Error) => void;\n\n\t// UI customization\n\tbuttonText?: string;\n\tdialogTitle?: string;\n\tshowDetails?: boolean;\n}\n\nexport function BuyListingButton({\n\tlisting,\n\tdisabled = false,\n\tloading = false,\n\tclassName,\n\tvariant = \"default\",\n\tsize = \"default\",\n\tonSuccess,\n\tonError,\n\tbuttonText = \"Buy Now\",\n\tdialogTitle = \"Confirm Purchase\",\n\tshowDetails = true,\n}: BuyListingButtonProps) {\n\tconst [open, setOpen] = useState(false);\n\n\tconst { user, isAuthenticated } = useBitcoinAuth();\n\tconst { buyListing, isLoading: isBuying } = useBuyListing({\n\t\tonSuccess: (result) => {\n\t\t\tsetOpen(false);\n\t\t\tonSuccess?.(result.txid || \"\");\n\t\t},\n\t\tonError: (error) => {\n\t\t\tonError?.(error);\n\t\t},\n\t});\n\n\tconst handleConfirmPurchase = async () => {\n\t\tif (!isAuthenticated || !user) {\n\t\t\tonError?.(new Error(\"Authentication required\"));\n\t\t\treturn;\n\t\t}\n\n\t\ttry {\n\t\t\tbuyListing(listing);\n\t\t} catch (error) {\n\t\t\tonError?.(error as Error);\n\t\t}\n\t};\n\n\tif (!isAuthenticated) {\n\t\treturn (\n\t\t\t<Button disabled variant=\"ghost\" size={size} className={className}>\n\t\t\t\tSign in to Buy\n\t\t\t</Button>\n\t\t);\n\t}\n\n\t// Check if user has sufficient balance\n\tconst walletProps = getWalletProps(user as WalletUser);\n\tconst availableBalance = walletProps.balance.confirmed || 0;\n\tconst estimatedFee = 500; // Basic fee estimate\n\tconst totalCost = listing.priceSats + estimatedFee;\n\tconst canAfford = totalCost <= availableBalance;\n\n\treturn (\n\t\t<Dialog open={open} onOpenChange={setOpen}>\n\t\t\t<DialogTrigger asChild>\n\t\t\t\t<Button\n\t\t\t\t\tvariant={variant}\n\t\t\t\t\tsize={size}\n\t\t\t\t\tdisabled={disabled || loading || !canAfford}\n\t\t\t\t\tclassName={className}\n\t\t\t\t>\n\t\t\t\t\t📦 {buttonText}\n\t\t\t\t</Button>\n\t\t\t</DialogTrigger>\n\n\t\t\t<DialogContent className=\"max-w-md\">\n\t\t\t\t<DialogTitle>{dialogTitle}</DialogTitle>\n\n\t\t\t\t<div className=\"flex flex-col gap-4\">\n\t\t\t\t\t{/* Listing Details */}\n\t\t\t\t\t{showDetails && (\n\t\t\t\t\t\t<div className=\"flex flex-col gap-3\">\n\t\t\t\t\t\t\t<div className=\"flex flex-col gap-2\">\n\t\t\t\t\t\t\t\t<p className=\"text-sm font-medium\">Item</p>\n\t\t\t\t\t\t\t\t<div className=\"flex items-center gap-2\">\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\tlisting.assetType === AssetType.Ordinals\n\t\t\t\t\t\t\t\t\t\t\t\t? \"bg-orange-100 text-orange-700\"\n\t\t\t\t\t\t\t\t\t\t\t\t: listing.assetType === AssetType.BSV20\n\t\t\t\t\t\t\t\t\t\t\t\t\t? \"bg-blue-100 text-blue-700\"\n\t\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)}\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t{listing.assetType.toUpperCase()}\n\t\t\t\t\t\t\t\t\t</Badge>\n\t\t\t\t\t\t\t\t\t{listing.symbol && (\n\t\t\t\t\t\t\t\t\t\t<p className=\"text-sm\">{listing.symbol}</p>\n\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t\t{listing.tokenId && (\n\t\t\t\t\t\t\t\t\t\t<p className=\"text-xs text-muted-foreground font-mono\">\n\t\t\t\t\t\t\t\t\t\t\t{listing.tokenId.slice(0, 8)}...\n\t\t\t\t\t\t\t\t\t\t\t{listing.tokenId.slice(-8)}\n\t\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t\t\t{listing.tokenAmount && (\n\t\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\tAmount:{\" \"}\n\t\t\t\t\t\t\t\t\t\t{formatTokenAmount(\n\t\t\t\t\t\t\t\t\t\t\tlisting.tokenAmount,\n\t\t\t\t\t\t\t\t\t\t\tlisting.decimals || 0,\n\t\t\t\t\t\t\t\t\t\t)}{\" \"}\n\t\t\t\t\t\t\t\t\t\t{listing.symbol}\n\t\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t\t<Separator />\n\n\t\t\t\t\t\t\t{/* Seller Info */}\n\t\t\t\t\t\t\t<div className=\"flex flex-col gap-2\">\n\t\t\t\t\t\t\t\t<p className=\"text-sm font-medium\">Seller</p>\n\t\t\t\t\t\t\t\t<p className=\"text-xs font-mono text-muted-foreground\">\n\t\t\t\t\t\t\t\t\t{listing.payAddress?.slice(0, 12) || \"Unknown\"}...\n\t\t\t\t\t\t\t\t\t{listing.payAddress?.slice(-8) || \"\"}\n\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t)}\n\n\t\t\t\t\t<Separator />\n\n\t\t\t\t\t{/* Price Breakdown */}\n\t\t\t\t\t<div className=\"flex flex-col gap-2 p-3 bg-muted rounded-md\">\n\t\t\t\t\t\t<p className=\"text-sm font-medium\">Purchase Summary</p>\n\n\t\t\t\t\t\t<div className=\"flex justify-between\">\n\t\t\t\t\t\t\t<p className=\"text-xs text-muted-foreground\">Item Price</p>\n\t\t\t\t\t\t\t<p className=\"text-xs\">{formatBSV(listing.priceSats)} BSV</p>\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t<div className=\"flex justify-between\">\n\t\t\t\t\t\t\t<p className=\"text-xs text-muted-foreground\">\n\t\t\t\t\t\t\t\tNetwork Fee (est.)\n\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t<p className=\"text-xs\">{formatBSV(estimatedFee)} BSV</p>\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t<Separator className=\"my-1\" />\n\n\t\t\t\t\t\t<div className=\"flex justify-between\">\n\t\t\t\t\t\t\t<p className=\"text-sm font-medium\">Total</p>\n\t\t\t\t\t\t\t<p className=\"text-sm font-medium\">{formatBSV(totalCost)} BSV</p>\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t{!canAfford && (\n\t\t\t\t\t\t\t<div className=\"flex items-center gap-2 mt-2\">\n\t\t\t\t\t\t\t\t<InfoCircledIcon className=\"text-destructive\" />\n\t\t\t\t\t\t\t\t<p className=\"text-xs text-destructive\">\n\t\t\t\t\t\t\t\t\tInsufficient balance. Need{\" \"}\n\t\t\t\t\t\t\t\t\t{formatBSV(totalCost - availableBalance)} more BSV\n\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t)}\n\t\t\t\t\t</div>\n\n\t\t\t\t\t{/* Balance Info */}\n\t\t\t\t\t<div className=\"flex justify-between items-center\">\n\t\t\t\t\t\t<p className=\"text-sm text-muted-foreground\">Your Balance</p>\n\t\t\t\t\t\t<p className=\"text-sm font-medium\">\n\t\t\t\t\t\t\t{formatBSV(availableBalance)} BSV\n\t\t\t\t\t\t</p>\n\t\t\t\t\t</div>\n\n\t\t\t\t\t{/* Warning */}\n\t\t\t\t\t<div className=\"flex flex-col gap-2 p-3 bg-amber-50 rounded-md border border-amber-200\">\n\t\t\t\t\t\t<div className=\"flex items-center gap-2\">\n\t\t\t\t\t\t\t<InfoCircledIcon className=\"text-amber-600\" />\n\t\t\t\t\t\t\t<p className=\"text-sm font-medium text-amber-700\">\n\t\t\t\t\t\t\t\tPurchase Confirmation\n\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t\t<p className=\"text-xs text-amber-700\">\n\t\t\t\t\t\t\tThis purchase is final and cannot be reversed. Make sure you want\n\t\t\t\t\t\t\tto buy this item.\n\t\t\t\t\t\t</p>\n\t\t\t\t\t</div>\n\n\t\t\t\t\t{/* Actions */}\n\t\t\t\t\t<div className=\"flex gap-3 mt-4 justify-end\">\n\t\t\t\t\t\t<DialogClose asChild>\n\t\t\t\t\t\t\t<Button variant=\"secondary\">Cancel</Button>\n\t\t\t\t\t\t</DialogClose>\n\n\t\t\t\t\t\t<Button\n\t\t\t\t\t\t\tonClick={handleConfirmPurchase}\n\t\t\t\t\t\t\tdisabled={isBuying || !canAfford}\n\t\t\t\t\t\t>\n\t\t\t\t\t\t\t{isBuying ? \"Purchasing...\" : \"Confirm Purchase\"}\n\t\t\t\t\t\t</Button>\n\t\t\t\t\t</div>\n\t\t\t\t</div>\n\n\t\t\t\t<DialogClose className=\"absolute top-2 right-2\">\n\t\t\t\t\t<Button variant=\"ghost\" size=\"sm\" className=\"h-8 w-8 p-0\">\n\t\t\t\t\t\t<Cross2Icon className=\"h-4 w-4\" />\n\t\t\t\t\t</Button>\n\t\t\t\t</DialogClose>\n\t\t\t</DialogContent>\n\t\t</Dialog>\n\t);\n}\n\n// Quick buy button with minimal UI\nexport function QuickBuyButton({\n\tlisting,\n\tonSuccess,\n\tonError,\n\t...props\n}: Omit<BuyListingButtonProps, \"buttonText\" | \"dialogTitle\" | \"showDetails\">) {\n\treturn (\n\t\t<BuyListingButton\n\t\t\tlisting={listing}\n\t\t\tonSuccess={onSuccess}\n\t\t\tonError={onError}\n\t\t\tbuttonText=\"Buy\"\n\t\t\tdialogTitle={`Buy ${listing.symbol || \"Item\"}`}\n\t\t\tshowDetails={false}\n\t\t\tsize=\"sm\"\n\t\t\tvariant=\"secondary\"\n\t\t\t{...props}\n\t\t/>\n\t);\n}\n",
      "target": "<%- config.aliases.components %>/buylistingbutton.tsx"
    }
  ]
}