{
  "name": "wallet-integrations-oauthproviders",
  "type": "registry:component",
  "dependencies": [
    "@radix-ui/react-icons"
  ],
  "devDependencies": [],
  "registryDependencies": [],
  "files": [
    {
      "path": "components/wallet-integrations/OAuthProviders.tsx",
      "type": "registry:component",
      "content": "\"use client\";\n\nimport { CheckIcon } from \"@radix-ui/react-icons\";\nimport type React from \"react\";\nimport { useState } from \"react\";\nimport { cn } from \"../../lib/utils.js\";\nimport { HandCashIcon } from \"../ui-components/HandCashIcon.js\";\nimport { Badge } from \"../ui/badge.js\";\nimport { Button } from \"../ui/button.js\";\n\n/**\n * Official brand colors for OAuth providers\n * These can be used optionally when you want to preserve brand identity\n */\nexport const OAUTH_BRAND_COLORS = {\n\tgoogle: {\n\t\tblue: \"#4285F4\",\n\t\tgreen: \"#34A853\",\n\t\tyellow: \"#FBBC05\",\n\t\tred: \"#EA4335\",\n\t},\n\thandcash: \"#38CB7C\",\n\tgithub: \"#24292e\",\n\ttwitter: \"#1DA1F2\",\n\tdiscord: \"#5865F2\",\n\tfacebook: \"#1877F2\",\n\tmicrosoft: \"#00BCF2\",\n} as const;\n\nexport interface OAuthProvider {\n\tid: string;\n\tname: string;\n\ticon: React.ReactNode;\n\tenabled?: boolean;\n}\n\nexport interface OAuthProvidersProps {\n\tproviders?: OAuthProvider[];\n\taction?: \"signin\" | \"signup\" | \"link\";\n\tcallbackUrl?: string;\n\tlinkedProviders?: string[];\n\tonProviderClick?: (provider: string) => void;\n\tdisabled?: boolean;\n\tloading?: boolean;\n\tclassName?: string;\n\tsize?: \"1\" | \"2\" | \"3\";\n}\n\nconst DEFAULT_PROVIDERS: OAuthProvider[] = [\n\t{\n\t\tid: \"google\",\n\t\tname: \"Google\",\n\t\tenabled: true,\n\t\ticon: (\n\t\t\t<svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\">\n\t\t\t\t<title>Google</title>\n\t\t\t\t<g fill=\"currentColor\">\n\t\t\t\t\t<path d=\"M22.56 12.25c0-.78-.07-1.53-.2-2.25H12v4.26h5.92c-.26 1.37-1.04 2.53-2.21 3.31v2.77h3.57c2.08-1.92 3.28-4.74 3.28-8.09z\" />\n\t\t\t\t\t<path d=\"M12 23c2.97 0 5.46-.98 7.28-2.66l-3.57-2.77c-.98.66-2.23 1.06-3.71 1.06-2.86 0-5.29-1.93-6.16-4.53H2.18v2.84C3.99 20.53 7.7 23 12 23z\" />\n\t\t\t\t\t<path d=\"M5.84 14.09c-.22-.66-.35-1.36-.35-2.09s.13-1.43.35-2.09V7.07H2.18C1.43 8.55 1 10.22 1 12s.43 3.45 1.18 4.93l2.85-2.22.81-.62z\" />\n\t\t\t\t\t<path d=\"M12 5.38c1.62 0 3.06.56 4.21 1.64l3.15-3.15C17.45 2.09 14.97 1 12 1 7.7 1 3.99 3.47 2.18 7.07l3.66 2.84c.87-2.6 3.3-4.53 6.16-4.53z\" />\n\t\t\t\t</g>\n\t\t\t</svg>\n\t\t),\n\t},\n\t{\n\t\tid: \"github\",\n\t\tname: \"GitHub\",\n\t\tenabled: true,\n\t\ticon: (\n\t\t\t<svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n\t\t\t\t<title>GitHub</title>\n\t\t\t\t<path d=\"M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z\" />\n\t\t\t</svg>\n\t\t),\n\t},\n\t{\n\t\tid: \"handcash\",\n\t\tname: \"HandCash\",\n\t\tenabled: true,\n\t\ticon: <HandCashIcon />,\n\t},\n\t{\n\t\tid: \"yours\",\n\t\tname: \"Yours Wallet\",\n\t\tenabled: true,\n\t\ticon: (\n\t\t\t<svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n\t\t\t\t<title>Yours Wallet</title>\n\t\t\t\t<path d=\"M12 2L2 7v10c0 5.55 3.84 10.74 9 12 5.16-1.26 9-6.45 9-12V7l-10-5zm0 2.18l8 4v9.82c0 4.28-2.88 8.24-7 9.34V4.18zm-1 5.82v8c-2.55-.9-4.65-3.08-5.58-5.95L11 9.82z\" />\n\t\t\t</svg>\n\t\t),\n\t},\n\t{\n\t\tid: \"twitter\",\n\t\tname: \"X (Twitter)\",\n\t\tenabled: false,\n\t\ticon: (\n\t\t\t<svg width=\"20\" height=\"20\" viewBox=\"0 0 24 24\" fill=\"currentColor\">\n\t\t\t\t<title>X (Twitter)</title>\n\t\t\t\t<path d=\"M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-5.214-6.817L4.99 21.75H1.68l7.73-8.835L1.254 2.25H8.08l4.713 6.231zm-1.161 17.52h1.833L7.084 4.126H5.117z\" />\n\t\t\t</svg>\n\t\t),\n\t},\n];\n\nexport function OAuthProviders({\n\tproviders = DEFAULT_PROVIDERS,\n\taction = \"signin\",\n\tcallbackUrl: _callbackUrl,\n\tlinkedProviders = [],\n\tonProviderClick,\n\tdisabled = false,\n\tloading = false,\n\tclassName = \"\",\n\tsize: _size = \"2\",\n}: OAuthProvidersProps) {\n\tconst [clickedProvider, setClickedProvider] = useState<string | null>(null);\n\n\tconst handleProviderClick = async (providerId: string) => {\n\t\tif (disabled || loading) return;\n\n\t\tsetClickedProvider(providerId);\n\n\t\ttry {\n\t\t\tif (onProviderClick) {\n\t\t\t\tawait onProviderClick(providerId);\n\t\t\t}\n\t\t} finally {\n\t\t\tsetClickedProvider(null);\n\t\t}\n\t};\n\n\t// Filter to only enabled providers\n\tconst enabledProviders = providers.filter((p) => p.enabled !== false);\n\n\tif (enabledProviders.length === 0) {\n\t\treturn (\n\t\t\t<div className={cn(\"flex flex-col items-center gap-2\", className)}>\n\t\t\t\t<p className=\"text-sm text-muted-foreground\">\n\t\t\t\t\tNo OAuth providers configured\n\t\t\t\t</p>\n\t\t\t</div>\n\t\t);\n\t}\n\n\tconst getActionText = (provider: OAuthProvider, isLinked: boolean) => {\n\t\tif (isLinked) return \"Linked\";\n\n\t\tswitch (action) {\n\t\t\tcase \"signin\":\n\t\t\t\treturn `Continue with ${provider.name}`;\n\t\t\tcase \"signup\":\n\t\t\t\treturn `Sign up with ${provider.name}`;\n\t\t\tcase \"link\":\n\t\t\t\treturn `Link ${provider.name}`;\n\t\t\tdefault:\n\t\t\t\treturn `Continue with ${provider.name}`;\n\t\t}\n\t};\n\n\treturn (\n\t\t<div className={cn(\"flex flex-col gap-3\", className)}>\n\t\t\t{enabledProviders.map((provider) => {\n\t\t\t\tconst isLinked = linkedProviders.includes(provider.id);\n\t\t\t\tconst isClicked = clickedProvider === provider.id;\n\t\t\t\tconst isDisabled = disabled || loading || isLinked;\n\n\t\t\t\treturn (\n\t\t\t\t\t<Button\n\t\t\t\t\t\tkey={provider.id}\n\t\t\t\t\t\tdata-provider={provider.id}\n\t\t\t\t\t\tvariant={isLinked ? \"secondary\" : \"outline\"}\n\t\t\t\t\t\tsize=\"lg\"\n\t\t\t\t\t\tdisabled={isDisabled}\n\t\t\t\t\t\tonClick={() => handleProviderClick(provider.id)}\n\t\t\t\t\t\tclassName={cn(\n\t\t\t\t\t\t\t\"w-full justify-start p-4 transition-all\",\n\t\t\t\t\t\t\tisLinked && \"border-green-500\",\n\t\t\t\t\t\t\tisDisabled && \"cursor-not-allowed\",\n\t\t\t\t\t\t)}\n\t\t\t\t\t>\n\t\t\t\t\t\t<div className=\"flex items-center gap-3 w-full\">\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\"shrink-0 w-6 h-6 flex items-center justify-center\",\n\t\t\t\t\t\t\t\t\tisLinked ? \"text-green-600\" : \"text-foreground\",\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{isClicked ? (\n\t\t\t\t\t\t\t\t\t<div className=\"animate-spin w-5 h-5\">\n\t\t\t\t\t\t\t\t\t\t<svg\n\t\t\t\t\t\t\t\t\t\t\twidth=\"20\"\n\t\t\t\t\t\t\t\t\t\t\theight=\"20\"\n\t\t\t\t\t\t\t\t\t\t\tviewBox=\"0 0 24 24\"\n\t\t\t\t\t\t\t\t\t\t\tfill=\"none\"\n\t\t\t\t\t\t\t\t\t\t\taria-label=\"Loading\"\n\t\t\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t\t\t<title>Loading</title>\n\t\t\t\t\t\t\t\t\t\t\t<circle\n\t\t\t\t\t\t\t\t\t\t\t\tcx=\"12\"\n\t\t\t\t\t\t\t\t\t\t\t\tcy=\"12\"\n\t\t\t\t\t\t\t\t\t\t\t\tr=\"10\"\n\t\t\t\t\t\t\t\t\t\t\t\tstroke=\"currentColor\"\n\t\t\t\t\t\t\t\t\t\t\t\tstrokeWidth=\"4\"\n\t\t\t\t\t\t\t\t\t\t\t\tclassName=\"opacity-25\"\n\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t\t<path\n\t\t\t\t\t\t\t\t\t\t\t\tfill=\"currentColor\"\n\t\t\t\t\t\t\t\t\t\t\t\td=\"M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z\"\n\t\t\t\t\t\t\t\t\t\t\t\tclassName=\"opacity-75\"\n\t\t\t\t\t\t\t\t\t\t\t/>\n\t\t\t\t\t\t\t\t\t\t</svg>\n\t\t\t\t\t\t\t\t\t</div>\n\t\t\t\t\t\t\t\t) : (\n\t\t\t\t\t\t\t\t\tprovider.icon\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<span className=\"flex-1 text-left text-base font-medium\">\n\t\t\t\t\t\t\t\t{getActionText(provider, isLinked)}\n\t\t\t\t\t\t\t</span>\n\n\t\t\t\t\t\t\t{isLinked && (\n\t\t\t\t\t\t\t\t<Badge\n\t\t\t\t\t\t\t\t\tvariant=\"secondary\"\n\t\t\t\t\t\t\t\t\tclassName=\"bg-green-100 text-green-800\"\n\t\t\t\t\t\t\t\t>\n\t\t\t\t\t\t\t\t\t<CheckIcon width=\"12\" height=\"12\" />\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\t\t\t\t\t</Button>\n\t\t\t\t);\n\t\t\t})}\n\n\t\t\t{action === \"signin\" && enabledProviders.length > 0 && (\n\t\t\t\t<p className=\"text-xs text-muted-foreground text-center mt-2\">\n\t\t\t\t\tBy continuing, you agree to our Terms of Service and Privacy Policy\n\t\t\t\t</p>\n\t\t\t)}\n\t\t</div>\n\t);\n}\n",
      "target": "<%- config.aliases.components %>/oauthproviders.tsx"
    }
  ]
}