@better-auth-ui/heroui 1.6.8 → 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 (270) 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.js +21 -64
  129. package/dist/components/auth/passkey/delete-passkey-dialog.js +13 -46
  130. package/dist/components/auth/passkey/passkey-button.d.ts +1 -1
  131. package/dist/components/auth/passkey/passkey-button.js +24 -23
  132. package/dist/components/auth/passkey/passkey-skeleton.js +4 -16
  133. package/dist/components/auth/passkey/passkey.d.ts +1 -1
  134. package/dist/components/auth/passkey/passkey.js +16 -47
  135. package/dist/components/auth/passkey/passkeys-empty.js +8 -34
  136. package/dist/components/auth/passkey/passkeys.d.ts +1 -1
  137. package/dist/components/auth/passkey/passkeys.js +15 -41
  138. package/dist/components/auth/provider-button.d.ts +2 -2
  139. package/dist/components/auth/provider-button.js +28 -24
  140. package/dist/components/auth/provider-buttons.js +28 -18
  141. package/dist/components/auth/reset-password.d.ts +1 -1
  142. package/dist/components/auth/reset-password.js +56 -146
  143. package/dist/components/auth/settings/account/account-settings.d.ts +2 -2
  144. package/dist/components/auth/settings/account/account-settings.js +21 -19
  145. package/dist/components/auth/settings/account/change-avatar.js +53 -88
  146. package/dist/components/auth/settings/account/change-email.d.ts +1 -1
  147. package/dist/components/auth/settings/account/change-email.js +27 -53
  148. package/dist/components/auth/settings/account/user-profile.d.ts +1 -1
  149. package/dist/components/auth/settings/account/user-profile.js +64 -85
  150. package/dist/components/auth/settings/security/active-session.d.ts +1 -1
  151. package/dist/components/auth/settings/security/active-session.js +50 -57
  152. package/dist/components/auth/settings/security/active-sessions.d.ts +1 -1
  153. package/dist/components/auth/settings/security/active-sessions.js +21 -32
  154. package/dist/components/auth/settings/security/change-password.d.ts +1 -1
  155. package/dist/components/auth/settings/security/change-password.js +76 -186
  156. package/dist/components/auth/settings/security/linked-account.d.ts +1 -1
  157. package/dist/components/auth/settings/security/linked-account.js +37 -55
  158. package/dist/components/auth/settings/security/linked-accounts.d.ts +1 -1
  159. package/dist/components/auth/settings/security/linked-accounts.js +34 -43
  160. package/dist/components/auth/settings/security/security-settings.d.ts +2 -2
  161. package/dist/components/auth/settings/security/security-settings.js +19 -21
  162. package/dist/components/auth/settings/settings.d.ts +4 -4
  163. package/dist/components/auth/settings/settings.js +52 -44
  164. package/dist/components/auth/sign-in.d.ts +2 -2
  165. package/dist/components/auth/sign-in.js +63 -132
  166. package/dist/components/auth/sign-out.js +32 -26
  167. package/dist/components/auth/sign-up.d.ts +2 -2
  168. package/dist/components/auth/sign-up.js +97 -211
  169. package/dist/components/auth/theme/appearance.d.ts +1 -1
  170. package/dist/components/auth/theme/appearance.js +23 -71
  171. package/dist/components/auth/theme/theme-toggle-item.js +56 -61
  172. package/dist/components/auth/user/user-avatar.d.ts +4 -4
  173. package/dist/components/auth/user/user-avatar.js +31 -28
  174. package/dist/components/auth/user/user-button.d.ts +6 -7
  175. package/dist/components/auth/user/user-button.js +38 -72
  176. package/dist/components/auth/user/user-view.d.ts +13 -7
  177. package/dist/components/auth/user/user-view.js +26 -36
  178. package/dist/components/auth/username/sign-in-username.d.ts +2 -2
  179. package/dist/components/auth/username/sign-in-username.js +87 -147
  180. package/dist/components/auth/username/username-field.d.ts +1 -1
  181. package/dist/components/auth/username/username-field.js +44 -52
  182. package/dist/email.d.ts +8 -7
  183. package/dist/email.js +8 -8
  184. package/dist/index.d.ts +21 -21
  185. package/dist/index.js +21 -22
  186. package/dist/lib/auth/api-key-plugin.d.ts +7 -4
  187. package/dist/lib/auth/api-key-plugin.js +12 -10
  188. package/dist/lib/auth/auth-plugin.d.ts +10 -6
  189. package/dist/lib/auth/auth-plugin.js +1 -0
  190. package/dist/lib/auth/delete-user-plugin.d.ts +6 -6
  191. package/dist/lib/auth/delete-user-plugin.js +6 -9
  192. package/dist/lib/auth/magic-link-plugin.d.ts +5 -5
  193. package/dist/lib/auth/magic-link-plugin.js +17 -12
  194. package/dist/lib/auth/multi-session-plugin.d.ts +5 -5
  195. package/dist/lib/auth/multi-session-plugin.js +8 -11
  196. package/dist/lib/auth/organization-plugin.d.ts +37 -0
  197. package/dist/lib/auth/organization-plugin.js +19 -0
  198. package/dist/lib/auth/passkey-plugin.d.ts +5 -5
  199. package/dist/lib/auth/passkey-plugin.js +8 -11
  200. package/dist/lib/auth/theme-plugin.d.ts +6 -6
  201. package/dist/lib/auth/theme-plugin.js +25 -22
  202. package/dist/lib/auth/username-plugin.d.ts +11 -11
  203. package/dist/lib/auth/username-plugin.js +18 -17
  204. package/dist/plugins.d.ts +46 -22
  205. package/dist/plugins.js +47 -22
  206. package/package.json +25 -19
  207. package/src/components/auth/api-key/api-key.tsx +25 -16
  208. package/src/components/auth/api-key/api-keys-empty.tsx +8 -4
  209. package/src/components/auth/api-key/api-keys.tsx +52 -14
  210. package/src/components/auth/api-key/create-api-key-dialog.tsx +15 -2
  211. package/src/components/auth/api-key/delete-api-key-dialog.tsx +10 -2
  212. package/src/components/auth/api-key/organization-api-keys.tsx +55 -0
  213. package/src/components/auth/delete-user/danger-zone.tsx +3 -3
  214. package/src/components/auth/delete-user/{delete-user.tsx → delete-account.tsx} +9 -9
  215. package/src/components/auth/email/organization-invitation-email.tsx +38 -0
  216. package/src/components/auth/error-toaster.tsx +28 -10
  217. package/src/components/auth/forgot-password.tsx +10 -2
  218. package/src/components/auth/multi-session/manage-accounts.tsx +1 -1
  219. package/src/components/auth/multi-session/switch-account-submenu-content.tsx +7 -2
  220. package/src/components/auth/multi-session/switch-account-submenu-item.tsx +4 -2
  221. package/src/components/auth/multi-session/switch-account-submenu.tsx +8 -4
  222. package/src/components/auth/organization/change-organization-logo.tsx +171 -0
  223. package/src/components/auth/organization/create-organization-dialog.tsx +133 -0
  224. package/src/components/auth/organization/delete-organization-dialog.tsx +97 -0
  225. package/src/components/auth/organization/delete-organization-skeleton.tsx +17 -0
  226. package/src/components/auth/organization/delete-organization.tsx +77 -0
  227. package/src/components/auth/organization/invite-member-dialog.tsx +177 -0
  228. package/src/components/auth/organization/leave-organization-dialog.tsx +95 -0
  229. package/src/components/auth/organization/leave-organization.tsx +59 -0
  230. package/src/components/auth/organization/organization-danger-zone.tsx +68 -0
  231. package/src/components/auth/organization/organization-invitation-row-skeleton.tsx +29 -0
  232. package/src/components/auth/organization/organization-invitation-row.tsx +97 -0
  233. package/src/components/auth/organization/organization-invitations-empty.tsx +39 -0
  234. package/src/components/auth/organization/organization-invitations.tsx +351 -0
  235. package/src/components/auth/organization/organization-logo.tsx +64 -0
  236. package/src/components/auth/organization/organization-member-row-skeleton.tsx +24 -0
  237. package/src/components/auth/organization/organization-member-row.tsx +157 -0
  238. package/src/components/auth/organization/organization-members.tsx +284 -0
  239. package/src/components/auth/organization/organization-people.tsx +26 -0
  240. package/src/components/auth/organization/organization-profile.tsx +141 -0
  241. package/src/components/auth/organization/organization-row.tsx +66 -0
  242. package/src/components/auth/organization/organization-settings.tsx +38 -0
  243. package/src/components/auth/organization/organization-switcher.tsx +233 -0
  244. package/src/components/auth/organization/organization-view-skeleton.tsx +36 -0
  245. package/src/components/auth/organization/organization-view.tsx +109 -0
  246. package/src/components/auth/organization/organization.tsx +162 -0
  247. package/src/components/auth/organization/organizations-empty.tsx +35 -0
  248. package/src/components/auth/organization/organizations-settings.tsx +36 -0
  249. package/src/components/auth/organization/organizations.tsx +80 -0
  250. package/src/components/auth/organization/remove-member-dialog.tsx +95 -0
  251. package/src/components/auth/organization/slug-field.tsx +111 -0
  252. package/src/components/auth/organization/user-invitation-row-skeleton.tsx +17 -0
  253. package/src/components/auth/organization/user-invitation-row.tsx +90 -0
  254. package/src/components/auth/organization/user-invitations-empty.tsx +30 -0
  255. package/src/components/auth/organization/user-invitations.tsx +59 -0
  256. package/src/components/auth/settings/account/change-email.tsx +1 -1
  257. package/src/components/auth/settings/account/user-profile.tsx +5 -5
  258. package/src/components/auth/settings/security/active-sessions.tsx +6 -4
  259. package/src/components/auth/settings/security/linked-accounts.tsx +2 -2
  260. package/src/components/auth/settings/settings.tsx +77 -14
  261. package/src/components/auth/user/user-avatar.tsx +4 -1
  262. package/src/components/auth/user/user-button.tsx +14 -11
  263. package/src/components/auth/user/user-view.tsx +32 -12
  264. package/src/email.ts +1 -0
  265. package/src/lib/auth/api-key-plugin.ts +10 -4
  266. package/src/lib/auth/auth-plugin.ts +6 -1
  267. package/src/lib/auth/organization-plugin.tsx +32 -0
  268. package/src/plugins.ts +25 -1
  269. package/dist/components/auth/delete-user/delete-user.d.ts +0 -9
  270. package/dist/components/auth/delete-user/delete-user.js +0 -95
@@ -0,0 +1,66 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useAuth, useAuthPlugin, useHasPermission, useListOrganizationInvitations } from "@better-auth-ui/react";
3
+ import { ChevronUp, Funnel, Xmark } from "@gravity-ui/icons";
4
+ import { Button, Chip, cn, Dropdown, Label, SearchField, Table } from "@heroui/react";
5
+ import { useMemo, useState } from "react";
6
+ import { organizationPlugin } from "../../../lib/auth/organization-plugin";
7
+ import { InviteMemberDialog } from "./invite-member-dialog";
8
+ import { OrganizationInvitationTableRow } from "./organization-invitation-row";
9
+ import { OrganizationInvitationRowSkeleton } from "./organization-invitation-row-skeleton";
10
+ import { OrganizationInvitationsEmpty } from "./organization-invitations-empty";
11
+ /**
12
+ * Organization invitations table with invite control and per-row actions.
13
+ */
14
+ export function OrganizationInvitations({ className, ...props }) {
15
+ const { authClient, localization } = useAuth();
16
+ const { localization: organizationLocalization, roles } = useAuthPlugin(organizationPlugin);
17
+ const { data: invitations, isPending: invitationsPending } = useListOrganizationInvitations(authClient);
18
+ const { isPending: invitationPermissionPending } = useHasPermission(authClient, {
19
+ permissions: { invitation: ["cancel"] }
20
+ });
21
+ const isPending = invitationsPending || invitationPermissionPending;
22
+ const [sortDescriptor, setSortDescriptor] = useState();
23
+ const [roleFilter, setRoleFilter] = useState("all");
24
+ const [statusFilter, setStatusFilter] = useState("all");
25
+ const [search, setSearch] = useState("");
26
+ const filteredInvitations = useMemo(() => {
27
+ return invitations?.filter((invitation) => (roleFilter === "all" || invitation.role === roleFilter) &&
28
+ (statusFilter === "all" || invitation.status === statusFilter) &&
29
+ invitation.email.toLowerCase().includes(search.toLowerCase()));
30
+ }, [search, invitations, roleFilter, statusFilter]);
31
+ const sortedInvitations = useMemo(() => {
32
+ if (!sortDescriptor)
33
+ return filteredInvitations;
34
+ if (!filteredInvitations)
35
+ return filteredInvitations;
36
+ return [...filteredInvitations].sort((a, b) => {
37
+ const col = sortDescriptor.column;
38
+ let cmp = 0;
39
+ if (col === "createdAt") {
40
+ cmp = new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime();
41
+ }
42
+ else {
43
+ cmp = String(a[col]).localeCompare(String(b[col]));
44
+ }
45
+ if (sortDescriptor.direction === "descending") {
46
+ cmp *= -1;
47
+ }
48
+ return cmp;
49
+ });
50
+ }, [sortDescriptor, filteredInvitations]);
51
+ const [inviteOpen, setInviteOpen] = useState(false);
52
+ return (_jsxs("div", { className: cn("flex flex-col gap-3", className), ...props, children: [_jsx("h3", { className: "truncate text-sm font-semibold", children: organizationLocalization.invitations }), _jsxs("div", { className: "flex flex-col gap-4", children: [_jsxs("div", { className: "flex items-center gap-3", children: [_jsx(SearchField, { className: "min-w-0", "aria-label": organizationLocalization.search, value: search, onChange: setSearch, isDisabled: isPending, children: _jsxs(SearchField.Group, { children: [_jsx(SearchField.SearchIcon, {}), _jsx(SearchField.Input, { placeholder: organizationLocalization.search, className: "sm:w-[200px]" }), _jsx(SearchField.ClearButton, {})] }) }), _jsxs(Dropdown, { children: [_jsxs(Button, { size: "sm", variant: "secondary", isDisabled: isPending, children: [_jsx(Funnel, {}), organizationLocalization.role] }), _jsx(Dropdown.Popover, { children: _jsxs(Dropdown.Menu, { selectionMode: "single", selectedKeys: new Set([roleFilter]), onSelectionChange: (keys) => {
53
+ const key = [...keys][0];
54
+ setRoleFilter(key ?? "all");
55
+ }, children: [_jsxs(Dropdown.Item, { id: "all", textValue: organizationLocalization.all, children: [_jsx(Label, { children: organizationLocalization.all }), _jsx(Dropdown.ItemIndicator, {})] }), Object.entries(roles).map(([key, label]) => (_jsxs(Dropdown.Item, { id: key, textValue: label, children: [_jsx(Label, { children: label }), _jsx(Dropdown.ItemIndicator, {})] }, key)))] }) })] }), _jsxs(Dropdown, { children: [_jsxs(Button, { size: "sm", variant: "secondary", isDisabled: isPending, children: [_jsx(Funnel, {}), organizationLocalization.status] }), _jsx(Dropdown.Popover, { children: _jsxs(Dropdown.Menu, { selectionMode: "single", selectedKeys: new Set([statusFilter]), onSelectionChange: (keys) => {
56
+ const key = [...keys][0];
57
+ setStatusFilter(key ?? "all");
58
+ }, children: [_jsxs(Dropdown.Item, { id: "all", textValue: organizationLocalization.all, children: [_jsx(Label, { children: organizationLocalization.all }), _jsx(Dropdown.ItemIndicator, {})] }), ["pending", "accepted", "rejected", "canceled"].map((status) => (_jsxs(Dropdown.Item, { id: status, textValue: organizationLocalization[status] ?? status, children: [_jsx(Label, { children: organizationLocalization[status] ?? status }), _jsx(Dropdown.ItemIndicator, {})] }, status)))] }) })] })] }), (roleFilter !== "all" || statusFilter !== "all") && (_jsxs("div", { className: "flex flex-wrap gap-2", children: [roleFilter !== "all" && (_jsxs(Chip, { size: "sm", variant: "secondary", className: "w-fit", children: [_jsxs(Chip.Label, { children: [organizationLocalization.role, ":", " ", _jsx("span", { className: "capitalize", children: roles?.[roleFilter] ?? roleFilter })] }), _jsx("button", { type: "button", "aria-label": organizationLocalization.clear, className: "text-muted hover:text-foreground inline-flex cursor-pointer items-center", onClick: () => setRoleFilter("all"), children: _jsx(Xmark, { className: "size-3" }) })] })), statusFilter !== "all" && (_jsxs(Chip, { size: "sm", variant: "secondary", className: "w-fit", children: [_jsxs(Chip.Label, { children: [organizationLocalization.status, ":", " ", organizationLocalization[statusFilter] ?? statusFilter] }), _jsx("button", { type: "button", "aria-label": organizationLocalization.clear, className: "text-muted hover:text-foreground inline-flex cursor-pointer items-center", onClick: () => setStatusFilter("all"), children: _jsx(Xmark, { className: "size-3" }) })] }))] })), _jsx(Table, { children: _jsx(Table.ScrollContainer, { children: _jsxs(Table.Content, { "aria-label": organizationLocalization.invitations, sortDescriptor: sortDescriptor, onSortChange: (descriptor) => {
59
+ const shouldReset = sortDescriptor?.column === descriptor.column &&
60
+ descriptor.direction === "ascending";
61
+ setSortDescriptor(shouldReset ? undefined : descriptor);
62
+ }, children: [_jsxs(Table.Header, { children: [_jsx(Table.Column, { allowsSorting: true, isRowHeader: true, id: "email", children: ({ sortDirection }) => (_jsx(SortableColumnHeader, { sortDirection: sortDirection, children: localization.auth.email })) }), _jsx(Table.Column, { allowsSorting: true, id: "createdAt", children: ({ sortDirection }) => (_jsx(SortableColumnHeader, { sortDirection: sortDirection, children: organizationLocalization.invitedAt })) }), _jsx(Table.Column, { allowsSorting: true, id: "role", children: ({ sortDirection }) => (_jsx(SortableColumnHeader, { sortDirection: sortDirection, children: organizationLocalization.role })) }), _jsx(Table.Column, { allowsSorting: true, id: "status", children: ({ sortDirection }) => (_jsx(SortableColumnHeader, { sortDirection: sortDirection, children: organizationLocalization.status })) }), _jsx(Table.Column, { className: "text-end", children: organizationLocalization.actions })] }), _jsx(Table.Body, { renderEmptyState: () => (_jsx(OrganizationInvitationsEmpty, { onInvitePress: () => setInviteOpen(true) })), children: isPending ? (_jsx(OrganizationInvitationRowSkeleton, {})) : (sortedInvitations?.map((invitation) => (_jsx(OrganizationInvitationTableRow, { invitation: invitation }, invitation.id)))) })] }) }) })] }), _jsx(InviteMemberDialog, { isOpen: inviteOpen, onOpenChange: setInviteOpen })] }));
63
+ }
64
+ function SortableColumnHeader({ children, sortDirection }) {
65
+ return (_jsxs("span", { className: "flex items-center justify-between", children: [children, !!sortDirection && (_jsx(ChevronUp, { className: cn("size-3 transform transition-transform duration-100 ease-out", sortDirection === "descending" ? "rotate-180" : "") }))] }));
66
+ }
@@ -0,0 +1,15 @@
1
+ import { type AvatarProps } from "@heroui/react";
2
+ import type { Organization } from "better-auth/client";
3
+ import type { ReactNode } from "react";
4
+ export type OrganizationLogoProps = {
5
+ className?: string;
6
+ fallback?: ReactNode;
7
+ isPending?: boolean;
8
+ organization?: Partial<Organization>;
9
+ size?: AvatarProps["size"];
10
+ };
11
+ /**
12
+ * Renders an organization logo image, initials fallback, or a briefcase icon;
13
+ * shows a circular skeleton while loading when `isPending` is set and `organization` is undefined.
14
+ */
15
+ export declare function OrganizationLogo({ className, fallback, isPending, organization, size, style, ...props }: OrganizationLogoProps & AvatarProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,14 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Briefcase } from "@gravity-ui/icons";
3
+ import { Avatar, cn, Skeleton } from "@heroui/react";
4
+ /**
5
+ * Renders an organization logo image, initials fallback, or a briefcase icon;
6
+ * shows a circular skeleton while loading when `isPending` is set and `organization` is undefined.
7
+ */
8
+ export function OrganizationLogo({ className, fallback, isPending, organization, size = "sm", style, ...props }) {
9
+ if (isPending && !organization) {
10
+ return (_jsx(Skeleton, { className: cn("rounded-full", size === "sm" ? "size-8" : size === "md" ? "size-10" : "size-12", className), style: style }));
11
+ }
12
+ const initials = organization?.name?.slice(0, 2).toUpperCase();
13
+ return (_jsxs(Avatar, { size: size, className: cn("rounded-full", className), style: style, ...props, children: [_jsx(Avatar.Image, { alt: organization?.name ?? "Organization", src: organization?.logo ?? undefined }), _jsx(Avatar.Fallback, { className: cn(size === "lg" ? "text-xl" : size === "md" ? "text-base" : "text-sm"), delayMs: organization?.logo ? 600 : undefined, children: fallback || initials || _jsx(Briefcase, { className: "size-4" }) })] }));
14
+ }
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Placeholder row matching {@link OrganizationMemberRow} while members load.
3
+ */
4
+ export declare function OrganizationMemberRowSkeleton(): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,9 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Skeleton, Table } from "@heroui/react";
3
+ import { UserView } from "../user/user-view";
4
+ /**
5
+ * Placeholder row matching {@link OrganizationMemberRow} while members load.
6
+ */
7
+ export function OrganizationMemberRowSkeleton() {
8
+ return (_jsxs(Table.Row, { children: [_jsx(Table.Cell, { children: _jsx(UserView, { isPending: true }) }), _jsx(Table.Cell, { children: _jsx(Skeleton, { className: "h-4 w-18 rounded-lg" }) }), _jsx(Table.Cell, { className: "flex justify-end", children: _jsx(Skeleton, { className: "size-8 rounded-full" }) })] }));
9
+ }
@@ -0,0 +1,9 @@
1
+ import type { Member, Organization, User } from "better-auth/client";
2
+ export type OrganizationMemberRowProps = {
3
+ member: Member & {
4
+ user: Partial<User>;
5
+ };
6
+ isOwner?: boolean;
7
+ organization: Organization;
8
+ };
9
+ export declare function OrganizationMemberRow({ member, isOwner, organization }: OrganizationMemberRowProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,34 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useAuth, useAuthPlugin, useHasPermission, useSession, useUpdateMemberRole } from "@better-auth-ui/react";
3
+ import { ArrowRightFromSquare, Pencil, TrashBin } from "@gravity-ui/icons";
4
+ import { Button, Dropdown, Label, Spinner, Table, toast } from "@heroui/react";
5
+ import { useState } from "react";
6
+ import { organizationPlugin } from "../../../lib/auth/organization-plugin";
7
+ import { UserView } from "../user/user-view";
8
+ import { LeaveOrganizationDialog } from "./leave-organization-dialog";
9
+ import { OrganizationMemberRowSkeleton } from "./organization-member-row-skeleton";
10
+ import { RemoveMemberDialog } from "./remove-member-dialog";
11
+ export function OrganizationMemberRow({ member, isOwner, organization }) {
12
+ const { authClient } = useAuth();
13
+ const { localization: organizationLocalization, roles } = useAuthPlugin(organizationPlugin);
14
+ const { data: session } = useSession(authClient);
15
+ const { data: hasUpdatePermission, isPending: updatePermissionPending } = useHasPermission(authClient, {
16
+ permissions: { member: ["update"] }
17
+ });
18
+ const { data: hasDeletePermission, isPending: deletePermissionPending } = useHasPermission(authClient, {
19
+ permissions: { member: ["delete"] }
20
+ });
21
+ const isPending = updatePermissionPending || deletePermissionPending;
22
+ const { mutate: updateMemberRole, isPending: isUpdatingRole } = useUpdateMemberRole(authClient, {
23
+ onSuccess: () => toast.success(organizationLocalization.memberRoleUpdated)
24
+ });
25
+ const roleLabel = roles?.[member.role] ?? member.role;
26
+ const assignableRoles = Object.entries(roles).filter(([key]) => isOwner || key !== "owner");
27
+ const isCurrentUser = session?.user.id === member.userId;
28
+ const [removeOpen, setRemoveOpen] = useState(false);
29
+ const [leaveOpen, setLeaveOpen] = useState(false);
30
+ if (isPending) {
31
+ return _jsx(OrganizationMemberRowSkeleton, {});
32
+ }
33
+ return (_jsxs(Table.Row, { children: [_jsx(Table.Cell, { children: _jsx(UserView, { user: member.user }) }), _jsx(Table.Cell, { children: roleLabel }), _jsxs(Table.Cell, { children: [_jsxs("div", { className: "flex items-center justify-end gap-1", children: [hasUpdatePermission?.success && (_jsxs(Dropdown, { children: [_jsx(Button, { isIconOnly: true, size: "sm", variant: "tertiary", isDisabled: isUpdatingRole, "aria-label": organizationLocalization.changeMemberRole, children: isUpdatingRole ? (_jsx(Spinner, { color: "current", size: "sm" })) : (_jsx(Pencil, {})) }), _jsx(Dropdown.Popover, { className: "min-w-fit", children: _jsx(Dropdown.Menu, { children: assignableRoles.map(([role, label]) => (_jsx(Dropdown.Item, { textValue: label, isDisabled: member.role === role, onAction: () => updateMemberRole({ memberId: member.id, role }), children: _jsx(Label, { children: label }) }, role))) }) })] })), isCurrentUser ? (_jsx(Button, { isIconOnly: true, size: "sm", variant: "danger-soft", "aria-label": organizationLocalization.leaveOrganization, onPress: () => setLeaveOpen(true), children: _jsx(ArrowRightFromSquare, {}) })) : (hasDeletePermission?.success && (_jsx(Button, { isIconOnly: true, size: "sm", variant: "danger-soft", "aria-label": organizationLocalization.removeMember, onPress: () => setRemoveOpen(true), children: _jsx(TrashBin, {}) })))] }), isCurrentUser && organization ? (_jsx(LeaveOrganizationDialog, { isOpen: leaveOpen, onOpenChange: setLeaveOpen, organization: organization })) : (hasDeletePermission?.success && (_jsx(RemoveMemberDialog, { isOpen: removeOpen, onOpenChange: setRemoveOpen, member: member })))] })] }));
34
+ }
@@ -0,0 +1,9 @@
1
+ import { type ComponentProps } from "react";
2
+ /** Props for the {@link OrganizationMembers} component. */
3
+ export type OrganizationMembersProps = {
4
+ className?: string;
5
+ };
6
+ /**
7
+ * Organization members table with title, invite control, and per-row actions.
8
+ */
9
+ export declare function OrganizationMembers({ className, ...props }: OrganizationMembersProps & ComponentProps<"div">): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,67 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useActiveOrganization, useAuth, useAuthPlugin, useHasPermission, useListOrganizationMembers, useSession } from "@better-auth-ui/react";
3
+ import { ChevronUp, Funnel, Xmark } from "@gravity-ui/icons";
4
+ import { Button, Chip, cn, Dropdown, Label, SearchField, Table } from "@heroui/react";
5
+ import { useMemo, useState } from "react";
6
+ import { organizationPlugin } from "../../../lib/auth/organization-plugin";
7
+ import { InviteMemberDialog } from "./invite-member-dialog";
8
+ import { OrganizationMemberRow } from "./organization-member-row";
9
+ import { OrganizationMemberRowSkeleton } from "./organization-member-row-skeleton";
10
+ /**
11
+ * Organization members table with title, invite control, and per-row actions.
12
+ */
13
+ export function OrganizationMembers({ className, ...props }) {
14
+ const { authClient } = useAuth();
15
+ const { localization: organizationLocalization, roles } = useAuthPlugin(organizationPlugin);
16
+ const { data: session } = useSession(authClient);
17
+ const { data: activeOrganization, isPending: activeOrganizationPending } = useActiveOrganization(authClient);
18
+ const { data: membersData, isPending: membersPending } = useListOrganizationMembers(authClient);
19
+ const { isPending: updatePermissionPending } = useHasPermission(authClient, {
20
+ permissions: { member: ["update"] }
21
+ });
22
+ const { isPending: deletePermissionPending } = useHasPermission(authClient, {
23
+ permissions: { member: ["delete"] }
24
+ });
25
+ const isPending = activeOrganizationPending ||
26
+ membersPending ||
27
+ updatePermissionPending ||
28
+ deletePermissionPending;
29
+ const [sortDescriptor, setSortDescriptor] = useState();
30
+ const [roleFilter, setRoleFilter] = useState("all");
31
+ const [search, setSearch] = useState("");
32
+ const filteredMembers = useMemo(() => {
33
+ return membersData?.members.filter((member) => (roleFilter === "all" || member.role === roleFilter) &&
34
+ (member.user.name.toLowerCase().includes(search.toLowerCase()) ||
35
+ member.user.email.toLowerCase().includes(search.toLowerCase())));
36
+ }, [search, membersData?.members, roleFilter]);
37
+ const sortedMembers = useMemo(() => {
38
+ if (!sortDescriptor)
39
+ return filteredMembers;
40
+ if (!filteredMembers)
41
+ return filteredMembers;
42
+ return [...filteredMembers].sort((a, b) => {
43
+ const col = sortDescriptor.column;
44
+ const first = col === "user" ? a.user.name || a.user.email : String(a[col]);
45
+ const second = col === "user" ? b.user.name || b.user.email : String(b[col]);
46
+ let cmp = first.localeCompare(second);
47
+ if (sortDescriptor.direction === "descending") {
48
+ cmp *= -1;
49
+ }
50
+ return cmp;
51
+ });
52
+ }, [sortDescriptor, filteredMembers]);
53
+ const [inviteOpen, setInviteOpen] = useState(false);
54
+ const isOwner = membersData?.members.some((member) => member.role === "owner" && member.userId === session?.user.id);
55
+ return (_jsxs("div", { className: cn("flex flex-col gap-3", className), ...props, children: [_jsxs("div", { className: "flex items-end justify-between gap-3", children: [_jsx("h3", { className: "truncate text-sm font-semibold", children: organizationLocalization.members }), _jsx(Button, { className: "shrink-0", size: "sm", isDisabled: isPending, onPress: () => setInviteOpen(true), children: organizationLocalization.inviteMember })] }), _jsxs("div", { className: "flex flex-col gap-4", children: [_jsxs("div", { className: "flex items-center gap-3", children: [_jsx(SearchField, { className: "min-w-0", "aria-label": organizationLocalization.search, value: search, onChange: setSearch, isDisabled: isPending, children: _jsxs(SearchField.Group, { children: [_jsx(SearchField.SearchIcon, {}), _jsx(SearchField.Input, { placeholder: organizationLocalization.search, className: "sm:w-[200px]" }), _jsx(SearchField.ClearButton, {})] }) }), _jsxs(Dropdown, { children: [_jsxs(Button, { size: "sm", variant: "secondary", isDisabled: isPending, children: [_jsx(Funnel, {}), organizationLocalization.role] }), _jsx(Dropdown.Popover, { children: _jsxs(Dropdown.Menu, { selectionMode: "single", selectedKeys: new Set([roleFilter]), onSelectionChange: (keys) => {
56
+ const key = [...keys][0];
57
+ setRoleFilter(key ?? "all");
58
+ }, children: [_jsxs(Dropdown.Item, { id: "all", textValue: organizationLocalization.all, children: [_jsx(Label, { children: organizationLocalization.all }), _jsx(Dropdown.ItemIndicator, {})] }), Object.entries(roles).map(([role, label]) => (_jsxs(Dropdown.Item, { id: role, textValue: label, children: [_jsx(Label, { children: label }), _jsx(Dropdown.ItemIndicator, {})] }, role)))] }) })] })] }), roleFilter !== "all" && (_jsxs(Chip, { size: "sm", variant: "secondary", className: "w-fit", children: [_jsxs(Chip.Label, { children: [organizationLocalization.role, ":", " ", _jsx("span", { className: "capitalize", children: roles?.[roleFilter] ?? roleFilter })] }), _jsx("button", { type: "button", "aria-label": organizationLocalization.clear, className: "text-muted hover:text-foreground inline-flex cursor-pointer items-center", onClick: () => setRoleFilter("all"), children: _jsx(Xmark, { className: "size-3" }) })] })), _jsx(Table, { children: _jsx(Table.ScrollContainer, { children: _jsxs(Table.Content, { "aria-label": organizationLocalization.members, sortDescriptor: sortDescriptor, onSortChange: (descriptor) => {
59
+ const shouldReset = sortDescriptor?.column === descriptor.column &&
60
+ descriptor.direction === "ascending";
61
+ setSortDescriptor(shouldReset ? undefined : descriptor);
62
+ }, children: [_jsxs(Table.Header, { children: [_jsx(Table.Column, { allowsSorting: true, isRowHeader: true, id: "user", children: ({ sortDirection }) => (_jsx(SortableColumnHeader, { sortDirection: sortDirection, children: organizationLocalization.member })) }), _jsx(Table.Column, { allowsSorting: true, id: "role", children: ({ sortDirection }) => (_jsx(SortableColumnHeader, { sortDirection: sortDirection, children: organizationLocalization.role })) }), _jsx(Table.Column, { className: "text-end", children: organizationLocalization.actions })] }), _jsx(Table.Body, { children: isPending ? (_jsx(OrganizationMemberRowSkeleton, {})) : (!!activeOrganization &&
63
+ sortedMembers?.map((member) => (_jsx(OrganizationMemberRow, { member: member, isOwner: isOwner, organization: activeOrganization }, member.id)))) })] }) }) })] }), _jsx(InviteMemberDialog, { isOpen: inviteOpen, onOpenChange: setInviteOpen })] }));
64
+ }
65
+ function SortableColumnHeader({ children, sortDirection }) {
66
+ return (_jsxs("span", { className: "flex items-center justify-between", children: [children, !!sortDirection && (_jsx(ChevronUp, { className: cn("size-3 transform transition-transform duration-100 ease-out", sortDirection === "descending" ? "rotate-180" : "") }))] }));
67
+ }
@@ -0,0 +1,10 @@
1
+ import type { ComponentProps } from "react";
2
+ /** Props for the {@link OrganizationPeople} component. */
3
+ export type OrganizationPeopleProps = {
4
+ className?: string;
5
+ };
6
+ /**
7
+ * Organization people UI: members table (see {@link OrganizationMembers}), then org
8
+ * invitations.
9
+ */
10
+ export declare function OrganizationPeople({ className, ...props }: OrganizationPeopleProps & ComponentProps<"div">): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,11 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { cn } from "@heroui/react";
3
+ import { OrganizationInvitations } from "./organization-invitations";
4
+ import { OrganizationMembers } from "./organization-members";
5
+ /**
6
+ * Organization people UI: members table (see {@link OrganizationMembers}), then org
7
+ * invitations.
8
+ */
9
+ export function OrganizationPeople({ className, ...props }) {
10
+ return (_jsxs("div", { className: cn("flex flex-col gap-4 md:gap-6", className), ...props, children: [_jsx(OrganizationMembers, {}), _jsx(OrganizationInvitations, {})] }));
11
+ }
@@ -0,0 +1,9 @@
1
+ import { type CardProps } from "@heroui/react";
2
+ export type OrganizationProfileProps = {
3
+ className?: string;
4
+ variant?: CardProps["variant"];
5
+ };
6
+ /**
7
+ * Profile card for the active organization: logo (when enabled), display name, and slug.
8
+ */
9
+ export declare function OrganizationProfile({ className, variant, ...props }: OrganizationProfileProps & Omit<CardProps, "children">): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,34 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useActiveOrganization, useAuth, useAuthPlugin, useUpdateOrganization } from "@better-auth-ui/react";
3
+ import { Button, Card, cn, FieldError, Form, Input, Label, Skeleton, Spinner, TextField, toast } from "@heroui/react";
4
+ import { useEffect, useState } from "react";
5
+ import { organizationPlugin } from "../../../lib/auth/organization-plugin";
6
+ import { ChangeOrganizationLogo } from "./change-organization-logo";
7
+ import { SlugField } from "./slug-field";
8
+ /**
9
+ * Profile card for the active organization: logo (when enabled), display name, and slug.
10
+ */
11
+ export function OrganizationProfile({ className, variant, ...props }) {
12
+ const { authClient, localization } = useAuth();
13
+ const { localization: organizationLocalization } = useAuthPlugin(organizationPlugin);
14
+ const { data: activeOrganization } = useActiveOrganization(authClient);
15
+ const [slug, setSlug] = useState(activeOrganization?.slug ?? "");
16
+ useEffect(() => {
17
+ setSlug(activeOrganization?.slug ?? "");
18
+ }, [activeOrganization?.slug]);
19
+ const { mutate: commitOrganizationUpdate, isPending } = useUpdateOrganization(authClient, {
20
+ onSuccess: () => toast.success(organizationLocalization.organizationUpdatedSuccess)
21
+ });
22
+ function handleSubmit(e) {
23
+ e.preventDefault();
24
+ if (!activeOrganization)
25
+ return;
26
+ const formData = new FormData(e.currentTarget);
27
+ const name = formData.get("name");
28
+ commitOrganizationUpdate({
29
+ data: { name, slug }
30
+ });
31
+ }
32
+ const inputVariant = variant === "transparent" ? "primary" : "secondary";
33
+ return (_jsxs("div", { children: [_jsx("h2", { className: cn("mb-3 text-sm font-semibold"), children: organizationLocalization.organizationProfile }), _jsx(Card, { className: cn(className), variant: variant, ...props, children: _jsx(Card.Content, { children: _jsxs(Form, { onSubmit: handleSubmit, className: "flex flex-col gap-4", children: [_jsx(ChangeOrganizationLogo, {}), _jsxs(TextField, { name: "name", defaultValue: activeOrganization?.name, isDisabled: isPending || !activeOrganization, children: [_jsx(Label, { children: organizationLocalization.name }), _jsx(Input, { className: cn(!activeOrganization && "hidden"), autoComplete: "organization", placeholder: organizationLocalization.namePlaceholder, variant: inputVariant }), !activeOrganization && (_jsx(Skeleton, { className: "h-10 w-full rounded-xl md:h-9" })), _jsx(FieldError, {})] }, `${activeOrganization?.id}-${activeOrganization?.name}-name`), activeOrganization ? (_jsx(SlugField, { value: slug, onChange: setSlug, currentSlug: activeOrganization.slug, isDisabled: isPending, variant: inputVariant })) : (_jsxs(TextField, { isDisabled: true, children: [_jsx(Label, { children: organizationLocalization.slug }), _jsx(Skeleton, { className: "h-10 w-full rounded-xl md:h-9" })] })), _jsxs(Button, { type: "submit", isPending: isPending, isDisabled: !activeOrganization, size: "sm", className: "mt-1", children: [isPending && _jsx(Spinner, { color: "current", size: "sm" }), localization.settings.saveChanges] })] }) }) })] }));
34
+ }
@@ -0,0 +1,8 @@
1
+ import type { Organization } from "better-auth/client";
2
+ export type OrganizationRowProps = {
3
+ organization: Organization;
4
+ };
5
+ /**
6
+ * Single organization row: logo and labels via {@link OrganizationView}, plus Manage.
7
+ */
8
+ export declare function OrganizationRow({ organization }: OrganizationRowProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,31 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useAuth, useAuthPlugin, useSetActiveOrganization } from "@better-auth-ui/react";
3
+ import { Gear } from "@gravity-ui/icons";
4
+ import { Button, Spinner } from "@heroui/react";
5
+ import { organizationPlugin } from "../../../lib/auth/organization-plugin";
6
+ import { OrganizationView } from "./organization-view";
7
+ /**
8
+ * Single organization row: logo and labels via {@link OrganizationView}, plus Manage.
9
+ */
10
+ export function OrganizationRow({ organization }) {
11
+ const { authClient, basePaths, navigate } = useAuth();
12
+ const { localization: organizationLocalization, viewPaths: organizationViewPaths, slug } = useAuthPlugin(organizationPlugin);
13
+ const { mutate: setActiveOrganization, isPending: setActivePending } = useSetActiveOrganization(authClient, {
14
+ onSuccess: () => {
15
+ navigate({
16
+ to: `${basePaths.organization}/${organizationViewPaths.organization.settings}`
17
+ });
18
+ }
19
+ });
20
+ function manageOrganization() {
21
+ if (slug !== undefined) {
22
+ navigate({
23
+ to: `${basePaths.organization}/${organization.slug}/${organizationViewPaths.organization.settings}`
24
+ });
25
+ }
26
+ else {
27
+ setActiveOrganization({ organizationId: organization.id });
28
+ }
29
+ }
30
+ return (_jsxs("div", { className: "flex items-center gap-3", children: [_jsx(OrganizationView, { organization: organization }), _jsxs(Button, { className: "ml-auto shrink-0", variant: "outline", size: "sm", isPending: setActivePending, onPress: manageOrganization, "aria-label": organizationLocalization.manage, children: [setActivePending ? _jsx(Spinner, { color: "current", size: "sm" }) : _jsx(Gear, {}), organizationLocalization.manage] })] }));
31
+ }
@@ -0,0 +1,12 @@
1
+ import { type CardProps } from "@heroui/react";
2
+ import type { ComponentProps } from "react";
3
+ /** Props for the {@link OrganizationSettings} component. */
4
+ export type OrganizationSettingsProps = {
5
+ className?: string;
6
+ variant?: CardProps["variant"];
7
+ };
8
+ /**
9
+ * Organization settings UI: profile card, plugin-contributed cards
10
+ * (`organizationCards`), then danger zone.
11
+ */
12
+ export declare function OrganizationSettings({ className, variant, ...props }: OrganizationSettingsProps & ComponentProps<"div">): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,13 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useAuth } from "@better-auth-ui/react";
3
+ import { cn } from "@heroui/react";
4
+ import { OrganizationDangerZone } from "./organization-danger-zone";
5
+ import { OrganizationProfile } from "./organization-profile";
6
+ /**
7
+ * Organization settings UI: profile card, plugin-contributed cards
8
+ * (`organizationCards`), then danger zone.
9
+ */
10
+ export function OrganizationSettings({ className, variant, ...props }) {
11
+ const { plugins } = useAuth();
12
+ return (_jsxs("div", { className: cn("flex flex-col gap-4 md:gap-6", className), ...props, children: [_jsx(OrganizationProfile, { variant: variant }), plugins.flatMap((plugin) => plugin.organizationCards?.map((Card, index) => (_jsx(Card, { variant: variant }, `${plugin.id}-${index.toString()}`)))), _jsx(OrganizationDangerZone, { variant: variant })] }));
13
+ }
@@ -0,0 +1,20 @@
1
+ import { type ButtonProps, type DropdownPopoverProps } from "@heroui/react";
2
+ import type { Organization } from "better-auth/client";
3
+ import { type ReactNode } from "react";
4
+ /** Props for the {@link OrganizationSwitcher} component. */
5
+ export type OrganizationSwitcherProps = {
6
+ className?: string;
7
+ placement?: DropdownPopoverProps["placement"];
8
+ variant?: ButtonProps["variant"];
9
+ trigger?: ReactNode;
10
+ hideCreate?: boolean;
11
+ hidePersonal?: boolean;
12
+ hideSettings?: boolean;
13
+ hideSlug?: boolean;
14
+ setActive?: (organization: Organization | null) => void;
15
+ };
16
+ /**
17
+ * Renders an organizations dropdown with a trigger button,
18
+ * header summary, and a menu of organizations to switch to.
19
+ */
20
+ export declare function OrganizationSwitcher({ className, hideCreate, hidePersonal, hideSettings, hideSlug, setActive, placement, variant, size, trigger, ...props }: OrganizationSwitcherProps & ButtonProps): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,48 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useActiveOrganization, useAuth, useAuthPlugin, useListOrganizations, useSession, useSetActiveOrganization } from "@better-auth-ui/react";
3
+ import { ChevronsExpandVertical, CirclePlus, Gear } from "@gravity-ui/icons";
4
+ import { Button, cn, Dropdown, Label, Link } from "@heroui/react";
5
+ import { buttonVariants } from "@heroui/styles";
6
+ import { useState } from "react";
7
+ import { organizationPlugin } from "../../../lib/auth/organization-plugin";
8
+ import { UserView } from "../user/user-view";
9
+ import { CreateOrganizationDialog } from "./create-organization-dialog";
10
+ import { OrganizationView } from "./organization-view";
11
+ /**
12
+ * Renders an organizations dropdown with a trigger button,
13
+ * header summary, and a menu of organizations to switch to.
14
+ */
15
+ export function OrganizationSwitcher({ className, hideCreate, hidePersonal, hideSettings, hideSlug = true, setActive, placement, variant = "ghost", size = "md", trigger, ...props }) {
16
+ const { authClient, navigate, basePaths, localization, viewPaths } = useAuth();
17
+ const { data: session, isPending: sessionPending } = useSession(authClient);
18
+ const { localization: organizationLocalization, viewPaths: organizationViewPaths, slug } = useAuthPlugin(organizationPlugin);
19
+ const { data: activeOrganization, isPending: activeOrganizationPending } = useActiveOrganization(authClient);
20
+ const { data: organizations, isPending: organizationsPending } = useListOrganizations(authClient);
21
+ const { mutate: setActiveOrganization } = useSetActiveOrganization(authClient);
22
+ const isPending = sessionPending ||
23
+ (!!session && (organizationsPending || activeOrganizationPending));
24
+ const [createOpen, setCreateOpen] = useState(false);
25
+ const [dropdownOpen, setDropdownOpen] = useState(false);
26
+ function handleSetActive(organization) {
27
+ if (setActive) {
28
+ setActive(organization);
29
+ }
30
+ else if (slug !== undefined) {
31
+ navigate({
32
+ to: organization
33
+ ? `${basePaths.organization}/${organization.slug}/${organizationViewPaths.organization.settings}`
34
+ : `${basePaths.settings}/${viewPaths.settings.account}`
35
+ });
36
+ }
37
+ else {
38
+ setActiveOrganization({ organizationId: organization?.id ?? null });
39
+ }
40
+ }
41
+ return (_jsxs(_Fragment, { children: [_jsxs(Dropdown, { isOpen: dropdownOpen, onOpenChange: setDropdownOpen, children: [trigger ? (_jsx(Dropdown.Trigger, { children: trigger })) : (_jsxs(Button, { variant: variant, className: cn("h-auto px-2 py-2 text-left", className), isDisabled: !session || isPending, "aria-label": organizationLocalization.organization, ...props, children: [isPending ? (_jsx(OrganizationView, { size: size, isPending: true, hideRole: true, hideSlug: hideSlug })) : activeOrganization ? (_jsx(OrganizationView, { size: size, hideRole: true, hideSlug: hideSlug })) : session && !hidePersonal ? (_jsx(UserView, { size: size, hideSubtitle: hideSlug })) : (_jsx(OrganizationView, { size: size, hideRole: true, hideSlug: hideSlug, organization: {
42
+ name: organizationLocalization.organization
43
+ } })), _jsx(ChevronsExpandVertical, { className: "size-3 shrink-0 text-muted" })] })), _jsxs(Dropdown.Popover, { placement: placement, className: "max-w-svw", children: [activeOrganization ? (_jsxs("div", { className: "flex items-center justify-between gap-4 px-4 pt-3", children: [_jsx(OrganizationView, { hideRole: true, hideSlug: hideSlug, organization: activeOrganization }), !hideSettings && (_jsxs(Link, { href: slug
44
+ ? `${basePaths.organization}/${slug}/${organizationViewPaths.organization.settings}`
45
+ : `${basePaths.organization}/${organizationViewPaths.organization.settings}`, className: cn(buttonVariants({ variant: "outline", size: "sm" }), "shrink-0 gap-2"), onPress: () => setDropdownOpen(false), children: [_jsx(Gear, { className: "text-muted" }), organizationLocalization.manage] }))] })) : !isPending && session?.user && !hidePersonal ? (_jsxs("div", { className: "flex items-center justify-between gap-4 px-4 pt-3", children: [_jsx(UserView, { hideSubtitle: hideSlug }), !hideSettings && (_jsxs(Link, { href: `${basePaths.settings}/${viewPaths.settings.account}`, className: cn(buttonVariants({ variant: "outline", size: "sm" }), "shrink-0 gap-2"), onPress: () => setDropdownOpen(false), children: [_jsx(Gear, { className: "text-muted" }), localization.settings.settings] }))] })) : null, _jsxs(Dropdown.Menu, { children: [!!activeOrganization && !hidePersonal && (_jsx(Dropdown.Item, { textValue: organizationLocalization.personalAccount, onPress: () => handleSetActive(null), children: _jsx(UserView, { hideSubtitle: hideSlug }) })), organizations
46
+ ?.filter((organization) => organization.id !== activeOrganization?.id)
47
+ ?.map((organization) => (_jsx(Dropdown.Item, { textValue: organization.name, onPress: () => handleSetActive(organization), children: _jsx(OrganizationView, { hideRole: true, hideSlug: hideSlug, organization: organization }) }, organization.id))), !hideCreate && (_jsxs(Dropdown.Item, { textValue: organizationLocalization.createOrganization, onPress: () => setCreateOpen(true), children: [_jsx(CirclePlus, { className: "text-muted" }), _jsx(Label, { children: organizationLocalization.createOrganization })] }))] })] })] }), _jsx(CreateOrganizationDialog, { isOpen: createOpen, onOpenChange: setCreateOpen })] }));
48
+ }
@@ -0,0 +1,6 @@
1
+ import type { ComponentProps } from "react";
2
+ import type { OrganizationViewProps } from "./organization-view";
3
+ /**
4
+ * Placeholder matching {@link OrganizationView} while organization data loads.
5
+ */
6
+ export declare function OrganizationViewSkeleton({ className, hideSlug, size, ...props }: OrganizationViewProps & ComponentProps<"div">): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,9 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { cn, Skeleton } from "@heroui/react";
3
+ import { OrganizationLogo } from "./organization-logo";
4
+ /**
5
+ * Placeholder matching {@link OrganizationView} while organization data loads.
6
+ */
7
+ export function OrganizationViewSkeleton({ className, hideSlug, size = "md", ...props }) {
8
+ return (_jsxs("div", { className: cn("flex min-w-0 items-center gap-2", className), ...props, children: [_jsx(OrganizationLogo, { isPending: true, className: size === "sm" ? "size-5" : undefined, size: size === "lg" ? "md" : "sm" }), _jsxs("div", { className: "flex flex-col min-w-0 gap-1", children: [_jsx(Skeleton, { className: "h-3.5 w-20 rounded-lg" }), !hideSlug && (_jsx(Skeleton, { className: "h-3 w-28 rounded-lg mt-[0.5px] mb-0.5" }))] })] }));
9
+ }
@@ -0,0 +1,15 @@
1
+ import { type AvatarProps } from "@heroui/react";
2
+ import type { Organization } from "better-auth/client";
3
+ import type { ComponentProps } from "react";
4
+ export type OrganizationViewProps = {
5
+ className?: string;
6
+ isPending?: boolean;
7
+ size?: AvatarProps["size"];
8
+ hideRole?: boolean;
9
+ hideSlug?: boolean;
10
+ organization?: Partial<Organization>;
11
+ };
12
+ /**
13
+ * Compact organization row: logo, primary name, secondary slug — analogous to {@link UserView}.
14
+ */
15
+ export declare function OrganizationView({ className, isPending, size, hideSlug, hideRole, organization, ...props }: OrganizationViewProps & ComponentProps<"div">): import("react/jsx-runtime").JSX.Element;
@@ -0,0 +1,31 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useActiveOrganization, useAuth, useAuthPlugin, useListOrganizationMembers, useSession } from "@better-auth-ui/react";
3
+ import { Chip, cn } from "@heroui/react";
4
+ import { organizationPlugin } from "../../../lib/auth/organization-plugin";
5
+ import { OrganizationLogo } from "./organization-logo";
6
+ import { OrganizationViewSkeleton } from "./organization-view-skeleton";
7
+ /**
8
+ * Compact organization row: logo, primary name, secondary slug — analogous to {@link UserView}.
9
+ */
10
+ export function OrganizationView({ className, isPending, size = "md", hideSlug, hideRole, organization, ...props }) {
11
+ const { authClient } = useAuth();
12
+ const { roles } = useAuthPlugin(organizationPlugin);
13
+ const { data: session } = useSession(authClient);
14
+ const { data: activeOrganization, isPending: activeOrganizationPending } = useActiveOrganization(authClient, {
15
+ enabled: !organization && !isPending
16
+ });
17
+ const resolvedOrganization = organization ?? activeOrganization;
18
+ const { data: membersList, isPending: membersPending } = useListOrganizationMembers(authClient, {
19
+ query: {
20
+ organizationId: resolvedOrganization?.id
21
+ },
22
+ enabled: !!resolvedOrganization?.id && !hideRole
23
+ });
24
+ const membership = membersList?.members?.find((member) => member.userId === session?.user.id);
25
+ if (isPending ||
26
+ (!organization && activeOrganizationPending) ||
27
+ (!hideRole && !!resolvedOrganization?.id && membersPending)) {
28
+ return (_jsx(OrganizationViewSkeleton, { className: className, hideSlug: hideSlug, size: size, ...props }));
29
+ }
30
+ return (_jsxs("div", { className: cn("flex min-w-0 items-center gap-2", className), ...props, children: [_jsx(OrganizationLogo, { organization: resolvedOrganization, className: size === "sm" ? "size-5 [&>span]:text-xs" : undefined, size: size === "lg" ? "md" : "sm" }), _jsxs("div", { className: "flex flex-col min-w-0", children: [_jsxs("div", { className: "flex min-w-0 items-center gap-2", children: [_jsx("p", { className: "text-foreground text-sm font-medium truncate leading-tight", children: resolvedOrganization?.name }), !hideRole && !!membership && (_jsx(Chip, { className: "shrink-0 -my-0.5", size: "sm", children: roles?.[membership.role] ?? membership.role }))] }), !hideSlug && (_jsx("p", { className: "text-muted text-xs truncate overflow-x-hidden font-mono leading-tight", children: resolvedOrganization?.slug }))] })] }));
31
+ }
@@ -0,0 +1,17 @@
1
+ import type { OrganizationView } from "@better-auth-ui/core/plugins";
2
+ import { type CardProps } from "@heroui/react";
3
+ import { type ComponentProps } from "react";
4
+ export type OrganizationProps = {
5
+ className?: string;
6
+ hideNav?: boolean;
7
+ path?: string;
8
+ variant?: CardProps["variant"];
9
+ /** @remarks `OrganizationView` */
10
+ view?: OrganizationView;
11
+ };
12
+ /**
13
+ * Organization management shell: tabs for profile / danger zone and for
14
+ * people (members / invitations). Path segments come from
15
+ * `useAuthPlugin(organizationPlugin).viewPaths.organization`.
16
+ */
17
+ export declare function Organization({ className, hideNav, path, variant, view, ...props }: OrganizationProps & ComponentProps<"div">): import("react/jsx-runtime").JSX.Element | null;