@better-auth-ui/heroui 1.6.0

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 (253) hide show
  1. package/dist/components/account/account.d.ts +18 -0
  2. package/dist/components/account/account.js +24 -0
  3. package/dist/components/account/settings/account-settings.d.ts +5 -0
  4. package/dist/components/account/settings/account-settings.js +6 -0
  5. package/dist/components/account/settings/settings.d.ts +5 -0
  6. package/dist/components/account/settings/settings.js +5 -0
  7. package/dist/components/account/settings/user-profile.d.ts +5 -0
  8. package/dist/components/account/settings/user-profile.js +20 -0
  9. package/dist/components/auth/auth-provider.d.ts +9 -0
  10. package/dist/components/auth/auth-provider.js +12 -0
  11. package/dist/components/auth/auth.d.ts +23 -0
  12. package/dist/components/auth/auth.js +42 -0
  13. package/dist/components/auth/field-separator.d.ts +4 -0
  14. package/dist/components/auth/field-separator.js +5 -0
  15. package/dist/components/auth/forgot-password.d.ts +15 -0
  16. package/dist/components/auth/forgot-password.js +28 -0
  17. package/dist/components/auth/magic-link-button.d.ts +13 -0
  18. package/dist/components/auth/magic-link-button.js +19 -0
  19. package/dist/components/auth/magic-link.d.ts +17 -0
  20. package/dist/components/auth/magic-link.js +43 -0
  21. package/dist/components/auth/provider-buttons.d.ts +19 -0
  22. package/dist/components/auth/provider-buttons.js +34 -0
  23. package/dist/components/auth/reset-password.d.ts +11 -0
  24. package/dist/components/auth/reset-password.js +58 -0
  25. package/dist/components/auth/sign-in.d.ts +14 -0
  26. package/dist/components/auth/sign-in.js +64 -0
  27. package/dist/components/auth/sign-out.d.ts +9 -0
  28. package/dist/components/auth/sign-out.js +33 -0
  29. package/dist/components/auth/sign-up.d.ts +19 -0
  30. package/dist/components/auth/sign-up.js +71 -0
  31. package/dist/components/email/email-changed-email.d.ts +3 -0
  32. package/dist/components/email/email-changed-email.js +23 -0
  33. package/dist/components/email/email-verification-email.d.ts +3 -0
  34. package/dist/components/email/email-verification-email.js +23 -0
  35. package/dist/components/email/magic-link-email.d.ts +3 -0
  36. package/dist/components/email/magic-link-email.js +23 -0
  37. package/dist/components/email/new-device-email.d.ts +3 -0
  38. package/dist/components/email/new-device-email.js +23 -0
  39. package/dist/components/email/otp-email.d.ts +3 -0
  40. package/dist/components/email/otp-email.js +22 -0
  41. package/dist/components/email/password-changed-email.d.ts +3 -0
  42. package/dist/components/email/password-changed-email.js +23 -0
  43. package/dist/components/email/reset-password-email.d.ts +3 -0
  44. package/dist/components/email/reset-password-email.js +23 -0
  45. package/dist/components/settings/account/account-settings.d.ts +16 -0
  46. package/dist/components/settings/account/account-settings.js +20 -0
  47. package/dist/components/settings/account/accounts.d.ts +14 -0
  48. package/dist/components/settings/account/accounts.js +26 -0
  49. package/dist/components/settings/account/appearance.d.ts +16 -0
  50. package/dist/components/settings/account/appearance.js +28 -0
  51. package/dist/components/settings/account/change-avatar.d.ts +4 -0
  52. package/dist/components/settings/account/change-avatar.js +56 -0
  53. package/dist/components/settings/account/change-email.d.ts +15 -0
  54. package/dist/components/settings/account/change-email.js +29 -0
  55. package/dist/components/settings/account/manage-account.d.ts +19 -0
  56. package/dist/components/settings/account/manage-account.js +32 -0
  57. package/dist/components/settings/account/manage-accounts.d.ts +14 -0
  58. package/dist/components/settings/account/manage-accounts.js +37 -0
  59. package/dist/components/settings/account/theme-selector.d.ts +16 -0
  60. package/dist/components/settings/account/theme-selector.js +33 -0
  61. package/dist/components/settings/account/theme-settings.d.ts +16 -0
  62. package/dist/components/settings/account/theme-settings.js +31 -0
  63. package/dist/components/settings/account/user-profile.d.ts +11 -0
  64. package/dist/components/settings/account/user-profile.js +23 -0
  65. package/dist/components/settings/account-settings.d.ts +5 -0
  66. package/dist/components/settings/account-settings.js +6 -0
  67. package/dist/components/settings/account.d.ts +18 -0
  68. package/dist/components/settings/account.js +24 -0
  69. package/dist/components/settings/security/active-session.d.ts +14 -0
  70. package/dist/components/settings/security/active-session.js +53 -0
  71. package/dist/components/settings/security/active-sessions.d.ts +14 -0
  72. package/dist/components/settings/security/active-sessions.js +28 -0
  73. package/dist/components/settings/security/change-password.d.ts +15 -0
  74. package/dist/components/settings/security/change-password.js +79 -0
  75. package/dist/components/settings/security/connected-account.d.ts +16 -0
  76. package/dist/components/settings/security/connected-account.js +46 -0
  77. package/dist/components/settings/security/connected-accounts.d.ts +15 -0
  78. package/dist/components/settings/security/connected-accounts.js +31 -0
  79. package/dist/components/settings/security/danger-zone.d.ts +11 -0
  80. package/dist/components/settings/security/danger-zone.js +12 -0
  81. package/dist/components/settings/security/delete-user.d.ts +9 -0
  82. package/dist/components/settings/security/delete-user.js +47 -0
  83. package/dist/components/settings/security/linked-account.d.ts +16 -0
  84. package/dist/components/settings/security/linked-account.js +48 -0
  85. package/dist/components/settings/security/linked-accounts.d.ts +15 -0
  86. package/dist/components/settings/security/linked-accounts.js +42 -0
  87. package/dist/components/settings/security/security-settings.d.ts +17 -0
  88. package/dist/components/settings/security/security-settings.js +21 -0
  89. package/dist/components/settings/security/sessions.d.ts +14 -0
  90. package/dist/components/settings/security/sessions.js +40 -0
  91. package/dist/components/settings/settings/account-settings.d.ts +5 -0
  92. package/dist/components/settings/settings/account-settings.js +6 -0
  93. package/dist/components/settings/settings/user-profile.d.ts +5 -0
  94. package/dist/components/settings/settings/user-profile.js +20 -0
  95. package/dist/components/settings/settings.d.ts +24 -0
  96. package/dist/components/settings/settings.js +28 -0
  97. package/dist/components/settings/user-profile.d.ts +5 -0
  98. package/dist/components/settings/user-profile.js +20 -0
  99. package/dist/components/user/switch-account-item.d.ts +16 -0
  100. package/dist/components/user/switch-account-item.js +16 -0
  101. package/dist/components/user/switch-account-menu.d.ts +10 -0
  102. package/dist/components/user/switch-account-menu.js +29 -0
  103. package/dist/components/user/switch-account-popover.d.ts +10 -0
  104. package/dist/components/user/switch-account-popover.js +25 -0
  105. package/dist/components/user/switch-account-submenu.d.ts +10 -0
  106. package/dist/components/user/switch-account-submenu.js +25 -0
  107. package/dist/components/user/user-avatar.d.ts +25 -0
  108. package/dist/components/user/user-avatar.js +31 -0
  109. package/dist/components/user/user-button.d.ts +25 -0
  110. package/dist/components/user/user-button.js +24 -0
  111. package/dist/components/user/user-view.d.ts +22 -0
  112. package/dist/components/user/user-view.js +24 -0
  113. package/dist/core.d.ts +1 -0
  114. package/dist/core.js +1 -0
  115. package/dist/hooks/use-auth.d.ts +2 -0
  116. package/dist/hooks/use-auth.js +8 -0
  117. package/dist/hooks/use-redirect-to.d.ts +8 -0
  118. package/dist/hooks/use-redirect-to.js +19 -0
  119. package/dist/hooks/use-sign-in.d.ts +8 -0
  120. package/dist/hooks/use-sign-in.js +53 -0
  121. package/dist/index.d.ts +33 -0
  122. package/dist/index.js +34 -0
  123. package/dist/lib/utils.d.ts +2 -0
  124. package/dist/lib/utils.js +5 -0
  125. package/dist/react-exports.d.ts +1 -0
  126. package/dist/react-exports.js +1 -0
  127. package/dist/server.d.ts +1 -0
  128. package/dist/server.js +1 -0
  129. package/dist/src/components/auth/auth-provider.d.ts +8 -0
  130. package/dist/src/components/auth/auth-provider.js +13 -0
  131. package/dist/src/components/auth/auth.d.ts +22 -0
  132. package/dist/src/components/auth/auth.js +42 -0
  133. package/dist/src/components/auth/field-separator.d.ts +4 -0
  134. package/dist/src/components/auth/field-separator.js +5 -0
  135. package/dist/src/components/auth/forgot-password.d.ts +15 -0
  136. package/dist/src/components/auth/forgot-password.js +29 -0
  137. package/dist/src/components/auth/magic-link-button.d.ts +13 -0
  138. package/dist/src/components/auth/magic-link-button.js +20 -0
  139. package/dist/src/components/auth/magic-link.d.ts +17 -0
  140. package/dist/src/components/auth/magic-link.js +37 -0
  141. package/dist/src/components/auth/provider-buttons.d.ts +19 -0
  142. package/dist/src/components/auth/provider-buttons.js +35 -0
  143. package/dist/src/components/auth/reset-password.d.ts +11 -0
  144. package/dist/src/components/auth/reset-password.js +59 -0
  145. package/dist/src/components/auth/sign-in.d.ts +14 -0
  146. package/dist/src/components/auth/sign-in.js +58 -0
  147. package/dist/src/components/auth/sign-out.d.ts +11 -0
  148. package/dist/src/components/auth/sign-out.js +34 -0
  149. package/dist/src/components/auth/sign-up.d.ts +19 -0
  150. package/dist/src/components/auth/sign-up.js +65 -0
  151. package/dist/src/components/email/email-changed-email.d.ts +3 -0
  152. package/dist/src/components/email/email-changed-email.js +23 -0
  153. package/dist/src/components/email/email-verification-email.d.ts +3 -0
  154. package/dist/src/components/email/email-verification-email.js +23 -0
  155. package/dist/src/components/email/magic-link-email.d.ts +3 -0
  156. package/dist/src/components/email/magic-link-email.js +23 -0
  157. package/dist/src/components/email/new-device-email.d.ts +3 -0
  158. package/dist/src/components/email/new-device-email.js +23 -0
  159. package/dist/src/components/email/otp-email.d.ts +3 -0
  160. package/dist/src/components/email/otp-email.js +22 -0
  161. package/dist/src/components/email/password-changed-email.d.ts +3 -0
  162. package/dist/src/components/email/password-changed-email.js +23 -0
  163. package/dist/src/components/email/reset-password-email.d.ts +3 -0
  164. package/dist/src/components/email/reset-password-email.js +23 -0
  165. package/dist/src/components/settings/account/account-settings.d.ts +13 -0
  166. package/dist/src/components/settings/account/account-settings.js +19 -0
  167. package/dist/src/components/settings/account/appearance.d.ts +16 -0
  168. package/dist/src/components/settings/account/appearance.js +28 -0
  169. package/dist/src/components/settings/account/change-email.d.ts +15 -0
  170. package/dist/src/components/settings/account/change-email.js +35 -0
  171. package/dist/src/components/settings/account/manage-account.d.ts +19 -0
  172. package/dist/src/components/settings/account/manage-account.js +27 -0
  173. package/dist/src/components/settings/account/manage-accounts.d.ts +14 -0
  174. package/dist/src/components/settings/account/manage-accounts.js +27 -0
  175. package/dist/src/components/settings/account/user-profile.d.ts +13 -0
  176. package/dist/src/components/settings/account/user-profile.js +29 -0
  177. package/dist/src/components/settings/security/active-session.d.ts +14 -0
  178. package/dist/src/components/settings/security/active-session.js +35 -0
  179. package/dist/src/components/settings/security/active-sessions.d.ts +14 -0
  180. package/dist/src/components/settings/security/active-sessions.js +29 -0
  181. package/dist/src/components/settings/security/change-password.d.ts +15 -0
  182. package/dist/src/components/settings/security/change-password.js +57 -0
  183. package/dist/src/components/settings/security/linked-account.d.ts +16 -0
  184. package/dist/src/components/settings/security/linked-account.js +46 -0
  185. package/dist/src/components/settings/security/linked-accounts.d.ts +15 -0
  186. package/dist/src/components/settings/security/linked-accounts.js +31 -0
  187. package/dist/src/components/settings/security/security-settings.d.ts +15 -0
  188. package/dist/src/components/settings/security/security-settings.js +18 -0
  189. package/dist/src/components/settings/settings.d.ts +29 -0
  190. package/dist/src/components/settings/settings.js +39 -0
  191. package/dist/src/components/user/switch-account-item.d.ts +16 -0
  192. package/dist/src/components/user/switch-account-item.js +14 -0
  193. package/dist/src/components/user/switch-account-menu.d.ts +10 -0
  194. package/dist/src/components/user/switch-account-menu.js +23 -0
  195. package/dist/src/components/user/user-avatar.d.ts +24 -0
  196. package/dist/src/components/user/user-avatar.js +31 -0
  197. package/dist/src/components/user/user-button.d.ts +21 -0
  198. package/dist/src/components/user/user-button.js +24 -0
  199. package/dist/src/components/user/user-view.d.ts +21 -0
  200. package/dist/src/components/user/user-view.js +24 -0
  201. package/dist/src/core.d.ts +1 -0
  202. package/dist/src/core.js +1 -0
  203. package/dist/src/index.d.ts +31 -0
  204. package/dist/src/index.js +32 -0
  205. package/dist/src/lib/utils.d.ts +2 -0
  206. package/dist/src/lib/utils.js +5 -0
  207. package/dist/src/react-exports.d.ts +1 -0
  208. package/dist/src/react-exports.js +1 -0
  209. package/dist/styles.css +1 -0
  210. package/package.json +63 -0
  211. package/src/components/auth/auth-provider.tsx +27 -0
  212. package/src/components/auth/auth.tsx +87 -0
  213. package/src/components/auth/field-separator.tsx +14 -0
  214. package/src/components/auth/forgot-password.tsx +107 -0
  215. package/src/components/auth/magic-link-button.tsx +43 -0
  216. package/src/components/auth/magic-link.tsx +181 -0
  217. package/src/components/auth/provider-buttons.tsx +85 -0
  218. package/src/components/auth/reset-password.tsx +210 -0
  219. package/src/components/auth/sign-in.tsx +260 -0
  220. package/src/components/auth/sign-out.tsx +44 -0
  221. package/src/components/auth/sign-up.tsx +314 -0
  222. package/src/components/email/email-changed-email.tsx +38 -0
  223. package/src/components/email/email-verification-email.tsx +38 -0
  224. package/src/components/email/magic-link-email.tsx +38 -0
  225. package/src/components/email/new-device-email.tsx +38 -0
  226. package/src/components/email/otp-email.tsx +33 -0
  227. package/src/components/email/password-changed-email.tsx +38 -0
  228. package/src/components/email/reset-password-email.tsx +38 -0
  229. package/src/components/settings/account/account-settings.tsx +48 -0
  230. package/src/components/settings/account/appearance.tsx +157 -0
  231. package/src/components/settings/account/change-avatar.tsx +142 -0
  232. package/src/components/settings/account/change-email.tsx +111 -0
  233. package/src/components/settings/account/manage-account.tsx +123 -0
  234. package/src/components/settings/account/manage-accounts.tsx +78 -0
  235. package/src/components/settings/account/user-profile.tsx +103 -0
  236. package/src/components/settings/security/active-session.tsx +123 -0
  237. package/src/components/settings/security/active-sessions.tsx +77 -0
  238. package/src/components/settings/security/change-password.tsx +335 -0
  239. package/src/components/settings/security/danger-zone.tsx +32 -0
  240. package/src/components/settings/security/delete-user.tsx +187 -0
  241. package/src/components/settings/security/linked-account.tsx +136 -0
  242. package/src/components/settings/security/linked-accounts.tsx +98 -0
  243. package/src/components/settings/security/security-settings.tsx +43 -0
  244. package/src/components/settings/settings.tsx +96 -0
  245. package/src/components/user/switch-account-item.tsx +39 -0
  246. package/src/components/user/switch-account-menu.tsx +64 -0
  247. package/src/components/user/user-avatar.tsx +88 -0
  248. package/src/components/user/user-button.tsx +221 -0
  249. package/src/components/user/user-view.tsx +74 -0
  250. package/src/core.ts +1 -0
  251. package/src/index.tsx +35 -0
  252. package/src/react-exports.ts +1 -0
  253. package/src/styles.css +1 -0
@@ -0,0 +1,19 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useAuth } from "@better-auth-ui/react";
3
+ import { cn } from "../../../lib/utils";
4
+ import { Appearance } from "./appearance";
5
+ import { ChangeEmail } from "./change-email";
6
+ import { ManageAccounts } from "./manage-accounts";
7
+ import { UserProfile } from "./user-profile";
8
+ /**
9
+ * Renders the account settings layout including user profile, change email, appearance, and accounts management.
10
+ *
11
+ * UserProfile, ChangeEmail, and Appearance are always rendered; Accounts is rendered when `multiSession` is enabled.
12
+ *
13
+ * @param className - Optional additional CSS class names for the outer container.
14
+ * @returns The account settings container as a JSX element.
15
+ */
16
+ export function AccountSettings({ className, ...props }) {
17
+ const { multiSession } = useAuth();
18
+ return (_jsxs("div", { className: cn("flex w-full flex-col gap-4 md:gap-6", className), ...props, children: [_jsx(UserProfile, {}), _jsx(ChangeEmail, {}), _jsx(Appearance, {}), multiSession && _jsx(ManageAccounts, {})] }));
19
+ }
@@ -0,0 +1,16 @@
1
+ import { type CardProps } from "@heroui/react";
2
+ export type AppearanceProps = {
3
+ className?: string;
4
+ variant?: CardProps["variant"];
5
+ };
6
+ /**
7
+ * Renders a theme selector card with visual theme previews.
8
+ *
9
+ * Displays a card containing radio buttons for selecting between system, light,
10
+ * and dark themes. Each option shows a visual preview of the theme. Only renders
11
+ * if theme settings are configured (theme, setTheme, and themes are provided).
12
+ *
13
+ * @param className - Optional additional CSS class names for the card container.
14
+ * @returns A JSX element containing the theme selector card, or null if theme settings are not configured.
15
+ */
16
+ export declare function Appearance({ className, variant, ...props }: AppearanceProps & CardProps): import("react/jsx-runtime").JSX.Element | null;
@@ -0,0 +1,28 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { ThemePreviewDark, ThemePreviewLight, ThemePreviewSystem, useAuth } from "@better-auth-ui/react";
3
+ import { Display, Moon, Sun } from "@gravity-ui/icons";
4
+ import { Card, cn, Label, Radio, RadioGroup, useIsHydrated } from "@heroui/react";
5
+ /**
6
+ * Renders a theme selector card with visual theme previews.
7
+ *
8
+ * Displays a card containing radio buttons for selecting between system, light,
9
+ * and dark themes. Each option shows a visual preview of the theme. Only renders
10
+ * if theme settings are configured (theme, setTheme, and themes are provided).
11
+ *
12
+ * @param className - Optional additional CSS class names for the card container.
13
+ * @returns A JSX element containing the theme selector card, or null if theme settings are not configured.
14
+ */
15
+ export function Appearance({ className, variant, ...props }) {
16
+ const { localization, settings: { theme, setTheme, themes } } = useAuth();
17
+ const hydrated = useIsHydrated();
18
+ if (!setTheme || !themes?.length) {
19
+ return null;
20
+ }
21
+ return (_jsxs(Card, { className: cn("p-4 md:p-6 gap-4", className), variant: variant, ...props, children: [_jsx(Card.Header, { children: _jsx(Card.Title, { className: "text-xl", children: localization.settings.appearance }) }), _jsx(Card.Content, { children: _jsxs(RadioGroup, { variant: variant === "transparent" ? "secondary" : "primary", value: hydrated ? theme : undefined, onChange: setTheme, isDisabled: !hydrated || !theme, children: [_jsx(Label, { className: "mb-2", children: localization.settings.theme }), _jsxs("div", { className: "grid gap-3 sm:grid-cols-2 lg:grid-cols-3", children: [themes.includes("system") && (_jsxs(Radio, { value: "system", className: cn(variant === "transparent"
22
+ ? "bg-surface"
23
+ : "bg-surface-secondary", "mt-0 group relative flex-col gap-4 rounded-xl border border-transparent px-5 py-4 transition-all data-[selected=true]:border-accent data-[selected=true]:bg-accent/10", "data-[focus-visible=true]:border-accent data-[focus-visible=true]:bg-accent/10"), children: [_jsx(Radio.Control, { className: "absolute top-3 right-3 size-5", children: _jsx(Radio.Indicator, {}) }), _jsxs(Radio.Content, { className: "flex flex-col gap-3", children: [_jsxs(Label, { className: "cursor-pointer font-medium flex gap-2 items-center", children: [_jsx(Display, { className: "text-muted" }), localization.settings.system] }), _jsx(ThemePreviewSystem, { className: "w-full" })] })] })), themes.includes("light") && (_jsxs(Radio, { value: "light", className: cn(variant === "transparent"
24
+ ? "bg-surface"
25
+ : "bg-surface-secondary", "mt-0 group relative flex-col gap-4 rounded-xl border border-transparent px-5 py-4 transition-all data-[selected=true]:border-accent data-[selected=true]:bg-accent/10", "data-[focus-visible=true]:border-accent data-[focus-visible=true]:bg-accent/10"), children: [_jsx(Radio.Control, { className: "absolute top-3 right-3 size-5", children: _jsx(Radio.Indicator, {}) }), _jsxs(Radio.Content, { className: "flex flex-col gap-3", children: [_jsxs(Label, { className: "cursor-pointer font-medium flex gap-2 items-center", children: [_jsx(Sun, { className: "text-muted" }), localization.settings.light] }), _jsx(ThemePreviewLight, { className: "w-full" })] })] })), themes.includes("dark") && (_jsxs(Radio, { value: "dark", className: cn(variant === "transparent"
26
+ ? "bg-surface"
27
+ : "bg-surface-secondary", "mt-0 group relative flex-col gap-4 rounded-xl border border-transparent px-5 py-4 transition-all data-[selected=true]:border-accent data-[selected=true]:bg-accent/10", "data-[focus-visible=true]:border-accent data-[focus-visible=true]:bg-accent/10"), children: [_jsx(Radio.Control, { className: "absolute top-3 right-3 size-5", children: _jsx(Radio.Indicator, {}) }), _jsxs(Radio.Content, { className: "flex flex-col gap-3", children: [_jsxs(Label, { className: "cursor-pointer font-medium flex gap-2 items-center", children: [_jsx(Moon, { className: "text-muted" }), localization.settings.dark] }), _jsx(ThemePreviewDark, { className: "w-full" })] })] }))] })] }) })] }));
28
+ }
@@ -0,0 +1,15 @@
1
+ import { type CardProps } from "@heroui/react";
2
+ export type ChangeEmailProps = {
3
+ className?: string;
4
+ variant?: CardProps["variant"];
5
+ };
6
+ /**
7
+ * Render a card containing a form to view and update the authenticated user's email.
8
+ *
9
+ * Shows a loading skeleton until session data is available, displays the current
10
+ * email as the form's default value, and sends a verification email to the
11
+ * new address upon successful submission.
12
+ *
13
+ * @returns A JSX element rendering the change-email card and form
14
+ */
15
+ export declare function ChangeEmail({ className, variant, ...props }: ChangeEmailProps & CardProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,35 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useAuth, useChangeEmail, useSession } from "@better-auth-ui/react";
3
+ import { Check, Envelope } from "@gravity-ui/icons";
4
+ import { Button, Card, cn, FieldError, Fieldset, Form, InputGroup, Label, Skeleton, Spinner, TextField, toast } from "@heroui/react";
5
+ /**
6
+ * Render a card containing a form to view and update the authenticated user's email.
7
+ *
8
+ * Shows a loading skeleton until session data is available, displays the current
9
+ * email as the form's default value, and sends a verification email to the
10
+ * new address upon successful submission.
11
+ *
12
+ * @returns A JSX element rendering the change-email card and form
13
+ */
14
+ export function ChangeEmail({ className, variant, ...props }) {
15
+ const { localization, baseURL, viewPaths } = useAuth();
16
+ const { data: sessionData } = useSession({
17
+ throwOnError: (error) => {
18
+ toast.danger(error.error?.message || error.message);
19
+ return false;
20
+ }
21
+ });
22
+ const { mutate: changeEmail, isPending } = useChangeEmail({
23
+ onSuccess: () => toast.success(localization.settings.changeEmailSuccess),
24
+ onError: (error) => toast.danger(error.error?.message || error.message)
25
+ });
26
+ function handleSubmit(e) {
27
+ e.preventDefault();
28
+ const formData = new FormData(e.currentTarget);
29
+ changeEmail({
30
+ newEmail: formData.get("email"),
31
+ callbackURL: `${baseURL}/${viewPaths.settings.account}`
32
+ });
33
+ }
34
+ return (_jsxs(Card, { className: cn("p-4 md:p-6 gap-4", className), variant: variant, ...props, children: [_jsx(Card.Header, { children: _jsx(Card.Title, { className: "text-xl", children: localization.settings.changeEmail }) }), _jsx(Card.Content, { children: _jsx(Form, { onSubmit: handleSubmit, children: _jsxs(Fieldset, { className: "w-full gap-4", children: [_jsx(Fieldset.Group, { children: _jsxs(TextField, { name: "email", type: "email", defaultValue: sessionData?.user.email, isDisabled: isPending || !sessionData, children: [_jsx(Label, { children: localization.auth.email }), sessionData ? (_jsxs(InputGroup, { variant: variant === "transparent" ? "primary" : "secondary", children: [_jsx(InputGroup.Prefix, { children: _jsx(Envelope, {}) }), _jsx(InputGroup.Input, { required: true, autoComplete: "email", placeholder: localization.auth.emailPlaceholder })] })) : (_jsx(Skeleton, { className: "h-10 md:h-9 w-full rounded-xl" })), _jsx(FieldError, {})] }, sessionData?.user.email) }), _jsx(Fieldset.Actions, { children: _jsxs(Button, { type: "submit", isPending: isPending, isDisabled: !sessionData, children: [isPending ? _jsx(Spinner, { color: "current", size: "sm" }) : _jsx(Check, {}), localization.settings.updateEmail] }) })] }) }) })] }));
35
+ }
@@ -0,0 +1,19 @@
1
+ import type { Session, User } from "better-auth";
2
+ export type DeviceSession = {
3
+ session: Session;
4
+ user: User;
5
+ };
6
+ export type ManageAccountProps = {
7
+ deviceSession?: DeviceSession | null;
8
+ isPending?: boolean;
9
+ };
10
+ /**
11
+ * Render a single account card with user info and switch/revoke controls.
12
+ *
13
+ * Shows the user's avatar and info. For non-active sessions, provides a switch button.
14
+ * All sessions have a revoke/sign-out button.
15
+ *
16
+ * @param deviceSession - The device session object containing session and user data
17
+ * @returns A JSX element containing the account card
18
+ */
19
+ export declare function ManageAccount({ deviceSession, isPending }: ManageAccountProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,27 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useAuth, useRevokeMultiSession, useSession, useSetActiveSession } from "@better-auth-ui/react";
3
+ import { ArrowRightArrowLeft, ArrowRightFromSquare } from "@gravity-ui/icons";
4
+ import { Button, Card, Spinner, toast } from "@heroui/react";
5
+ import { UserView } from "../../user/user-view";
6
+ /**
7
+ * Render a single account card with user info and switch/revoke controls.
8
+ *
9
+ * Shows the user's avatar and info. For non-active sessions, provides a switch button.
10
+ * All sessions have a revoke/sign-out button.
11
+ *
12
+ * @param deviceSession - The device session object containing session and user data
13
+ * @returns A JSX element containing the account card
14
+ */
15
+ export function ManageAccount({ deviceSession, isPending }) {
16
+ const { localization } = useAuth();
17
+ const { data: sessionData } = useSession();
18
+ const { mutate: setActiveSession, isPending: isSwitching } = useSetActiveSession({
19
+ onError: (error) => toast.danger(error.error?.message || error.message)
20
+ });
21
+ const { mutate: revokeSession, isPending: isRevoking } = useRevokeMultiSession({
22
+ onError: (error) => toast.danger(error.error?.message || error.message),
23
+ onSuccess: () => toast.success(localization.settings.revokeSessionSuccess)
24
+ });
25
+ const isActive = deviceSession?.session.userId === sessionData?.session.userId;
26
+ return (_jsxs(Card, { className: "flex-row items-center border p-3 shadow-none", children: [_jsx(UserView, { user: deviceSession?.user, isPending: isPending, size: "md" }), deviceSession && (_jsxs("div", { className: "flex items-center gap-1 shrink-0 ml-auto", children: [!isActive && (_jsx(Button, { isIconOnly: true, variant: "ghost", size: "sm", onPress: () => setActiveSession({ sessionToken: deviceSession.session.token }), isPending: isSwitching || isRevoking, "aria-label": localization.auth.switchAccount, children: isSwitching ? (_jsx(Spinner, { color: "current", size: "sm" })) : (_jsx(ArrowRightArrowLeft, {})) })), _jsx(Button, { isIconOnly: true, variant: "ghost", size: "sm", onPress: () => revokeSession({ sessionToken: deviceSession.session.token }), isPending: isSwitching || isRevoking, "aria-label": localization.auth.signOut, children: isRevoking ? (_jsx(Spinner, { color: "current", size: "sm" })) : (_jsx(ArrowRightFromSquare, {})) })] }))] }));
27
+ }
@@ -0,0 +1,14 @@
1
+ import { type CardProps } from "@heroui/react";
2
+ export type ManageAccountsProps = {
3
+ className?: string;
4
+ variant?: CardProps["variant"];
5
+ };
6
+ /**
7
+ * Render a card that lists and manages all device sessions for the current user.
8
+ *
9
+ * Shows each session with user information and actions to switch to or revoke a session.
10
+ * When device session data is loading, a pending placeholder row is displayed.
11
+ *
12
+ * @returns A JSX element containing the accounts management card
13
+ */
14
+ export declare function ManageAccounts({ className, ...props }: ManageAccountsProps & CardProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,27 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useAuth, useListDeviceSessions, useSession } from "@better-auth-ui/react";
3
+ import { CirclePlus } from "@gravity-ui/icons";
4
+ import { buttonVariants, Card, cn, Link, toast } from "@heroui/react";
5
+ import { ManageAccount } from "./manage-account";
6
+ /**
7
+ * Render a card that lists and manages all device sessions for the current user.
8
+ *
9
+ * Shows each session with user information and actions to switch to or revoke a session.
10
+ * When device session data is loading, a pending placeholder row is displayed.
11
+ *
12
+ * @returns A JSX element containing the accounts management card
13
+ */
14
+ export function ManageAccounts({ className, ...props }) {
15
+ const { basePaths, localization, viewPaths } = useAuth();
16
+ const { data: sessionData } = useSession();
17
+ const { data: deviceSessions, isPending } = useListDeviceSessions({
18
+ throwOnError: (error) => {
19
+ if (error.error)
20
+ toast.danger(error.error.message);
21
+ return false;
22
+ }
23
+ });
24
+ return (_jsxs(Card, { className: cn("p-4 md:p-6 gap-4", className), ...props, children: [_jsx(Card.Header, { children: _jsx(Card.Title, { className: "text-xl", children: localization.settings.manageAccounts }) }), _jsxs(Card.Content, { className: "gap-3", children: [_jsx(ManageAccount, { isPending: isPending, deviceSession: !isPending ? sessionData : null }), deviceSessions
25
+ ?.filter((deviceSession) => deviceSession.session.id !== sessionData?.session.id)
26
+ .map((deviceSession) => (_jsx(ManageAccount, { deviceSession: deviceSession }, deviceSession.session.id)))] }), _jsx(Card.Footer, { children: _jsxs(Link, { href: `${basePaths.auth}/${viewPaths.auth.signIn}`, className: cn("gap-2", buttonVariants({ variant: "secondary" })), children: [_jsx(CirclePlus, {}), localization.auth.addAccount] }) })] }));
27
+ }
@@ -0,0 +1,13 @@
1
+ import { type CardProps } from "@heroui/react";
2
+ export type UserProfileProps = {
3
+ className?: string;
4
+ variant?: CardProps["variant"];
5
+ };
6
+ /**
7
+ * Render a profile card that lets the authenticated user view and update their display name.
8
+ *
9
+ * The component uses auth localization for labels, reflects session loading state with skeletons, shows the best-available user identifier (display username, name, or email), and wires the form submission to the user update action from auth hooks.
10
+ *
11
+ * @returns A JSX element containing the user profile card and an editable name form
12
+ */
13
+ export declare function UserProfile({ className, variant, ...props }: UserProfileProps & CardProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,29 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useAuth, useSession, useUpdateUser } from "@better-auth-ui/react";
3
+ import { FloppyDisk, Pencil } from "@gravity-ui/icons";
4
+ import { Button, Card, cn, FieldError, Fieldset, Form, Input, Label, Skeleton, Spinner, TextField, toast } from "@heroui/react";
5
+ import { UserAvatar } from "../../user/user-avatar";
6
+ /**
7
+ * Render a profile card that lets the authenticated user view and update their display name.
8
+ *
9
+ * The component uses auth localization for labels, reflects session loading state with skeletons, shows the best-available user identifier (display username, name, or email), and wires the form submission to the user update action from auth hooks.
10
+ *
11
+ * @returns A JSX element containing the user profile card and an editable name form
12
+ */
13
+ export function UserProfile({ className, variant, ...props }) {
14
+ const { localization } = useAuth();
15
+ const { data: sessionData } = useSession();
16
+ const { mutate: updateUser, isPending } = useUpdateUser({
17
+ onError: (error) => toast.danger(error.error?.message || error.message),
18
+ onSuccess: () => toast.success(localization.settings.profileUpdatedSuccess)
19
+ });
20
+ function handleSubmit(e) {
21
+ e.preventDefault();
22
+ const formData = new FormData(e.currentTarget);
23
+ updateUser({ name: formData.get("name") });
24
+ }
25
+ return (_jsxs(Card, { className: cn("p-4 md:p-6 gap-4", className), variant: variant, ...props, children: [_jsx(Card.Header, { children: _jsx(Card.Title, { className: "text-xl", children: localization.settings.profile }) }), _jsx(Card.Content, { children: _jsx(Form, { onSubmit: handleSubmit, children: _jsxs(Fieldset, { className: "w-full gap-4", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsxs(Button, { type: "button", isIconOnly: true, variant: "ghost", className: "p-0 h-auto w-auto rounded-full", isDisabled: !sessionData, children: [_jsx(UserAvatar, { size: "lg" }), _jsx("span", { className: "absolute right-0 bottom-0 size-4 rounded-full bg-background ring ring-2 ring-border flex items-center justify-center", children: _jsx(Pencil, { className: "size-2.5 text-muted" }) })] }), sessionData ? (_jsxs("div", { className: "flex flex-col", children: [_jsx("p", { className: "text-sm font-medium leading-tight", children: sessionData?.user?.displayUsername ||
26
+ sessionData?.user?.name ||
27
+ sessionData?.user?.email }), (sessionData?.user?.displayUsername ||
28
+ sessionData?.user?.name) && (_jsx("p", { className: "text-muted text-xs", children: sessionData?.user?.email }))] })) : (_jsxs("div", { className: "flex flex-col gap-1", children: [_jsx(Skeleton, { className: "h-3.5 w-24 rounded-lg" }), _jsx(Skeleton, { className: "h-3 w-32 rounded-lg" })] }))] }), _jsx(Fieldset.Group, { children: _jsxs(TextField, { name: "name", defaultValue: sessionData?.user?.name, isDisabled: isPending || !sessionData, children: [_jsx(Label, { children: localization.auth.name }), sessionData ? (_jsx(Input, { autoComplete: "name", placeholder: localization.auth.name, variant: variant === "transparent" ? "primary" : "secondary" })) : (_jsx(Skeleton, { className: "h-10 md:h-9 w-full rounded-xl" })), _jsx(FieldError, {})] }, sessionData?.user?.name) }), _jsx(Fieldset.Actions, { children: _jsxs(Button, { type: "submit", isPending: isPending, isDisabled: !sessionData, children: [isPending ? (_jsx(Spinner, { color: "current", size: "sm" })) : (_jsx(FloppyDisk, {})), localization.settings.saveChanges] }) })] }) }) })] }));
29
+ }
@@ -0,0 +1,14 @@
1
+ import type { Session } from "better-auth";
2
+ export type ActiveSessionProps = {
3
+ session: Session;
4
+ };
5
+ /**
6
+ * Render a single active session card with device info and revoke control.
7
+ *
8
+ * Shows the session's browser, OS, IP address, and creation time. The current session is marked
9
+ * and navigates to sign-out on click, while other sessions can be revoked individually.
10
+ *
11
+ * @param session - The session object containing id, token, userAgent, ipAddress, and createdAt
12
+ * @returns A JSX element containing the active session card
13
+ */
14
+ export declare function ActiveSession({ session }: ActiveSessionProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,35 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useAuth, useRevokeSession, useSession } from "@better-auth-ui/react";
3
+ import { ArrowRightFromSquare, Display, Smartphone } from "@gravity-ui/icons";
4
+ import { Button, Card, Chip, Spinner, toast } from "@heroui/react";
5
+ import { UAParser } from "ua-parser-js";
6
+ /**
7
+ * Render a single active session card with device info and revoke control.
8
+ *
9
+ * Shows the session's browser, OS, IP address, and creation time. The current session is marked
10
+ * and navigates to sign-out on click, while other sessions can be revoked individually.
11
+ *
12
+ * @param session - The session object containing id, token, userAgent, ipAddress, and createdAt
13
+ * @returns A JSX element containing the active session card
14
+ */
15
+ export function ActiveSession({ session }) {
16
+ const { basePaths, localization, viewPaths, navigate } = useAuth();
17
+ const { data: sessionData } = useSession();
18
+ const { mutate: revokeSession, isPending: isRevoking } = useRevokeSession({
19
+ onError: (error) => toast.danger(error.error?.message || error.message),
20
+ onSuccess: () => toast.success(localization.settings.revokeSessionSuccess)
21
+ });
22
+ const isCurrentSession = session.token === sessionData?.session.token;
23
+ const ua = new UAParser(session.userAgent || "").getResult();
24
+ const isMobile = ua.device.type === "mobile" ||
25
+ ua.device.type === "tablet" ||
26
+ ua.device.type === "wearable";
27
+ return (_jsxs(Card, { className: "flex-row border items-center p-3 shadow-none", children: [_jsx("div", { className: "flex size-10 items-center justify-center rounded-xl bg-surface-secondary", children: isMobile ? (_jsx(Smartphone, { className: "size-5" })) : (_jsx(Display, { className: "size-5" })) }), _jsxs("div", { className: "flex flex-col", children: [_jsxs("div", { className: "flex items-center gap-1.5", children: [_jsxs("span", { className: "text-sm font-medium truncate", children: [ua.browser.name || "Unknown Browser", ua.os.name ? `, ${ua.os.name}` : ""] }), isCurrentSession && (_jsx(Chip, { color: "accent", size: "sm", className: "px-1.5", children: localization.settings.currentSession }))] }), _jsxs("div", { className: "flex items-center gap-1.5 text-xs text-muted", children: [session.ipAddress && _jsx("span", { children: session.ipAddress }), session.createdAt && (_jsxs(_Fragment, { children: [session.ipAddress && _jsx("span", { children: "\u2022" }), _jsx("span", { children: session.createdAt.toLocaleString([], {
28
+ dateStyle: "short",
29
+ timeStyle: "short"
30
+ }) })] }))] })] }), _jsx(Button, { isIconOnly: true, className: "ml-auto", variant: "ghost", size: "sm", onPress: () => isCurrentSession
31
+ ? navigate({
32
+ to: `${basePaths.auth}/${viewPaths.auth.signOut}`
33
+ })
34
+ : revokeSession({ token: session.token }), isPending: isRevoking, "aria-label": localization.settings.revokeSession, children: isRevoking ? (_jsx(Spinner, { color: "current", size: "sm" })) : (_jsx(ArrowRightFromSquare, {})) })] }));
35
+ }
@@ -0,0 +1,14 @@
1
+ import { type CardProps } from "@heroui/react";
2
+ export type ActiveSessionsProps = {
3
+ className?: string;
4
+ variant?: CardProps["variant"];
5
+ };
6
+ /**
7
+ * Render a card listing all active sessions for the current user with revoke controls.
8
+ *
9
+ * Shows each session's browser, OS, IP address, and creation time. The current session is marked
10
+ * and navigates to sign-out on click, while other sessions can be revoked individually.
11
+ *
12
+ * @returns A JSX element containing the sessions card
13
+ */
14
+ export declare function ActiveSessions({ className, ...props }: ActiveSessionsProps & CardProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,29 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useAuth, useListSessions, useSession } from "@better-auth-ui/react";
3
+ import { Card, cn, Skeleton, toast } from "@heroui/react";
4
+ import { ActiveSession } from "./active-session";
5
+ /**
6
+ * Render a card listing all active sessions for the current user with revoke controls.
7
+ *
8
+ * Shows each session's browser, OS, IP address, and creation time. The current session is marked
9
+ * and navigates to sign-out on click, while other sessions can be revoked individually.
10
+ *
11
+ * @returns A JSX element containing the sessions card
12
+ */
13
+ export function ActiveSessions({ className, ...props }) {
14
+ const { localization } = useAuth();
15
+ const { data: sessionData } = useSession();
16
+ const { data: sessions, isPending } = useListSessions({
17
+ throwOnError: (error) => {
18
+ if (error.error)
19
+ toast.danger(error.error.message);
20
+ return false;
21
+ }
22
+ });
23
+ return (_jsxs(Card, { className: cn("p-4 md:p-6 gap-4", className), ...props, children: [_jsx(Card.Header, { children: _jsx(Card.Title, { className: "text-xl", children: localization.settings.activeSessions }) }), _jsx(Card.Content, { className: "gap-3", children: isPending ? (_jsx(SessionRowSkeleton, {})) : (sessions
24
+ ?.toSorted((session) => session.id === sessionData?.session.id ? -1 : 1)
25
+ .map((session) => (_jsx(ActiveSession, { session: session }, session.id)))) })] }));
26
+ }
27
+ function SessionRowSkeleton() {
28
+ return (_jsx("div", { className: "flex items-center rounded-3xl border p-3 justify-between", children: _jsxs("div", { className: "flex items-center gap-3", children: [_jsx(Skeleton, { className: "size-10 rounded-xl" }), _jsxs("div", { className: "flex flex-col gap-1", children: [_jsx(Skeleton, { className: "h-4 w-32 rounded-lg" }), _jsx(Skeleton, { className: "h-3 w-24 rounded-lg" })] })] }) }));
29
+ }
@@ -0,0 +1,15 @@
1
+ import { type CardProps } from "@heroui/react";
2
+ export type ChangePasswordProps = {
3
+ className?: string;
4
+ variant?: CardProps["variant"];
5
+ };
6
+ /**
7
+ * Render a card form for changing the authenticated user's password.
8
+ *
9
+ * Displays a card with fields for current password, new password, and optionally
10
+ * confirm password (based on `emailAndPassword.confirmPassword`). All other sessions
11
+ * are revoked upon successful password change.
12
+ *
13
+ * @returns A JSX element containing the change-password card and form
14
+ */
15
+ export declare function ChangePassword({ className, variant, ...props }: ChangePasswordProps & CardProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,57 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useAuth, useChangePassword, useSession } from "@better-auth-ui/react";
3
+ import { Check, Eye, EyeSlash } from "@gravity-ui/icons";
4
+ import { Button, Card, cn, FieldError, Fieldset, Form, Input, InputGroup, Label, Skeleton, Spinner, TextField, toast } from "@heroui/react";
5
+ import { useState } from "react";
6
+ /**
7
+ * Render a card form for changing the authenticated user's password.
8
+ *
9
+ * Displays a card with fields for current password, new password, and optionally
10
+ * confirm password (based on `emailAndPassword.confirmPassword`). All other sessions
11
+ * are revoked upon successful password change.
12
+ *
13
+ * @returns A JSX element containing the change-password card and form
14
+ */
15
+ export function ChangePassword({ className, variant, ...props }) {
16
+ const { emailAndPassword, localization } = useAuth();
17
+ const { data: sessionData } = useSession();
18
+ const [currentPassword, setCurrentPassword] = useState("");
19
+ const [newPassword, setNewPassword] = useState("");
20
+ const [confirmPassword, setConfirmPassword] = useState("");
21
+ const { mutate: changePassword, isPending } = useChangePassword({
22
+ onError: (error) => {
23
+ setCurrentPassword("");
24
+ setNewPassword("");
25
+ setConfirmPassword("");
26
+ toast.danger(error.error?.message || error.message);
27
+ },
28
+ onSuccess: () => {
29
+ setCurrentPassword("");
30
+ setNewPassword("");
31
+ setConfirmPassword("");
32
+ toast.success(localization.settings.changePasswordSuccess);
33
+ }
34
+ });
35
+ const [isNewPasswordVisible, setIsNewPasswordVisible] = useState(false);
36
+ const [isConfirmPasswordVisible, setIsConfirmPasswordVisible] = useState(false);
37
+ const handleSubmit = (e) => {
38
+ e.preventDefault();
39
+ if (emailAndPassword?.confirmPassword && newPassword !== confirmPassword) {
40
+ setCurrentPassword("");
41
+ setNewPassword("");
42
+ setConfirmPassword("");
43
+ toast.danger(localization.auth.passwordsDoNotMatch);
44
+ return;
45
+ }
46
+ changePassword({
47
+ currentPassword,
48
+ newPassword,
49
+ revokeOtherSessions: true
50
+ });
51
+ };
52
+ return (_jsxs(Card, { className: cn("p-4 md:p-6 gap-4", className), variant: variant, ...props, children: [_jsx(Card.Header, { children: _jsx(Card.Title, { className: "text-xl", children: localization.settings.changePassword }) }), _jsx(Card.Content, { children: _jsx(Form, { onSubmit: handleSubmit, children: _jsxs(Fieldset, { className: "w-full gap-4", children: [_jsxs(Fieldset.Group, { children: [_jsxs(TextField, { name: "currentPassword", type: "password", isDisabled: isPending || !sessionData, value: currentPassword, onChange: setCurrentPassword, children: [_jsx(Label, { children: localization.settings.currentPassword }), sessionData ? (_jsx(Input, { autoComplete: "current-password", placeholder: localization.settings.currentPasswordPlaceholder, required: true, variant: variant === "transparent" ? "primary" : "secondary" })) : (_jsx(Skeleton, { className: "h-10 md:h-9 w-full rounded-xl" })), _jsx(FieldError, {})] }), _jsxs(TextField, { minLength: emailAndPassword?.minPasswordLength, maxLength: emailAndPassword?.maxPasswordLength, isDisabled: isPending || !sessionData, value: newPassword, onChange: setNewPassword, children: [_jsx(Label, { children: localization.auth.newPassword }), sessionData ? (_jsxs(InputGroup, { variant: variant === "transparent" ? "primary" : "secondary", children: [_jsx(InputGroup.Input, { name: "newPassword", type: isNewPasswordVisible ? "text" : "password", autoComplete: "new-password", placeholder: localization.auth.newPasswordPlaceholder, required: true }), _jsx(InputGroup.Suffix, { className: "px-0", children: _jsx(Button, { isIconOnly: true, "aria-label": isNewPasswordVisible
53
+ ? localization.auth.hidePassword
54
+ : localization.auth.showPassword, size: "sm", variant: "ghost", onPress: () => setIsNewPasswordVisible(!isNewPasswordVisible), isDisabled: isPending, children: isNewPasswordVisible ? _jsx(EyeSlash, {}) : _jsx(Eye, {}) }) })] })) : (_jsx(Skeleton, { className: "h-10 md:h-9 w-full rounded-xl" })), _jsx(FieldError, {})] }), emailAndPassword?.confirmPassword && (_jsxs(TextField, { minLength: emailAndPassword?.minPasswordLength, maxLength: emailAndPassword?.maxPasswordLength, isDisabled: isPending || !sessionData, isRequired: true, value: confirmPassword, onChange: setConfirmPassword, children: [_jsx(Label, { children: localization.auth.confirmPassword }), sessionData ? (_jsxs(InputGroup, { variant: variant === "transparent" ? "primary" : "secondary", children: [_jsx(InputGroup.Input, { name: "confirmPassword", type: isConfirmPasswordVisible ? "text" : "password", autoComplete: "new-password", placeholder: localization.auth.confirmPasswordPlaceholder, required: true }), _jsx(InputGroup.Suffix, { className: "px-0", children: _jsx(Button, { isIconOnly: true, "aria-label": isConfirmPasswordVisible
55
+ ? localization.auth.hidePassword
56
+ : localization.auth.showPassword, size: "sm", variant: "ghost", onPress: () => setIsConfirmPasswordVisible(!isConfirmPasswordVisible), isDisabled: isPending, children: isConfirmPasswordVisible ? _jsx(EyeSlash, {}) : _jsx(Eye, {}) }) })] })) : (_jsx(Skeleton, { className: "h-10 md:h-9 w-full rounded-xl" })), _jsx(FieldError, {})] }))] }), _jsx(Fieldset.Actions, { children: _jsxs(Button, { type: "submit", isPending: isPending, isDisabled: !sessionData, children: [isPending ? _jsx(Spinner, { color: "current", size: "sm" }) : _jsx(Check, {}), localization.settings.updatePassword] }) })] }) }) })] }));
57
+ }
@@ -0,0 +1,16 @@
1
+ import type { Account, SocialProvider } from "better-auth";
2
+ export type LinkedAccountProps = {
3
+ account?: Account;
4
+ provider: SocialProvider;
5
+ };
6
+ /**
7
+ * Render a single linked social account card with provider info and unlink control.
8
+ *
9
+ * Fetches additional account information from the provider using the accountInfo API
10
+ * and displays the provider name, account details, and an unlink button.
11
+ *
12
+ * @param account - The account object containing id, accountId, and providerId
13
+ * @param provider - The provider id
14
+ * @returns A JSX element containing the linked account card
15
+ */
16
+ export declare function LinkedAccount({ account, provider }: LinkedAccountProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,46 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { getProviderName } from "@better-auth-ui/core";
3
+ import { providerIcons, useAccountInfo, useAuth, useLinkSocial, useUnlinkAccount } from "@better-auth-ui/react";
4
+ import { Link, PlugConnection, Xmark } from "@gravity-ui/icons";
5
+ import { Button, Card, cn, Skeleton, Spinner, toast } from "@heroui/react";
6
+ /**
7
+ * Render a single linked social account card with provider info and unlink control.
8
+ *
9
+ * Fetches additional account information from the provider using the accountInfo API
10
+ * and displays the provider name, account details, and an unlink button.
11
+ *
12
+ * @param account - The account object containing id, accountId, and providerId
13
+ * @param provider - The provider id
14
+ * @returns A JSX element containing the linked account card
15
+ */
16
+ export function LinkedAccount({ account, provider }) {
17
+ const { baseURL, localization } = useAuth();
18
+ const { data: accountInfo, isPending: isLoadingInfo } = useAccountInfo(account?.accountId, {
19
+ enabled: !!account,
20
+ throwOnError: (error) => {
21
+ if (error.error)
22
+ toast.danger(error.error.message);
23
+ return false;
24
+ }
25
+ });
26
+ const { mutate: linkSocial, isPending: isLinking } = useLinkSocial({
27
+ onError: (error) => toast.danger(error.error?.message || error.message)
28
+ });
29
+ const { mutate: unlinkAccount, isPending: isUnlinking } = useUnlinkAccount({
30
+ onError: (error) => toast.danger(error.error?.message || error.message),
31
+ onSuccess: () => toast.success(localization.settings.accountUnlinked)
32
+ });
33
+ const ProviderIcon = providerIcons[provider];
34
+ const providerName = getProviderName(provider);
35
+ const displayName = accountInfo?.data?.login ||
36
+ accountInfo?.data?.username ||
37
+ accountInfo?.user?.email ||
38
+ accountInfo?.user?.name ||
39
+ account?.accountId;
40
+ return (_jsxs(Card, { className: cn("flex-row items-center border p-3 shadow-none", !account && "border-dashed"), children: [_jsx("div", { className: "flex size-10 items-center justify-center rounded-xl bg-surface-secondary", children: ProviderIcon ? (_jsx(ProviderIcon, { className: cn("size-5", !account && "opacity-50") })) : (_jsx(PlugConnection, { className: cn("size-5", !account && "opacity-50") })) }), _jsxs("div", { className: "flex flex-col", children: [_jsx("span", { className: "text-sm font-medium leading-tight", children: providerName }), account && isLoadingInfo ? (_jsx(Skeleton, { className: "h-3 w-24 my-0.5 rounded-lg" })) : (_jsx("span", { className: "text-xs text-muted", children: account
41
+ ? displayName
42
+ : localization.settings.linkProvider.replace("{{provider}}", providerName) }))] }), account ? (_jsx(Button, { className: "ml-auto", isIconOnly: true, variant: "ghost", size: "sm", onPress: () => unlinkAccount({ providerId: account.providerId }), isPending: isUnlinking, "aria-label": localization.settings.unlinkProvider.replace("{{provider}}", providerName), children: isUnlinking ? _jsx(Spinner, { color: "current", size: "sm" }) : _jsx(Xmark, {}) })) : (_jsxs(Button, { className: "ml-auto", variant: "secondary", size: "sm", onPress: () => linkSocial({
43
+ provider,
44
+ callbackURL: `${baseURL}${window.location.pathname}`
45
+ }), isPending: isLinking, "aria-label": localization.settings.linkProvider.replace("{{provider}}", providerName), children: [isLinking ? _jsx(Spinner, { color: "current", size: "sm" }) : _jsx(Link, {}), localization.settings.link] }))] }));
46
+ }
@@ -0,0 +1,15 @@
1
+ import { type CardProps } from "@heroui/react";
2
+ export type LinkedAccountsProps = {
3
+ className?: string;
4
+ variant?: CardProps["variant"];
5
+ };
6
+ /**
7
+ * Render a card showing linked social accounts and available social providers to link.
8
+ *
9
+ * Linked accounts (excluding the "credential" provider) are shown with an unlink control;
10
+ * available providers are shown with a link control. Button states and labels reflect
11
+ * ongoing link/unlink activity and use localization for provider-specific text.
12
+ *
13
+ * @returns A JSX element containing the linked accounts card
14
+ */
15
+ export declare function LinkedAccounts({ className, ...props }: LinkedAccountsProps & CardProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,31 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useAuth, useListAccounts } from "@better-auth-ui/react";
3
+ import { Card, cn, Skeleton, toast } from "@heroui/react";
4
+ import { LinkedAccount } from "./linked-account";
5
+ /**
6
+ * Render a card showing linked social accounts and available social providers to link.
7
+ *
8
+ * Linked accounts (excluding the "credential" provider) are shown with an unlink control;
9
+ * available providers are shown with a link control. Button states and labels reflect
10
+ * ongoing link/unlink activity and use localization for provider-specific text.
11
+ *
12
+ * @returns A JSX element containing the linked accounts card
13
+ */
14
+ export function LinkedAccounts({ className, ...props }) {
15
+ const { localization, socialProviders } = useAuth();
16
+ const { data: accounts, isPending } = useListAccounts({
17
+ throwOnError: (error) => {
18
+ if (error.error)
19
+ toast.danger(error.error.message);
20
+ return false;
21
+ }
22
+ });
23
+ return (_jsxs(Card, { className: cn("p-4 md:p-6 gap-4", className), ...props, children: [_jsx(Card.Header, { children: _jsx(Card.Title, { className: "text-xl", children: localization.settings.linkedAccounts }) }), _jsx(Card.Content, { className: "gap-3", children: isPending ? (_jsx(AccountRowSkeleton, {})) : (_jsxs(_Fragment, { children: [accounts
24
+ ?.filter((account) => account.providerId !== "credential")
25
+ .map((account) => (_jsx(LinkedAccount, { account: account, provider: account.providerId }, account.id))), socialProviders?.map((provider) => {
26
+ return _jsx(LinkedAccount, { provider: provider }, provider);
27
+ })] })) })] }));
28
+ }
29
+ function AccountRowSkeleton() {
30
+ return (_jsx("div", { className: "flex items-center rounded-3xl border p-3 justify-between", children: _jsxs("div", { className: "flex items-center gap-3", children: [_jsx(Skeleton, { className: "size-10 rounded-xl" }), _jsxs("div", { className: "flex flex-col gap-1", children: [_jsx(Skeleton, { className: "h-4 w-20 rounded-lg" }), _jsx(Skeleton, { className: "h-3 w-32 rounded-lg" })] })] }) }));
31
+ }
@@ -0,0 +1,15 @@
1
+ import type { CardProps } from "@heroui/react";
2
+ import type { ComponentProps } from "react";
3
+ export type SecuritySettingsProps = {
4
+ className?: string;
5
+ variant?: CardProps["variant"];
6
+ };
7
+ /**
8
+ * Renders the security settings layout including password management, linked accounts, and active sessions.
9
+ *
10
+ * ChangePassword is rendered when password authentication is enabled; LinkedAccounts is rendered when social providers are present.
11
+ *
12
+ * @param className - Optional additional CSS class names for the outer container.
13
+ * @returns The security settings container as a JSX element.
14
+ */
15
+ export declare function SecuritySettings({ className, ...props }: SecuritySettingsProps & ComponentProps<"div">): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,18 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useAuth } from "@better-auth-ui/react";
3
+ import { cn } from "../../../lib/utils";
4
+ import { ActiveSessions } from "./active-sessions";
5
+ import { ChangePassword } from "./change-password";
6
+ import { LinkedAccounts } from "./linked-accounts";
7
+ /**
8
+ * Renders the security settings layout including password management, linked accounts, and active sessions.
9
+ *
10
+ * ChangePassword is rendered when password authentication is enabled; LinkedAccounts is rendered when social providers are present.
11
+ *
12
+ * @param className - Optional additional CSS class names for the outer container.
13
+ * @returns The security settings container as a JSX element.
14
+ */
15
+ export function SecuritySettings({ className, ...props }) {
16
+ const { emailAndPassword, socialProviders } = useAuth();
17
+ return (_jsxs("div", { className: cn("flex w-full flex-col gap-4 md:gap-6", className), ...props, children: [emailAndPassword?.enabled && _jsx(ChangePassword, {}), socialProviders?.length && _jsx(LinkedAccounts, {}), _jsx(ActiveSessions, {})] }));
18
+ }