@b3dotfun/sdk 0.0.64-alpha.2 → 0.0.65-test.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (229) hide show
  1. package/dist/cjs/anyspend/react/components/AnySpend.js +73 -33
  2. package/dist/cjs/anyspend/react/components/AnySpendCustom.js +1 -1
  3. package/dist/cjs/anyspend/react/components/common/CryptoPaymentMethod.js +1 -1
  4. package/dist/cjs/anyspend/react/components/common/FeeDetailPanel.js +1 -1
  5. package/dist/cjs/anyspend/react/components/common/FiatPaymentMethod.js +2 -2
  6. package/dist/cjs/anyspend/react/components/common/OrderHistory.d.ts +1 -1
  7. package/dist/cjs/anyspend/react/components/common/OrderHistory.js +7 -3
  8. package/dist/cjs/anyspend/react/components/common/PanelOnrampPayment.js +1 -1
  9. package/dist/cjs/anyspend/react/components/common/PointsDetailPanel.js +1 -1
  10. package/dist/cjs/anyspend/react/components/common/RecipientSelection.js +1 -1
  11. package/dist/cjs/global-account/react/components/AccountAssets/AccountAssets.js +38 -2
  12. package/dist/cjs/global-account/react/components/AvatarEditor/AvatarEditor.js +139 -33
  13. package/dist/cjs/global-account/react/components/B3DynamicModal.js +25 -6
  14. package/dist/cjs/global-account/react/components/Deposit/Deposit.d.ts +1 -0
  15. package/dist/cjs/global-account/react/components/Deposit/Deposit.js +65 -0
  16. package/dist/cjs/global-account/react/components/LinkAccount/LinkAccount.d.ts +6 -4
  17. package/dist/cjs/global-account/react/components/LinkAccount/LinkAccount.js +113 -279
  18. package/dist/cjs/global-account/react/components/LinkAccount/LinkNewAccount.d.ts +4 -0
  19. package/dist/cjs/global-account/react/components/LinkAccount/LinkNewAccount.js +331 -0
  20. package/dist/cjs/global-account/react/components/ManageAccount/AppsContent.d.ts +6 -0
  21. package/dist/cjs/global-account/react/components/ManageAccount/AppsContent.js +34 -0
  22. package/dist/cjs/global-account/react/components/ManageAccount/BalanceContent.js +2 -2
  23. package/dist/cjs/global-account/react/components/ManageAccount/BottomNavigation.d.ts +2 -0
  24. package/dist/cjs/global-account/react/components/ManageAccount/BottomNavigation.js +23 -0
  25. package/dist/cjs/global-account/react/components/ManageAccount/Header.d.ts +3 -0
  26. package/dist/cjs/global-account/react/components/ManageAccount/Header.js +120 -0
  27. package/dist/cjs/global-account/react/components/ManageAccount/HomeActions.d.ts +5 -0
  28. package/dist/cjs/global-account/react/components/ManageAccount/HomeActions.js +43 -0
  29. package/dist/cjs/global-account/react/components/ManageAccount/HomeContent.d.ts +6 -0
  30. package/dist/cjs/global-account/react/components/ManageAccount/HomeContent.js +16 -0
  31. package/dist/cjs/global-account/react/components/ManageAccount/ManageAccount.js +24 -193
  32. package/dist/cjs/global-account/react/components/ManageAccount/NFTContent.d.ts +2 -0
  33. package/dist/cjs/global-account/react/components/ManageAccount/NFTContent.js +15 -0
  34. package/dist/cjs/global-account/react/components/ManageAccount/ProfileSection.d.ts +2 -0
  35. package/dist/cjs/global-account/react/components/ManageAccount/ProfileSection.js +44 -0
  36. package/dist/cjs/global-account/react/components/ManageAccount/SettingsContent.d.ts +7 -0
  37. package/dist/cjs/global-account/react/components/ManageAccount/SettingsContent.js +50 -0
  38. package/dist/cjs/global-account/react/components/ManageAccount/SettingsMenuItem.d.ts +9 -0
  39. package/dist/cjs/global-account/react/components/ManageAccount/SettingsMenuItem.js +8 -0
  40. package/dist/cjs/global-account/react/components/ManageAccount/SettingsProfileCard.d.ts +2 -0
  41. package/dist/cjs/global-account/react/components/ManageAccount/SettingsProfileCard.js +38 -0
  42. package/dist/cjs/global-account/react/components/ManageAccount/TokenContent.d.ts +2 -0
  43. package/dist/cjs/global-account/react/components/ManageAccount/TokenContent.js +22 -0
  44. package/dist/cjs/global-account/react/components/ModalHeader/ModalHeader.d.ts +10 -0
  45. package/dist/cjs/global-account/react/components/ModalHeader/ModalHeader.js +12 -0
  46. package/dist/cjs/global-account/react/components/Send/Send.d.ts +5 -0
  47. package/dist/cjs/global-account/react/components/Send/Send.js +187 -0
  48. package/dist/cjs/global-account/react/components/icons/BellIcon.d.ts +3 -0
  49. package/dist/cjs/global-account/react/components/icons/BellIcon.js +5 -0
  50. package/dist/cjs/global-account/react/components/icons/ChevronDownIcon.d.ts +2 -0
  51. package/dist/cjs/global-account/react/components/icons/ChevronDownIcon.js +7 -0
  52. package/dist/cjs/global-account/react/components/icons/CopyIcon.d.ts +2 -0
  53. package/dist/cjs/global-account/react/components/icons/CopyIcon.js +7 -0
  54. package/dist/cjs/global-account/react/components/icons/LinkIcon.d.ts +3 -0
  55. package/dist/cjs/global-account/react/components/icons/LinkIcon.js +5 -0
  56. package/dist/cjs/global-account/react/components/icons/LockIcon.d.ts +3 -0
  57. package/dist/cjs/global-account/react/components/icons/LockIcon.js +5 -0
  58. package/dist/cjs/global-account/react/components/icons/WalletIcon.d.ts +2 -0
  59. package/dist/cjs/global-account/react/components/icons/WalletIcon.js +7 -0
  60. package/dist/cjs/global-account/react/components/index.d.ts +4 -2
  61. package/dist/cjs/global-account/react/components/index.js +11 -4
  62. package/dist/cjs/global-account/react/components/ui/Tabs.js +2 -2
  63. package/dist/cjs/global-account/react/components/ui/dialog.js +2 -2
  64. package/dist/cjs/global-account/react/hooks/index.d.ts +1 -1
  65. package/dist/cjs/global-account/react/hooks/index.js +3 -1
  66. package/dist/cjs/global-account/react/hooks/useB3BalanceFromAddresses.js +1 -0
  67. package/dist/cjs/global-account/react/stores/index.d.ts +1 -0
  68. package/dist/cjs/global-account/react/stores/index.js +3 -1
  69. package/dist/cjs/global-account/react/stores/useModalStore.d.ts +34 -3
  70. package/dist/cjs/global-account/react/stores/useRecentAddressesStore.d.ts +25 -0
  71. package/dist/cjs/global-account/react/stores/useRecentAddressesStore.js +36 -0
  72. package/dist/cjs/global-account/react/utils/profileDisplay.d.ts +2 -0
  73. package/dist/cjs/global-account/react/utils/profileDisplay.js +2 -2
  74. package/dist/cjs/shared/constants/chains/supported.d.ts +2 -2
  75. package/dist/cjs/shared/utils/ipfs.js +1 -1
  76. package/dist/esm/anyspend/react/components/AnySpend.js +74 -34
  77. package/dist/esm/anyspend/react/components/AnySpendCustom.js +1 -1
  78. package/dist/esm/anyspend/react/components/common/CryptoPaymentMethod.js +1 -1
  79. package/dist/esm/anyspend/react/components/common/FeeDetailPanel.js +1 -1
  80. package/dist/esm/anyspend/react/components/common/FiatPaymentMethod.js +2 -2
  81. package/dist/esm/anyspend/react/components/common/OrderHistory.d.ts +1 -1
  82. package/dist/esm/anyspend/react/components/common/OrderHistory.js +6 -5
  83. package/dist/esm/anyspend/react/components/common/PanelOnrampPayment.js +1 -1
  84. package/dist/esm/anyspend/react/components/common/PointsDetailPanel.js +1 -1
  85. package/dist/esm/anyspend/react/components/common/RecipientSelection.js +1 -1
  86. package/dist/esm/global-account/react/components/AccountAssets/AccountAssets.js +38 -2
  87. package/dist/esm/global-account/react/components/AvatarEditor/AvatarEditor.js +140 -34
  88. package/dist/esm/global-account/react/components/B3DynamicModal.js +25 -6
  89. package/dist/esm/global-account/react/components/Deposit/Deposit.d.ts +1 -0
  90. package/dist/esm/global-account/react/components/Deposit/Deposit.js +59 -0
  91. package/dist/esm/global-account/react/components/LinkAccount/LinkAccount.d.ts +6 -4
  92. package/dist/esm/global-account/react/components/LinkAccount/LinkAccount.js +113 -280
  93. package/dist/esm/global-account/react/components/LinkAccount/LinkNewAccount.d.ts +4 -0
  94. package/dist/esm/global-account/react/components/LinkAccount/LinkNewAccount.js +325 -0
  95. package/dist/esm/global-account/react/components/ManageAccount/AppsContent.d.ts +6 -0
  96. package/dist/esm/global-account/react/components/ManageAccount/AppsContent.js +32 -0
  97. package/dist/esm/global-account/react/components/ManageAccount/BalanceContent.js +2 -2
  98. package/dist/esm/global-account/react/components/ManageAccount/BottomNavigation.d.ts +2 -0
  99. package/dist/esm/global-account/react/components/ManageAccount/BottomNavigation.js +21 -0
  100. package/dist/esm/global-account/react/components/ManageAccount/Header.d.ts +3 -0
  101. package/dist/esm/global-account/react/components/ManageAccount/Header.js +81 -0
  102. package/dist/esm/global-account/react/components/ManageAccount/HomeActions.d.ts +5 -0
  103. package/dist/esm/global-account/react/components/ManageAccount/HomeActions.js +41 -0
  104. package/dist/esm/global-account/react/components/ManageAccount/HomeContent.d.ts +6 -0
  105. package/dist/esm/global-account/react/components/ManageAccount/HomeContent.js +10 -0
  106. package/dist/esm/global-account/react/components/ManageAccount/ManageAccount.js +26 -195
  107. package/dist/esm/global-account/react/components/ManageAccount/NFTContent.d.ts +2 -0
  108. package/dist/esm/global-account/react/components/ManageAccount/NFTContent.js +13 -0
  109. package/dist/esm/global-account/react/components/ManageAccount/ProfileSection.d.ts +2 -0
  110. package/dist/esm/global-account/react/components/ManageAccount/ProfileSection.js +42 -0
  111. package/dist/esm/global-account/react/components/ManageAccount/SettingsContent.d.ts +7 -0
  112. package/dist/esm/global-account/react/components/ManageAccount/SettingsContent.js +45 -0
  113. package/dist/esm/global-account/react/components/ManageAccount/SettingsMenuItem.d.ts +9 -0
  114. package/dist/esm/global-account/react/components/ManageAccount/SettingsMenuItem.js +6 -0
  115. package/dist/esm/global-account/react/components/ManageAccount/SettingsProfileCard.d.ts +2 -0
  116. package/dist/esm/global-account/react/components/ManageAccount/SettingsProfileCard.js +36 -0
  117. package/dist/esm/global-account/react/components/ManageAccount/TokenContent.d.ts +2 -0
  118. package/dist/esm/global-account/react/components/ManageAccount/TokenContent.js +20 -0
  119. package/dist/esm/global-account/react/components/ModalHeader/ModalHeader.d.ts +10 -0
  120. package/dist/esm/global-account/react/components/ModalHeader/ModalHeader.js +10 -0
  121. package/dist/esm/global-account/react/components/Send/Send.d.ts +5 -0
  122. package/dist/esm/global-account/react/components/Send/Send.js +181 -0
  123. package/dist/esm/global-account/react/components/icons/BellIcon.d.ts +3 -0
  124. package/dist/esm/global-account/react/components/icons/BellIcon.js +3 -0
  125. package/dist/esm/global-account/react/components/icons/ChevronDownIcon.d.ts +2 -0
  126. package/dist/esm/global-account/react/components/icons/ChevronDownIcon.js +4 -0
  127. package/dist/esm/global-account/react/components/icons/CopyIcon.d.ts +2 -0
  128. package/dist/esm/global-account/react/components/icons/CopyIcon.js +4 -0
  129. package/dist/esm/global-account/react/components/icons/LinkIcon.d.ts +3 -0
  130. package/dist/esm/global-account/react/components/icons/LinkIcon.js +3 -0
  131. package/dist/esm/global-account/react/components/icons/LockIcon.d.ts +3 -0
  132. package/dist/esm/global-account/react/components/icons/LockIcon.js +3 -0
  133. package/dist/esm/global-account/react/components/icons/WalletIcon.d.ts +2 -0
  134. package/dist/esm/global-account/react/components/icons/WalletIcon.js +4 -0
  135. package/dist/esm/global-account/react/components/index.d.ts +4 -2
  136. package/dist/esm/global-account/react/components/index.js +7 -2
  137. package/dist/esm/global-account/react/components/ui/Tabs.js +2 -2
  138. package/dist/esm/global-account/react/components/ui/dialog.js +2 -2
  139. package/dist/esm/global-account/react/hooks/index.d.ts +1 -1
  140. package/dist/esm/global-account/react/hooks/index.js +1 -1
  141. package/dist/esm/global-account/react/hooks/useB3BalanceFromAddresses.js +1 -0
  142. package/dist/esm/global-account/react/stores/index.d.ts +1 -0
  143. package/dist/esm/global-account/react/stores/index.js +1 -0
  144. package/dist/esm/global-account/react/stores/useModalStore.d.ts +34 -3
  145. package/dist/esm/global-account/react/stores/useRecentAddressesStore.d.ts +25 -0
  146. package/dist/esm/global-account/react/stores/useRecentAddressesStore.js +33 -0
  147. package/dist/esm/global-account/react/utils/profileDisplay.d.ts +2 -0
  148. package/dist/esm/global-account/react/utils/profileDisplay.js +2 -2
  149. package/dist/esm/shared/constants/chains/supported.d.ts +2 -2
  150. package/dist/esm/shared/utils/ipfs.js +1 -1
  151. package/dist/styles/index.css +1 -1
  152. package/dist/types/anyspend/react/components/common/OrderHistory.d.ts +1 -1
  153. package/dist/types/global-account/react/components/Deposit/Deposit.d.ts +1 -0
  154. package/dist/types/global-account/react/components/LinkAccount/LinkAccount.d.ts +6 -4
  155. package/dist/types/global-account/react/components/LinkAccount/LinkNewAccount.d.ts +4 -0
  156. package/dist/types/global-account/react/components/ManageAccount/AppsContent.d.ts +6 -0
  157. package/dist/types/global-account/react/components/ManageAccount/BottomNavigation.d.ts +2 -0
  158. package/dist/types/global-account/react/components/ManageAccount/Header.d.ts +3 -0
  159. package/dist/types/global-account/react/components/ManageAccount/HomeActions.d.ts +5 -0
  160. package/dist/types/global-account/react/components/ManageAccount/HomeContent.d.ts +6 -0
  161. package/dist/types/global-account/react/components/ManageAccount/NFTContent.d.ts +2 -0
  162. package/dist/types/global-account/react/components/ManageAccount/ProfileSection.d.ts +2 -0
  163. package/dist/types/global-account/react/components/ManageAccount/SettingsContent.d.ts +7 -0
  164. package/dist/types/global-account/react/components/ManageAccount/SettingsMenuItem.d.ts +9 -0
  165. package/dist/types/global-account/react/components/ManageAccount/SettingsProfileCard.d.ts +2 -0
  166. package/dist/types/global-account/react/components/ManageAccount/TokenContent.d.ts +2 -0
  167. package/dist/types/global-account/react/components/ModalHeader/ModalHeader.d.ts +10 -0
  168. package/dist/types/global-account/react/components/Send/Send.d.ts +5 -0
  169. package/dist/types/global-account/react/components/icons/BellIcon.d.ts +3 -0
  170. package/dist/types/global-account/react/components/icons/ChevronDownIcon.d.ts +2 -0
  171. package/dist/types/global-account/react/components/icons/CopyIcon.d.ts +2 -0
  172. package/dist/types/global-account/react/components/icons/LinkIcon.d.ts +3 -0
  173. package/dist/types/global-account/react/components/icons/LockIcon.d.ts +3 -0
  174. package/dist/types/global-account/react/components/icons/WalletIcon.d.ts +2 -0
  175. package/dist/types/global-account/react/components/index.d.ts +4 -2
  176. package/dist/types/global-account/react/hooks/index.d.ts +1 -1
  177. package/dist/types/global-account/react/stores/index.d.ts +1 -0
  178. package/dist/types/global-account/react/stores/useModalStore.d.ts +34 -3
  179. package/dist/types/global-account/react/stores/useRecentAddressesStore.d.ts +25 -0
  180. package/dist/types/global-account/react/utils/profileDisplay.d.ts +2 -0
  181. package/dist/types/shared/constants/chains/supported.d.ts +2 -2
  182. package/package.json +1 -1
  183. package/src/anyspend/react/components/AnySpend.tsx +225 -167
  184. package/src/anyspend/react/components/AnySpendCustom.tsx +1 -1
  185. package/src/anyspend/react/components/common/CryptoPaymentMethod.tsx +1 -1
  186. package/src/anyspend/react/components/common/FeeDetailPanel.tsx +1 -1
  187. package/src/anyspend/react/components/common/FiatPaymentMethod.tsx +2 -2
  188. package/src/anyspend/react/components/common/OrderHistory.tsx +8 -13
  189. package/src/anyspend/react/components/common/PanelOnrampPayment.tsx +1 -1
  190. package/src/anyspend/react/components/common/PointsDetailPanel.tsx +1 -1
  191. package/src/anyspend/react/components/common/RecipientSelection.tsx +1 -1
  192. package/src/global-account/react/components/AccountAssets/AccountAssets.tsx +115 -25
  193. package/src/global-account/react/components/AvatarEditor/AvatarEditor.tsx +303 -126
  194. package/src/global-account/react/components/B3DynamicModal.tsx +28 -6
  195. package/src/global-account/react/components/Deposit/Deposit.tsx +211 -0
  196. package/src/global-account/react/components/LinkAccount/LinkAccount.tsx +332 -433
  197. package/src/global-account/react/components/LinkAccount/LinkNewAccount.tsx +490 -0
  198. package/src/global-account/react/components/ManageAccount/AppsContent.tsx +79 -0
  199. package/src/global-account/react/components/ManageAccount/BalanceContent.tsx +2 -3
  200. package/src/global-account/react/components/ManageAccount/BottomNavigation.tsx +83 -0
  201. package/src/global-account/react/components/ManageAccount/Header.tsx +230 -0
  202. package/src/global-account/react/components/ManageAccount/HomeActions.tsx +118 -0
  203. package/src/global-account/react/components/ManageAccount/HomeContent.tsx +42 -0
  204. package/src/global-account/react/components/ManageAccount/ManageAccount.tsx +73 -589
  205. package/src/global-account/react/components/ManageAccount/NFTContent.tsx +24 -0
  206. package/src/global-account/react/components/ManageAccount/ProfileSection.tsx +74 -0
  207. package/src/global-account/react/components/ManageAccount/SettingsContent.tsx +87 -0
  208. package/src/global-account/react/components/ManageAccount/SettingsMenuItem.tsx +31 -0
  209. package/src/global-account/react/components/ManageAccount/SettingsProfileCard.tsx +74 -0
  210. package/src/global-account/react/components/ManageAccount/TokenContent.tsx +41 -0
  211. package/src/global-account/react/components/ModalHeader/ModalHeader.tsx +50 -0
  212. package/src/global-account/react/components/Send/Send.tsx +585 -0
  213. package/src/global-account/react/components/icons/BellIcon.tsx +15 -0
  214. package/src/global-account/react/components/icons/ChevronDownIcon.tsx +17 -0
  215. package/src/global-account/react/components/icons/CopyIcon.tsx +22 -0
  216. package/src/global-account/react/components/icons/LinkIcon.tsx +15 -0
  217. package/src/global-account/react/components/icons/LockIcon.tsx +15 -0
  218. package/src/global-account/react/components/icons/WalletIcon.tsx +21 -0
  219. package/src/global-account/react/components/index.ts +9 -2
  220. package/src/global-account/react/components/ui/Tabs.tsx +5 -13
  221. package/src/global-account/react/components/ui/dialog.tsx +32 -14
  222. package/src/global-account/react/hooks/index.ts +3 -0
  223. package/src/global-account/react/hooks/useB3BalanceFromAddresses.ts +1 -0
  224. package/src/global-account/react/stores/index.ts +1 -0
  225. package/src/global-account/react/stores/useModalStore.ts +39 -2
  226. package/src/global-account/react/stores/useRecentAddressesStore.ts +55 -0
  227. package/src/global-account/react/utils/profileDisplay.ts +4 -2
  228. package/src/shared/utils/ipfs.ts +1 -1
  229. package/src/styles/index.css +6 -1
@@ -1,138 +1,69 @@
1
1
  import app from "@b3dotfun/sdk/global-account/app";
2
- import { ecosystemWalletId } from "@b3dotfun/sdk/shared/constants";
3
- import { thirdwebB3Mainnet } from "@b3dotfun/sdk/shared/constants/chains/b3Chain";
2
+ import { Button, ManageAccountModalProps, useB3, useModalStore, useQueryB3 } from "@b3dotfun/sdk/global-account/react";
4
3
  import { client } from "@b3dotfun/sdk/shared/utils/thirdweb";
5
- import { Loader2, Mail, Phone, WalletIcon } from "lucide-react";
6
- import { useCallback, useEffect, useState } from "react";
4
+ import { truncateAddress } from "@b3dotfun/sdk/shared/utils/truncateAddress";
5
+ import { Copy, Loader2, Pencil, UnlinkIcon } from "lucide-react";
6
+ import { useRef, useState } from "react";
7
7
  import { toast } from "sonner";
8
- import { useLinkProfile, useProfiles } from "thirdweb/react";
9
- import { createWallet, preAuthenticate, WalletId } from "thirdweb/wallets";
10
- import { WalletRow } from "../..";
11
- import { LinkAccountModalProps, useModalStore } from "../../stores/useModalStore";
8
+ import { useProfiles, useUnlinkProfile } from "thirdweb/react";
9
+
10
+ import { Chain } from "thirdweb";
12
11
  import { getProfileDisplayInfo } from "../../utils/profileDisplay";
13
- import { useB3 } from "../B3Provider/useB3";
14
- import { AppleIcon } from "../icons/AppleIcon";
15
- import { DiscordIcon } from "../icons/DiscordIcon";
16
- import { FarcasterIcon } from "../icons/FarcasterIcon";
17
- import { GoogleIcon } from "../icons/GoogleIcon";
18
- import { XIcon } from "../icons/XIcon";
19
- import { Button } from "../ui/button";
20
- type OTPStrategy = "email" | "phone";
21
- type SocialStrategy = "google" | "x" | "discord" | "apple" | "farcaster";
22
- type Strategy = OTPStrategy | SocialStrategy | "wallet";
23
12
 
24
- interface AuthMethod {
25
- id: Strategy;
26
- label: string;
27
- enabled: boolean;
28
- icon: React.ReactNode;
29
- walletType?: WalletId;
30
- }
13
+ // Helper function to check if a string is a wallet address and format it
14
+ const formatProfileTitle = (title: string): { displayTitle: string; isAddress: boolean } => {
15
+ // Check if title looks like an Ethereum address (0x followed by 40 hex characters)
16
+ const isEthereumAddress = /^0x[a-fA-F0-9]{40}$/.test(title);
31
17
 
32
- const AUTH_METHODS: AuthMethod[] = [
33
- { id: "email", label: "Email", enabled: true, icon: <Mail className="text-b3-primary-blue size-6" /> },
34
- { id: "phone", label: "Phone", enabled: true, icon: <Phone className="text-b3-primary-blue size-6" /> },
35
- { id: "google", label: "Google", enabled: true, icon: <GoogleIcon className="size-6" /> },
36
- { id: "x", label: "X (Twitter)", enabled: true, icon: <XIcon className="size-6" /> },
37
- { id: "discord", label: "Discord", enabled: true, icon: <DiscordIcon className="size-6" /> },
38
- { id: "apple", label: "Apple", enabled: true, icon: <AppleIcon className="size-6" /> },
39
- {
40
- id: "farcaster",
41
- label: "Farcaster",
42
- enabled: true,
43
- icon: <FarcasterIcon className="size-6" />,
44
- },
45
- ];
18
+ if (isEthereumAddress) {
19
+ return {
20
+ displayTitle: truncateAddress(title),
21
+ isAddress: true,
22
+ };
23
+ }
46
24
 
47
- const WALLET_METHODS: AuthMethod[] = [
48
- {
49
- id: "wallet",
50
- label: "Wallet",
51
- enabled: true,
52
- icon: <WalletIcon className="size-6" />,
53
- walletType: "com.coinbase.wallet",
54
- },
55
- { id: "wallet", label: "Wallet", enabled: true, icon: <WalletIcon className="size-6" />, walletType: "io.metamask" },
56
- {
57
- id: "wallet",
58
- label: "Wallet",
59
- enabled: true,
60
- icon: <WalletIcon className="size-6" />,
61
- walletType: "me.rainbow",
62
- },
63
- {
64
- id: "wallet",
65
- label: "Wallet",
66
- enabled: true,
67
- icon: <WalletIcon className="size-6" />,
68
- walletType: "app.phantom",
69
- },
70
- { id: "wallet", label: "Wallet", enabled: true, icon: <WalletIcon className="size-6" />, walletType: "io.rabby" },
71
- {
72
- id: "wallet",
73
- label: "Wallet",
74
- enabled: true,
75
- icon: <WalletIcon className="size-6" />,
76
- walletType: "walletConnect",
77
- },
78
- ];
25
+ return {
26
+ displayTitle: title,
27
+ isAddress: false,
28
+ };
29
+ };
79
30
 
80
- export function LinkAccount({
81
- onSuccess: onSuccessCallback,
82
- onError,
83
- onClose,
84
- chain,
85
- partnerId,
86
- className,
87
- }: LinkAccountModalProps & { className?: string }) {
88
- const { isLinking, linkingMethod, setLinkingState, navigateBack, setB3ModalContentType } = useModalStore();
89
- const [selectedMethod, setSelectedMethod] = useState<Strategy | null>(null);
90
- const [email, setEmail] = useState("");
91
- const [phone, setPhone] = useState("");
92
- const [otp, setOtp] = useState("");
93
- const [otpSent, setOtpSent] = useState(false);
94
- const [error, setError] = useState<string | null>(null);
95
- const { data: profilesRaw = [] } = useProfiles({ client });
31
+ import { Referrals, Users } from "@b3dotfun/b3-api";
32
+ import ModalHeader from "../ModalHeader/ModalHeader";
96
33
 
97
- // Get connected auth methods
98
- const connectedAuthMethods = profilesRaw
99
- .filter((profile: any) => !["custom_auth_endpoint"].includes(profile.type))
100
- .map((profile: any) => profile.type as Strategy);
101
-
102
- // Filter available auth methods
103
- const availableAuthMethods = AUTH_METHODS.filter(
104
- method => !connectedAuthMethods.includes(method.id) && method.enabled,
34
+ export const LinkAccount = ({
35
+ partnerId,
36
+ onLogout,
37
+ chain,
38
+ }: {
39
+ partnerId: string;
40
+ onLogout?: () => void;
41
+ chain: Chain;
42
+ }) => {
43
+ const [unlinkingAccountId, setUnlinkingAccountId] = useState<string | null>(null);
44
+ const { data: profilesRaw = [], isLoading: isLoadingProfiles } = useProfiles({ client });
45
+ const { mutate: unlinkProfile, isPending: isUnlinking } = useUnlinkProfile();
46
+ const setB3ModalContentType = useModalStore(state => state.setB3ModalContentType);
47
+ const isLinking = useModalStore(state => state.isLinking);
48
+ const setB3ModalOpen = useModalStore(state => state.setB3ModalOpen);
49
+ const contentType = useModalStore(state => state.contentType);
50
+ const { user, setUser } = useB3();
51
+ const [isUpdatingCode, setIsUpdatingCode] = useState(false);
52
+ const [newReferralCode, setNewReferralCode] = useState("");
53
+ const [isEditingCode, setIsEditingCode] = useState(false);
54
+ const referallCodeRef = useRef<HTMLInputElement>(null);
55
+ const { data: referrals, isLoading: isLoadingReferrals } = useQueryB3(
56
+ "referrals",
57
+ "find",
58
+ { query: { referrerId: user?.userId } },
59
+ !!user?.userId,
105
60
  );
106
-
107
- const profiles = profilesRaw
108
- .filter((profile: any) => !["custom_auth_endpoint"].includes(profile.type))
109
- .map((profile: any) => ({
110
- ...getProfileDisplayInfo(profile),
111
- originalProfile: profile,
112
- }));
113
-
114
- const { account } = useB3();
115
- const { mutate: linkProfile } = useLinkProfile();
116
-
117
- const onSuccess = useCallback(async () => {
118
- await onSuccessCallback?.();
119
- }, [onSuccessCallback]);
120
-
121
- // Reset linking state when component unmounts
122
- useEffect(() => {
123
- return () => {
124
- if (isLinking) {
125
- setLinkingState(false);
126
- }
127
- };
128
- }, [isLinking, setLinkingState]);
61
+ const showReferralInfo = (contentType as ManageAccountModalProps)?.showReferralInfo ?? false;
129
62
 
130
63
  const mutationOptions = {
131
64
  onError: (error: Error) => {
132
- console.error("Error linking account:", error);
65
+ console.error("Error Unlinking account:", error);
133
66
  toast.error(error.message);
134
- setLinkingState(false);
135
- onError?.(error);
136
67
  },
137
68
  onSuccess: async (data: any) => {
138
69
  console.log("Raw Link Account Data:", data);
@@ -145,348 +76,316 @@ export function LinkAccount({
145
76
  },
146
77
  };
147
78
 
148
- const validateInput = () => {
149
- if (selectedMethod === "email") {
150
- if (!email) {
151
- setError("Please enter your email address");
152
- return false;
153
- }
154
- if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
155
- setError("Please enter a valid email address");
156
- return false;
157
- }
158
- } else if (selectedMethod === "phone") {
159
- if (!phone) {
160
- setError("Please enter your phone number");
161
- return false;
162
- }
163
- if (!/^\+?[\d\s-]{10,}$/.test(phone)) {
164
- setError("Please enter a valid phone number");
165
- return false;
166
- }
167
- }
168
- setError(null);
169
- return true;
170
- };
171
-
172
- const handleSendOTP = async () => {
173
- if (!validateInput()) return;
79
+ // Fetch referred users
80
+ const currentReferralCode = user?.referralCode || user?.userId || "";
174
81
 
82
+ const handleCopyCode = async () => {
175
83
  try {
176
- setLinkingState(true, selectedMethod);
177
- setError(null);
178
-
179
- if (selectedMethod === "email") {
180
- await preAuthenticate({
181
- client,
182
- strategy: "email",
183
- email,
184
- ecosystem: {
185
- id: ecosystemWalletId,
186
- partnerId: partnerId,
187
- },
188
- });
189
- } else if (selectedMethod === "phone") {
190
- await preAuthenticate({
191
- client,
192
- strategy: "phone",
193
- phoneNumber: phone,
194
- ecosystem: {
195
- id: ecosystemWalletId,
196
- partnerId: partnerId,
197
- },
198
- });
199
- }
200
-
201
- setOtpSent(true);
84
+ await navigator.clipboard.writeText(currentReferralCode);
85
+ toast.success("Referral code copied to clipboard!");
202
86
  } catch (error) {
203
- console.error("Error sending OTP:", error);
204
- setError(error instanceof Error ? error.message : "Failed to send OTP");
205
- onError?.(error as Error);
206
- setLinkingState(false);
87
+ toast.error("Failed to copy referral code");
207
88
  }
208
89
  };
209
90
 
210
- const handleLinkAccount = async () => {
211
- if (!otp) {
212
- console.error("No OTP entered");
213
- setError("Please enter the verification code");
214
- return;
215
- }
91
+ const handleUpdateReferralCode = async () => {
92
+ if (!newReferralCode) return;
216
93
 
94
+ setIsUpdatingCode(true);
217
95
  try {
218
- setOtpSent(false);
219
- setLinkingState(true, selectedMethod);
220
- setError(null);
221
-
222
- if (selectedMethod === "email") {
223
- await linkProfile(
224
- {
225
- client,
226
- strategy: "email",
227
- email,
228
- verificationCode: otp,
229
- },
230
- mutationOptions,
231
- );
232
- } else if (selectedMethod === "phone") {
233
- await linkProfile(
234
- {
235
- client,
236
- strategy: "phone",
237
- phoneNumber: phone,
238
- verificationCode: otp,
239
- },
240
- mutationOptions,
241
- );
242
- }
96
+ // @ts-expect-error - setReferralCode is not typed for some reason
97
+ const newUser = await app.service("users").setReferralCode({
98
+ userId: user?.userId,
99
+ referralCode: newReferralCode,
100
+ });
101
+ setUser(newUser as unknown as Users);
102
+ toast.success("Referral code updated successfully!");
103
+ setIsEditingCode(false);
104
+ setNewReferralCode("");
243
105
  } catch (error) {
244
- console.error("Error linking account:", error);
245
- setError(error instanceof Error ? error.message : "Failed to link account");
246
- onError?.(error as Error);
106
+ toast.error("Failed to update referral code");
107
+ } finally {
108
+ setIsUpdatingCode(false);
247
109
  }
248
110
  };
249
111
 
250
- const handleLinkWallet = async (walletType: WalletId) => {
251
- setLinkingState(true, "wallet");
252
- console.log("selectedMethod", walletType);
253
- try {
254
- if (!walletType) {
255
- throw new Error("Wallet type not found");
256
- }
257
- await linkProfile(
258
- {
259
- client,
260
- strategy: "wallet",
261
- wallet: createWallet(walletType),
262
- chain: thirdwebB3Mainnet,
263
- },
264
- mutationOptions,
265
- );
266
- } catch (error) {
267
- console.error("Error linking account:", error);
268
- setError(error instanceof Error ? error.message : "Failed to link account");
269
- onError?.(error as Error);
270
- }
271
- };
112
+ const profiles = profilesRaw
113
+ .filter((profile: any) => !["custom_auth_endpoint"].includes(profile.type))
114
+ .map((profile: any) => ({
115
+ ...getProfileDisplayInfo(profile),
116
+ originalProfile: profile,
117
+ }));
272
118
 
273
- const handleSocialLink = async (strategy: SocialStrategy) => {
119
+ const handleUnlink = async (profile: any) => {
120
+ setUnlinkingAccountId(profile.title);
274
121
  try {
275
- console.log("handleSocialLink", strategy);
276
- setLinkingState(true, strategy);
277
- setError(null);
278
-
279
- const result = await linkProfile(
280
- {
281
- client,
282
- strategy,
283
- },
284
- mutationOptions,
285
- );
286
-
287
- console.log("result", result);
288
-
289
- // Don't close the modal yet, wait for auth to complete
290
- onSuccess?.();
122
+ unlinkProfile({ client, profileToUnlink: profile.originalProfile }, mutationOptions);
291
123
  } catch (error) {
292
- console.error("Error linking with social:", error);
293
- setError(error instanceof Error ? error.message : "Failed to link social account");
294
- onError?.(error as Error);
295
- setLinkingState(false);
124
+ console.error("Error unlinking account:", error);
125
+ } finally {
126
+ setUnlinkingAccountId(null);
296
127
  }
297
128
  };
298
129
 
299
- // Add effect to handle social auth completion
300
- useEffect(() => {
301
- if (isLinking && linkingMethod && !selectedMethod) {
302
- // This means we're in a social auth flow
303
- const checkAuthStatus = async () => {
304
- try {
305
- // Wait a bit to ensure auth is complete
306
- await new Promise(resolve => setTimeout(resolve, 1000));
307
- onClose?.();
308
- } catch (error) {
309
- console.error("Error checking auth status:", error);
310
- setLinkingState(false);
311
- }
312
- };
313
-
314
- checkAuthStatus();
315
- }
316
- }, [isLinking, linkingMethod, selectedMethod, onClose, setLinkingState]);
317
-
318
- const handleBack = useCallback(() => {
319
- if (isLinking) return;
320
- setSelectedMethod(null);
321
- setEmail("");
322
- setPhone("");
323
- setOtp("");
324
- setOtpSent(false);
325
- setError(null);
326
- setLinkingState(false);
327
- }, [isLinking, setSelectedMethod, setEmail, setPhone, setOtp, setOtpSent, setError, setLinkingState]);
328
-
329
- const handleFinishedLinking = useCallback(
330
- (success: boolean) => {
331
- if (success) {
332
- onSuccess?.();
333
- onClose?.();
334
- }
335
-
336
- setLinkingState(false);
337
- navigateBack();
338
- setB3ModalContentType({
339
- type: "manageAccount",
340
- activeTab: "settings",
341
- setActiveTab: () => {},
342
- chain,
343
- partnerId,
344
- });
345
- },
346
- [chain, navigateBack, partnerId, setB3ModalContentType, setLinkingState, onSuccess, onClose],
347
- );
348
-
349
- useEffect(() => {
350
- if (isLinking) {
351
- handleFinishedLinking(true);
352
- }
353
- // eslint-disable-next-line react-hooks/exhaustive-deps
354
- }, [profiles.length]);
355
-
356
- if (!account) {
357
- return <div className="text-b3-foreground-muted py-8 text-center">Please connect your account first</div>;
358
- }
130
+ const handleOpenLinkModal = () => {
131
+ setB3ModalOpen(true);
132
+ setB3ModalContentType({
133
+ type: "linkNewAccount",
134
+ partnerId,
135
+ chain,
136
+ onSuccess: async () => {
137
+ // Let the LinkAccount component handle modal closing
138
+ },
139
+ onError: () => {
140
+ // Let the LinkAccount component handle errors
141
+ },
142
+ onClose: () => {
143
+ // Let the LinkAccount component handle closing
144
+ },
145
+ });
146
+ };
359
147
 
360
148
  return (
361
- <div className={`b3-link-account space-y-6 p-6 ${className || ""}`} data-testid="link-account">
362
- <div className="b3-link-account-header flex items-center justify-between">
363
- <h2 className="b3-link-account-title text-b3-grey font-neue-montreal-semibold text-2xl">Link New Account</h2>
364
- {selectedMethod && (
365
- <Button variant="ghost" className="text-b3-grey hover:text-b3-grey/80" onClick={handleBack}>
366
- Backs
367
- </Button>
368
- )}
369
- </div>
370
-
371
- {!selectedMethod ? (
372
- <div className="b3-link-account-methods grid gap-3">
373
- {availableAuthMethods.map(method => (
374
- <Button
375
- key={method.id}
376
- variant="outline"
377
- className="b3-link-account-method-button border-b3-line hover:border-b3-primary-blue/30 hover:bg-b3-primary-blue/5 text-b3-grey font-neue-montreal-medium h-14 justify-start bg-transparent px-6 text-base transition-all duration-200"
378
- data-method={method.id}
379
- onClick={() => {
380
- if (method.id === "email" || method.id === "phone") {
381
- setSelectedMethod(method.id);
382
- } else {
383
- handleSocialLink(method.id as SocialStrategy);
384
- }
385
- }}
386
- disabled={linkingMethod === method.id}
387
- >
388
- {isLinking && linkingMethod === method.id ? (
389
- <Loader2 className="h-5 w-5 animate-spin" />
390
- ) : (
391
- <div className="b3-link-account-method-content flex items-center gap-4">
392
- <div className="b3-link-account-method-icon flex items-center justify-center rounded-full">
393
- {method.icon}
149
+ <div className="linked-accounts-settings">
150
+ <ModalHeader title="Linked Accounts" />
151
+ {/* Linked Accounts Section */}
152
+ <div className="linked-accounts-section space-y-4 p-5">
153
+ {isLoadingProfiles ? (
154
+ <div className="linked-accounts-loading flex justify-center py-8">
155
+ <Loader2 className="text-b3-grey animate-spin" />
156
+ </div>
157
+ ) : profiles.length > 0 ? (
158
+ <div className="linked-accounts-list space-y-4">
159
+ {profiles.map(profile => (
160
+ <div
161
+ key={profile.title}
162
+ className="linked-account-item hover:bg-b3-line group flex cursor-pointer items-center justify-between rounded-xl p-4 transition-colors"
163
+ >
164
+ <div className="linked-account-info flex items-center gap-3">
165
+ {profile.imageUrl ? (
166
+ <img
167
+ src={profile.imageUrl}
168
+ alt={profile.title}
169
+ className="linked-account-avatar linked-account-avatar-image size-10 rounded-full"
170
+ />
171
+ ) : (
172
+ <div className="linked-account-avatar linked-account-avatar-placeholder bg-b3-primary-wash flex h-10 w-10 items-center justify-center rounded-full">
173
+ <span className="linked-account-initial text-b3-grey font-neue-montreal-semibold text-sm uppercase">
174
+ {profile.initial}
175
+ </span>
176
+ </div>
177
+ )}
178
+ <div className="linked-account-details">
179
+ <div className="linked-account-title-row flex items-center gap-2">
180
+ {(() => {
181
+ const { displayTitle, isAddress } = formatProfileTitle(profile.title);
182
+
183
+ const handleCopyAddress = async (e: React.MouseEvent) => {
184
+ e.stopPropagation();
185
+ try {
186
+ await navigator.clipboard.writeText(profile.title);
187
+ toast.success("Address copied to clipboard!");
188
+ } catch (error) {
189
+ toast.error("Failed to copy address");
190
+ }
191
+ };
192
+
193
+ return (
194
+ <div className="flex items-center gap-1">
195
+ <span
196
+ className={`linked-account-title text-b3-grey font-neue-montreal-semibold ${
197
+ isAddress
198
+ ? "font-mono text-sm" // Use monospace font for addresses
199
+ : "break-words" // Use break-words for emails/names (better than break-all)
200
+ }`}
201
+ title={isAddress ? profile.title : undefined} // Show full address on hover
202
+ >
203
+ {displayTitle}
204
+ </span>
205
+ {isAddress && (
206
+ <button
207
+ onClick={handleCopyAddress}
208
+ className="linked-account-copy-button ml-1 rounded p-1 opacity-0 transition-opacity hover:bg-gray-100 group-hover:opacity-100"
209
+ title="Copy full address"
210
+ >
211
+ <Copy size={12} className="text-gray-500 hover:text-gray-700" />
212
+ </button>
213
+ )}
214
+ </div>
215
+ );
216
+ })()}
217
+ <span className="linked-account-type text-b3-foreground-muted font-neue-montreal-medium bg-b3-primary-wash rounded px-2 py-0.5 text-xs">
218
+ {profile.type.toUpperCase()}
219
+ </span>
220
+ </div>
221
+ <div className="linked-account-subtitle text-b3-foreground-muted font-neue-montreal-medium text-sm">
222
+ {profile.subtitle}
223
+ </div>
394
224
  </div>
395
- <span className="b3-link-account-method-label font-medium">{method.label}</span>
396
225
  </div>
397
- )}
398
- </Button>
399
- ))}
400
- {WALLET_METHODS.map(method => {
401
- if (!method.walletType) {
402
- return null;
403
- }
404
-
405
- return (
406
- <WalletRow
407
- key={method.walletType}
408
- walletId={method.walletType as WalletId}
409
- onClick={() => handleLinkWallet(method.walletType as WalletId)}
410
- isLoading={isLinking}
411
- />
412
- );
413
- })}
414
- {availableAuthMethods.length === 0 && (
415
- <div className="text-b3-foreground-muted py-8 text-center">
416
- All available authentication methods have been connected
417
- </div>
418
- )}
419
- </div>
420
- ) : (
421
- <div className="b3-link-account-form space-y-4">
422
- {selectedMethod === "email" && (
423
- <div className="space-y-2">
424
- <label className="text-b3-grey font-neue-montreal-medium text-sm">Email Address</label>
425
- <input
426
- type="email"
427
- placeholder="Enter your email"
428
- className="bg-b3-line text-b3-grey font-neue-montreal-medium focus:ring-b3-primary-blue/20 w-full rounded-xl p-4 focus:outline-none focus:ring-2"
429
- value={email}
430
- onChange={e => setEmail(e.target.value)}
431
- disabled={otpSent || (isLinking && linkingMethod === "email")}
432
- />
433
- </div>
434
- )}
226
+ <Button
227
+ variant="ghost"
228
+ size="icon"
229
+ className="linked-account-unlink-button text-b3-grey hover:text-b3-negative"
230
+ onClick={() => handleUnlink(profile)}
231
+ disabled={unlinkingAccountId === profile.title || isUnlinking}
232
+ >
233
+ {unlinkingAccountId === profile.title || isUnlinking ? (
234
+ <Loader2 className="linked-account-unlink-loading animate-spin" />
235
+ ) : (
236
+ <UnlinkIcon size={16} className="linked-account-unlink-icon" />
237
+ )}
238
+ </Button>
239
+ </div>
240
+ ))}
241
+ </div>
242
+ ) : (
243
+ <div className="linked-accounts-empty text-b3-foreground-muted py-8 text-center">
244
+ No linked accounts found
245
+ </div>
246
+ )}
435
247
 
436
- {selectedMethod === "phone" && (
437
- <div className="space-y-2">
438
- <label className="text-b3-grey font-neue-montreal-medium text-sm">Phone Number</label>
439
- <input
440
- type="tel"
441
- placeholder="Enter your phone number"
442
- className="bg-b3-line text-b3-grey font-neue-montreal-medium focus:ring-b3-primary-blue/20 w-full rounded-xl p-4 focus:outline-none focus:ring-2"
443
- value={phone}
444
- onChange={e => setPhone(e.target.value)}
445
- disabled={otpSent || (isLinking && linkingMethod === "phone")}
248
+ {/* Link New Account Button */}
249
+ <Button
250
+ onClick={handleOpenLinkModal}
251
+ disabled={isLinking}
252
+ className="bg-b3-primary-blue hover:bg-b3-primary-blue/90 border-white/12 group relative h-12 w-full rounded-xl border-2 px-[18px] text-base font-semibold text-white shadow-[inset_0px_0px_0px_1px_rgba(10,13,18,0.18),inset_0px_-2px_0px_0px_rgba(10,13,18,0.05)] transition-all"
253
+ >
254
+ <div className="flex items-center justify-center gap-1.5">
255
+ <svg
256
+ width="20"
257
+ height="20"
258
+ viewBox="0 0 20 20"
259
+ fill="none"
260
+ xmlns="http://www.w3.org/2000/svg"
261
+ className="shrink-0"
262
+ >
263
+ <path
264
+ d="M10.0001 13.3333V6.66667M6.66675 10H13.3334M18.3334 10C18.3334 14.6024 14.6025 18.3333 10.0001 18.3333C5.39771 18.3333 1.66675 14.6024 1.66675 10C1.66675 5.39763 5.39771 1.66667 10.0001 1.66667C14.6025 1.66667 18.3334 5.39763 18.3334 10Z"
265
+ stroke="currentColor"
266
+ strokeWidth="1.67"
267
+ strokeLinecap="round"
268
+ strokeLinejoin="round"
446
269
  />
447
- <p className="text-b3-foreground-muted font-neue-montreal-medium text-sm">
448
- Include country code (e.g., +1 for US)
449
- </p>
450
- </div>
451
- )}
452
-
453
- {error && <div className="text-b3-negative font-neue-montreal-medium py-2 text-sm">{error}</div>}
270
+ </svg>
271
+ <span>Link new account</span>
272
+ </div>
273
+ </Button>
274
+ </div>
454
275
 
455
- {(selectedMethod === "email" || selectedMethod === "phone") &&
456
- (otpSent ? (
457
- <div className="space-y-4">
458
- <div className="space-y-2">
459
- <label className="text-b3-grey font-neue-montreal-medium text-sm">Verification Code</label>
460
- <input
461
- type="text"
462
- placeholder="Enter verification code"
463
- className="bg-b3-line text-b3-grey font-neue-montreal-medium focus:ring-b3-primary-blue/20 w-full rounded-xl p-4 focus:outline-none focus:ring-2"
464
- value={otp}
465
- onChange={e => setOtp(e.target.value)}
466
- />
276
+ {showReferralInfo && (
277
+ /* Referral Section */
278
+ <div className="referrals-section space-y-4">
279
+ <h3 className="referrals-title text-b3-grey font-neue-montreal-semibold text-xl">Referrals</h3>
280
+
281
+ {/* Referral Code */}
282
+ <div className="referral-code-container bg-b3-line rounded-xl p-4">
283
+ {isEditingCode && (
284
+ <div className="referral-code-header-editing">
285
+ <div className="referral-code-title text-b3-grey font-neue-montreal-semibold">Your Referral Code</div>
286
+ <div className="referral-code-description text-b3-foreground-muted font-neue-montreal-medium text-sm">
287
+ Share this code with friends to earn rewards
467
288
  </div>
468
- <Button
469
- className="bg-b3-primary-blue hover:bg-b3-primary-blue/90 font-neue-montreal-semibold h-12 w-full text-white"
470
- onClick={handleLinkAccount}
471
- >
472
- Link Account
473
- </Button>
474
289
  </div>
475
- ) : (
476
- <Button
477
- className="bg-b3-primary-blue hover:bg-b3-primary-blue/90 font-neue-montreal-semibold h-12 w-full text-white"
478
- onClick={handleSendOTP}
479
- disabled={(!email && !phone) || (isLinking && linkingMethod === selectedMethod)}
480
- >
481
- {isLinking && linkingMethod === selectedMethod ? (
482
- <Loader2 className="animate-spin" />
290
+ )}
291
+ <div className="referral-code-content flex items-center justify-between">
292
+ {!isEditingCode && (
293
+ <div className="referral-code-header">
294
+ <div className="referral-code-title text-b3-grey font-neue-montreal-semibold">Your Referral Code</div>
295
+ <div className="referral-code-description text-b3-foreground-muted font-neue-montreal-medium text-sm">
296
+ Share this code with friends to earn rewards
297
+ </div>
298
+ </div>
299
+ )}
300
+ <div className="referral-code-actions flex items-center gap-2">
301
+ {isEditingCode ? (
302
+ <div className="referral-code-edit-form flex items-center gap-2">
303
+ <input
304
+ type="text"
305
+ value={newReferralCode}
306
+ onChange={e => setNewReferralCode(e.target.value)}
307
+ className="referral-code-input rounded-lg border border-gray-200 bg-white px-3 py-1.5 text-sm"
308
+ placeholder="Enter new code"
309
+ ref={referallCodeRef}
310
+ />
311
+ <Button
312
+ size="sm"
313
+ className="referral-code-save-button"
314
+ onClick={handleUpdateReferralCode}
315
+ disabled={isUpdatingCode || !newReferralCode}
316
+ >
317
+ {isUpdatingCode ? (
318
+ <Loader2 className="referral-code-save-loading h-4 w-4 animate-spin" />
319
+ ) : (
320
+ "Save"
321
+ )}
322
+ </Button>
323
+ <Button
324
+ size="sm"
325
+ variant="ghost"
326
+ className="referral-code-cancel-button"
327
+ onClick={() => {
328
+ setIsEditingCode(false);
329
+ setNewReferralCode("");
330
+ }}
331
+ >
332
+ Cancel
333
+ </Button>
334
+ </div>
483
335
  ) : (
484
- "Send Verification Code"
336
+ <>
337
+ <div className="referral-code-display rounded-lg border border-gray-200 bg-white px-3 py-1.5 text-sm">
338
+ {currentReferralCode}
339
+ </div>
340
+ <Button size="icon" variant="ghost" className="referral-code-copy-button" onClick={handleCopyCode}>
341
+ <Copy className="referral-code-copy-icon h-4 w-4" />
342
+ </Button>
343
+ <Button
344
+ size="icon"
345
+ variant="ghost"
346
+ className="referral-code-edit-button"
347
+ onClick={() => {
348
+ setIsEditingCode(true);
349
+ setTimeout(() => {
350
+ referallCodeRef.current?.focus();
351
+ }, 100);
352
+ }}
353
+ >
354
+ <Pencil className="referral-code-edit-icon h-4 w-4" />
355
+ </Button>
356
+ </>
485
357
  )}
486
- </Button>
487
- ))}
358
+ </div>
359
+ </div>
360
+ </div>
361
+
362
+ {/* Referred Users */}
363
+ <div className="referred-users-container bg-b3-line rounded-xl p-4">
364
+ <div className="referred-users-title text-b3-grey font-neue-montreal-semibold mb-4">Referred Users</div>
365
+ {isLoadingReferrals ? (
366
+ <div className="referred-users-loading flex justify-center py-4">
367
+ <Loader2 className="h-6 w-6 animate-spin text-gray-400" />
368
+ </div>
369
+ ) : referrals?.data?.length ? (
370
+ <div className="referred-users-list space-y-3">
371
+ {referrals.data.map((referral: Referrals) => (
372
+ <div
373
+ key={String(referral._id)}
374
+ className="referred-user-item flex items-center justify-between rounded-lg bg-white p-3"
375
+ >
376
+ <div className="referred-user-id text-sm font-medium">{referral.referreeId}</div>
377
+ <div className="referred-user-date text-sm text-gray-500">
378
+ {new Date(referral.createdAt).toLocaleDateString()}
379
+ </div>
380
+ </div>
381
+ ))}
382
+ </div>
383
+ ) : (
384
+ <div className="referred-users-empty py-4 text-center text-gray-500">No referred users yet</div>
385
+ )}
386
+ </div>
488
387
  </div>
489
388
  )}
490
389
  </div>
491
390
  );
492
- }
391
+ };