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