UNPKG

bigblocks

Version:

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

15 lines 8.16 kB
{ "name": "profile-management-profileviewer", "type": "registry:component", "dependencies": [], "devDependencies": [], "registryDependencies": [], "files": [ { "path": "components/profile-management/ProfileViewer.tsx", "type": "registry:component", "content": "\"use client\";\n\nimport {\n\tCheckIcon,\n\tCopyIcon,\n\tDotsHorizontalIcon,\n\tExternalLinkIcon,\n\tInfoCircledIcon,\n\tPencil1Icon,\n\tRocketIcon,\n} from \"@radix-ui/react-icons\";\nimport React from \"react\";\nimport { CONTAINER_WIDTHS, SPACING } from \"../../lib/layout-constants.js\";\nimport type { ProfileInfo } from \"../../lib/types.js\";\nimport { BitcoinAvatar } from \"../ui-components/BitcoinAvatar.js\";\nimport { Badge } from \"../ui/badge.js\";\nimport { Button } from \"../ui/button.js\";\nimport { Card } from \"../ui/card.js\";\nimport {\n\tDropdownMenu,\n\tDropdownMenuContent,\n\tDropdownMenuItem,\n\tDropdownMenuTrigger,\n} from \"../ui/dropdown-menu.js\";\n\nexport interface ProfileViewerProps {\n\tprofile: ProfileInfo;\n\tshowAddress?: boolean;\n\tshowPublishStatus?: boolean;\n\tshowActions?: boolean;\n\tonEdit?: () => void;\n\tonPublish?: () => void;\n\tclassName?: string;\n\tvariant?: \"surface\" | \"classic\" | \"ghost\";\n\tsize?: \"1\" | \"2\" | \"3\" | \"4\";\n}\n\n/**\n * ProfileViewer displays a complete view of a BAP profile\n *\n * Features:\n * - Profile avatar, name, and description\n * - Bitcoin address with copy functionality\n * - Published/unpublished status\n * - Edit and publish actions\n * - Responsive layout\n */\nexport function ProfileViewer({\n\tprofile,\n\tshowAddress = true,\n\tshowPublishStatus = true,\n\tshowActions = true,\n\tonEdit,\n\tonPublish,\n\tclassName = \"\",\n\tvariant = \"surface\",\n\tsize = \"3\",\n}: ProfileViewerProps) {\n\tconst [copied, setCopied] = React.useState(false);\n\n\tconst handleCopyAddress = async () => {\n\t\ttry {\n\t\t\tawait navigator.clipboard.writeText(profile.address);\n\t\t\tsetCopied(true);\n\t\t\tsetTimeout(() => setCopied(false), 2000);\n\t\t} catch (error) {\n\t\t\tconsole.error(\"Failed to copy address:\", error);\n\t\t}\n\t};\n\n\tconst getDisplayName = () => {\n\t\t// Priority order for name fields\n\t\treturn (\n\t\t\tprofile.name ||\n\t\t\tprofile.alternateName ||\n\t\t\tprofile.givenName ||\n\t\t\tprofile.familyName ||\n\t\t\tprofile.legalName ||\n\t\t\t\"Unnamed Profile\"\n\t\t);\n\t};\n\n\tconst getInitials = (name?: string) => {\n\t\tif (!name) return \"?\";\n\t\treturn name\n\t\t\t.split(\" \")\n\t\t\t.map((word) => word[0])\n\t\t\t.join(\"\")\n\t\t\t.toUpperCase()\n\t\t\t.slice(0, 2);\n\t};\n\n\treturn (\n\t\t<Card className={`p-6 max-w-2xl ${className}`}>\n\t\t\t<div className=\"flex flex-col gap-4\">\n\t\t\t\t{/* Profile Header */}\n\t\t\t\t<div className=\"flex items-start gap-4\">\n\t\t\t\t\t<BitcoinAvatar\n\t\t\t\t\t\tsize=\"lg\"\n\t\t\t\t\t\tsrc={profile.image}\n\t\t\t\t\t\talt={getDisplayName()}\n\t\t\t\t\t\tfallback={getInitials(getDisplayName())}\n\t\t\t\t\t/>\n\n\t\t\t\t\t<div className=\"flex flex-col gap-1 flex-1\">\n\t\t\t\t\t\t<div className=\"flex items-center gap-2\">\n\t\t\t\t\t\t\t<h2 className=\"text-xl font-bold\">{getDisplayName()}</h2>\n\t\t\t\t\t\t\t{showPublishStatus && (\n\t\t\t\t\t\t\t\t<Badge\n\t\t\t\t\t\t\t\t\tvariant={profile.isPublished ? \"default\" : \"secondary\"}\n\t\t\t\t\t\t\t\t\tclassName={`text-xs ${\n\t\t\t\t\t\t\t\t\t\tprofile.isPublished ? \"text-green-700\" : \"text-orange-700\"\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\t{profile.isPublished ? \"Published\" : \"Unpublished\"}\n\t\t\t\t\t\t\t\t</Badge>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t</div>\n\n\t\t\t\t\t\t{profile.description && (\n\t\t\t\t\t\t\t<p className=\"text-sm text-muted-foreground\">\n\t\t\t\t\t\t\t\t{profile.description}\n\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t)}\n\t\t\t\t\t</div>\n\n\t\t\t\t\t{/* Actions dropdown */}\n\t\t\t\t\t{showActions && (onEdit || onPublish) && (\n\t\t\t\t\t\t<DropdownMenu>\n\t\t\t\t\t\t\t<DropdownMenuTrigger asChild>\n\t\t\t\t\t\t\t\t<Button variant=\"ghost\" size=\"sm\" className=\"rounded-full\">\n\t\t\t\t\t\t\t\t\t<DotsHorizontalIcon className=\"h-4 w-4\" />\n\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t</DropdownMenuTrigger>\n\t\t\t\t\t\t\t<DropdownMenuContent>\n\t\t\t\t\t\t\t\t{onEdit && (\n\t\t\t\t\t\t\t\t\t<DropdownMenuItem onClick={onEdit}>\n\t\t\t\t\t\t\t\t\t\t<Pencil1Icon className=\"h-4 w-4 mr-2\" />\n\t\t\t\t\t\t\t\t\t\tEdit Profile\n\t\t\t\t\t\t\t\t\t</DropdownMenuItem>\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t{onPublish && !profile.isPublished && (\n\t\t\t\t\t\t\t\t\t<DropdownMenuItem onClick={onPublish}>\n\t\t\t\t\t\t\t\t\t\t<RocketIcon className=\"h-4 w-4 mr-2\" />\n\t\t\t\t\t\t\t\t\t\tPublish to Blockchain\n\t\t\t\t\t\t\t\t\t</DropdownMenuItem>\n\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t</DropdownMenuContent>\n\t\t\t\t\t\t</DropdownMenu>\n\t\t\t\t\t)}\n\t\t\t\t</div>\n\n\t\t\t\t{/* Profile Details */}\n\t\t\t\t<div className=\"space-y-3\">\n\t\t\t\t\t<div className=\"flex items-center\">\n\t\t\t\t\t\t<div className=\"w-24 text-sm text-muted-foreground\">Profile ID</div>\n\t\t\t\t\t\t<div className=\"flex-1\">\n\t\t\t\t\t\t\t<p className=\"text-sm text-muted-foreground\">{profile.id}</p>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\n\t\t\t\t\t{showAddress && (\n\t\t\t\t\t\t<div className=\"flex items-center\">\n\t\t\t\t\t\t\t<div className=\"w-24 text-sm text-muted-foreground\">Address</div>\n\t\t\t\t\t\t\t<div className=\"flex-1 flex items-center gap-2\">\n\t\t\t\t\t\t\t\t<p className=\"text-sm font-mono\">\n\t\t\t\t\t\t\t\t\t{profile.address.slice(0, 8)}...{profile.address.slice(-6)}\n\t\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t\t\t<Button variant=\"ghost\" size=\"sm\" onClick={handleCopyAddress}>\n\t\t\t\t\t\t\t\t\t{copied ? (\n\t\t\t\t\t\t\t\t\t\t<CheckIcon className=\"h-3 w-3\" />\n\t\t\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\t\t\t<CopyIcon className=\"h-3 w-3\" />\n\t\t\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t\t<Button variant=\"ghost\" size=\"sm\" asChild>\n\t\t\t\t\t\t\t\t\t<a\n\t\t\t\t\t\t\t\t\t\thref={`https://whatsonchain.com/address/${profile.address}`}\n\t\t\t\t\t\t\t\t\t\ttarget=\"_blank\"\n\t\t\t\t\t\t\t\t\t\trel=\"noopener noreferrer\"\n\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t<ExternalLinkIcon className=\"h-3 w-3\" />\n\t\t\t\t\t\t\t\t\t</a>\n\t\t\t\t\t\t\t\t</Button>\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{profile.isPublished && (\n\t\t\t\t\t\t<div className=\"flex items-center\">\n\t\t\t\t\t\t\t<div className=\"w-24 text-sm text-muted-foreground\">Status</div>\n\t\t\t\t\t\t\t<div className=\"flex-1 flex items-center gap-2\">\n\t\t\t\t\t\t\t\t<div className=\"w-2 h-2 rounded-full bg-green-500\" />\n\t\t\t\t\t\t\t\t<p className=\"text-sm text-green-700\">\n\t\t\t\t\t\t\t\t\tPublished to blockchain\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\t\t\t\t</div>\n\n\t\t\t\t{/* Additional Info */}\n\t\t\t\t{!profile.isPublished && (\n\t\t\t\t\t<Card className=\"p-3 bg-orange-50 border border-orange-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=\"h-4 w-4 text-orange-600\" />\n\t\t\t\t\t\t\t<p className=\"text-sm text-orange-700\">\n\t\t\t\t\t\t\t\tThis profile is not yet published to the blockchain.\n\t\t\t\t\t\t\t</p>\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</Card>\n\t\t\t\t)}\n\n\t\t\t\t{/* Action buttons - placed at bottom for better flow */}\n\t\t\t\t{showActions && (onEdit || (onPublish && !profile.isPublished)) && (\n\t\t\t\t\t<div className=\"border-t pt-4 mt-2\">\n\t\t\t\t\t\t<div className=\"flex gap-2 justify-end\">\n\t\t\t\t\t\t\t{onEdit && (\n\t\t\t\t\t\t\t\t<Button variant=\"outline\" size=\"sm\" onClick={onEdit}>\n\t\t\t\t\t\t\t\t\tEdit\n\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t\t{onPublish && !profile.isPublished && (\n\t\t\t\t\t\t\t\t<Button variant=\"default\" size=\"sm\" onClick={onPublish}>\n\t\t\t\t\t\t\t\t\tPublish\n\t\t\t\t\t\t\t\t</Button>\n\t\t\t\t\t\t\t)}\n\t\t\t\t\t\t</div>\n\t\t\t\t\t</div>\n\t\t\t\t)}\n\t\t\t</div>\n\t\t</Card>\n\t);\n}\n", "target": "<%- config.aliases.components %>/profileviewer.tsx" } ] }