@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,80 @@
1
+ import {
2
+ type OrganizationAuthClient,
3
+ useAuth,
4
+ useAuthPlugin,
5
+ useListOrganizations
6
+ } from "@better-auth-ui/react"
7
+ import { Button, Card, type CardProps } from "@heroui/react"
8
+ import { useState } from "react"
9
+
10
+ import { organizationPlugin } from "../../../lib/auth/organization-plugin"
11
+ import { CreateOrganizationDialog } from "./create-organization-dialog"
12
+ import { OrganizationRow } from "./organization-row"
13
+ import { OrganizationViewSkeleton } from "./organization-view-skeleton"
14
+ import { OrganizationsEmpty } from "./organizations-empty"
15
+
16
+ export type OrganizationsProps = {
17
+ variant?: CardProps["variant"]
18
+ }
19
+
20
+ /**
21
+ * Lists organizations the user belongs to (via {@link useListOrganizations}): loading skeleton,
22
+ * empty state with create, or a card of rows with a Manage control per organization.
23
+ * Owns {@link CreateOrganizationDialog} open state and the create actions.
24
+ */
25
+ export function Organizations({ variant }: OrganizationsProps) {
26
+ const { authClient } = useAuth()
27
+ const { localization: organizationLocalization } =
28
+ useAuthPlugin(organizationPlugin)
29
+
30
+ const [createOpen, setCreateOpen] = useState(false)
31
+
32
+ const { data: organizations, isPending: organizationsPending } =
33
+ useListOrganizations(authClient as OrganizationAuthClient)
34
+
35
+ return (
36
+ <>
37
+ <div className="flex flex-col gap-3">
38
+ <div className="flex items-end justify-between gap-3">
39
+ <h2 className="truncate text-sm font-semibold">
40
+ {organizationLocalization.organizations}
41
+ </h2>
42
+
43
+ <Button
44
+ className="shrink-0"
45
+ size="sm"
46
+ isDisabled={organizationsPending}
47
+ onPress={() => setCreateOpen(true)}
48
+ >
49
+ {organizationLocalization.createOrganization}
50
+ </Button>
51
+ </div>
52
+
53
+ <Card variant={variant}>
54
+ <Card.Content className="gap-0">
55
+ {organizationsPending ? (
56
+ <OrganizationViewSkeleton />
57
+ ) : !organizations?.length ? (
58
+ <OrganizationsEmpty onCreatePress={() => setCreateOpen(true)} />
59
+ ) : (
60
+ organizations.map((organization, index) => (
61
+ <div key={organization.id}>
62
+ {index > 0 && (
63
+ <div className="-mx-4 my-4 border-b border-dashed" />
64
+ )}
65
+
66
+ <OrganizationRow organization={organization} />
67
+ </div>
68
+ ))
69
+ )}
70
+ </Card.Content>
71
+ </Card>
72
+ </div>
73
+
74
+ <CreateOrganizationDialog
75
+ isOpen={createOpen}
76
+ onOpenChange={setCreateOpen}
77
+ />
78
+ </>
79
+ )
80
+ }
@@ -0,0 +1,95 @@
1
+ import {
2
+ type OrganizationAuthClient,
3
+ useAuth,
4
+ useAuthPlugin,
5
+ useRemoveMember
6
+ } from "@better-auth-ui/react"
7
+ import { TrashBin } from "@gravity-ui/icons"
8
+ import { AlertDialog, Button, Card, Chip, Spinner, toast } from "@heroui/react"
9
+ import type { Member, User } from "better-auth/client"
10
+
11
+ import { organizationPlugin } from "../../../lib/auth/organization-plugin"
12
+ import { UserView } from "../user/user-view"
13
+
14
+ export type RemoveMemberDialogProps = {
15
+ isOpen: boolean
16
+ onOpenChange: (open: boolean) => void
17
+ member: Member & { user: Partial<User> }
18
+ }
19
+
20
+ export function RemoveMemberDialog({
21
+ isOpen,
22
+ onOpenChange,
23
+ member
24
+ }: RemoveMemberDialogProps) {
25
+ const { authClient, localization } = useAuth()
26
+ const { localization: organizationLocalization, roles } =
27
+ useAuthPlugin(organizationPlugin)
28
+
29
+ const { mutate: removeMember, isPending } = useRemoveMember(
30
+ authClient as OrganizationAuthClient,
31
+ {
32
+ onSuccess: () => {
33
+ onOpenChange(false)
34
+ toast.success(organizationLocalization.memberRemoved)
35
+ }
36
+ }
37
+ )
38
+
39
+ return (
40
+ <AlertDialog.Backdrop isOpen={isOpen} onOpenChange={onOpenChange}>
41
+ <AlertDialog.Container>
42
+ <AlertDialog.Dialog>
43
+ <AlertDialog.CloseTrigger />
44
+
45
+ <AlertDialog.Header>
46
+ <AlertDialog.Icon status="danger">
47
+ <TrashBin />
48
+ </AlertDialog.Icon>
49
+
50
+ <AlertDialog.Heading>
51
+ {organizationLocalization.removeMember}
52
+ </AlertDialog.Heading>
53
+ </AlertDialog.Header>
54
+
55
+ <AlertDialog.Body className="flex flex-col gap-4 overflow-visible">
56
+ <p className="text-muted text-sm">
57
+ {organizationLocalization.removeMemberWarning}
58
+ </p>
59
+
60
+ <Card variant="secondary">
61
+ <Card.Content className="justify-between flex-row items-center gap-2">
62
+ <UserView user={member.user} />
63
+
64
+ <Chip size="sm" variant="tertiary">
65
+ <Chip.Label>{roles?.[member.role] ?? member.role}</Chip.Label>
66
+ </Chip>
67
+ </Card.Content>
68
+ </Card>
69
+ </AlertDialog.Body>
70
+
71
+ <AlertDialog.Footer>
72
+ <Button slot="close" variant="tertiary" isDisabled={isPending}>
73
+ {localization.settings.cancel}
74
+ </Button>
75
+
76
+ <Button
77
+ variant="danger"
78
+ isPending={isPending}
79
+ onPress={() =>
80
+ removeMember({
81
+ memberIdOrEmail: member.id,
82
+ organizationId: member.organizationId
83
+ })
84
+ }
85
+ >
86
+ {isPending && <Spinner color="current" size="sm" />}
87
+
88
+ {organizationLocalization.removeMember}
89
+ </Button>
90
+ </AlertDialog.Footer>
91
+ </AlertDialog.Dialog>
92
+ </AlertDialog.Container>
93
+ </AlertDialog.Backdrop>
94
+ )
95
+ }
@@ -0,0 +1,111 @@
1
+ import {
2
+ type OrganizationAuthClient,
3
+ useAuth,
4
+ useAuthPlugin,
5
+ useCheckSlug
6
+ } from "@better-auth-ui/react"
7
+ import { Check, Xmark } from "@gravity-ui/icons"
8
+ import {
9
+ FieldError,
10
+ InputGroup,
11
+ type InputProps,
12
+ Label,
13
+ Spinner,
14
+ TextField,
15
+ type TextFieldProps
16
+ } from "@heroui/react"
17
+ import { useDebouncer } from "@tanstack/react-pacer"
18
+ import { useEffect } from "react"
19
+
20
+ import { organizationPlugin } from "../../../lib/auth/organization-plugin"
21
+
22
+ /** Props for the {@link SlugField} component. */
23
+ export type SlugFieldProps = {
24
+ value: string
25
+ onChange: (value: string) => void
26
+ currentSlug?: string
27
+ isDisabled?: boolean
28
+ variant?: InputProps["variant"]
29
+ }
30
+
31
+ /**
32
+ * Sanitize a slug value so it only contains lowercase alphanumeric characters
33
+ * and dashes. Runs of disallowed characters are collapsed to a single dash, but
34
+ * leading/trailing dashes are preserved while the user is still typing.
35
+ */
36
+ export function sanitizeSlug(value: string) {
37
+ return value.toLowerCase().replace(/[^a-z0-9]+/g, "-")
38
+ }
39
+
40
+ /**
41
+ * Organization slug field with debounced availability checking.
42
+ */
43
+ export function SlugField({
44
+ value,
45
+ onChange,
46
+ currentSlug,
47
+ variant,
48
+ ...props
49
+ }: SlugFieldProps & TextFieldProps) {
50
+ const { authClient } = useAuth()
51
+ const { localization, checkSlug: checkSlugEnabled } =
52
+ useAuthPlugin(organizationPlugin)
53
+
54
+ const {
55
+ mutate: checkSlug,
56
+ data: checkSlugData,
57
+ error: checkSlugError,
58
+ reset: resetCheckSlug
59
+ } = useCheckSlug(authClient as OrganizationAuthClient)
60
+
61
+ const debouncer = useDebouncer(
62
+ (value) => {
63
+ if (!checkSlugEnabled || !value.trim() || value.trim() === currentSlug)
64
+ return
65
+
66
+ checkSlug({ slug: value.trim() })
67
+ },
68
+ { wait: 500 }
69
+ )
70
+
71
+ useEffect(() => {
72
+ if (!checkSlugEnabled) return
73
+
74
+ resetCheckSlug()
75
+ debouncer.maybeExecute(value)
76
+ }, [checkSlugEnabled, value, debouncer.maybeExecute, resetCheckSlug])
77
+
78
+ const handleChange = (next: string) => {
79
+ onChange(sanitizeSlug(next))
80
+ }
81
+
82
+ return (
83
+ <TextField
84
+ id="slug"
85
+ name="slug"
86
+ {...props}
87
+ value={value}
88
+ onChange={handleChange}
89
+ >
90
+ <Label>{localization.slug}</Label>
91
+
92
+ <InputGroup variant={variant}>
93
+ <InputGroup.Input placeholder={localization.slugPlaceholder} required />
94
+
95
+ {checkSlugEnabled && !!value.trim() && value.trim() !== currentSlug && (
96
+ <InputGroup.Suffix className="px-2">
97
+ {checkSlugData?.status ? (
98
+ <Check className="text-success" />
99
+ ) : checkSlugError ? (
100
+ <Xmark className="text-danger" />
101
+ ) : (
102
+ <Spinner size="sm" color="current" />
103
+ )}
104
+ </InputGroup.Suffix>
105
+ )}
106
+ </InputGroup>
107
+
108
+ <FieldError />
109
+ </TextField>
110
+ )
111
+ }
@@ -0,0 +1,17 @@
1
+ import { Skeleton } from "@heroui/react"
2
+
3
+ /**
4
+ * Placeholder row matching {@link UserInvitationRow} while invitations load.
5
+ */
6
+ export function UserInvitationRowSkeleton() {
7
+ return (
8
+ <div className="flex items-center gap-3">
9
+ <Skeleton className="size-10 shrink-0 rounded-xl" />
10
+
11
+ <div className="flex flex-col gap-1">
12
+ <Skeleton className="h-4 w-40 rounded-lg" />
13
+ <Skeleton className="h-3 w-28 rounded-lg" />
14
+ </div>
15
+ </div>
16
+ )
17
+ }
@@ -0,0 +1,90 @@
1
+ import {
2
+ type OrganizationAuthClient,
3
+ useAcceptInvitation,
4
+ useAuth,
5
+ useAuthPlugin,
6
+ useRejectInvitation
7
+ } from "@better-auth-ui/react"
8
+ import { Check, Clock, Xmark } from "@gravity-ui/icons"
9
+ import { Button, Chip, Spinner } from "@heroui/react"
10
+ import type { Invitation } from "better-auth/client"
11
+
12
+ import { organizationPlugin } from "../../../lib/auth/organization-plugin"
13
+
14
+ export type UserInvitationRowProps = {
15
+ invitation: Invitation & { organizationName?: string }
16
+ }
17
+
18
+ /**
19
+ * Single invitation row with accept/reject actions for the current user.
20
+ */
21
+ export function UserInvitationRow({ invitation }: UserInvitationRowProps) {
22
+ const { authClient } = useAuth()
23
+ const { localization: organizationLocalization, roles } =
24
+ useAuthPlugin(organizationPlugin)
25
+
26
+ const { mutate: acceptInvitation, isPending: isAccepting } =
27
+ useAcceptInvitation(authClient as OrganizationAuthClient)
28
+
29
+ const { mutate: rejectInvitation, isPending: isRejecting } =
30
+ useRejectInvitation(authClient as OrganizationAuthClient)
31
+
32
+ return (
33
+ <div className="flex items-center gap-3">
34
+ <div className="flex size-10 shrink-0 items-center justify-center rounded-xl bg-surface-secondary">
35
+ <Clock className="size-4.5" />
36
+ </div>
37
+
38
+ <div className="flex flex-col">
39
+ <div className="flex items-center gap-1.5">
40
+ <span className="truncate text-sm font-medium leading-tight">
41
+ {invitation.organizationName}
42
+ </span>
43
+
44
+ <Chip size="sm">{roles?.[invitation.role] ?? invitation.role}</Chip>
45
+ </div>
46
+
47
+ <span className="truncate text-muted text-xs">
48
+ {new Date(invitation.createdAt).toLocaleString(undefined, {
49
+ dateStyle: "medium",
50
+ timeStyle: "short"
51
+ })}
52
+ </span>
53
+ </div>
54
+
55
+ <div className="ml-auto flex shrink-0 items-center gap-2">
56
+ <Button
57
+ variant="outline"
58
+ size="sm"
59
+ isPending={isAccepting}
60
+ isDisabled={isRejecting}
61
+ onPress={() =>
62
+ acceptInvitation({
63
+ invitationId: invitation.id
64
+ })
65
+ }
66
+ >
67
+ {isAccepting ? <Spinner color="current" size="sm" /> : <Check />}
68
+
69
+ {organizationLocalization.accept}
70
+ </Button>
71
+
72
+ <Button
73
+ variant="danger-soft"
74
+ size="sm"
75
+ isIconOnly
76
+ isPending={isRejecting}
77
+ isDisabled={isAccepting}
78
+ onPress={() =>
79
+ rejectInvitation({
80
+ invitationId: invitation.id
81
+ })
82
+ }
83
+ aria-label={organizationLocalization.rejectInvitation}
84
+ >
85
+ {isRejecting ? <Spinner color="current" size="sm" /> : <Xmark />}
86
+ </Button>
87
+ </div>
88
+ </div>
89
+ )
90
+ }
@@ -0,0 +1,30 @@
1
+ import { useAuthPlugin } from "@better-auth-ui/react"
2
+ import { PaperPlane } from "@gravity-ui/icons"
3
+ import { EmptyState } from "@heroui/react"
4
+ import { organizationPlugin } from "../../../lib/auth/organization-plugin"
5
+
6
+ /**
7
+ * Empty state for `UserInvitations`
8
+ */
9
+ export function UserInvitationsEmpty() {
10
+ const { localization: organizationLocalization } =
11
+ useAuthPlugin(organizationPlugin)
12
+
13
+ return (
14
+ <EmptyState className="flex flex-col items-center gap-4 text-center">
15
+ <div className="flex size-12 items-center justify-center rounded-full bg-surface-secondary">
16
+ <PaperPlane className="size-5" />
17
+ </div>
18
+
19
+ <div className="flex flex-col gap-2">
20
+ <p className="text-sm font-semibold text-foreground">
21
+ {organizationLocalization.noInvitations}
22
+ </p>
23
+
24
+ <span className="text-sm text-muted">
25
+ {organizationLocalization.userInvitationsEmptyDescription}
26
+ </span>
27
+ </div>
28
+ </EmptyState>
29
+ )
30
+ }
@@ -0,0 +1,59 @@
1
+ import {
2
+ type OrganizationAuthClient,
3
+ useAuth,
4
+ useAuthPlugin,
5
+ useListUserInvitations
6
+ } from "@better-auth-ui/react"
7
+ import { Card, type CardProps } from "@heroui/react"
8
+
9
+ import { organizationPlugin } from "../../../lib/auth/organization-plugin"
10
+ import { UserInvitationRow } from "./user-invitation-row"
11
+ import { UserInvitationRowSkeleton } from "./user-invitation-row-skeleton"
12
+ import { UserInvitationsEmpty } from "./user-invitations-empty"
13
+
14
+ export type UserInvitationsProps = {
15
+ variant?: CardProps["variant"]
16
+ }
17
+
18
+ /**
19
+ * Organization invitations for the signed-in user (from
20
+ * {@link useListUserInvitations}). Always renders the section card; uses
21
+ * {@link UserInvitationsEmpty} when there are no pending invitations.
22
+ */
23
+ export function UserInvitations({ variant }: UserInvitationsProps) {
24
+ const { authClient } = useAuth()
25
+ const { localization: organizationLocalization } =
26
+ useAuthPlugin(organizationPlugin)
27
+
28
+ const { data: invitations, isPending } = useListUserInvitations(
29
+ authClient as OrganizationAuthClient
30
+ )
31
+
32
+ return (
33
+ <div className="flex flex-col gap-3">
34
+ <h2 className="truncate text-sm font-semibold">
35
+ {organizationLocalization.invitations}
36
+ </h2>
37
+
38
+ <Card variant={variant}>
39
+ <Card.Content>
40
+ {isPending ? (
41
+ <UserInvitationRowSkeleton />
42
+ ) : !invitations?.length ? (
43
+ <UserInvitationsEmpty />
44
+ ) : (
45
+ invitations?.map((invitation, index) => (
46
+ <div key={invitation.id}>
47
+ {index > 0 && (
48
+ <div className="border-b border-dashed -mx-4 my-4" />
49
+ )}
50
+
51
+ <UserInvitationRow invitation={invitation} />
52
+ </div>
53
+ ))
54
+ )}
55
+ </Card.Content>
56
+ </Card>
57
+ </div>
58
+ )
59
+ }
@@ -0,0 +1,109 @@
1
+ import {
2
+ type PasskeyAuthClient,
3
+ useAddPasskey,
4
+ useAuth,
5
+ useAuthPlugin
6
+ } from "@better-auth-ui/react"
7
+ import { Fingerprint } 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 } from "react"
19
+
20
+ import { passkeyPlugin } from "../../../lib/auth/passkey-plugin"
21
+
22
+ export type AddPasskeyDialogProps = {
23
+ isOpen: boolean
24
+ onOpenChange: (open: boolean) => void
25
+ }
26
+
27
+ export function AddPasskeyDialog({
28
+ isOpen,
29
+ onOpenChange
30
+ }: AddPasskeyDialogProps) {
31
+ const { authClient, localization } = useAuth()
32
+ const { localization: passkeyLocalization } = useAuthPlugin(passkeyPlugin)
33
+
34
+ const { mutate: addPasskey, isPending: isAdding } = useAddPasskey(
35
+ authClient as PasskeyAuthClient
36
+ )
37
+
38
+ const handleOpenChange = (open: boolean) => {
39
+ onOpenChange(open)
40
+ }
41
+
42
+ const handleSubmit = (e: SyntheticEvent<HTMLFormElement>) => {
43
+ e.preventDefault()
44
+
45
+ const formData = new FormData(e.target as HTMLFormElement)
46
+ const name = (formData.get("name") as string)?.trim()
47
+
48
+ addPasskey(name ? { name } : undefined, {
49
+ onSuccess: () => handleOpenChange(false)
50
+ })
51
+ }
52
+
53
+ return (
54
+ <AlertDialog.Backdrop isOpen={isOpen} onOpenChange={handleOpenChange}>
55
+ <AlertDialog.Container>
56
+ <AlertDialog.Dialog>
57
+ <Form onSubmit={handleSubmit}>
58
+ <AlertDialog.CloseTrigger />
59
+
60
+ <AlertDialog.Header>
61
+ <AlertDialog.Icon status="default">
62
+ <Fingerprint />
63
+ </AlertDialog.Icon>
64
+
65
+ <AlertDialog.Heading>
66
+ {passkeyLocalization.addPasskey}
67
+ </AlertDialog.Heading>
68
+ </AlertDialog.Header>
69
+
70
+ <AlertDialog.Body className="overflow-visible">
71
+ <p className="text-muted text-sm">
72
+ {passkeyLocalization.passkeysDescription}
73
+ </p>
74
+
75
+ <TextField
76
+ className="mt-4"
77
+ id="name"
78
+ name="name"
79
+ isDisabled={isAdding}
80
+ >
81
+ <Label>{passkeyLocalization.name}</Label>
82
+
83
+ <Input
84
+ autoFocus
85
+ placeholder={localization.settings.optional}
86
+ variant="secondary"
87
+ />
88
+
89
+ <FieldError />
90
+ </TextField>
91
+ </AlertDialog.Body>
92
+
93
+ <AlertDialog.Footer>
94
+ <Button slot="close" variant="tertiary" isDisabled={isAdding}>
95
+ {localization.settings.cancel}
96
+ </Button>
97
+
98
+ <Button type="submit" isPending={isAdding}>
99
+ {isAdding && <Spinner color="current" size="sm" />}
100
+
101
+ {passkeyLocalization.addPasskey}
102
+ </Button>
103
+ </AlertDialog.Footer>
104
+ </Form>
105
+ </AlertDialog.Dialog>
106
+ </AlertDialog.Container>
107
+ </AlertDialog.Backdrop>
108
+ )
109
+ }
@@ -0,0 +1,95 @@
1
+ import {
2
+ type PasskeyAuthClient,
3
+ useAuth,
4
+ useAuthPlugin,
5
+ useDeletePasskey
6
+ } from "@better-auth-ui/react"
7
+ import { Fingerprint } from "@gravity-ui/icons"
8
+ import {
9
+ AlertDialog,
10
+ Button,
11
+ Input,
12
+ Label,
13
+ Spinner,
14
+ TextField
15
+ } from "@heroui/react"
16
+
17
+ import { passkeyPlugin } from "../../../lib/auth/passkey-plugin"
18
+
19
+ export type ListedPasskey = {
20
+ id: string
21
+ name?: string | null
22
+ createdAt: Date
23
+ }
24
+
25
+ export type DeletePasskeyDialogProps = {
26
+ isOpen: boolean
27
+ onOpenChange: (open: boolean) => void
28
+ passkey: ListedPasskey
29
+ }
30
+
31
+ export function DeletePasskeyDialog({
32
+ isOpen,
33
+ onOpenChange,
34
+ passkey
35
+ }: DeletePasskeyDialogProps) {
36
+ const { authClient, localization } = useAuth()
37
+ const { localization: passkeyLocalization } = useAuthPlugin(passkeyPlugin)
38
+
39
+ const passkeyName = passkey.name || passkeyLocalization.passkey
40
+
41
+ const { mutate: deletePasskey, isPending: isDeleting } = useDeletePasskey(
42
+ authClient as PasskeyAuthClient,
43
+ {
44
+ onSuccess: () => onOpenChange(false)
45
+ }
46
+ )
47
+
48
+ return (
49
+ <AlertDialog.Backdrop isOpen={isOpen} onOpenChange={onOpenChange}>
50
+ <AlertDialog.Container>
51
+ <AlertDialog.Dialog>
52
+ <AlertDialog.CloseTrigger />
53
+
54
+ <AlertDialog.Header>
55
+ <AlertDialog.Icon status="danger">
56
+ <Fingerprint />
57
+ </AlertDialog.Icon>
58
+
59
+ <AlertDialog.Heading>
60
+ {passkeyLocalization.deletePasskeyTitle}
61
+ </AlertDialog.Heading>
62
+ </AlertDialog.Header>
63
+
64
+ <AlertDialog.Body className="flex flex-col gap-4 overflow-visible">
65
+ <p className="text-muted text-sm">
66
+ {passkeyLocalization.deletePasskeyWarning}
67
+ </p>
68
+
69
+ <TextField value={passkeyName} variant="secondary">
70
+ <Label>{passkey.name || passkeyLocalization.passkey}</Label>
71
+
72
+ <Input readOnly />
73
+ </TextField>
74
+ </AlertDialog.Body>
75
+
76
+ <AlertDialog.Footer>
77
+ <Button slot="close" variant="tertiary" isDisabled={isDeleting}>
78
+ {localization.settings.cancel}
79
+ </Button>
80
+
81
+ <Button
82
+ variant="danger"
83
+ onPress={() => deletePasskey({ id: passkey.id })}
84
+ isPending={isDeleting}
85
+ >
86
+ {isDeleting && <Spinner color="current" size="sm" />}
87
+
88
+ {passkeyLocalization.deletePasskeyTitle}
89
+ </Button>
90
+ </AlertDialog.Footer>
91
+ </AlertDialog.Dialog>
92
+ </AlertDialog.Container>
93
+ </AlertDialog.Backdrop>
94
+ )
95
+ }