@better-auth-ui/heroui 1.6.7 → 1.6.9

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 (280) hide show
  1. package/dist/components/auth/additional-field.d.ts +2 -2
  2. package/dist/components/auth/additional-field.js +133 -266
  3. package/dist/components/auth/api-key/api-key-skeleton.js +4 -20
  4. package/dist/components/auth/api-key/api-key.d.ts +6 -2
  5. package/dist/components/auth/api-key/api-key.js +16 -54
  6. package/dist/components/auth/api-key/api-keys-empty.d.ts +3 -1
  7. package/dist/components/auth/api-key/api-keys-empty.js +8 -34
  8. package/dist/components/auth/api-key/api-keys.d.ts +10 -2
  9. package/dist/components/auth/api-key/api-keys.js +21 -40
  10. package/dist/components/auth/api-key/create-api-key-dialog.d.ts +3 -1
  11. package/dist/components/auth/api-key/create-api-key-dialog.js +43 -73
  12. package/dist/components/auth/api-key/delete-api-key-dialog.d.ts +4 -2
  13. package/dist/components/auth/api-key/delete-api-key-dialog.js +16 -50
  14. package/dist/components/auth/api-key/new-api-key-dialog.js +23 -59
  15. package/dist/components/auth/api-key/organization-api-keys.d.ts +14 -0
  16. package/dist/components/auth/api-key/organization-api-keys.js +22 -0
  17. package/dist/components/auth/auth-provider.d.ts +1 -1
  18. package/dist/components/auth/auth-provider.js +10 -16
  19. package/dist/components/auth/auth.d.ts +3 -3
  20. package/dist/components/auth/auth.js +86 -61
  21. package/dist/components/auth/delete-user/danger-zone.d.ts +3 -3
  22. package/dist/components/auth/delete-user/danger-zone.js +11 -17
  23. package/dist/components/auth/delete-user/delete-account.d.ts +9 -0
  24. package/dist/components/auth/delete-user/delete-account.js +50 -0
  25. package/dist/components/auth/email/email-changed-email.d.ts +2 -2
  26. package/dist/components/auth/email/email-changed-email.js +22 -29
  27. package/dist/components/auth/email/email-verification-email.d.ts +2 -2
  28. package/dist/components/auth/email/email-verification-email.js +22 -29
  29. package/dist/components/auth/email/magic-link-email.d.ts +2 -2
  30. package/dist/components/auth/email/magic-link-email.js +22 -29
  31. package/dist/components/auth/email/new-device-email.d.ts +2 -2
  32. package/dist/components/auth/email/new-device-email.js +22 -29
  33. package/dist/components/auth/email/organization-invitation-email.d.ts +3 -0
  34. package/dist/components/auth/email/organization-invitation-email.js +23 -0
  35. package/dist/components/auth/email/otp-email.d.ts +2 -2
  36. package/dist/components/auth/email/otp-email.js +21 -28
  37. package/dist/components/auth/email/password-changed-email.d.ts +2 -2
  38. package/dist/components/auth/email/password-changed-email.js +22 -29
  39. package/dist/components/auth/email/reset-password-email.d.ts +2 -2
  40. package/dist/components/auth/email/reset-password-email.js +22 -29
  41. package/dist/components/auth/error-toaster.js +32 -16
  42. package/dist/components/auth/field-separator.d.ts +1 -1
  43. package/dist/components/auth/field-separator.js +4 -17
  44. package/dist/components/auth/forgot-password.d.ts +1 -1
  45. package/dist/components/auth/forgot-password.js +36 -88
  46. package/dist/components/auth/magic-link/magic-link-button.d.ts +1 -1
  47. package/dist/components/auth/magic-link/magic-link-button.js +33 -19
  48. package/dist/components/auth/magic-link/magic-link.d.ts +2 -2
  49. package/dist/components/auth/magic-link/magic-link.js +39 -86
  50. package/dist/components/auth/multi-session/manage-account.d.ts +1 -1
  51. package/dist/components/auth/multi-session/manage-account.js +32 -48
  52. package/dist/components/auth/multi-session/manage-accounts.d.ts +1 -1
  53. package/dist/components/auth/multi-session/manage-accounts.js +32 -33
  54. package/dist/components/auth/multi-session/switch-account-submenu-content.d.ts +3 -1
  55. package/dist/components/auth/multi-session/switch-account-submenu-content.js +24 -27
  56. package/dist/components/auth/multi-session/switch-account-submenu-item.d.ts +3 -2
  57. package/dist/components/auth/multi-session/switch-account-submenu-item.js +16 -19
  58. package/dist/components/auth/multi-session/switch-account-submenu.d.ts +2 -1
  59. package/dist/components/auth/multi-session/switch-account-submenu.js +23 -23
  60. package/dist/components/auth/organization/change-organization-logo.d.ts +4 -0
  61. package/dist/components/auth/organization/change-organization-logo.js +69 -0
  62. package/dist/components/auth/organization/create-organization-dialog.d.ts +13 -0
  63. package/dist/components/auth/organization/create-organization-dialog.js +37 -0
  64. package/dist/components/auth/organization/delete-organization-dialog.d.ts +7 -0
  65. package/dist/components/auth/organization/delete-organization-dialog.js +25 -0
  66. package/dist/components/auth/organization/delete-organization-skeleton.d.ts +4 -0
  67. package/dist/components/auth/organization/delete-organization-skeleton.js +8 -0
  68. package/dist/components/auth/organization/delete-organization.d.ts +5 -0
  69. package/dist/components/auth/organization/delete-organization.js +27 -0
  70. package/dist/components/auth/organization/invite-member-dialog.d.ts +13 -0
  71. package/dist/components/auth/organization/invite-member-dialog.js +47 -0
  72. package/dist/components/auth/organization/leave-organization-dialog.d.ts +7 -0
  73. package/dist/components/auth/organization/leave-organization-dialog.js +21 -0
  74. package/dist/components/auth/organization/leave-organization.d.ts +4 -0
  75. package/dist/components/auth/organization/leave-organization.js +16 -0
  76. package/dist/components/auth/organization/organization-danger-zone.d.ts +16 -0
  77. package/dist/components/auth/organization/organization-danger-zone.js +23 -0
  78. package/dist/components/auth/organization/organization-invitation-row-skeleton.d.ts +5 -0
  79. package/dist/components/auth/organization/organization-invitation-row-skeleton.js +9 -0
  80. package/dist/components/auth/organization/organization-invitation-row.d.ts +5 -0
  81. package/dist/components/auth/organization/organization-invitation-row.js +31 -0
  82. package/dist/components/auth/organization/organization-invitations-empty.d.ts +8 -0
  83. package/dist/components/auth/organization/organization-invitations-empty.js +13 -0
  84. package/dist/components/auth/organization/organization-invitations.d.ts +9 -0
  85. package/dist/components/auth/organization/organization-invitations.js +66 -0
  86. package/dist/components/auth/organization/organization-logo.d.ts +15 -0
  87. package/dist/components/auth/organization/organization-logo.js +14 -0
  88. package/dist/components/auth/organization/organization-member-row-skeleton.d.ts +4 -0
  89. package/dist/components/auth/organization/organization-member-row-skeleton.js +9 -0
  90. package/dist/components/auth/organization/organization-member-row.d.ts +9 -0
  91. package/dist/components/auth/organization/organization-member-row.js +34 -0
  92. package/dist/components/auth/organization/organization-members.d.ts +9 -0
  93. package/dist/components/auth/organization/organization-members.js +67 -0
  94. package/dist/components/auth/organization/organization-people.d.ts +10 -0
  95. package/dist/components/auth/organization/organization-people.js +11 -0
  96. package/dist/components/auth/organization/organization-profile.d.ts +9 -0
  97. package/dist/components/auth/organization/organization-profile.js +34 -0
  98. package/dist/components/auth/organization/organization-row.d.ts +8 -0
  99. package/dist/components/auth/organization/organization-row.js +31 -0
  100. package/dist/components/auth/organization/organization-settings.d.ts +12 -0
  101. package/dist/components/auth/organization/organization-settings.js +13 -0
  102. package/dist/components/auth/organization/organization-switcher.d.ts +20 -0
  103. package/dist/components/auth/organization/organization-switcher.js +48 -0
  104. package/dist/components/auth/organization/organization-view-skeleton.d.ts +6 -0
  105. package/dist/components/auth/organization/organization-view-skeleton.js +9 -0
  106. package/dist/components/auth/organization/organization-view.d.ts +15 -0
  107. package/dist/components/auth/organization/organization-view.js +31 -0
  108. package/dist/components/auth/organization/organization.d.ts +17 -0
  109. package/dist/components/auth/organization/organization.js +60 -0
  110. package/dist/components/auth/organization/organizations-empty.d.ts +4 -0
  111. package/dist/components/auth/organization/organizations-empty.js +9 -0
  112. package/dist/components/auth/organization/organizations-settings.d.ts +17 -0
  113. package/dist/components/auth/organization/organizations-settings.js +17 -0
  114. package/dist/components/auth/organization/organizations.d.ts +10 -0
  115. package/dist/components/auth/organization/organizations.js +21 -0
  116. package/dist/components/auth/organization/remove-member-dialog.d.ts +9 -0
  117. package/dist/components/auth/organization/remove-member-dialog.js +20 -0
  118. package/dist/components/auth/organization/slug-field.d.ts +19 -0
  119. package/dist/components/auth/organization/slug-field.js +38 -0
  120. package/dist/components/auth/organization/user-invitation-row-skeleton.d.ts +4 -0
  121. package/dist/components/auth/organization/user-invitation-row-skeleton.js +8 -0
  122. package/dist/components/auth/organization/user-invitation-row.d.ts +10 -0
  123. package/dist/components/auth/organization/user-invitation-row.js +22 -0
  124. package/dist/components/auth/organization/user-invitations-empty.d.ts +4 -0
  125. package/dist/components/auth/organization/user-invitations-empty.js +12 -0
  126. package/dist/components/auth/organization/user-invitations.d.ts +10 -0
  127. package/dist/components/auth/organization/user-invitations.js +18 -0
  128. package/dist/components/auth/passkey/add-passkey-dialog.d.ts +5 -0
  129. package/dist/components/auth/passkey/add-passkey-dialog.js +22 -0
  130. package/dist/components/auth/passkey/delete-passkey-dialog.d.ts +11 -0
  131. package/dist/components/auth/passkey/delete-passkey-dialog.js +14 -0
  132. package/dist/components/auth/passkey/passkey-button.d.ts +1 -1
  133. package/dist/components/auth/passkey/passkey-button.js +24 -23
  134. package/dist/components/auth/passkey/passkey-skeleton.d.ts +1 -0
  135. package/dist/components/auth/passkey/passkey-skeleton.js +5 -0
  136. package/dist/components/auth/passkey/passkey.d.ts +2 -5
  137. package/dist/components/auth/passkey/passkey.js +16 -44
  138. package/dist/components/auth/passkey/passkeys-empty.d.ts +4 -0
  139. package/dist/components/auth/passkey/passkeys-empty.js +9 -0
  140. package/dist/components/auth/passkey/passkeys.d.ts +1 -1
  141. package/dist/components/auth/passkey/passkeys.js +15 -105
  142. package/dist/components/auth/provider-button.d.ts +2 -2
  143. package/dist/components/auth/provider-button.js +28 -24
  144. package/dist/components/auth/provider-buttons.js +28 -18
  145. package/dist/components/auth/reset-password.d.ts +1 -1
  146. package/dist/components/auth/reset-password.js +56 -146
  147. package/dist/components/auth/settings/account/account-settings.d.ts +2 -2
  148. package/dist/components/auth/settings/account/account-settings.js +21 -19
  149. package/dist/components/auth/settings/account/change-avatar.js +53 -88
  150. package/dist/components/auth/settings/account/change-email.d.ts +1 -1
  151. package/dist/components/auth/settings/account/change-email.js +27 -53
  152. package/dist/components/auth/settings/account/user-profile.d.ts +1 -1
  153. package/dist/components/auth/settings/account/user-profile.js +64 -85
  154. package/dist/components/auth/settings/security/active-session.d.ts +1 -1
  155. package/dist/components/auth/settings/security/active-session.js +50 -57
  156. package/dist/components/auth/settings/security/active-sessions.d.ts +1 -1
  157. package/dist/components/auth/settings/security/active-sessions.js +21 -32
  158. package/dist/components/auth/settings/security/change-password.d.ts +1 -1
  159. package/dist/components/auth/settings/security/change-password.js +76 -186
  160. package/dist/components/auth/settings/security/linked-account.d.ts +1 -1
  161. package/dist/components/auth/settings/security/linked-account.js +37 -55
  162. package/dist/components/auth/settings/security/linked-accounts.d.ts +1 -1
  163. package/dist/components/auth/settings/security/linked-accounts.js +34 -43
  164. package/dist/components/auth/settings/security/security-settings.d.ts +2 -2
  165. package/dist/components/auth/settings/security/security-settings.js +19 -21
  166. package/dist/components/auth/settings/settings.d.ts +4 -4
  167. package/dist/components/auth/settings/settings.js +52 -44
  168. package/dist/components/auth/sign-in.d.ts +2 -2
  169. package/dist/components/auth/sign-in.js +63 -132
  170. package/dist/components/auth/sign-out.js +32 -26
  171. package/dist/components/auth/sign-up.d.ts +2 -2
  172. package/dist/components/auth/sign-up.js +97 -211
  173. package/dist/components/auth/theme/appearance.d.ts +1 -1
  174. package/dist/components/auth/theme/appearance.js +23 -71
  175. package/dist/components/auth/theme/theme-toggle-item.js +56 -61
  176. package/dist/components/auth/user/user-avatar.d.ts +4 -4
  177. package/dist/components/auth/user/user-avatar.js +31 -28
  178. package/dist/components/auth/user/user-button.d.ts +6 -7
  179. package/dist/components/auth/user/user-button.js +38 -72
  180. package/dist/components/auth/user/user-view.d.ts +13 -7
  181. package/dist/components/auth/user/user-view.js +26 -36
  182. package/dist/components/auth/username/sign-in-username.d.ts +2 -2
  183. package/dist/components/auth/username/sign-in-username.js +87 -147
  184. package/dist/components/auth/username/username-field.d.ts +1 -1
  185. package/dist/components/auth/username/username-field.js +44 -52
  186. package/dist/email.d.ts +8 -7
  187. package/dist/email.js +8 -8
  188. package/dist/index.d.ts +21 -21
  189. package/dist/index.js +21 -22
  190. package/dist/lib/auth/api-key-plugin.d.ts +7 -4
  191. package/dist/lib/auth/api-key-plugin.js +12 -10
  192. package/dist/lib/auth/auth-plugin.d.ts +10 -6
  193. package/dist/lib/auth/auth-plugin.js +1 -0
  194. package/dist/lib/auth/delete-user-plugin.d.ts +6 -6
  195. package/dist/lib/auth/delete-user-plugin.js +6 -9
  196. package/dist/lib/auth/magic-link-plugin.d.ts +5 -5
  197. package/dist/lib/auth/magic-link-plugin.js +17 -12
  198. package/dist/lib/auth/multi-session-plugin.d.ts +5 -5
  199. package/dist/lib/auth/multi-session-plugin.js +8 -11
  200. package/dist/lib/auth/organization-plugin.d.ts +37 -0
  201. package/dist/lib/auth/organization-plugin.js +19 -0
  202. package/dist/lib/auth/passkey-plugin.d.ts +9 -6
  203. package/dist/lib/auth/passkey-plugin.js +8 -11
  204. package/dist/lib/auth/theme-plugin.d.ts +6 -6
  205. package/dist/lib/auth/theme-plugin.js +25 -22
  206. package/dist/lib/auth/username-plugin.d.ts +11 -11
  207. package/dist/lib/auth/username-plugin.js +18 -17
  208. package/dist/plugins.d.ts +46 -22
  209. package/dist/plugins.js +47 -22
  210. package/package.json +25 -19
  211. package/src/components/auth/api-key/api-key.tsx +25 -16
  212. package/src/components/auth/api-key/api-keys-empty.tsx +8 -4
  213. package/src/components/auth/api-key/api-keys.tsx +52 -14
  214. package/src/components/auth/api-key/create-api-key-dialog.tsx +17 -4
  215. package/src/components/auth/api-key/delete-api-key-dialog.tsx +10 -2
  216. package/src/components/auth/api-key/organization-api-keys.tsx +55 -0
  217. package/src/components/auth/delete-user/danger-zone.tsx +3 -3
  218. package/src/components/auth/delete-user/{delete-user.tsx → delete-account.tsx} +9 -9
  219. package/src/components/auth/email/organization-invitation-email.tsx +38 -0
  220. package/src/components/auth/error-toaster.tsx +28 -10
  221. package/src/components/auth/forgot-password.tsx +10 -2
  222. package/src/components/auth/multi-session/manage-accounts.tsx +1 -1
  223. package/src/components/auth/multi-session/switch-account-submenu-content.tsx +7 -2
  224. package/src/components/auth/multi-session/switch-account-submenu-item.tsx +4 -2
  225. package/src/components/auth/multi-session/switch-account-submenu.tsx +8 -4
  226. package/src/components/auth/organization/change-organization-logo.tsx +171 -0
  227. package/src/components/auth/organization/create-organization-dialog.tsx +133 -0
  228. package/src/components/auth/organization/delete-organization-dialog.tsx +97 -0
  229. package/src/components/auth/organization/delete-organization-skeleton.tsx +17 -0
  230. package/src/components/auth/organization/delete-organization.tsx +77 -0
  231. package/src/components/auth/organization/invite-member-dialog.tsx +177 -0
  232. package/src/components/auth/organization/leave-organization-dialog.tsx +95 -0
  233. package/src/components/auth/organization/leave-organization.tsx +59 -0
  234. package/src/components/auth/organization/organization-danger-zone.tsx +68 -0
  235. package/src/components/auth/organization/organization-invitation-row-skeleton.tsx +29 -0
  236. package/src/components/auth/organization/organization-invitation-row.tsx +97 -0
  237. package/src/components/auth/organization/organization-invitations-empty.tsx +39 -0
  238. package/src/components/auth/organization/organization-invitations.tsx +351 -0
  239. package/src/components/auth/organization/organization-logo.tsx +64 -0
  240. package/src/components/auth/organization/organization-member-row-skeleton.tsx +24 -0
  241. package/src/components/auth/organization/organization-member-row.tsx +157 -0
  242. package/src/components/auth/organization/organization-members.tsx +284 -0
  243. package/src/components/auth/organization/organization-people.tsx +26 -0
  244. package/src/components/auth/organization/organization-profile.tsx +141 -0
  245. package/src/components/auth/organization/organization-row.tsx +66 -0
  246. package/src/components/auth/organization/organization-settings.tsx +38 -0
  247. package/src/components/auth/organization/organization-switcher.tsx +233 -0
  248. package/src/components/auth/organization/organization-view-skeleton.tsx +36 -0
  249. package/src/components/auth/organization/organization-view.tsx +109 -0
  250. package/src/components/auth/organization/organization.tsx +162 -0
  251. package/src/components/auth/organization/organizations-empty.tsx +35 -0
  252. package/src/components/auth/organization/organizations-settings.tsx +36 -0
  253. package/src/components/auth/organization/organizations.tsx +80 -0
  254. package/src/components/auth/organization/remove-member-dialog.tsx +95 -0
  255. package/src/components/auth/organization/slug-field.tsx +111 -0
  256. package/src/components/auth/organization/user-invitation-row-skeleton.tsx +17 -0
  257. package/src/components/auth/organization/user-invitation-row.tsx +90 -0
  258. package/src/components/auth/organization/user-invitations-empty.tsx +30 -0
  259. package/src/components/auth/organization/user-invitations.tsx +59 -0
  260. package/src/components/auth/passkey/add-passkey-dialog.tsx +109 -0
  261. package/src/components/auth/passkey/delete-passkey-dialog.tsx +95 -0
  262. package/src/components/auth/passkey/passkey-skeleton.tsx +16 -0
  263. package/src/components/auth/passkey/passkey.tsx +23 -22
  264. package/src/components/auth/passkey/passkeys-empty.tsx +35 -0
  265. package/src/components/auth/passkey/passkeys.tsx +33 -154
  266. package/src/components/auth/settings/account/change-email.tsx +1 -1
  267. package/src/components/auth/settings/account/user-profile.tsx +5 -5
  268. package/src/components/auth/settings/security/active-sessions.tsx +6 -4
  269. package/src/components/auth/settings/security/linked-accounts.tsx +2 -2
  270. package/src/components/auth/settings/settings.tsx +77 -14
  271. package/src/components/auth/user/user-avatar.tsx +4 -1
  272. package/src/components/auth/user/user-button.tsx +14 -11
  273. package/src/components/auth/user/user-view.tsx +32 -12
  274. package/src/email.ts +1 -0
  275. package/src/lib/auth/api-key-plugin.ts +10 -4
  276. package/src/lib/auth/auth-plugin.ts +6 -1
  277. package/src/lib/auth/organization-plugin.tsx +32 -0
  278. package/src/plugins.ts +25 -1
  279. package/dist/components/auth/delete-user/delete-user.d.ts +0 -9
  280. package/dist/components/auth/delete-user/delete-user.js +0 -95
@@ -0,0 +1,55 @@
1
+ import {
2
+ type OrganizationAuthClient,
3
+ useActiveOrganization,
4
+ useAuth,
5
+ useListOrganizationMembers,
6
+ useSession
7
+ } from "@better-auth-ui/react"
8
+ import type { CardProps } from "@heroui/react"
9
+
10
+ import { ApiKeys } from "./api-keys"
11
+
12
+ export type OrganizationApiKeysProps = {
13
+ className?: string
14
+ variant?: CardProps["variant"]
15
+ }
16
+
17
+ /**
18
+ * {@link ApiKeys} scoped to the active organization.
19
+ *
20
+ * Hidden for members whose role isn't `owner`. Better Auth's
21
+ * `/organization/has-permission` endpoint isn't usable for `apiKey:*` checks
22
+ * (it doesn't pass `allowCreatorAllPermissions` and the default org AC has no
23
+ * `apiKey` statements), so we gate on role directly.
24
+ */
25
+ export function OrganizationApiKeys({
26
+ className,
27
+ variant
28
+ }: OrganizationApiKeysProps) {
29
+ const { authClient } = useAuth()
30
+ const { data: session } = useSession(authClient)
31
+
32
+ const { data: activeOrganization, isPending: activeOrganizationPending } =
33
+ useActiveOrganization(authClient as OrganizationAuthClient)
34
+
35
+ const { data: membersData } = useListOrganizationMembers(
36
+ authClient as OrganizationAuthClient
37
+ )
38
+
39
+ const canManageApiKeys = membersData?.members.some(
40
+ (member) => member.role === "owner" && member.userId === session?.user.id
41
+ )
42
+
43
+ if (!canManageApiKeys) {
44
+ return null
45
+ }
46
+
47
+ return (
48
+ <ApiKeys
49
+ className={className}
50
+ variant={variant}
51
+ organizationId={activeOrganization?.id}
52
+ isPending={activeOrganizationPending}
53
+ />
54
+ )
55
+ }
@@ -2,7 +2,7 @@ import { useAuth } from "@better-auth-ui/react"
2
2
  import { type CardProps, cn } from "@heroui/react"
3
3
  import type { ComponentProps } from "react"
4
4
 
5
- import { DeleteUser } from "./delete-user"
5
+ import { DeleteAccount } from "./delete-account"
6
6
 
7
7
  export type DangerZoneProps = {
8
8
  className?: string
@@ -10,7 +10,7 @@ export type DangerZoneProps = {
10
10
  }
11
11
 
12
12
  /**
13
- * Renders the danger zone heading and {@link DeleteUser}.
13
+ * Renders the danger zone heading and {@link DeleteAccount}.
14
14
  * Registered as a `securityCard` by `deleteUserPlugin()`; gate by registering the plugin.
15
15
  */
16
16
  export function DangerZone({
@@ -26,7 +26,7 @@ export function DangerZone({
26
26
  {localization.settings.dangerZone}
27
27
  </h2>
28
28
 
29
- <DeleteUser variant={variant} />
29
+ <DeleteAccount variant={variant} />
30
30
  </div>
31
31
  )
32
32
  }
@@ -24,7 +24,7 @@ import { type SyntheticEvent, useState } from "react"
24
24
 
25
25
  import { deleteUserPlugin } from "../../../lib/auth/delete-user-plugin"
26
26
 
27
- export type DeleteUserProps = {
27
+ export type DeleteAccountProps = {
28
28
  className?: string
29
29
  variant?: CardProps["variant"]
30
30
  }
@@ -32,11 +32,11 @@ export type DeleteUserProps = {
32
32
  /**
33
33
  * Danger-zone card to delete the authenticated account, with a confirmation dialog and toasts.
34
34
  */
35
- export function DeleteUser({
35
+ export function DeleteAccount({
36
36
  className,
37
37
  variant,
38
38
  ...props
39
- }: DeleteUserProps & Omit<CardProps, "children">) {
39
+ }: DeleteAccountProps & Omit<CardProps, "children">) {
40
40
  const { authClient, basePaths, localization, navigate, viewPaths } = useAuth()
41
41
 
42
42
  const {
@@ -94,11 +94,11 @@ export function DeleteUser({
94
94
  <Card.Content className="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
95
95
  <div>
96
96
  <p className="text-sm font-medium leading-tight">
97
- {deleteUserLocalization.deleteUser}
97
+ {deleteUserLocalization.deleteAccount}
98
98
  </p>
99
99
 
100
100
  <p className="text-muted text-xs mt-0.5">
101
- {deleteUserLocalization.deleteUserDescription}
101
+ {deleteUserLocalization.deleteAccountDescription}
102
102
  </p>
103
103
  </div>
104
104
 
@@ -109,7 +109,7 @@ export function DeleteUser({
109
109
  variant="danger-soft"
110
110
  onPress={() => setConfirmOpen(true)}
111
111
  >
112
- {deleteUserLocalization.deleteUser}
112
+ {deleteUserLocalization.deleteAccount}
113
113
  </Button>
114
114
 
115
115
  <AlertDialog.Backdrop
@@ -127,13 +127,13 @@ export function DeleteUser({
127
127
  </AlertDialog.Icon>
128
128
 
129
129
  <AlertDialog.Heading>
130
- {deleteUserLocalization.deleteUser}
130
+ {deleteUserLocalization.deleteAccount}
131
131
  </AlertDialog.Heading>
132
132
  </AlertDialog.Header>
133
133
 
134
134
  <AlertDialog.Body className="overflow-visible">
135
135
  <p className="text-muted text-sm">
136
- {deleteUserLocalization.deleteUserDescription}
136
+ {deleteUserLocalization.deleteAccountDescription}
137
137
  </p>
138
138
 
139
139
  {needsPassword && (
@@ -175,7 +175,7 @@ export function DeleteUser({
175
175
  >
176
176
  {isPending && <Spinner color="current" size="sm" />}
177
177
 
178
- {deleteUserLocalization.deleteUser}
178
+ {deleteUserLocalization.deleteAccount}
179
179
  </Button>
180
180
  </AlertDialog.Footer>
181
181
  </Form>
@@ -0,0 +1,38 @@
1
+ import {
2
+ OrganizationInvitationEmail as OrganizationInvitationEmailPrimitive,
3
+ type OrganizationInvitationEmailProps
4
+ } from "@better-auth-ui/react/email"
5
+ import { cn } from "@heroui/react"
6
+
7
+ export type { OrganizationInvitationEmailProps } from "@better-auth-ui/react/email"
8
+
9
+ export function OrganizationInvitationEmail({
10
+ colors,
11
+ classNames,
12
+ ...props
13
+ }: OrganizationInvitationEmailProps) {
14
+ return (
15
+ <OrganizationInvitationEmailPrimitive
16
+ colors={{
17
+ light: {
18
+ background: "#F5F5F5",
19
+ primary: "#0285F7",
20
+ primaryForeground: "#FCFCFC",
21
+ ...colors?.light
22
+ },
23
+ dark: {
24
+ background: "#060607",
25
+ primary: "#0584F6",
26
+ primaryForeground: "#FCFCFC",
27
+ ...colors?.dark
28
+ }
29
+ }}
30
+ classNames={{
31
+ ...classNames,
32
+ card: cn("border-none rounded-3xl", classNames?.card),
33
+ button: cn("rounded-full", classNames?.button)
34
+ }}
35
+ {...props}
36
+ />
37
+ )
38
+ }
@@ -1,5 +1,10 @@
1
+ import { authMutationKeys, authQueryKeys } from "@better-auth-ui/core"
1
2
  import { toast } from "@heroui/react"
2
- import { useQueryClient } from "@tanstack/react-query"
3
+ import {
4
+ matchMutation,
5
+ matchQuery,
6
+ useQueryClient
7
+ } from "@tanstack/react-query"
3
8
  import type { BetterFetchError } from "better-auth/react"
4
9
  import { useEffect } from "react"
5
10
 
@@ -7,18 +12,31 @@ export function ErrorToaster() {
7
12
  const queryClient = useQueryClient()
8
13
 
9
14
  useEffect(() => {
10
- queryClient.getQueryCache().config.onError = (error) => {
11
- const err = error as BetterFetchError
15
+ const unsubscribeQuery = queryClient.getQueryCache().subscribe((event) => {
16
+ if (event.type !== "updated" || event.action.type !== "error") return
17
+ if (!matchQuery({ queryKey: authQueryKeys.all }, event.query)) return
18
+
19
+ const err = event.action.error as BetterFetchError
12
20
  if (err?.error) toast.danger(err.error.message)
13
- }
21
+ })
14
22
 
15
- queryClient.setMutationDefaults([], {
16
- onError: (error) => {
17
- toast.danger(
18
- (error as BetterFetchError)?.error?.message || error.message
23
+ const unsubscribeMutation = queryClient
24
+ .getMutationCache()
25
+ .subscribe((event) => {
26
+ if (event.type !== "updated" || event.action.type !== "error") return
27
+ if (
28
+ !matchMutation({ mutationKey: authMutationKeys.all }, event.mutation)
19
29
  )
20
- }
21
- })
30
+ return
31
+
32
+ const err = event.action.error as BetterFetchError
33
+ toast.danger(err.error?.message || err.message)
34
+ })
35
+
36
+ return () => {
37
+ unsubscribeQuery()
38
+ unsubscribeMutation()
39
+ }
22
40
  }, [queryClient])
23
41
 
24
42
  return null
@@ -39,8 +39,15 @@ export function ForgotPassword({
39
39
  variant,
40
40
  ...props
41
41
  }: ForgotPasswordProps & Omit<CardProps, "children">) {
42
- const { authClient, basePaths, localization, viewPaths, navigate, plugins } =
43
- useAuth()
42
+ const {
43
+ authClient,
44
+ baseURL,
45
+ basePaths,
46
+ localization,
47
+ viewPaths,
48
+ navigate,
49
+ plugins
50
+ } = useAuth()
44
51
 
45
52
  const { fetchOptions, resetFetchOptions } = useFetchOptions()
46
53
 
@@ -64,6 +71,7 @@ export function ForgotPassword({
64
71
  const formData = new FormData(e.currentTarget)
65
72
  requestPasswordReset({
66
73
  email: formData.get("email") as string,
74
+ redirectTo: `${baseURL}${basePaths.auth}/${viewPaths.auth.resetPassword}`,
67
75
  fetchOptions
68
76
  })
69
77
  }
@@ -66,7 +66,7 @@ export function ManageAccounts({
66
66
  {allRows.map((row, index) => (
67
67
  <div key={row.key}>
68
68
  {index > 0 && (
69
- <div className="border-b border-dashed -mx-4 md:-mx-6 my-4" />
69
+ <div className="border-b border-dashed -mx-4 my-4" />
70
70
  )}
71
71
 
72
72
  <ManageAccount
@@ -21,7 +21,11 @@ import { SwitchAccountSubmenuItem } from "./switch-account-submenu-item"
21
21
  *
22
22
  * @returns The switch account submenu content as a JSX element
23
23
  */
24
- export function SwitchAccountSubmenuContent() {
24
+ export function SwitchAccountSubmenuContent({
25
+ hideSubtitle
26
+ }: {
27
+ hideSubtitle?: boolean
28
+ }) {
25
29
  const { authClient, basePaths, viewPaths } = useAuth()
26
30
  const { localization: multiSessionLocalization } =
27
31
  useAuthPlugin(multiSessionPlugin)
@@ -34,7 +38,7 @@ export function SwitchAccountSubmenuContent() {
34
38
  <Dropdown.Popover className="min-w-40 md:min-w-56 max-w-[48svw]">
35
39
  <Dropdown.Menu>
36
40
  <Dropdown.Item className="px-2">
37
- <UserView isPending={isPending} />
41
+ <UserView isPending={isPending} hideSubtitle={hideSubtitle} />
38
42
 
39
43
  {!isPending && <Check className="ml-auto" />}
40
44
  </Dropdown.Item>
@@ -47,6 +51,7 @@ export function SwitchAccountSubmenuContent() {
47
51
  <SwitchAccountSubmenuItem
48
52
  key={deviceSession.session.id}
49
53
  deviceSession={deviceSession}
54
+ hideSubtitle={hideSubtitle}
50
55
  />
51
56
  ))}
52
57
 
@@ -14,6 +14,7 @@ type DeviceSession = {
14
14
 
15
15
  export type SwitchAccountSubmenuItemProps = {
16
16
  deviceSession: DeviceSession
17
+ hideSubtitle?: boolean
17
18
  }
18
19
 
19
20
  /**
@@ -23,7 +24,8 @@ export type SwitchAccountSubmenuItemProps = {
23
24
  * @returns The switch account dropdown item as a JSX element
24
25
  */
25
26
  export function SwitchAccountSubmenuItem({
26
- deviceSession
27
+ deviceSession,
28
+ hideSubtitle
27
29
  }: SwitchAccountSubmenuItemProps) {
28
30
  const { authClient } = useAuth()
29
31
  const { mutate: setActiveSession, isPending } = useSetActiveSession(
@@ -41,7 +43,7 @@ export function SwitchAccountSubmenuItem({
41
43
  setActiveSession({ sessionToken: deviceSession.session.token })
42
44
  }
43
45
  >
44
- <UserView user={deviceSession.user} />
46
+ <UserView user={deviceSession.user} hideSubtitle={hideSubtitle} />
45
47
 
46
48
  {isPending && <Spinner color="current" size="sm" className="ml-auto" />}
47
49
  </Dropdown.Item>
@@ -1,5 +1,5 @@
1
1
  import { useAuth, useAuthPlugin, useSession } from "@better-auth-ui/react"
2
- import { Persons } from "@gravity-ui/icons"
2
+ import { ArrowRightArrowLeft } from "@gravity-ui/icons"
3
3
  import { Dropdown, Label } from "@heroui/react"
4
4
 
5
5
  import { multiSessionPlugin } from "../../../lib/auth/multi-session-plugin"
@@ -8,6 +8,7 @@ import { SwitchAccountSubmenuContent } from "./switch-account-submenu-content"
8
8
 
9
9
  export type SwitchAccountSubmenuProps = {
10
10
  className?: string
11
+ hideSubtitle?: boolean
11
12
  }
12
13
 
13
14
  /**
@@ -19,7 +20,10 @@ export type SwitchAccountSubmenuProps = {
19
20
  * @param className - Optional additional CSS class names
20
21
  * @returns The switch account submenu as a JSX element
21
22
  */
22
- export function SwitchAccountSubmenu({ className }: SwitchAccountSubmenuProps) {
23
+ export function SwitchAccountSubmenu({
24
+ className,
25
+ hideSubtitle
26
+ }: SwitchAccountSubmenuProps) {
23
27
  const { authClient } = useAuth()
24
28
  const { data: session } = useSession(authClient)
25
29
  const { localization: multiSessionLocalization } =
@@ -35,7 +39,7 @@ export function SwitchAccountSubmenu({ className }: SwitchAccountSubmenuProps) {
35
39
  className={className}
36
40
  textValue={multiSessionLocalization.switchAccount}
37
41
  >
38
- <Persons className="text-muted" />
42
+ <ArrowRightArrowLeft className="text-muted" />
39
43
 
40
44
  <Label>{multiSessionLocalization.switchAccount}</Label>
41
45
 
@@ -43,7 +47,7 @@ export function SwitchAccountSubmenu({ className }: SwitchAccountSubmenuProps) {
43
47
  </Dropdown.Item>
44
48
 
45
49
  <Dropdown.Popover className="min-w-40 md:min-w-56 max-w-[48svw]">
46
- <SwitchAccountSubmenuContent />
50
+ <SwitchAccountSubmenuContent hideSubtitle={hideSubtitle} />
47
51
  </Dropdown.Popover>
48
52
  </Dropdown.SubmenuTrigger>
49
53
  )
@@ -0,0 +1,171 @@
1
+ import { fileToBase64 } from "@better-auth-ui/core"
2
+ import {
3
+ type OrganizationAuthClient,
4
+ useActiveOrganization,
5
+ useAuth,
6
+ useAuthPlugin,
7
+ useUpdateOrganization
8
+ } from "@better-auth-ui/react"
9
+ import { CloudArrowUpIn, TrashBin } from "@gravity-ui/icons"
10
+ import { Button, cn, Dropdown, Label, Spinner, toast } from "@heroui/react"
11
+ import { type ChangeEvent, useRef, useState } from "react"
12
+ import { organizationPlugin } from "../../../lib/auth/organization-plugin"
13
+ import { OrganizationLogo } from "./organization-logo"
14
+
15
+ export type ChangeOrganizationLogoProps = {
16
+ className?: string
17
+ }
18
+
19
+ export function ChangeOrganizationLogo({
20
+ className
21
+ }: ChangeOrganizationLogoProps) {
22
+ const { authClient } = useAuth()
23
+ const { logo, localization: organizationLocalization } =
24
+ useAuthPlugin(organizationPlugin)
25
+
26
+ const { data: activeOrganization, isPending: activeOrganizationPending } =
27
+ useActiveOrganization(authClient as OrganizationAuthClient)
28
+
29
+ const { mutate: updateOrganization, isPending: updatePending } =
30
+ useUpdateOrganization(authClient as OrganizationAuthClient)
31
+
32
+ const fileInputRef = useRef<HTMLInputElement>(null)
33
+ const [isUploading, setIsUploading] = useState(false)
34
+ const [isDeleting, setIsDeleting] = useState(false)
35
+
36
+ const isPending = updatePending || isUploading || isDeleting
37
+
38
+ async function handleFileChange(e: ChangeEvent<HTMLInputElement>) {
39
+ const file = e.target.files?.[0]
40
+ if (!file || !activeOrganization) return
41
+
42
+ e.target.value = ""
43
+
44
+ setIsUploading(true)
45
+
46
+ try {
47
+ const resized =
48
+ (await logo.resize?.(file, logo.size, logo.extension)) || file
49
+
50
+ const image =
51
+ (await logo.upload?.(resized)) || (await fileToBase64(resized))
52
+
53
+ updateOrganization(
54
+ { data: { logo: image } },
55
+ {
56
+ onSuccess: () =>
57
+ toast.success(organizationLocalization.logoChangedSuccess),
58
+ onSettled: () => setIsUploading(false)
59
+ }
60
+ )
61
+ } catch (error) {
62
+ setIsUploading(false)
63
+ if (error instanceof Error) {
64
+ toast.danger(error.message)
65
+ }
66
+ }
67
+ }
68
+
69
+ async function handleDelete() {
70
+ const currentLogo = activeOrganization?.logo
71
+
72
+ updateOrganization(
73
+ {
74
+ data: { logo: "" }
75
+ },
76
+ {
77
+ onSuccess: async () => {
78
+ if (!currentLogo) {
79
+ toast.success(organizationLocalization.logoDeletedSuccess)
80
+ return
81
+ }
82
+
83
+ setIsDeleting(true)
84
+ try {
85
+ await logo.delete?.(currentLogo)
86
+ toast.success(organizationLocalization.logoDeletedSuccess)
87
+ } catch (error) {
88
+ if (error instanceof Error) {
89
+ toast.danger(error.message)
90
+ }
91
+ } finally {
92
+ setIsDeleting(false)
93
+ }
94
+ }
95
+ }
96
+ )
97
+ }
98
+
99
+ if (!logo.enabled) {
100
+ return null
101
+ }
102
+
103
+ return (
104
+ <div className={cn("flex flex-col gap-1", className)}>
105
+ <Label isDisabled={!activeOrganization}>
106
+ {organizationLocalization.logo}
107
+ </Label>
108
+
109
+ <input
110
+ ref={fileInputRef}
111
+ type="file"
112
+ accept="image/*"
113
+ className="hidden"
114
+ onChange={handleFileChange}
115
+ />
116
+
117
+ <div className="flex items-center gap-4">
118
+ <Button
119
+ type="button"
120
+ isIconOnly
121
+ variant="ghost"
122
+ className="p-0 h-auto w-auto rounded-full"
123
+ isDisabled={!activeOrganization || isPending}
124
+ onPress={() => fileInputRef.current?.click()}
125
+ >
126
+ <OrganizationLogo
127
+ size="lg"
128
+ isPending={activeOrganizationPending}
129
+ organization={activeOrganization}
130
+ />
131
+ </Button>
132
+
133
+ <Dropdown>
134
+ <Button
135
+ isDisabled={!activeOrganization || isPending}
136
+ size="sm"
137
+ variant="secondary"
138
+ >
139
+ {isPending && <Spinner size="sm" />}
140
+
141
+ {organizationLocalization.changeLogo}
142
+ </Button>
143
+
144
+ <Dropdown.Popover className="min-w-fit">
145
+ <Dropdown.Menu>
146
+ <Dropdown.Item
147
+ textValue={organizationLocalization.uploadLogo}
148
+ onAction={() => fileInputRef.current?.click()}
149
+ >
150
+ <CloudArrowUpIn className="text-muted" />
151
+
152
+ <Label>{organizationLocalization.uploadLogo}</Label>
153
+ </Dropdown.Item>
154
+
155
+ <Dropdown.Item
156
+ textValue={organizationLocalization.deleteLogo}
157
+ isDisabled={!activeOrganization?.logo}
158
+ onAction={handleDelete}
159
+ variant="danger"
160
+ >
161
+ <TrashBin className="text-danger" />
162
+
163
+ <Label>{organizationLocalization.deleteLogo}</Label>
164
+ </Dropdown.Item>
165
+ </Dropdown.Menu>
166
+ </Dropdown.Popover>
167
+ </Dropdown>
168
+ </div>
169
+ </div>
170
+ )
171
+ }
@@ -0,0 +1,133 @@
1
+ import {
2
+ type OrganizationAuthClient,
3
+ useAuth,
4
+ useAuthPlugin,
5
+ useCreateOrganization
6
+ } from "@better-auth-ui/react"
7
+ import { Briefcase } from "@gravity-ui/icons"
8
+ import {
9
+ AlertDialog,
10
+ Button,
11
+ FieldError,
12
+ Form,
13
+ Input,
14
+ Label,
15
+ Spinner,
16
+ TextField
17
+ } from "@heroui/react"
18
+ import { type SyntheticEvent, useEffect, useState } from "react"
19
+
20
+ import { organizationPlugin } from "../../../lib/auth/organization-plugin"
21
+ import { SlugField, sanitizeSlug } from "./slug-field"
22
+
23
+ /** Props for the {@link CreateOrganizationDialog} component. */
24
+ export type CreateOrganizationDialogProps = {
25
+ isOpen: boolean
26
+ onOpenChange: (open: boolean) => void
27
+ }
28
+
29
+ /**
30
+ * Render a dialog for creating a new organization.
31
+ *
32
+ * @param isOpen - Whether the dialog is open
33
+ * @param onOpenChange - Callback for when the dialog open state changes
34
+ * @returns The create organization dialog as a JSX element
35
+ */
36
+ export function CreateOrganizationDialog({
37
+ isOpen,
38
+ onOpenChange
39
+ }: CreateOrganizationDialogProps) {
40
+ const { authClient, localization } = useAuth()
41
+ const { localization: organizationLocalization } =
42
+ useAuthPlugin(organizationPlugin)
43
+
44
+ const [name, setName] = useState("")
45
+ const [slug, setSlug] = useState("")
46
+
47
+ const { mutate: createOrganization, isPending: isCreating } =
48
+ useCreateOrganization(authClient as OrganizationAuthClient, {
49
+ onSuccess: () => onOpenChange(false)
50
+ })
51
+
52
+ const handleSubmit = (e: SyntheticEvent<HTMLFormElement>) => {
53
+ e.preventDefault()
54
+ createOrganization({ name, slug })
55
+ }
56
+
57
+ useEffect(() => {
58
+ if (!isOpen) {
59
+ setSlug("")
60
+ setName("")
61
+ }
62
+ }, [isOpen])
63
+
64
+ useEffect(() => {
65
+ setSlug(sanitizeSlug(name))
66
+ }, [name])
67
+
68
+ return (
69
+ <AlertDialog.Backdrop isOpen={isOpen} onOpenChange={onOpenChange}>
70
+ <AlertDialog.Container>
71
+ <AlertDialog.Dialog>
72
+ <Form onSubmit={handleSubmit}>
73
+ <AlertDialog.CloseTrigger />
74
+
75
+ <AlertDialog.Header>
76
+ <AlertDialog.Icon status="default">
77
+ <Briefcase />
78
+ </AlertDialog.Icon>
79
+
80
+ <AlertDialog.Heading>
81
+ {organizationLocalization.createOrganization}
82
+ </AlertDialog.Heading>
83
+ </AlertDialog.Header>
84
+
85
+ <AlertDialog.Body className="flex flex-col gap-4 overflow-visible">
86
+ <p className="text-muted text-sm">
87
+ {organizationLocalization.organizationsDescription}
88
+ </p>
89
+
90
+ <TextField
91
+ id="name"
92
+ name="name"
93
+ isDisabled={isCreating}
94
+ value={name}
95
+ onChange={setName}
96
+ >
97
+ <Label>{organizationLocalization.name}</Label>
98
+
99
+ <Input
100
+ required
101
+ autoFocus
102
+ placeholder={organizationLocalization.namePlaceholder}
103
+ variant="secondary"
104
+ />
105
+
106
+ <FieldError />
107
+ </TextField>
108
+
109
+ <SlugField
110
+ value={slug}
111
+ onChange={setSlug}
112
+ isDisabled={isCreating}
113
+ variant="secondary"
114
+ />
115
+ </AlertDialog.Body>
116
+
117
+ <AlertDialog.Footer>
118
+ <Button slot="close" variant="tertiary" isDisabled={isCreating}>
119
+ {localization.settings.cancel}
120
+ </Button>
121
+
122
+ <Button type="submit" isPending={isCreating}>
123
+ {isCreating && <Spinner color="current" size="sm" />}
124
+
125
+ {organizationLocalization.createOrganization}
126
+ </Button>
127
+ </AlertDialog.Footer>
128
+ </Form>
129
+ </AlertDialog.Dialog>
130
+ </AlertDialog.Container>
131
+ </AlertDialog.Backdrop>
132
+ )
133
+ }