@better-auth-ui/heroui 1.6.7 → 1.6.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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.d.ts +5 -0
- package/dist/components/auth/passkey/add-passkey-dialog.js +22 -0
- package/dist/components/auth/passkey/delete-passkey-dialog.d.ts +11 -0
- package/dist/components/auth/passkey/delete-passkey-dialog.js +14 -0
- 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.d.ts +1 -0
- package/dist/components/auth/passkey/passkey-skeleton.js +5 -0
- package/dist/components/auth/passkey/passkey.d.ts +2 -5
- package/dist/components/auth/passkey/passkey.js +16 -44
- package/dist/components/auth/passkey/passkeys-empty.d.ts +4 -0
- package/dist/components/auth/passkey/passkeys-empty.js +9 -0
- package/dist/components/auth/passkey/passkeys.d.ts +1 -1
- package/dist/components/auth/passkey/passkeys.js +15 -105
- 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 +9 -6
- 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 +17 -4
- 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/passkey/add-passkey-dialog.tsx +109 -0
- package/src/components/auth/passkey/delete-passkey-dialog.tsx +95 -0
- package/src/components/auth/passkey/passkey-skeleton.tsx +16 -0
- package/src/components/auth/passkey/passkey.tsx +23 -22
- package/src/components/auth/passkey/passkeys-empty.tsx +35 -0
- package/src/components/auth/passkey/passkeys.tsx +33 -154
- 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
|
+
}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type PasskeyAuthClient,
|
|
3
|
+
useAddPasskey,
|
|
4
|
+
useAuth,
|
|
5
|
+
useAuthPlugin
|
|
6
|
+
} from "@better-auth-ui/react"
|
|
7
|
+
import { Fingerprint } from "@gravity-ui/icons"
|
|
8
|
+
import {
|
|
9
|
+
AlertDialog,
|
|
10
|
+
Button,
|
|
11
|
+
FieldError,
|
|
12
|
+
Form,
|
|
13
|
+
Input,
|
|
14
|
+
Label,
|
|
15
|
+
Spinner,
|
|
16
|
+
TextField
|
|
17
|
+
} from "@heroui/react"
|
|
18
|
+
import type { SyntheticEvent } from "react"
|
|
19
|
+
|
|
20
|
+
import { passkeyPlugin } from "../../../lib/auth/passkey-plugin"
|
|
21
|
+
|
|
22
|
+
export type AddPasskeyDialogProps = {
|
|
23
|
+
isOpen: boolean
|
|
24
|
+
onOpenChange: (open: boolean) => void
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function AddPasskeyDialog({
|
|
28
|
+
isOpen,
|
|
29
|
+
onOpenChange
|
|
30
|
+
}: AddPasskeyDialogProps) {
|
|
31
|
+
const { authClient, localization } = useAuth()
|
|
32
|
+
const { localization: passkeyLocalization } = useAuthPlugin(passkeyPlugin)
|
|
33
|
+
|
|
34
|
+
const { mutate: addPasskey, isPending: isAdding } = useAddPasskey(
|
|
35
|
+
authClient as PasskeyAuthClient
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
const handleOpenChange = (open: boolean) => {
|
|
39
|
+
onOpenChange(open)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const handleSubmit = (e: SyntheticEvent<HTMLFormElement>) => {
|
|
43
|
+
e.preventDefault()
|
|
44
|
+
|
|
45
|
+
const formData = new FormData(e.target as HTMLFormElement)
|
|
46
|
+
const name = (formData.get("name") as string)?.trim()
|
|
47
|
+
|
|
48
|
+
addPasskey(name ? { name } : undefined, {
|
|
49
|
+
onSuccess: () => handleOpenChange(false)
|
|
50
|
+
})
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return (
|
|
54
|
+
<AlertDialog.Backdrop isOpen={isOpen} onOpenChange={handleOpenChange}>
|
|
55
|
+
<AlertDialog.Container>
|
|
56
|
+
<AlertDialog.Dialog>
|
|
57
|
+
<Form onSubmit={handleSubmit}>
|
|
58
|
+
<AlertDialog.CloseTrigger />
|
|
59
|
+
|
|
60
|
+
<AlertDialog.Header>
|
|
61
|
+
<AlertDialog.Icon status="default">
|
|
62
|
+
<Fingerprint />
|
|
63
|
+
</AlertDialog.Icon>
|
|
64
|
+
|
|
65
|
+
<AlertDialog.Heading>
|
|
66
|
+
{passkeyLocalization.addPasskey}
|
|
67
|
+
</AlertDialog.Heading>
|
|
68
|
+
</AlertDialog.Header>
|
|
69
|
+
|
|
70
|
+
<AlertDialog.Body className="overflow-visible">
|
|
71
|
+
<p className="text-muted text-sm">
|
|
72
|
+
{passkeyLocalization.passkeysDescription}
|
|
73
|
+
</p>
|
|
74
|
+
|
|
75
|
+
<TextField
|
|
76
|
+
className="mt-4"
|
|
77
|
+
id="name"
|
|
78
|
+
name="name"
|
|
79
|
+
isDisabled={isAdding}
|
|
80
|
+
>
|
|
81
|
+
<Label>{passkeyLocalization.name}</Label>
|
|
82
|
+
|
|
83
|
+
<Input
|
|
84
|
+
autoFocus
|
|
85
|
+
placeholder={localization.settings.optional}
|
|
86
|
+
variant="secondary"
|
|
87
|
+
/>
|
|
88
|
+
|
|
89
|
+
<FieldError />
|
|
90
|
+
</TextField>
|
|
91
|
+
</AlertDialog.Body>
|
|
92
|
+
|
|
93
|
+
<AlertDialog.Footer>
|
|
94
|
+
<Button slot="close" variant="tertiary" isDisabled={isAdding}>
|
|
95
|
+
{localization.settings.cancel}
|
|
96
|
+
</Button>
|
|
97
|
+
|
|
98
|
+
<Button type="submit" isPending={isAdding}>
|
|
99
|
+
{isAdding && <Spinner color="current" size="sm" />}
|
|
100
|
+
|
|
101
|
+
{passkeyLocalization.addPasskey}
|
|
102
|
+
</Button>
|
|
103
|
+
</AlertDialog.Footer>
|
|
104
|
+
</Form>
|
|
105
|
+
</AlertDialog.Dialog>
|
|
106
|
+
</AlertDialog.Container>
|
|
107
|
+
</AlertDialog.Backdrop>
|
|
108
|
+
)
|
|
109
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type PasskeyAuthClient,
|
|
3
|
+
useAuth,
|
|
4
|
+
useAuthPlugin,
|
|
5
|
+
useDeletePasskey
|
|
6
|
+
} from "@better-auth-ui/react"
|
|
7
|
+
import { Fingerprint } from "@gravity-ui/icons"
|
|
8
|
+
import {
|
|
9
|
+
AlertDialog,
|
|
10
|
+
Button,
|
|
11
|
+
Input,
|
|
12
|
+
Label,
|
|
13
|
+
Spinner,
|
|
14
|
+
TextField
|
|
15
|
+
} from "@heroui/react"
|
|
16
|
+
|
|
17
|
+
import { passkeyPlugin } from "../../../lib/auth/passkey-plugin"
|
|
18
|
+
|
|
19
|
+
export type ListedPasskey = {
|
|
20
|
+
id: string
|
|
21
|
+
name?: string | null
|
|
22
|
+
createdAt: Date
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export type DeletePasskeyDialogProps = {
|
|
26
|
+
isOpen: boolean
|
|
27
|
+
onOpenChange: (open: boolean) => void
|
|
28
|
+
passkey: ListedPasskey
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function DeletePasskeyDialog({
|
|
32
|
+
isOpen,
|
|
33
|
+
onOpenChange,
|
|
34
|
+
passkey
|
|
35
|
+
}: DeletePasskeyDialogProps) {
|
|
36
|
+
const { authClient, localization } = useAuth()
|
|
37
|
+
const { localization: passkeyLocalization } = useAuthPlugin(passkeyPlugin)
|
|
38
|
+
|
|
39
|
+
const passkeyName = passkey.name || passkeyLocalization.passkey
|
|
40
|
+
|
|
41
|
+
const { mutate: deletePasskey, isPending: isDeleting } = useDeletePasskey(
|
|
42
|
+
authClient as PasskeyAuthClient,
|
|
43
|
+
{
|
|
44
|
+
onSuccess: () => onOpenChange(false)
|
|
45
|
+
}
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
return (
|
|
49
|
+
<AlertDialog.Backdrop isOpen={isOpen} onOpenChange={onOpenChange}>
|
|
50
|
+
<AlertDialog.Container>
|
|
51
|
+
<AlertDialog.Dialog>
|
|
52
|
+
<AlertDialog.CloseTrigger />
|
|
53
|
+
|
|
54
|
+
<AlertDialog.Header>
|
|
55
|
+
<AlertDialog.Icon status="danger">
|
|
56
|
+
<Fingerprint />
|
|
57
|
+
</AlertDialog.Icon>
|
|
58
|
+
|
|
59
|
+
<AlertDialog.Heading>
|
|
60
|
+
{passkeyLocalization.deletePasskeyTitle}
|
|
61
|
+
</AlertDialog.Heading>
|
|
62
|
+
</AlertDialog.Header>
|
|
63
|
+
|
|
64
|
+
<AlertDialog.Body className="flex flex-col gap-4 overflow-visible">
|
|
65
|
+
<p className="text-muted text-sm">
|
|
66
|
+
{passkeyLocalization.deletePasskeyWarning}
|
|
67
|
+
</p>
|
|
68
|
+
|
|
69
|
+
<TextField value={passkeyName} variant="secondary">
|
|
70
|
+
<Label>{passkey.name || passkeyLocalization.passkey}</Label>
|
|
71
|
+
|
|
72
|
+
<Input readOnly />
|
|
73
|
+
</TextField>
|
|
74
|
+
</AlertDialog.Body>
|
|
75
|
+
|
|
76
|
+
<AlertDialog.Footer>
|
|
77
|
+
<Button slot="close" variant="tertiary" isDisabled={isDeleting}>
|
|
78
|
+
{localization.settings.cancel}
|
|
79
|
+
</Button>
|
|
80
|
+
|
|
81
|
+
<Button
|
|
82
|
+
variant="danger"
|
|
83
|
+
onPress={() => deletePasskey({ id: passkey.id })}
|
|
84
|
+
isPending={isDeleting}
|
|
85
|
+
>
|
|
86
|
+
{isDeleting && <Spinner color="current" size="sm" />}
|
|
87
|
+
|
|
88
|
+
{passkeyLocalization.deletePasskeyTitle}
|
|
89
|
+
</Button>
|
|
90
|
+
</AlertDialog.Footer>
|
|
91
|
+
</AlertDialog.Dialog>
|
|
92
|
+
</AlertDialog.Container>
|
|
93
|
+
</AlertDialog.Backdrop>
|
|
94
|
+
)
|
|
95
|
+
}
|