@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.
- package/dist/components/auth/additional-field.d.ts +2 -2
- package/dist/components/auth/additional-field.js +133 -266
- package/dist/components/auth/api-key/api-key-skeleton.js +4 -20
- package/dist/components/auth/api-key/api-key.d.ts +6 -2
- package/dist/components/auth/api-key/api-key.js +16 -54
- package/dist/components/auth/api-key/api-keys-empty.d.ts +3 -1
- package/dist/components/auth/api-key/api-keys-empty.js +8 -34
- package/dist/components/auth/api-key/api-keys.d.ts +10 -2
- package/dist/components/auth/api-key/api-keys.js +21 -40
- package/dist/components/auth/api-key/create-api-key-dialog.d.ts +3 -1
- package/dist/components/auth/api-key/create-api-key-dialog.js +43 -73
- package/dist/components/auth/api-key/delete-api-key-dialog.d.ts +4 -2
- package/dist/components/auth/api-key/delete-api-key-dialog.js +16 -50
- package/dist/components/auth/api-key/new-api-key-dialog.js +23 -59
- package/dist/components/auth/api-key/organization-api-keys.d.ts +14 -0
- package/dist/components/auth/api-key/organization-api-keys.js +22 -0
- package/dist/components/auth/auth-provider.d.ts +1 -1
- package/dist/components/auth/auth-provider.js +10 -16
- package/dist/components/auth/auth.d.ts +3 -3
- package/dist/components/auth/auth.js +86 -61
- package/dist/components/auth/delete-user/danger-zone.d.ts +3 -3
- package/dist/components/auth/delete-user/danger-zone.js +11 -17
- package/dist/components/auth/delete-user/delete-account.d.ts +9 -0
- package/dist/components/auth/delete-user/delete-account.js +50 -0
- package/dist/components/auth/email/email-changed-email.d.ts +2 -2
- package/dist/components/auth/email/email-changed-email.js +22 -29
- package/dist/components/auth/email/email-verification-email.d.ts +2 -2
- package/dist/components/auth/email/email-verification-email.js +22 -29
- package/dist/components/auth/email/magic-link-email.d.ts +2 -2
- package/dist/components/auth/email/magic-link-email.js +22 -29
- package/dist/components/auth/email/new-device-email.d.ts +2 -2
- package/dist/components/auth/email/new-device-email.js +22 -29
- package/dist/components/auth/email/organization-invitation-email.d.ts +3 -0
- package/dist/components/auth/email/organization-invitation-email.js +23 -0
- package/dist/components/auth/email/otp-email.d.ts +2 -2
- package/dist/components/auth/email/otp-email.js +21 -28
- package/dist/components/auth/email/password-changed-email.d.ts +2 -2
- package/dist/components/auth/email/password-changed-email.js +22 -29
- package/dist/components/auth/email/reset-password-email.d.ts +2 -2
- package/dist/components/auth/email/reset-password-email.js +22 -29
- package/dist/components/auth/error-toaster.js +32 -16
- package/dist/components/auth/field-separator.d.ts +1 -1
- package/dist/components/auth/field-separator.js +4 -17
- package/dist/components/auth/forgot-password.d.ts +1 -1
- package/dist/components/auth/forgot-password.js +36 -88
- package/dist/components/auth/magic-link/magic-link-button.d.ts +1 -1
- package/dist/components/auth/magic-link/magic-link-button.js +33 -19
- package/dist/components/auth/magic-link/magic-link.d.ts +2 -2
- package/dist/components/auth/magic-link/magic-link.js +39 -86
- package/dist/components/auth/multi-session/manage-account.d.ts +1 -1
- package/dist/components/auth/multi-session/manage-account.js +32 -48
- package/dist/components/auth/multi-session/manage-accounts.d.ts +1 -1
- package/dist/components/auth/multi-session/manage-accounts.js +32 -33
- package/dist/components/auth/multi-session/switch-account-submenu-content.d.ts +3 -1
- package/dist/components/auth/multi-session/switch-account-submenu-content.js +24 -27
- package/dist/components/auth/multi-session/switch-account-submenu-item.d.ts +3 -2
- package/dist/components/auth/multi-session/switch-account-submenu-item.js +16 -19
- package/dist/components/auth/multi-session/switch-account-submenu.d.ts +2 -1
- package/dist/components/auth/multi-session/switch-account-submenu.js +23 -23
- package/dist/components/auth/organization/change-organization-logo.d.ts +4 -0
- package/dist/components/auth/organization/change-organization-logo.js +69 -0
- package/dist/components/auth/organization/create-organization-dialog.d.ts +13 -0
- package/dist/components/auth/organization/create-organization-dialog.js +37 -0
- package/dist/components/auth/organization/delete-organization-dialog.d.ts +7 -0
- package/dist/components/auth/organization/delete-organization-dialog.js +25 -0
- package/dist/components/auth/organization/delete-organization-skeleton.d.ts +4 -0
- package/dist/components/auth/organization/delete-organization-skeleton.js +8 -0
- package/dist/components/auth/organization/delete-organization.d.ts +5 -0
- package/dist/components/auth/organization/delete-organization.js +27 -0
- package/dist/components/auth/organization/invite-member-dialog.d.ts +13 -0
- package/dist/components/auth/organization/invite-member-dialog.js +47 -0
- package/dist/components/auth/organization/leave-organization-dialog.d.ts +7 -0
- package/dist/components/auth/organization/leave-organization-dialog.js +21 -0
- package/dist/components/auth/organization/leave-organization.d.ts +4 -0
- package/dist/components/auth/organization/leave-organization.js +16 -0
- package/dist/components/auth/organization/organization-danger-zone.d.ts +16 -0
- package/dist/components/auth/organization/organization-danger-zone.js +23 -0
- package/dist/components/auth/organization/organization-invitation-row-skeleton.d.ts +5 -0
- package/dist/components/auth/organization/organization-invitation-row-skeleton.js +9 -0
- package/dist/components/auth/organization/organization-invitation-row.d.ts +5 -0
- package/dist/components/auth/organization/organization-invitation-row.js +31 -0
- package/dist/components/auth/organization/organization-invitations-empty.d.ts +8 -0
- package/dist/components/auth/organization/organization-invitations-empty.js +13 -0
- package/dist/components/auth/organization/organization-invitations.d.ts +9 -0
- package/dist/components/auth/organization/organization-invitations.js +66 -0
- package/dist/components/auth/organization/organization-logo.d.ts +15 -0
- package/dist/components/auth/organization/organization-logo.js +14 -0
- package/dist/components/auth/organization/organization-member-row-skeleton.d.ts +4 -0
- package/dist/components/auth/organization/organization-member-row-skeleton.js +9 -0
- package/dist/components/auth/organization/organization-member-row.d.ts +9 -0
- package/dist/components/auth/organization/organization-member-row.js +34 -0
- package/dist/components/auth/organization/organization-members.d.ts +9 -0
- package/dist/components/auth/organization/organization-members.js +67 -0
- package/dist/components/auth/organization/organization-people.d.ts +10 -0
- package/dist/components/auth/organization/organization-people.js +11 -0
- package/dist/components/auth/organization/organization-profile.d.ts +9 -0
- package/dist/components/auth/organization/organization-profile.js +34 -0
- package/dist/components/auth/organization/organization-row.d.ts +8 -0
- package/dist/components/auth/organization/organization-row.js +31 -0
- package/dist/components/auth/organization/organization-settings.d.ts +12 -0
- package/dist/components/auth/organization/organization-settings.js +13 -0
- package/dist/components/auth/organization/organization-switcher.d.ts +20 -0
- package/dist/components/auth/organization/organization-switcher.js +48 -0
- package/dist/components/auth/organization/organization-view-skeleton.d.ts +6 -0
- package/dist/components/auth/organization/organization-view-skeleton.js +9 -0
- package/dist/components/auth/organization/organization-view.d.ts +15 -0
- package/dist/components/auth/organization/organization-view.js +31 -0
- package/dist/components/auth/organization/organization.d.ts +17 -0
- package/dist/components/auth/organization/organization.js +60 -0
- package/dist/components/auth/organization/organizations-empty.d.ts +4 -0
- package/dist/components/auth/organization/organizations-empty.js +9 -0
- package/dist/components/auth/organization/organizations-settings.d.ts +17 -0
- package/dist/components/auth/organization/organizations-settings.js +17 -0
- package/dist/components/auth/organization/organizations.d.ts +10 -0
- package/dist/components/auth/organization/organizations.js +21 -0
- package/dist/components/auth/organization/remove-member-dialog.d.ts +9 -0
- package/dist/components/auth/organization/remove-member-dialog.js +20 -0
- package/dist/components/auth/organization/slug-field.d.ts +19 -0
- package/dist/components/auth/organization/slug-field.js +38 -0
- package/dist/components/auth/organization/user-invitation-row-skeleton.d.ts +4 -0
- package/dist/components/auth/organization/user-invitation-row-skeleton.js +8 -0
- package/dist/components/auth/organization/user-invitation-row.d.ts +10 -0
- package/dist/components/auth/organization/user-invitation-row.js +22 -0
- package/dist/components/auth/organization/user-invitations-empty.d.ts +4 -0
- package/dist/components/auth/organization/user-invitations-empty.js +12 -0
- package/dist/components/auth/organization/user-invitations.d.ts +10 -0
- package/dist/components/auth/organization/user-invitations.js +18 -0
- package/dist/components/auth/passkey/add-passkey-dialog.js +21 -64
- package/dist/components/auth/passkey/delete-passkey-dialog.js +13 -46
- package/dist/components/auth/passkey/passkey-button.d.ts +1 -1
- package/dist/components/auth/passkey/passkey-button.js +24 -23
- package/dist/components/auth/passkey/passkey-skeleton.js +4 -16
- package/dist/components/auth/passkey/passkey.d.ts +1 -1
- package/dist/components/auth/passkey/passkey.js +16 -47
- package/dist/components/auth/passkey/passkeys-empty.js +8 -34
- package/dist/components/auth/passkey/passkeys.d.ts +1 -1
- package/dist/components/auth/passkey/passkeys.js +15 -41
- package/dist/components/auth/provider-button.d.ts +2 -2
- package/dist/components/auth/provider-button.js +28 -24
- package/dist/components/auth/provider-buttons.js +28 -18
- package/dist/components/auth/reset-password.d.ts +1 -1
- package/dist/components/auth/reset-password.js +56 -146
- package/dist/components/auth/settings/account/account-settings.d.ts +2 -2
- package/dist/components/auth/settings/account/account-settings.js +21 -19
- package/dist/components/auth/settings/account/change-avatar.js +53 -88
- package/dist/components/auth/settings/account/change-email.d.ts +1 -1
- package/dist/components/auth/settings/account/change-email.js +27 -53
- package/dist/components/auth/settings/account/user-profile.d.ts +1 -1
- package/dist/components/auth/settings/account/user-profile.js +64 -85
- package/dist/components/auth/settings/security/active-session.d.ts +1 -1
- package/dist/components/auth/settings/security/active-session.js +50 -57
- package/dist/components/auth/settings/security/active-sessions.d.ts +1 -1
- package/dist/components/auth/settings/security/active-sessions.js +21 -32
- package/dist/components/auth/settings/security/change-password.d.ts +1 -1
- package/dist/components/auth/settings/security/change-password.js +76 -186
- package/dist/components/auth/settings/security/linked-account.d.ts +1 -1
- package/dist/components/auth/settings/security/linked-account.js +37 -55
- package/dist/components/auth/settings/security/linked-accounts.d.ts +1 -1
- package/dist/components/auth/settings/security/linked-accounts.js +34 -43
- package/dist/components/auth/settings/security/security-settings.d.ts +2 -2
- package/dist/components/auth/settings/security/security-settings.js +19 -21
- package/dist/components/auth/settings/settings.d.ts +4 -4
- package/dist/components/auth/settings/settings.js +52 -44
- package/dist/components/auth/sign-in.d.ts +2 -2
- package/dist/components/auth/sign-in.js +63 -132
- package/dist/components/auth/sign-out.js +32 -26
- package/dist/components/auth/sign-up.d.ts +2 -2
- package/dist/components/auth/sign-up.js +97 -211
- package/dist/components/auth/theme/appearance.d.ts +1 -1
- package/dist/components/auth/theme/appearance.js +23 -71
- package/dist/components/auth/theme/theme-toggle-item.js +56 -61
- package/dist/components/auth/user/user-avatar.d.ts +4 -4
- package/dist/components/auth/user/user-avatar.js +31 -28
- package/dist/components/auth/user/user-button.d.ts +6 -7
- package/dist/components/auth/user/user-button.js +38 -72
- package/dist/components/auth/user/user-view.d.ts +13 -7
- package/dist/components/auth/user/user-view.js +26 -36
- package/dist/components/auth/username/sign-in-username.d.ts +2 -2
- package/dist/components/auth/username/sign-in-username.js +87 -147
- package/dist/components/auth/username/username-field.d.ts +1 -1
- package/dist/components/auth/username/username-field.js +44 -52
- package/dist/email.d.ts +8 -7
- package/dist/email.js +8 -8
- package/dist/index.d.ts +21 -21
- package/dist/index.js +21 -22
- package/dist/lib/auth/api-key-plugin.d.ts +7 -4
- package/dist/lib/auth/api-key-plugin.js +12 -10
- package/dist/lib/auth/auth-plugin.d.ts +10 -6
- package/dist/lib/auth/auth-plugin.js +1 -0
- package/dist/lib/auth/delete-user-plugin.d.ts +6 -6
- package/dist/lib/auth/delete-user-plugin.js +6 -9
- package/dist/lib/auth/magic-link-plugin.d.ts +5 -5
- package/dist/lib/auth/magic-link-plugin.js +17 -12
- package/dist/lib/auth/multi-session-plugin.d.ts +5 -5
- package/dist/lib/auth/multi-session-plugin.js +8 -11
- package/dist/lib/auth/organization-plugin.d.ts +37 -0
- package/dist/lib/auth/organization-plugin.js +19 -0
- package/dist/lib/auth/passkey-plugin.d.ts +5 -5
- package/dist/lib/auth/passkey-plugin.js +8 -11
- package/dist/lib/auth/theme-plugin.d.ts +6 -6
- package/dist/lib/auth/theme-plugin.js +25 -22
- package/dist/lib/auth/username-plugin.d.ts +11 -11
- package/dist/lib/auth/username-plugin.js +18 -17
- package/dist/plugins.d.ts +46 -22
- package/dist/plugins.js +47 -22
- package/package.json +25 -19
- package/src/components/auth/api-key/api-key.tsx +25 -16
- package/src/components/auth/api-key/api-keys-empty.tsx +8 -4
- package/src/components/auth/api-key/api-keys.tsx +52 -14
- package/src/components/auth/api-key/create-api-key-dialog.tsx +15 -2
- package/src/components/auth/api-key/delete-api-key-dialog.tsx +10 -2
- package/src/components/auth/api-key/organization-api-keys.tsx +55 -0
- package/src/components/auth/delete-user/danger-zone.tsx +3 -3
- package/src/components/auth/delete-user/{delete-user.tsx → delete-account.tsx} +9 -9
- package/src/components/auth/email/organization-invitation-email.tsx +38 -0
- package/src/components/auth/error-toaster.tsx +28 -10
- package/src/components/auth/forgot-password.tsx +10 -2
- package/src/components/auth/multi-session/manage-accounts.tsx +1 -1
- package/src/components/auth/multi-session/switch-account-submenu-content.tsx +7 -2
- package/src/components/auth/multi-session/switch-account-submenu-item.tsx +4 -2
- package/src/components/auth/multi-session/switch-account-submenu.tsx +8 -4
- package/src/components/auth/organization/change-organization-logo.tsx +171 -0
- package/src/components/auth/organization/create-organization-dialog.tsx +133 -0
- package/src/components/auth/organization/delete-organization-dialog.tsx +97 -0
- package/src/components/auth/organization/delete-organization-skeleton.tsx +17 -0
- package/src/components/auth/organization/delete-organization.tsx +77 -0
- package/src/components/auth/organization/invite-member-dialog.tsx +177 -0
- package/src/components/auth/organization/leave-organization-dialog.tsx +95 -0
- package/src/components/auth/organization/leave-organization.tsx +59 -0
- package/src/components/auth/organization/organization-danger-zone.tsx +68 -0
- package/src/components/auth/organization/organization-invitation-row-skeleton.tsx +29 -0
- package/src/components/auth/organization/organization-invitation-row.tsx +97 -0
- package/src/components/auth/organization/organization-invitations-empty.tsx +39 -0
- package/src/components/auth/organization/organization-invitations.tsx +351 -0
- package/src/components/auth/organization/organization-logo.tsx +64 -0
- package/src/components/auth/organization/organization-member-row-skeleton.tsx +24 -0
- package/src/components/auth/organization/organization-member-row.tsx +157 -0
- package/src/components/auth/organization/organization-members.tsx +284 -0
- package/src/components/auth/organization/organization-people.tsx +26 -0
- package/src/components/auth/organization/organization-profile.tsx +141 -0
- package/src/components/auth/organization/organization-row.tsx +66 -0
- package/src/components/auth/organization/organization-settings.tsx +38 -0
- package/src/components/auth/organization/organization-switcher.tsx +233 -0
- package/src/components/auth/organization/organization-view-skeleton.tsx +36 -0
- package/src/components/auth/organization/organization-view.tsx +109 -0
- package/src/components/auth/organization/organization.tsx +162 -0
- package/src/components/auth/organization/organizations-empty.tsx +35 -0
- package/src/components/auth/organization/organizations-settings.tsx +36 -0
- package/src/components/auth/organization/organizations.tsx +80 -0
- package/src/components/auth/organization/remove-member-dialog.tsx +95 -0
- package/src/components/auth/organization/slug-field.tsx +111 -0
- package/src/components/auth/organization/user-invitation-row-skeleton.tsx +17 -0
- package/src/components/auth/organization/user-invitation-row.tsx +90 -0
- package/src/components/auth/organization/user-invitations-empty.tsx +30 -0
- package/src/components/auth/organization/user-invitations.tsx +59 -0
- package/src/components/auth/settings/account/change-email.tsx +1 -1
- package/src/components/auth/settings/account/user-profile.tsx +5 -5
- package/src/components/auth/settings/security/active-sessions.tsx +6 -4
- package/src/components/auth/settings/security/linked-accounts.tsx +2 -2
- package/src/components/auth/settings/settings.tsx +77 -14
- package/src/components/auth/user/user-avatar.tsx +4 -1
- package/src/components/auth/user/user-button.tsx +14 -11
- package/src/components/auth/user/user-view.tsx +32 -12
- package/src/email.ts +1 -0
- package/src/lib/auth/api-key-plugin.ts +10 -4
- package/src/lib/auth/auth-plugin.ts +6 -1
- package/src/lib/auth/organization-plugin.tsx +32 -0
- package/src/plugins.ts +25 -1
- package/dist/components/auth/delete-user/delete-user.d.ts +0 -9
- package/dist/components/auth/delete-user/delete-user.js +0 -95
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { Session, User } from
|
|
1
|
+
import type { Session, User } from "better-auth";
|
|
2
2
|
type DeviceSession = {
|
|
3
3
|
session: Session;
|
|
4
4
|
user: User;
|
|
5
5
|
};
|
|
6
6
|
export type SwitchAccountSubmenuItemProps = {
|
|
7
7
|
deviceSession: DeviceSession;
|
|
8
|
+
hideSubtitle?: boolean;
|
|
8
9
|
};
|
|
9
10
|
/**
|
|
10
11
|
* Render a dropdown item for switching to a different authenticated session.
|
|
@@ -12,5 +13,5 @@ export type SwitchAccountSubmenuItemProps = {
|
|
|
12
13
|
* @param deviceSession - The device session to display and switch to when pressed
|
|
13
14
|
* @returns The switch account dropdown item as a JSX element
|
|
14
15
|
*/
|
|
15
|
-
export declare function SwitchAccountSubmenuItem({ deviceSession }: SwitchAccountSubmenuItemProps): import("react/jsx-runtime").JSX.Element;
|
|
16
|
+
export declare function SwitchAccountSubmenuItem({ deviceSession, hideSubtitle }: SwitchAccountSubmenuItemProps): import("react/jsx-runtime").JSX.Element;
|
|
16
17
|
export {};
|
|
@@ -1,20 +1,17 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { useAuth
|
|
3
|
-
import { Dropdown
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
});
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useAuth, useSetActiveSession } from "@better-auth-ui/react";
|
|
3
|
+
import { Dropdown, Spinner } from "@heroui/react";
|
|
4
|
+
import { UserView } from "../user/user-view";
|
|
5
|
+
/**
|
|
6
|
+
* Render a dropdown item for switching to a different authenticated session.
|
|
7
|
+
*
|
|
8
|
+
* @param deviceSession - The device session to display and switch to when pressed
|
|
9
|
+
* @returns The switch account dropdown item as a JSX element
|
|
10
|
+
*/
|
|
11
|
+
export function SwitchAccountSubmenuItem({ deviceSession, hideSubtitle }) {
|
|
12
|
+
const { authClient } = useAuth();
|
|
13
|
+
const { mutate: setActiveSession, isPending } = useSetActiveSession(authClient, {
|
|
14
|
+
onSuccess: () => window.scrollTo({ top: 0 })
|
|
15
|
+
});
|
|
16
|
+
return (_jsxs(Dropdown.Item, { className: "px-2", isDisabled: isPending, onPress: () => setActiveSession({ sessionToken: deviceSession.session.token }), children: [_jsx(UserView, { user: deviceSession.user, hideSubtitle: hideSubtitle }), isPending && _jsx(Spinner, { color: "current", size: "sm", className: "ml-auto" })] }));
|
|
18
17
|
}
|
|
19
|
-
//#endregion
|
|
20
|
-
export { s as SwitchAccountSubmenuItem };
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export type SwitchAccountSubmenuProps = {
|
|
2
2
|
className?: string;
|
|
3
|
+
hideSubtitle?: boolean;
|
|
3
4
|
};
|
|
4
5
|
/**
|
|
5
6
|
* Render a dropdown submenu for switching between multiple authenticated sessions.
|
|
@@ -10,4 +11,4 @@ export type SwitchAccountSubmenuProps = {
|
|
|
10
11
|
* @param className - Optional additional CSS class names
|
|
11
12
|
* @returns The switch account submenu as a JSX element
|
|
12
13
|
*/
|
|
13
|
-
export declare function SwitchAccountSubmenu({ className }: SwitchAccountSubmenuProps): import("react/jsx-runtime").JSX.Element | null;
|
|
14
|
+
export declare function SwitchAccountSubmenu({ className, hideSubtitle }: SwitchAccountSubmenuProps): import("react/jsx-runtime").JSX.Element | null;
|
|
@@ -1,24 +1,24 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useAuth, useAuthPlugin, useSession } from "@better-auth-ui/react";
|
|
3
|
+
import { ArrowRightArrowLeft } from "@gravity-ui/icons";
|
|
4
|
+
import { Dropdown, Label } from "@heroui/react";
|
|
5
|
+
import { multiSessionPlugin } from "../../../lib/auth/multi-session-plugin";
|
|
6
|
+
import { SwitchAccountSubmenuContent } from "./switch-account-submenu-content";
|
|
7
|
+
/**
|
|
8
|
+
* Render a dropdown submenu for switching between multiple authenticated sessions.
|
|
9
|
+
*
|
|
10
|
+
* This component renders as a submenu trigger that opens the switch account menu.
|
|
11
|
+
* It should be rendered inside a Dropdown.Menu.
|
|
12
|
+
*
|
|
13
|
+
* @param className - Optional additional CSS class names
|
|
14
|
+
* @returns The switch account submenu as a JSX element
|
|
15
|
+
*/
|
|
16
|
+
export function SwitchAccountSubmenu({ className, hideSubtitle }) {
|
|
17
|
+
const { authClient } = useAuth();
|
|
18
|
+
const { data: session } = useSession(authClient);
|
|
19
|
+
const { localization: multiSessionLocalization } = useAuthPlugin(multiSessionPlugin);
|
|
20
|
+
if (!session) {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
return (_jsxs(Dropdown.SubmenuTrigger, { children: [_jsxs(Dropdown.Item, { className: className, textValue: multiSessionLocalization.switchAccount, children: [_jsx(ArrowRightArrowLeft, { className: "text-muted" }), _jsx(Label, { children: multiSessionLocalization.switchAccount }), _jsx(Dropdown.SubmenuIndicator, {})] }), _jsx(Dropdown.Popover, { className: "min-w-40 md:min-w-56 max-w-[48svw]", children: _jsx(SwitchAccountSubmenuContent, { hideSubtitle: hideSubtitle }) })] }));
|
|
22
24
|
}
|
|
23
|
-
//#endregion
|
|
24
|
-
export { u as SwitchAccountSubmenu };
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { fileToBase64 } from "@better-auth-ui/core";
|
|
3
|
+
import { useActiveOrganization, useAuth, useAuthPlugin, useUpdateOrganization } from "@better-auth-ui/react";
|
|
4
|
+
import { CloudArrowUpIn, TrashBin } from "@gravity-ui/icons";
|
|
5
|
+
import { Button, cn, Dropdown, Label, Spinner, toast } from "@heroui/react";
|
|
6
|
+
import { useRef, useState } from "react";
|
|
7
|
+
import { organizationPlugin } from "../../../lib/auth/organization-plugin";
|
|
8
|
+
import { OrganizationLogo } from "./organization-logo";
|
|
9
|
+
export function ChangeOrganizationLogo({ className }) {
|
|
10
|
+
const { authClient } = useAuth();
|
|
11
|
+
const { logo, localization: organizationLocalization } = useAuthPlugin(organizationPlugin);
|
|
12
|
+
const { data: activeOrganization, isPending: activeOrganizationPending } = useActiveOrganization(authClient);
|
|
13
|
+
const { mutate: updateOrganization, isPending: updatePending } = useUpdateOrganization(authClient);
|
|
14
|
+
const fileInputRef = useRef(null);
|
|
15
|
+
const [isUploading, setIsUploading] = useState(false);
|
|
16
|
+
const [isDeleting, setIsDeleting] = useState(false);
|
|
17
|
+
const isPending = updatePending || isUploading || isDeleting;
|
|
18
|
+
async function handleFileChange(e) {
|
|
19
|
+
const file = e.target.files?.[0];
|
|
20
|
+
if (!file || !activeOrganization)
|
|
21
|
+
return;
|
|
22
|
+
e.target.value = "";
|
|
23
|
+
setIsUploading(true);
|
|
24
|
+
try {
|
|
25
|
+
const resized = (await logo.resize?.(file, logo.size, logo.extension)) || file;
|
|
26
|
+
const image = (await logo.upload?.(resized)) || (await fileToBase64(resized));
|
|
27
|
+
updateOrganization({ data: { logo: image } }, {
|
|
28
|
+
onSuccess: () => toast.success(organizationLocalization.logoChangedSuccess),
|
|
29
|
+
onSettled: () => setIsUploading(false)
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
setIsUploading(false);
|
|
34
|
+
if (error instanceof Error) {
|
|
35
|
+
toast.danger(error.message);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
async function handleDelete() {
|
|
40
|
+
const currentLogo = activeOrganization?.logo;
|
|
41
|
+
updateOrganization({
|
|
42
|
+
data: { logo: "" }
|
|
43
|
+
}, {
|
|
44
|
+
onSuccess: async () => {
|
|
45
|
+
if (!currentLogo) {
|
|
46
|
+
toast.success(organizationLocalization.logoDeletedSuccess);
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
setIsDeleting(true);
|
|
50
|
+
try {
|
|
51
|
+
await logo.delete?.(currentLogo);
|
|
52
|
+
toast.success(organizationLocalization.logoDeletedSuccess);
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
if (error instanceof Error) {
|
|
56
|
+
toast.danger(error.message);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
finally {
|
|
60
|
+
setIsDeleting(false);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
if (!logo.enabled) {
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
return (_jsxs("div", { className: cn("flex flex-col gap-1", className), children: [_jsx(Label, { isDisabled: !activeOrganization, children: organizationLocalization.logo }), _jsx("input", { ref: fileInputRef, type: "file", accept: "image/*", className: "hidden", onChange: handleFileChange }), _jsxs("div", { className: "flex items-center gap-4", children: [_jsx(Button, { type: "button", isIconOnly: true, variant: "ghost", className: "p-0 h-auto w-auto rounded-full", isDisabled: !activeOrganization || isPending, onPress: () => fileInputRef.current?.click(), children: _jsx(OrganizationLogo, { size: "lg", isPending: activeOrganizationPending, organization: activeOrganization }) }), _jsxs(Dropdown, { children: [_jsxs(Button, { isDisabled: !activeOrganization || isPending, size: "sm", variant: "secondary", children: [isPending && _jsx(Spinner, { size: "sm" }), organizationLocalization.changeLogo] }), _jsx(Dropdown.Popover, { className: "min-w-fit", children: _jsxs(Dropdown.Menu, { children: [_jsxs(Dropdown.Item, { textValue: organizationLocalization.uploadLogo, onAction: () => fileInputRef.current?.click(), children: [_jsx(CloudArrowUpIn, { className: "text-muted" }), _jsx(Label, { children: organizationLocalization.uploadLogo })] }), _jsxs(Dropdown.Item, { textValue: organizationLocalization.deleteLogo, isDisabled: !activeOrganization?.logo, onAction: handleDelete, variant: "danger", children: [_jsx(TrashBin, { className: "text-danger" }), _jsx(Label, { children: organizationLocalization.deleteLogo })] })] }) })] })] })] }));
|
|
69
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/** Props for the {@link CreateOrganizationDialog} component. */
|
|
2
|
+
export type CreateOrganizationDialogProps = {
|
|
3
|
+
isOpen: boolean;
|
|
4
|
+
onOpenChange: (open: boolean) => void;
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* Render a dialog for creating a new organization.
|
|
8
|
+
*
|
|
9
|
+
* @param isOpen - Whether the dialog is open
|
|
10
|
+
* @param onOpenChange - Callback for when the dialog open state changes
|
|
11
|
+
* @returns The create organization dialog as a JSX element
|
|
12
|
+
*/
|
|
13
|
+
export declare function CreateOrganizationDialog({ isOpen, onOpenChange }: CreateOrganizationDialogProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useAuth, useAuthPlugin, useCreateOrganization } from "@better-auth-ui/react";
|
|
3
|
+
import { Briefcase } from "@gravity-ui/icons";
|
|
4
|
+
import { AlertDialog, Button, FieldError, Form, Input, Label, Spinner, TextField } from "@heroui/react";
|
|
5
|
+
import { useEffect, useState } from "react";
|
|
6
|
+
import { organizationPlugin } from "../../../lib/auth/organization-plugin";
|
|
7
|
+
import { SlugField, sanitizeSlug } from "./slug-field";
|
|
8
|
+
/**
|
|
9
|
+
* Render a dialog for creating a new organization.
|
|
10
|
+
*
|
|
11
|
+
* @param isOpen - Whether the dialog is open
|
|
12
|
+
* @param onOpenChange - Callback for when the dialog open state changes
|
|
13
|
+
* @returns The create organization dialog as a JSX element
|
|
14
|
+
*/
|
|
15
|
+
export function CreateOrganizationDialog({ isOpen, onOpenChange }) {
|
|
16
|
+
const { authClient, localization } = useAuth();
|
|
17
|
+
const { localization: organizationLocalization } = useAuthPlugin(organizationPlugin);
|
|
18
|
+
const [name, setName] = useState("");
|
|
19
|
+
const [slug, setSlug] = useState("");
|
|
20
|
+
const { mutate: createOrganization, isPending: isCreating } = useCreateOrganization(authClient, {
|
|
21
|
+
onSuccess: () => onOpenChange(false)
|
|
22
|
+
});
|
|
23
|
+
const handleSubmit = (e) => {
|
|
24
|
+
e.preventDefault();
|
|
25
|
+
createOrganization({ name, slug });
|
|
26
|
+
};
|
|
27
|
+
useEffect(() => {
|
|
28
|
+
if (!isOpen) {
|
|
29
|
+
setSlug("");
|
|
30
|
+
setName("");
|
|
31
|
+
}
|
|
32
|
+
}, [isOpen]);
|
|
33
|
+
useEffect(() => {
|
|
34
|
+
setSlug(sanitizeSlug(name));
|
|
35
|
+
}, [name]);
|
|
36
|
+
return (_jsx(AlertDialog.Backdrop, { isOpen: isOpen, onOpenChange: onOpenChange, children: _jsx(AlertDialog.Container, { children: _jsx(AlertDialog.Dialog, { children: _jsxs(Form, { onSubmit: handleSubmit, children: [_jsx(AlertDialog.CloseTrigger, {}), _jsxs(AlertDialog.Header, { children: [_jsx(AlertDialog.Icon, { status: "default", children: _jsx(Briefcase, {}) }), _jsx(AlertDialog.Heading, { children: organizationLocalization.createOrganization })] }), _jsxs(AlertDialog.Body, { className: "flex flex-col gap-4 overflow-visible", children: [_jsx("p", { className: "text-muted text-sm", children: organizationLocalization.organizationsDescription }), _jsxs(TextField, { id: "name", name: "name", isDisabled: isCreating, value: name, onChange: setName, children: [_jsx(Label, { children: organizationLocalization.name }), _jsx(Input, { required: true, autoFocus: true, placeholder: organizationLocalization.namePlaceholder, variant: "secondary" }), _jsx(FieldError, {})] }), _jsx(SlugField, { value: slug, onChange: setSlug, isDisabled: isCreating, variant: "secondary" })] }), _jsxs(AlertDialog.Footer, { children: [_jsx(Button, { slot: "close", variant: "tertiary", isDisabled: isCreating, children: localization.settings.cancel }), _jsxs(Button, { type: "submit", isPending: isCreating, children: [isCreating && _jsx(Spinner, { color: "current", size: "sm" }), organizationLocalization.createOrganization] })] })] }) }) }) }));
|
|
37
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Organization } from "better-auth/client";
|
|
2
|
+
export type DeleteOrganizationDialogProps = {
|
|
3
|
+
isOpen: boolean;
|
|
4
|
+
onOpenChange: (open: boolean) => void;
|
|
5
|
+
organization: Organization;
|
|
6
|
+
};
|
|
7
|
+
export declare function DeleteOrganizationDialog({ isOpen, onOpenChange, organization }: DeleteOrganizationDialogProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useAuth, useAuthPlugin, useDeleteOrganization } from "@better-auth-ui/react";
|
|
3
|
+
import { TriangleExclamation } from "@gravity-ui/icons";
|
|
4
|
+
import { AlertDialog, Button, Card, Form, Spinner, toast } from "@heroui/react";
|
|
5
|
+
import { organizationPlugin } from "../../../lib/auth/organization-plugin";
|
|
6
|
+
import { OrganizationView } from "./organization-view";
|
|
7
|
+
export function DeleteOrganizationDialog({ isOpen, onOpenChange, organization }) {
|
|
8
|
+
const { authClient, basePaths, localization, navigate } = useAuth();
|
|
9
|
+
const { localization: organizationLocalization, viewPaths: organizationPluginViewPaths } = useAuthPlugin(organizationPlugin);
|
|
10
|
+
const { mutate: deleteOrganization, isPending } = useDeleteOrganization(authClient, {
|
|
11
|
+
onSuccess: () => {
|
|
12
|
+
onOpenChange(false);
|
|
13
|
+
toast.success(organizationLocalization.organizationDeleted);
|
|
14
|
+
navigate({
|
|
15
|
+
to: `${basePaths.settings}/${organizationPluginViewPaths.settings.organizations}`,
|
|
16
|
+
replace: true
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
function handleSubmit(e) {
|
|
21
|
+
e.preventDefault();
|
|
22
|
+
deleteOrganization({ organizationId: organization.id });
|
|
23
|
+
}
|
|
24
|
+
return (_jsx(AlertDialog.Backdrop, { isOpen: isOpen, onOpenChange: onOpenChange, children: _jsx(AlertDialog.Container, { children: _jsx(AlertDialog.Dialog, { children: _jsxs(Form, { onSubmit: handleSubmit, children: [_jsx(AlertDialog.CloseTrigger, {}), _jsxs(AlertDialog.Header, { children: [_jsx(AlertDialog.Icon, { status: "danger", children: _jsx(TriangleExclamation, {}) }), _jsx(AlertDialog.Heading, { children: organizationLocalization.deleteOrganization })] }), _jsxs(AlertDialog.Body, { className: "flex flex-col gap-4 overflow-visible", children: [_jsx("p", { className: "text-muted text-sm", children: organizationLocalization.deleteOrganizationDescription }), _jsx(Card, { variant: "secondary", children: _jsx(Card.Content, { children: _jsx(OrganizationView, { organization: organization, hideRole: true }) }) })] }), _jsxs(AlertDialog.Footer, { children: [_jsx(Button, { slot: "close", variant: "tertiary", isDisabled: isPending, children: localization.settings.cancel }), _jsxs(Button, { type: "submit", variant: "danger", isPending: isPending, children: [isPending && _jsx(Spinner, { color: "current", size: "sm" }), organizationLocalization.deleteOrganization] })] })] }) }) }) }));
|
|
25
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Skeleton } from "@heroui/react";
|
|
3
|
+
/**
|
|
4
|
+
* Placeholder matching {@link DeleteOrganization} while the delete permission resolves.
|
|
5
|
+
*/
|
|
6
|
+
export function DeleteOrganizationSkeleton() {
|
|
7
|
+
return (_jsxs("div", { className: "flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between", children: [_jsxs("div", { className: "flex flex-col gap-1", children: [_jsx(Skeleton, { className: "h-3.5 w-40 rounded-lg" }), _jsx(Skeleton, { className: "h-3 w-64 rounded-lg" })] }), _jsx(Skeleton, { className: "h-8 w-36 shrink-0 rounded-full" })] }));
|
|
8
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useActiveOrganization, useAuth, useAuthPlugin, useHasPermission } from "@better-auth-ui/react";
|
|
3
|
+
import { AlertDialog, Button } from "@heroui/react";
|
|
4
|
+
import { useState } from "react";
|
|
5
|
+
import { organizationPlugin } from "../../../lib/auth/organization-plugin";
|
|
6
|
+
import { DeleteOrganizationDialog } from "./delete-organization-dialog";
|
|
7
|
+
import { DeleteOrganizationSkeleton } from "./delete-organization-skeleton";
|
|
8
|
+
/**
|
|
9
|
+
* Danger-zone row to delete the active organization. Hidden for members without
|
|
10
|
+
* the `organization:delete` permission.
|
|
11
|
+
*/
|
|
12
|
+
export function DeleteOrganization() {
|
|
13
|
+
const { authClient } = useAuth();
|
|
14
|
+
const { localization: organizationLocalization } = useAuthPlugin(organizationPlugin);
|
|
15
|
+
const { data: activeOrganization } = useActiveOrganization(authClient);
|
|
16
|
+
const { data: permission, isPending: permissionPending } = useHasPermission(authClient, {
|
|
17
|
+
permissions: { organization: ["delete"] }
|
|
18
|
+
});
|
|
19
|
+
const [confirmOpen, setConfirmOpen] = useState(false);
|
|
20
|
+
if (permissionPending) {
|
|
21
|
+
return _jsx(DeleteOrganizationSkeleton, {});
|
|
22
|
+
}
|
|
23
|
+
if (!permission?.success) {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
return (_jsxs("div", { className: "flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between", children: [_jsxs("div", { children: [_jsx("p", { className: "text-sm font-medium leading-tight", children: organizationLocalization.deleteOrganization }), _jsx("p", { className: "text-muted mt-0.5 text-xs", children: organizationLocalization.deleteOrganizationDescription })] }), _jsxs(AlertDialog, { children: [_jsx(Button, { isDisabled: !activeOrganization, size: "sm", variant: "danger-soft", onPress: () => setConfirmOpen(true), children: organizationLocalization.deleteOrganization }), activeOrganization && (_jsx(DeleteOrganizationDialog, { isOpen: confirmOpen, onOpenChange: setConfirmOpen, organization: activeOrganization }))] })] }));
|
|
27
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/** Props for the {@link InviteMemberDialog} component. */
|
|
2
|
+
export type InviteMemberDialogProps = {
|
|
3
|
+
isOpen: boolean;
|
|
4
|
+
onOpenChange: (open: boolean) => void;
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* Render a dialog for inviting a member to the organization.
|
|
8
|
+
*
|
|
9
|
+
* @param isOpen - Whether the dialog is open
|
|
10
|
+
* @param onOpenChange - Callback for when the dialog open state changes
|
|
11
|
+
* @returns The invite member dialog as a JSX element
|
|
12
|
+
*/
|
|
13
|
+
export declare function InviteMemberDialog({ isOpen, onOpenChange }: InviteMemberDialogProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useAuth, useAuthPlugin, useInviteMember } from "@better-auth-ui/react";
|
|
3
|
+
import { PersonPlus } from "@gravity-ui/icons";
|
|
4
|
+
import { AlertDialog, Button, FieldError, Form, Input, Label, ListBox, Select, Spinner, TextField, toast } from "@heroui/react";
|
|
5
|
+
import { useEffect, useState } from "react";
|
|
6
|
+
import { organizationPlugin } from "../../../lib/auth/organization-plugin";
|
|
7
|
+
const pickDefaultRole = (keys) => keys.includes("member") ? "member" : (keys.at(-1) ?? "");
|
|
8
|
+
/**
|
|
9
|
+
* Render a dialog for inviting a member to the organization.
|
|
10
|
+
*
|
|
11
|
+
* @param isOpen - Whether the dialog is open
|
|
12
|
+
* @param onOpenChange - Callback for when the dialog open state changes
|
|
13
|
+
* @returns The invite member dialog as a JSX element
|
|
14
|
+
*/
|
|
15
|
+
export function InviteMemberDialog({ isOpen, onOpenChange }) {
|
|
16
|
+
const { authClient, localization } = useAuth();
|
|
17
|
+
const { localization: organizationLocalization, roles } = useAuthPlugin(organizationPlugin);
|
|
18
|
+
const [role, setRole] = useState(() => pickDefaultRole(Object.keys(roles)));
|
|
19
|
+
useEffect(() => {
|
|
20
|
+
setRole((current) => {
|
|
21
|
+
const keys = Object.keys(roles);
|
|
22
|
+
return keys.includes(current) ? current : pickDefaultRole(keys);
|
|
23
|
+
});
|
|
24
|
+
}, [roles]);
|
|
25
|
+
const { mutate: inviteMember, isPending: isInviting } = useInviteMember(authClient, {
|
|
26
|
+
onSuccess: () => {
|
|
27
|
+
onOpenChange(false);
|
|
28
|
+
toast.success(organizationLocalization.inviteMemberSuccess);
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
const isRoleValid = Object.keys(roles).includes(role);
|
|
32
|
+
const handleSubmit = (e) => {
|
|
33
|
+
e.preventDefault();
|
|
34
|
+
if (!isRoleValid)
|
|
35
|
+
return;
|
|
36
|
+
const formData = new FormData(e.target);
|
|
37
|
+
const email = formData.get("email");
|
|
38
|
+
inviteMember({
|
|
39
|
+
email: email.trim(),
|
|
40
|
+
role: role
|
|
41
|
+
});
|
|
42
|
+
};
|
|
43
|
+
return (_jsx(AlertDialog.Backdrop, { isOpen: isOpen, onOpenChange: onOpenChange, children: _jsx(AlertDialog.Container, { children: _jsx(AlertDialog.Dialog, { children: _jsxs(Form, { onSubmit: handleSubmit, children: [_jsx(AlertDialog.CloseTrigger, {}), _jsxs(AlertDialog.Header, { children: [_jsx(AlertDialog.Icon, { status: "default", children: _jsx(PersonPlus, {}) }), _jsx(AlertDialog.Heading, { children: organizationLocalization.inviteMember })] }), _jsxs(AlertDialog.Body, { className: "flex flex-col gap-4 overflow-visible", children: [_jsx("p", { className: "text-muted text-sm", children: organizationLocalization.inviteMemberDescription }), _jsxs(TextField, { id: "email", name: "email", type: "email", isDisabled: isInviting, children: [_jsx(Label, { children: localization.auth.email }), _jsx(Input, { autoFocus: true, placeholder: localization.auth.email, variant: "secondary", required: true }), _jsx(FieldError, {})] }), _jsxs(Select, { name: "role", value: role, onChange: (value) => {
|
|
44
|
+
if (typeof value === "string")
|
|
45
|
+
setRole(value);
|
|
46
|
+
}, isDisabled: isInviting, variant: "secondary", fullWidth: true, children: [_jsx(Label, { children: organizationLocalization.role }), _jsxs(Select.Trigger, { children: [_jsx(Select.Value, {}), _jsx(Select.Indicator, {})] }), _jsx(Select.Popover, { children: _jsx(ListBox, { children: Object.entries(roles).map(([key, label]) => (_jsxs(ListBox.Item, { id: key, textValue: label, children: [label, _jsx(ListBox.ItemIndicator, {})] }, key))) }) }), _jsx(FieldError, {})] })] }), _jsxs(AlertDialog.Footer, { children: [_jsx(Button, { slot: "close", variant: "tertiary", isDisabled: isInviting, children: localization.settings.cancel }), _jsxs(Button, { type: "submit", isPending: isInviting, isDisabled: !isRoleValid, children: [isInviting && _jsx(Spinner, { color: "current", size: "sm" }), organizationLocalization.inviteMember] })] })] }) }) }) }));
|
|
47
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { Organization } from "better-auth/client";
|
|
2
|
+
export type LeaveOrganizationDialogProps = {
|
|
3
|
+
isOpen: boolean;
|
|
4
|
+
onOpenChange: (open: boolean) => void;
|
|
5
|
+
organization: Organization;
|
|
6
|
+
};
|
|
7
|
+
export declare function LeaveOrganizationDialog({ isOpen, onOpenChange, organization }: LeaveOrganizationDialogProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useAuth, useAuthPlugin, useLeaveOrganization } from "@better-auth-ui/react";
|
|
3
|
+
import { ArrowRightFromSquare } from "@gravity-ui/icons";
|
|
4
|
+
import { AlertDialog, Button, Card, Spinner, toast } from "@heroui/react";
|
|
5
|
+
import { organizationPlugin } from "../../../lib/auth/organization-plugin";
|
|
6
|
+
import { OrganizationView } from "./organization-view";
|
|
7
|
+
export function LeaveOrganizationDialog({ isOpen, onOpenChange, organization }) {
|
|
8
|
+
const { authClient, basePaths, localization, navigate } = useAuth();
|
|
9
|
+
const { localization: organizationLocalization, viewPaths: organizationPluginViewPaths } = useAuthPlugin(organizationPlugin);
|
|
10
|
+
const { mutate: leaveOrganization, isPending } = useLeaveOrganization(authClient, {
|
|
11
|
+
onSuccess: () => {
|
|
12
|
+
onOpenChange(false);
|
|
13
|
+
toast.success(organizationLocalization.leftOrganization);
|
|
14
|
+
navigate({
|
|
15
|
+
to: `${basePaths.settings}/${organizationPluginViewPaths.settings.organizations}`,
|
|
16
|
+
replace: true
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
return (_jsx(AlertDialog.Backdrop, { isOpen: isOpen, onOpenChange: onOpenChange, children: _jsx(AlertDialog.Container, { children: _jsxs(AlertDialog.Dialog, { children: [_jsx(AlertDialog.CloseTrigger, {}), _jsxs(AlertDialog.Header, { children: [_jsx(AlertDialog.Icon, { status: "danger", children: _jsx(ArrowRightFromSquare, {}) }), _jsx(AlertDialog.Heading, { children: organizationLocalization.leaveOrganization })] }), _jsxs(AlertDialog.Body, { className: "flex flex-col gap-4 overflow-visible", children: [_jsx("p", { className: "text-muted text-sm", children: organizationLocalization.leaveOrganizationDescription }), _jsx(Card, { variant: "secondary", children: _jsx(Card.Content, { children: _jsx(OrganizationView, { organization: organization, hideRole: true }) }) })] }), _jsxs(AlertDialog.Footer, { children: [_jsx(Button, { slot: "close", variant: "tertiary", isDisabled: isPending, children: localization.settings.cancel }), _jsxs(Button, { variant: "danger", isPending: isPending, onPress: () => leaveOrganization({ organizationId: organization.id }), children: [isPending && _jsx(Spinner, { color: "current", size: "sm" }), organizationLocalization.leaveOrganization] })] })] }) }) }));
|
|
21
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useActiveOrganization, useAuth, useAuthPlugin } from "@better-auth-ui/react";
|
|
3
|
+
import { AlertDialog, Button } from "@heroui/react";
|
|
4
|
+
import { useState } from "react";
|
|
5
|
+
import { organizationPlugin } from "../../../lib/auth/organization-plugin";
|
|
6
|
+
import { LeaveOrganizationDialog } from "./leave-organization-dialog";
|
|
7
|
+
/**
|
|
8
|
+
* Danger-zone row to leave the active organization.
|
|
9
|
+
*/
|
|
10
|
+
export function LeaveOrganization() {
|
|
11
|
+
const { authClient } = useAuth();
|
|
12
|
+
const { localization: organizationLocalization } = useAuthPlugin(organizationPlugin);
|
|
13
|
+
const { data: activeOrganization } = useActiveOrganization(authClient);
|
|
14
|
+
const [confirmOpen, setConfirmOpen] = useState(false);
|
|
15
|
+
return (_jsxs("div", { className: "flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between", children: [_jsxs("div", { children: [_jsx("p", { className: "text-sm font-medium leading-tight", children: organizationLocalization.leaveOrganization }), _jsx("p", { className: "text-muted mt-0.5 text-xs", children: organizationLocalization.leaveOrganizationDescription })] }), _jsxs(AlertDialog, { children: [_jsx(Button, { isDisabled: !activeOrganization, size: "sm", variant: "danger-soft", onPress: () => setConfirmOpen(true), children: organizationLocalization.leaveOrganization }), activeOrganization && (_jsx(LeaveOrganizationDialog, { isOpen: confirmOpen, onOpenChange: setConfirmOpen, organization: activeOrganization }))] })] }));
|
|
16
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type CardProps } from "@heroui/react";
|
|
2
|
+
import type { ComponentProps } from "react";
|
|
3
|
+
export type OrganizationDangerZoneProps = {
|
|
4
|
+
className?: string;
|
|
5
|
+
variant?: CardProps["variant"];
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* Danger zone heading with {@link LeaveOrganization} and {@link DeleteOrganization}
|
|
9
|
+
* for the active organization in a single card.
|
|
10
|
+
*
|
|
11
|
+
* Resolves the `organization:delete` permission before rendering anything to
|
|
12
|
+
* avoid flashing {@link LeaveOrganization} (and a stray separator) before the
|
|
13
|
+
* delete row appears or disappears. Inner {@link DeleteOrganization} also
|
|
14
|
+
* self-gates so it stays safe to use standalone.
|
|
15
|
+
*/
|
|
16
|
+
export declare function OrganizationDangerZone({ className, variant, ...props }: OrganizationDangerZoneProps & ComponentProps<"div">): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useAuth, useHasPermission } from "@better-auth-ui/react";
|
|
3
|
+
import { Card, cn } from "@heroui/react";
|
|
4
|
+
import { DeleteOrganization } from "./delete-organization";
|
|
5
|
+
import { DeleteOrganizationSkeleton } from "./delete-organization-skeleton";
|
|
6
|
+
import { LeaveOrganization } from "./leave-organization";
|
|
7
|
+
/**
|
|
8
|
+
* Danger zone heading with {@link LeaveOrganization} and {@link DeleteOrganization}
|
|
9
|
+
* for the active organization in a single card.
|
|
10
|
+
*
|
|
11
|
+
* Resolves the `organization:delete` permission before rendering anything to
|
|
12
|
+
* avoid flashing {@link LeaveOrganization} (and a stray separator) before the
|
|
13
|
+
* delete row appears or disappears. Inner {@link DeleteOrganization} also
|
|
14
|
+
* self-gates so it stays safe to use standalone.
|
|
15
|
+
*/
|
|
16
|
+
export function OrganizationDangerZone({ className, variant, ...props }) {
|
|
17
|
+
const { authClient, localization } = useAuth();
|
|
18
|
+
const { data: deletePermission, isPending: deletePermissionPending } = useHasPermission(authClient, {
|
|
19
|
+
permissions: { organization: ["delete"] }
|
|
20
|
+
});
|
|
21
|
+
const canDelete = !!deletePermission?.success;
|
|
22
|
+
return (_jsxs("div", { className: cn("flex w-full flex-col", className), ...props, children: [_jsx("h2", { className: cn("mb-3 text-sm font-semibold text-danger"), children: localization.settings.dangerZone }), _jsx(Card, { variant: variant, children: _jsx(Card.Content, { className: "gap-0", children: deletePermissionPending ? (_jsx(DeleteOrganizationSkeleton, {})) : (_jsxs(_Fragment, { children: [_jsx(LeaveOrganization, {}), canDelete && (_jsxs(_Fragment, { children: [_jsx("div", { className: "border-b border-dashed -mx-4 my-4" }), _jsx(DeleteOrganization, {})] }))] })) }) })] }));
|
|
23
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { Skeleton, Table } from "@heroui/react";
|
|
3
|
+
/**
|
|
4
|
+
* Placeholder row matching {@link OrganizationInvitationTableRow} while
|
|
5
|
+
* invitations load.
|
|
6
|
+
*/
|
|
7
|
+
export function OrganizationInvitationRowSkeleton() {
|
|
8
|
+
return (_jsxs(Table.Row, { children: [_jsx(Table.Cell, { children: _jsx(Skeleton, { className: "h-4 w-48 rounded-lg" }) }), _jsx(Table.Cell, { children: _jsx(Skeleton, { className: "h-4 w-36 rounded-lg" }) }), _jsx(Table.Cell, { children: _jsx(Skeleton, { className: "h-4 w-16 rounded-lg" }) }), _jsx(Table.Cell, { children: _jsx(Skeleton, { className: "h-4 w-14 rounded-full" }) }), _jsx(Table.Cell, {})] }));
|
|
9
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { Invitation } from "better-auth/client";
|
|
2
|
+
export type OrganizationInvitationTableRowProps = {
|
|
3
|
+
invitation: Invitation;
|
|
4
|
+
};
|
|
5
|
+
export declare function OrganizationInvitationTableRow({ invitation }: OrganizationInvitationTableRowProps): 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, useCancelInvitation, useHasPermission } from "@better-auth-ui/react";
|
|
3
|
+
import { Xmark } from "@gravity-ui/icons";
|
|
4
|
+
import { Button, Chip, Spinner, Table } from "@heroui/react";
|
|
5
|
+
import { organizationPlugin } from "../../../lib/auth/organization-plugin";
|
|
6
|
+
import { OrganizationInvitationRowSkeleton } from "./organization-invitation-row-skeleton";
|
|
7
|
+
export function OrganizationInvitationTableRow({ invitation }) {
|
|
8
|
+
const { authClient } = useAuth();
|
|
9
|
+
const { localization: organizationLocalization, roles } = useAuthPlugin(organizationPlugin);
|
|
10
|
+
const { data: cancelInvitationPermission, isPending: cancelPermissionPending } = useHasPermission(authClient, {
|
|
11
|
+
permissions: { invitation: ["cancel"] }
|
|
12
|
+
});
|
|
13
|
+
const { mutate: cancelInvitation, isPending: cancelPending } = useCancelInvitation(authClient);
|
|
14
|
+
const roleLabel = roles?.[invitation.role] ?? invitation.role;
|
|
15
|
+
const statusLabel = organizationLocalization[invitation.status] ?? invitation.status;
|
|
16
|
+
const statusColor = invitation.status === "pending"
|
|
17
|
+
? "warning"
|
|
18
|
+
: invitation.status === "accepted"
|
|
19
|
+
? "success"
|
|
20
|
+
: invitation.status === "rejected"
|
|
21
|
+
? "danger"
|
|
22
|
+
: "default";
|
|
23
|
+
if (cancelPermissionPending) {
|
|
24
|
+
return _jsx(OrganizationInvitationRowSkeleton, {});
|
|
25
|
+
}
|
|
26
|
+
return (_jsxs(Table.Row, { children: [_jsx(Table.Cell, { className: "font-medium text-sm", children: invitation.email }), _jsx(Table.Cell, { className: "text-muted text-xs tabular-nums whitespace-nowrap", children: new Date(invitation.createdAt).toLocaleString(undefined, {
|
|
27
|
+
dateStyle: "short",
|
|
28
|
+
timeStyle: "short"
|
|
29
|
+
}) }), _jsx(Table.Cell, { className: "text-sm", children: roleLabel }), _jsx(Table.Cell, { className: "text-sm", children: _jsx(Chip, { color: statusColor, size: "sm", variant: "soft", children: statusLabel }) }), _jsx(Table.Cell, { className: "text-end", children: cancelInvitationPermission?.success &&
|
|
30
|
+
invitation.status === "pending" && (_jsx(Button, { isIconOnly: true, size: "sm", variant: "danger-soft", isPending: cancelPending, onPress: () => cancelInvitation({ invitationId: invitation.id }), "aria-label": organizationLocalization.cancelInvitation, children: cancelPending ? (_jsx(Spinner, { color: "current", size: "sm" })) : (_jsx(Xmark, {})) })) })] }));
|
|
31
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export type OrganizationInvitationsEmptyProps = {
|
|
2
|
+
onInvitePress: () => void;
|
|
3
|
+
};
|
|
4
|
+
/**
|
|
5
|
+
* Empty state for {@link OrganizationInvitations} — mirrors `ApiKeysEmpty`
|
|
6
|
+
* (icon, title, description) with an "Invite member" call to action.
|
|
7
|
+
*/
|
|
8
|
+
export declare function OrganizationInvitationsEmpty({ onInvitePress }: OrganizationInvitationsEmptyProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useAuthPlugin } from "@better-auth-ui/react";
|
|
3
|
+
import { PaperPlane } from "@gravity-ui/icons";
|
|
4
|
+
import { Button, EmptyState } from "@heroui/react";
|
|
5
|
+
import { organizationPlugin } from "../../../lib/auth/organization-plugin";
|
|
6
|
+
/**
|
|
7
|
+
* Empty state for {@link OrganizationInvitations} — mirrors `ApiKeysEmpty`
|
|
8
|
+
* (icon, title, description) with an "Invite member" call to action.
|
|
9
|
+
*/
|
|
10
|
+
export function OrganizationInvitationsEmpty({ onInvitePress }) {
|
|
11
|
+
const { localization: organizationLocalization } = useAuthPlugin(organizationPlugin);
|
|
12
|
+
return (_jsxs(EmptyState, { className: "flex flex-col items-center gap-4 text-center p-4", children: [_jsx(PaperPlane, { className: "size-6 text-muted" }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx("p", { className: "text-sm font-semibold text-foreground", children: organizationLocalization.noInvitations }), _jsx("span", { className: "text-sm text-muted", children: organizationLocalization.organizationInvitationsEmptyDescription })] }), _jsx(Button, { size: "sm", onPress: onInvitePress, children: organizationLocalization.inviteMember })] }));
|
|
13
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type ComponentProps } from "react";
|
|
2
|
+
/** Props for the {@link OrganizationInvitations} component. */
|
|
3
|
+
export type OrganizationInvitationsProps = {
|
|
4
|
+
className?: string;
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* Organization invitations table with invite control and per-row actions.
|
|
8
|
+
*/
|
|
9
|
+
export declare function OrganizationInvitations({ className, ...props }: OrganizationInvitationsProps & ComponentProps<"div">): import("react/jsx-runtime").JSX.Element;
|