{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "linked-accounts",
  "title": "Linked Accounts",
  "description": "Display and manage linked social accounts. Shows all linked social accounts with the ability to unlink them, followed by available providers that can be linked.",
  "dependencies": [
    "@better-auth-ui/react@latest",
    "@better-auth-ui/core@latest",
    "better-auth",
    "lucide-react"
  ],
  "registryDependencies": [
    "button",
    "card",
    "item",
    "skeleton",
    "sonner",
    "spinner"
  ],
  "files": [
    {
      "path": "src/components/auth/settings/security/linked-accounts.tsx",
      "content": "\"use client\"\n\nimport { useAuth, useListAccounts } from \"@better-auth-ui/react\"\nimport { Fragment } from \"react\"\nimport { Card, CardContent } from \"@/components/ui/card\"\nimport {\n  Item,\n  ItemContent,\n  ItemGroup,\n  ItemMedia,\n  ItemSeparator\n} from \"@/components/ui/item\"\nimport { Skeleton } from \"@/components/ui/skeleton\"\nimport { cn } from \"@/lib/utils\"\nimport { LinkedAccount } from \"./linked-account\"\n\nexport type LinkedAccountsProps = {\n  className?: string\n}\n\n/**\n * Render a card showing linked social accounts and available social providers to link.\n *\n * Linked accounts (excluding the \"credential\" provider) are shown with an unlink control;\n * available providers are shown with a link control. Button states and labels reflect\n * ongoing link/unlink activity and use localization for provider-specific text.\n *\n * @returns A JSX element containing the linked accounts card\n */\nexport function LinkedAccounts({ className }: LinkedAccountsProps) {\n  const {\n    authClient,\n    localization,\n    multipleAccountsPerProvider,\n    socialProviders\n  } = useAuth()\n\n  const { data: accounts, isPending } = useListAccounts(authClient)\n\n  const linkedAccounts = accounts?.filter(\n    (account) => account.providerId !== \"credential\"\n  )\n\n  const linkedProviderIds = new Set(linkedAccounts?.map((a) => a.providerId))\n\n  const availableProviders =\n    multipleAccountsPerProvider === false\n      ? socialProviders?.filter((p) => !linkedProviderIds.has(p))\n      : socialProviders\n\n  const allRows = [\n    ...(linkedAccounts?.map((account) => ({\n      key: account.id,\n      account,\n      provider: account.providerId\n    })) ?? []),\n    ...(availableProviders?.map((provider) => ({\n      key: provider,\n      account: undefined,\n      provider\n    })) ?? [])\n  ]\n\n  return (\n    <div>\n      <h2 className=\"text-sm font-semibold mb-3\">\n        {localization.settings.linkedAccounts}\n      </h2>\n\n      <Card className={cn(\"p-0\", className)}>\n        <CardContent className=\"p-0\">\n          <ItemGroup className=\"gap-0\">\n            {isPending\n              ? socialProviders?.map((provider, index) => (\n                  <Fragment key={provider}>\n                    {index > 0 && <ItemSeparator />}\n                    <AccountRowSkeleton />\n                  </Fragment>\n                ))\n              : allRows.map((row, index) => (\n                  <Fragment key={row.key}>\n                    {index > 0 && <ItemSeparator />}\n                    <LinkedAccount\n                      account={row.account}\n                      provider={row.provider}\n                    />\n                  </Fragment>\n                ))}\n          </ItemGroup>\n        </CardContent>\n      </Card>\n    </div>\n  )\n}\n\nfunction AccountRowSkeleton() {\n  return (\n    <Item>\n      <ItemMedia>\n        <Skeleton className=\"size-10 rounded-md\" />\n      </ItemMedia>\n      <ItemContent>\n        <Skeleton className=\"h-4 w-20\" />\n        <Skeleton className=\"h-3 w-32\" />\n      </ItemContent>\n    </Item>\n  )\n}\n",
      "type": "registry:component",
      "target": "@components/auth/settings/security/linked-accounts.tsx"
    },
    {
      "path": "src/components/auth/settings/security/linked-account.tsx",
      "content": "\"use client\"\n\nimport { getProviderName } from \"@better-auth-ui/core\"\nimport {\n  providerIcons,\n  useAccountInfo,\n  useAuth,\n  useLinkSocial,\n  useUnlinkAccount\n} from \"@better-auth-ui/react\"\nimport type { Account, SocialProvider } from \"better-auth\"\nimport { Link2, Link2Off, Plug } from \"lucide-react\"\nimport { toast } from \"sonner\"\n\nimport { Button } from \"@/components/ui/button\"\nimport {\n  Item,\n  ItemActions,\n  ItemContent,\n  ItemDescription,\n  ItemMedia,\n  ItemTitle\n} from \"@/components/ui/item\"\nimport { Skeleton } from \"@/components/ui/skeleton\"\nimport { Spinner } from \"@/components/ui/spinner\"\nimport { cn } from \"@/lib/utils\"\n\nexport type LinkedAccountProps = {\n  account?: Account\n  provider: SocialProvider\n}\n\n/**\n * Render a single linked social account row with provider info and link/unlink control.\n *\n * Fetches additional account information from the provider using the accountInfo API\n * and displays the provider name, account details, and a link/unlink button.\n *\n * @param account - The account object containing id, accountId, and providerId\n * @param provider - The provider id\n * @returns A JSX element containing the linked account row\n */\nexport function LinkedAccount({ account, provider }: LinkedAccountProps) {\n  const { authClient, baseURL, localization } = useAuth()\n\n  const { data: accountInfo, isPending: isLoadingInfo } = useAccountInfo(\n    authClient,\n    { query: { accountId: account?.accountId } }\n  )\n\n  const { mutate: linkSocial, isPending: isLinking } = useLinkSocial(authClient)\n\n  const { mutate: unlinkAccount, isPending: isUnlinking } = useUnlinkAccount(\n    authClient,\n    {\n      onSuccess: () => toast.success(localization.settings.accountUnlinked)\n    }\n  )\n\n  const ProviderIcon = providerIcons[provider]\n  const providerName = getProviderName(provider)\n\n  const displayName =\n    accountInfo?.data?.login ||\n    accountInfo?.data?.username ||\n    accountInfo?.user?.email ||\n    accountInfo?.user?.name ||\n    account?.accountId\n\n  return (\n    <Item>\n      <ItemMedia variant=\"icon\">\n        {ProviderIcon ? (\n          <ProviderIcon className={cn(!account && \"opacity-50\")} />\n        ) : (\n          <Plug className={cn(!account && \"opacity-50\")} />\n        )}\n      </ItemMedia>\n      <ItemContent>\n        <ItemTitle>{providerName}</ItemTitle>\n        {account && isLoadingInfo ? (\n          <Skeleton className=\"my-0.5 h-3 w-24\" />\n        ) : (\n          <ItemDescription>\n            {account\n              ? displayName\n              : localization.settings.linkProvider.replace(\n                  \"{{provider}}\",\n                  providerName\n                )}\n          </ItemDescription>\n        )}\n      </ItemContent>\n      <ItemActions>\n        {account ? (\n          <Button\n            variant=\"outline\"\n            size=\"sm\"\n            onClick={() => unlinkAccount({ providerId: account.providerId })}\n            disabled={isUnlinking}\n            aria-label={localization.settings.unlinkProvider.replace(\n              \"{{provider}}\",\n              providerName\n            )}\n          >\n            {isUnlinking ? <Spinner /> : <Link2Off />}\n            {localization.settings.unlinkProvider\n              .replace(\"{{provider}}\", \"\")\n              .trim()}\n          </Button>\n        ) : (\n          <Button\n            variant=\"outline\"\n            size=\"sm\"\n            onClick={() =>\n              linkSocial({\n                provider,\n                callbackURL: `${baseURL}${window.location.pathname}`\n              })\n            }\n            disabled={isLinking}\n            aria-label={localization.settings.linkProvider.replace(\n              \"{{provider}}\",\n              providerName\n            )}\n          >\n            {isLinking ? <Spinner /> : <Link2 />}\n            {localization.settings.link}\n          </Button>\n        )}\n      </ItemActions>\n    </Item>\n  )\n}\n",
      "type": "registry:component",
      "target": "@components/auth/settings/security/linked-account.tsx"
    }
  ],
  "type": "registry:component"
}