@better-auth-ui/heroui 1.6.9 → 1.6.10
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/api-key/api-key.d.ts +1 -1
- package/dist/components/auth/api-key/api-keys.d.ts +1 -1
- package/dist/components/auth/api-key/delete-api-key-dialog.d.ts +1 -1
- package/dist/components/auth/api-key/organization-api-keys.d.ts +1 -1
- package/dist/components/auth/auth-provider.d.ts +1 -1
- package/dist/components/auth/auth.d.ts +3 -3
- package/dist/components/auth/delete-user/danger-zone.d.ts +2 -2
- package/dist/components/auth/delete-user/delete-account.d.ts +1 -1
- package/dist/components/auth/email/email-changed-email.d.ts +2 -2
- package/dist/components/auth/email/email-verification-email.d.ts +2 -2
- package/dist/components/auth/email/magic-link-email.d.ts +2 -2
- package/dist/components/auth/email/new-device-email.d.ts +2 -2
- package/dist/components/auth/email/organization-invitation-email.d.ts +2 -2
- package/dist/components/auth/email/otp-email.d.ts +2 -2
- package/dist/components/auth/email/password-changed-email.d.ts +2 -2
- package/dist/components/auth/email/reset-password-email.d.ts +2 -2
- package/dist/components/auth/field-separator.d.ts +1 -1
- package/dist/components/auth/forgot-password.d.ts +1 -1
- package/dist/components/auth/magic-link/magic-link-button.d.ts +1 -1
- package/dist/components/auth/magic-link/magic-link.d.ts +2 -2
- package/dist/components/auth/multi-session/manage-account.d.ts +1 -1
- package/dist/components/auth/multi-session/manage-accounts.d.ts +1 -1
- package/dist/components/auth/multi-session/switch-account-submenu-item.d.ts +1 -1
- package/dist/components/auth/organization/delete-organization-dialog.d.ts +1 -1
- package/dist/components/auth/organization/leave-organization-dialog.d.ts +1 -1
- package/dist/components/auth/organization/organization-danger-zone.d.ts +2 -2
- package/dist/components/auth/organization/organization-invitation-row.d.ts +1 -1
- package/dist/components/auth/organization/organization-invitations.d.ts +1 -1
- package/dist/components/auth/organization/organization-logo.d.ts +3 -3
- package/dist/components/auth/organization/organization-member-row.d.ts +1 -1
- package/dist/components/auth/organization/organization-members.d.ts +1 -1
- package/dist/components/auth/organization/organization-people.d.ts +1 -1
- package/dist/components/auth/organization/organization-profile.d.ts +1 -1
- package/dist/components/auth/organization/organization-row.d.ts +1 -1
- package/dist/components/auth/organization/organization-settings.d.ts +2 -2
- package/dist/components/auth/organization/organization-switcher.d.ts +3 -3
- package/dist/components/auth/organization/organization-view-skeleton.d.ts +2 -2
- package/dist/components/auth/organization/organization-view.d.ts +3 -3
- package/dist/components/auth/organization/organization.d.ts +3 -3
- package/dist/components/auth/organization/organizations-settings.d.ts +2 -2
- package/dist/components/auth/organization/organizations.d.ts +1 -1
- package/dist/components/auth/organization/remove-member-dialog.d.ts +1 -1
- package/dist/components/auth/organization/slug-field.d.ts +1 -1
- package/dist/components/auth/organization/user-invitation-row.d.ts +1 -1
- package/dist/components/auth/organization/user-invitations.d.ts +1 -1
- package/dist/components/auth/passkey/passkey-button.d.ts +1 -1
- package/dist/components/auth/passkey/passkey.d.ts +1 -1
- package/dist/components/auth/passkey/passkeys.d.ts +1 -1
- package/dist/components/auth/provider-button.d.ts +2 -2
- package/dist/components/auth/reset-password.d.ts +1 -1
- package/dist/components/auth/settings/account/account-settings.d.ts +2 -2
- package/dist/components/auth/settings/account/change-email.d.ts +1 -1
- package/dist/components/auth/settings/account/user-profile.d.ts +1 -1
- package/dist/components/auth/settings/security/active-session.d.ts +1 -1
- package/dist/components/auth/settings/security/active-sessions.d.ts +1 -1
- package/dist/components/auth/settings/security/change-password.d.ts +1 -1
- package/dist/components/auth/settings/security/linked-account.d.ts +1 -1
- package/dist/components/auth/settings/security/linked-accounts.d.ts +1 -1
- package/dist/components/auth/settings/security/security-settings.d.ts +2 -2
- package/dist/components/auth/settings/settings.d.ts +3 -3
- package/dist/components/auth/sign-in.d.ts +2 -2
- package/dist/components/auth/sign-up.d.ts +2 -2
- package/dist/components/auth/theme/appearance.d.ts +1 -1
- package/dist/components/auth/user/user-avatar.d.ts +3 -3
- package/dist/components/auth/user/user-button.d.ts +2 -2
- package/dist/components/auth/user/user-view.d.ts +3 -3
- package/dist/components/auth/username/sign-in-username.d.ts +2 -2
- package/dist/components/auth/username/username-field.d.ts +1 -1
- package/dist/email.d.ts +8 -8
- package/dist/email.js +211 -8
- package/dist/index.d.ts +21 -21
- package/dist/index.js +1682 -21
- package/dist/lib/auth/api-key-plugin.d.ts +3 -3
- package/dist/lib/auth/auth-plugin.d.ts +5 -5
- package/dist/lib/auth/delete-user-plugin.d.ts +2 -2
- package/dist/lib/auth/magic-link-plugin.d.ts +3 -3
- package/dist/lib/auth/multi-session-plugin.d.ts +3 -3
- package/dist/lib/auth/organization-plugin.d.ts +2 -2
- package/dist/lib/auth/passkey-plugin.d.ts +3 -3
- package/dist/lib/auth/theme-plugin.d.ts +3 -3
- package/dist/lib/auth/username-plugin.d.ts +3 -3
- package/dist/plugins.d.ts +46 -46
- package/dist/plugins.js +2824 -47
- package/dist/user-view--47tVtJ3.js +113 -0
- package/package.json +2 -3
- package/dist/components/auth/additional-field.js +0 -137
- package/dist/components/auth/api-key/api-key-skeleton.js +0 -5
- package/dist/components/auth/api-key/api-key.js +0 -17
- package/dist/components/auth/api-key/api-keys-empty.js +0 -9
- package/dist/components/auth/api-key/api-keys.js +0 -22
- package/dist/components/auth/api-key/create-api-key-dialog.js +0 -44
- package/dist/components/auth/api-key/delete-api-key-dialog.js +0 -17
- package/dist/components/auth/api-key/new-api-key-dialog.js +0 -24
- package/dist/components/auth/api-key/organization-api-keys.js +0 -22
- package/dist/components/auth/auth-provider.js +0 -11
- package/dist/components/auth/auth.js +0 -88
- package/dist/components/auth/delete-user/danger-zone.js +0 -12
- package/dist/components/auth/delete-user/delete-account.js +0 -50
- package/dist/components/auth/email/email-changed-email.js +0 -23
- package/dist/components/auth/email/email-verification-email.js +0 -23
- package/dist/components/auth/email/magic-link-email.js +0 -23
- package/dist/components/auth/email/new-device-email.js +0 -23
- package/dist/components/auth/email/organization-invitation-email.js +0 -23
- package/dist/components/auth/email/otp-email.js +0 -22
- package/dist/components/auth/email/password-changed-email.js +0 -23
- package/dist/components/auth/email/reset-password-email.js +0 -23
- package/dist/components/auth/error-toaster.js +0 -33
- package/dist/components/auth/field-separator.js +0 -5
- package/dist/components/auth/forgot-password.js +0 -37
- package/dist/components/auth/magic-link/magic-link-button.js +0 -34
- package/dist/components/auth/magic-link/magic-link.js +0 -40
- package/dist/components/auth/multi-session/manage-account.js +0 -33
- package/dist/components/auth/multi-session/manage-accounts.js +0 -33
- package/dist/components/auth/multi-session/switch-account-submenu-content.js +0 -25
- package/dist/components/auth/multi-session/switch-account-submenu-item.js +0 -17
- package/dist/components/auth/multi-session/switch-account-submenu.js +0 -24
- package/dist/components/auth/organization/change-organization-logo.js +0 -69
- package/dist/components/auth/organization/create-organization-dialog.js +0 -37
- package/dist/components/auth/organization/delete-organization-dialog.js +0 -25
- package/dist/components/auth/organization/delete-organization-skeleton.js +0 -8
- package/dist/components/auth/organization/delete-organization.js +0 -27
- package/dist/components/auth/organization/invite-member-dialog.js +0 -47
- package/dist/components/auth/organization/leave-organization-dialog.js +0 -21
- package/dist/components/auth/organization/leave-organization.js +0 -16
- package/dist/components/auth/organization/organization-danger-zone.js +0 -23
- package/dist/components/auth/organization/organization-invitation-row-skeleton.js +0 -9
- package/dist/components/auth/organization/organization-invitation-row.js +0 -31
- package/dist/components/auth/organization/organization-invitations-empty.js +0 -13
- package/dist/components/auth/organization/organization-invitations.js +0 -66
- package/dist/components/auth/organization/organization-logo.js +0 -14
- package/dist/components/auth/organization/organization-member-row-skeleton.js +0 -9
- package/dist/components/auth/organization/organization-member-row.js +0 -34
- package/dist/components/auth/organization/organization-members.js +0 -67
- package/dist/components/auth/organization/organization-people.js +0 -11
- package/dist/components/auth/organization/organization-profile.js +0 -34
- package/dist/components/auth/organization/organization-row.js +0 -31
- package/dist/components/auth/organization/organization-settings.js +0 -13
- package/dist/components/auth/organization/organization-switcher.js +0 -48
- package/dist/components/auth/organization/organization-view-skeleton.js +0 -9
- package/dist/components/auth/organization/organization-view.js +0 -31
- package/dist/components/auth/organization/organization.js +0 -60
- package/dist/components/auth/organization/organizations-empty.js +0 -9
- package/dist/components/auth/organization/organizations-settings.js +0 -17
- package/dist/components/auth/organization/organizations.js +0 -21
- package/dist/components/auth/organization/remove-member-dialog.js +0 -20
- package/dist/components/auth/organization/slug-field.js +0 -38
- package/dist/components/auth/organization/user-invitation-row-skeleton.js +0 -8
- package/dist/components/auth/organization/user-invitation-row.js +0 -22
- package/dist/components/auth/organization/user-invitations-empty.js +0 -12
- package/dist/components/auth/organization/user-invitations.js +0 -18
- package/dist/components/auth/passkey/add-passkey-dialog.js +0 -22
- package/dist/components/auth/passkey/delete-passkey-dialog.js +0 -14
- package/dist/components/auth/passkey/passkey-button.js +0 -25
- package/dist/components/auth/passkey/passkey-skeleton.js +0 -5
- package/dist/components/auth/passkey/passkey.js +0 -17
- package/dist/components/auth/passkey/passkeys-empty.js +0 -9
- package/dist/components/auth/passkey/passkeys.js +0 -16
- package/dist/components/auth/provider-button.js +0 -29
- package/dist/components/auth/provider-buttons.js +0 -29
- package/dist/components/auth/reset-password.js +0 -57
- package/dist/components/auth/settings/account/account-settings.js +0 -22
- package/dist/components/auth/settings/account/change-avatar.js +0 -54
- package/dist/components/auth/settings/account/change-email.js +0 -28
- package/dist/components/auth/settings/account/user-profile.js +0 -65
- package/dist/components/auth/settings/security/active-session.js +0 -52
- package/dist/components/auth/settings/security/active-sessions.js +0 -23
- package/dist/components/auth/settings/security/change-password.js +0 -79
- package/dist/components/auth/settings/security/linked-account.js +0 -38
- package/dist/components/auth/settings/security/linked-accounts.js +0 -36
- package/dist/components/auth/settings/security/security-settings.js +0 -20
- package/dist/components/auth/settings/settings.js +0 -53
- package/dist/components/auth/sign-in.js +0 -64
- package/dist/components/auth/sign-out.js +0 -33
- package/dist/components/auth/sign-up.js +0 -98
- package/dist/components/auth/theme/appearance.js +0 -24
- package/dist/components/auth/theme/theme-toggle-item.js +0 -57
- package/dist/components/auth/user/user-avatar.js +0 -32
- package/dist/components/auth/user/user-button.js +0 -40
- package/dist/components/auth/user/user-view.js +0 -27
- package/dist/components/auth/username/sign-in-username.js +0 -88
- package/dist/components/auth/username/username-field.js +0 -45
- package/dist/lib/auth/api-key-plugin.js +0 -12
- package/dist/lib/auth/auth-plugin.js +0 -1
- package/dist/lib/auth/delete-user-plugin.js +0 -7
- package/dist/lib/auth/magic-link-plugin.js +0 -18
- package/dist/lib/auth/multi-session-plugin.js +0 -9
- package/dist/lib/auth/organization-plugin.js +0 -19
- package/dist/lib/auth/passkey-plugin.js +0 -9
- package/dist/lib/auth/theme-plugin.js +0 -26
- package/dist/lib/auth/username-plugin.js +0 -19
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { authMutationKeys as e, getProviderName as t } from "@better-auth-ui/core";
|
|
2
|
+
import { providerIcons as n, useAuth as r, useSession as i, useSignInSocial as a } from "@better-auth-ui/react";
|
|
3
|
+
import { Person as o } from "@gravity-ui/icons";
|
|
4
|
+
import { Avatar as s, Button as c, Separator as l, Skeleton as u, Spinner as d, cn as f } from "@heroui/react";
|
|
5
|
+
import { useMemo as p } from "react";
|
|
6
|
+
import { jsx as m, jsxs as h } from "react/jsx-runtime";
|
|
7
|
+
import { useIsMutating as g } from "@tanstack/react-query";
|
|
8
|
+
//#region src/components/auth/field-separator.tsx
|
|
9
|
+
function _({ children: e }) {
|
|
10
|
+
return /* @__PURE__ */ h("div", {
|
|
11
|
+
className: "flex items-center gap-4",
|
|
12
|
+
children: [
|
|
13
|
+
/* @__PURE__ */ m(l, { className: "flex-1" }),
|
|
14
|
+
/* @__PURE__ */ m("p", {
|
|
15
|
+
className: "text-xs text-muted shrink-0",
|
|
16
|
+
children: e
|
|
17
|
+
}),
|
|
18
|
+
/* @__PURE__ */ m(l, { className: "flex-1" })
|
|
19
|
+
]
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
//#endregion
|
|
23
|
+
//#region src/components/auth/provider-button.tsx
|
|
24
|
+
function v({ provider: i, display: o = "full", variant: s = "tertiary", ...l }) {
|
|
25
|
+
let { authClient: u, baseURL: f, localization: p, redirectTo: _ } = r(), v = `${f}${_}`, { mutate: y, isPending: b } = a(u), x = n[i];
|
|
26
|
+
return /* @__PURE__ */ h(c, {
|
|
27
|
+
variant: s,
|
|
28
|
+
isPending: g({ mutationKey: e.signIn.all }) + g({ mutationKey: e.signUp.all }) > 0,
|
|
29
|
+
onPress: () => y({
|
|
30
|
+
provider: i,
|
|
31
|
+
callbackURL: v
|
|
32
|
+
}),
|
|
33
|
+
...l,
|
|
34
|
+
"aria-label": t(i),
|
|
35
|
+
children: [b ? /* @__PURE__ */ m(d, {
|
|
36
|
+
color: "current",
|
|
37
|
+
size: "sm"
|
|
38
|
+
}) : /* @__PURE__ */ m(x, {}), o === "full" ? p.auth.continueWith.replace("{{provider}}", t(i)) : o === "name" ? t(i) : null]
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
//#endregion
|
|
42
|
+
//#region src/components/auth/provider-buttons.tsx
|
|
43
|
+
function y({ socialLayout: e = "auto" }) {
|
|
44
|
+
let { socialProviders: t } = r(), n = p(() => e === "auto" ? t?.length && t.length >= 4 ? "horizontal" : "vertical" : e, [e, t?.length]);
|
|
45
|
+
return /* @__PURE__ */ m("div", {
|
|
46
|
+
className: f("gap-3", n === "grid" && "grid grid-cols-2", n === "vertical" && "flex flex-col", n === "horizontal" && "flex flex-wrap"),
|
|
47
|
+
children: t?.map((e) => /* @__PURE__ */ m(v, {
|
|
48
|
+
provider: e,
|
|
49
|
+
display: n === "vertical" ? "full" : n === "grid" ? "name" : "icon",
|
|
50
|
+
className: f("w-full", n === "horizontal" && "flex-1")
|
|
51
|
+
}, e))
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
//#endregion
|
|
55
|
+
//#region src/components/auth/user/user-avatar.tsx
|
|
56
|
+
function b({ className: e, fallback: t, isPending: n, user: a, size: c = "sm", style: l, ...d }) {
|
|
57
|
+
let { authClient: p } = r(), { data: g, isPending: _ } = i(p, { enabled: !a && !n });
|
|
58
|
+
if ((n || _) && !a) return /* @__PURE__ */ m(u, {
|
|
59
|
+
className: f("rounded-full", c === "sm" ? "size-8" : c === "md" ? "size-10" : "size-12", e),
|
|
60
|
+
style: l
|
|
61
|
+
});
|
|
62
|
+
let v = a ?? g?.user, y = (v?.username || v?.name || v?.email)?.slice(0, 2).toUpperCase();
|
|
63
|
+
return /* @__PURE__ */ h(s, {
|
|
64
|
+
size: c,
|
|
65
|
+
className: f("rounded-full", e),
|
|
66
|
+
style: l,
|
|
67
|
+
...d,
|
|
68
|
+
children: [/* @__PURE__ */ m(s.Image, {
|
|
69
|
+
alt: v?.displayUsername || v?.name || v?.email,
|
|
70
|
+
src: v?.image ?? void 0
|
|
71
|
+
}), /* @__PURE__ */ m(s.Fallback, {
|
|
72
|
+
className: f(c === "lg" ? "text-xl" : c === "md" ? "text-base" : "text-sm"),
|
|
73
|
+
delayMs: v?.image ? 600 : void 0,
|
|
74
|
+
children: t || y || /* @__PURE__ */ m(o, { className: "size-4" })
|
|
75
|
+
})]
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
//#endregion
|
|
79
|
+
//#region src/components/auth/user/user-view.tsx
|
|
80
|
+
function x({ className: e, isPending: t, size: n = "md", hideSubtitle: a = !1, user: o, ...s }) {
|
|
81
|
+
let { authClient: c } = r(), { data: l, isPending: d } = i(c, { enabled: !o && !t }), p = o ?? l?.user;
|
|
82
|
+
return (t || d) && !o ? /* @__PURE__ */ h("div", {
|
|
83
|
+
className: f("flex items-center gap-2 min-w-0", e),
|
|
84
|
+
...s,
|
|
85
|
+
children: [/* @__PURE__ */ m(b, {
|
|
86
|
+
isPending: !0,
|
|
87
|
+
className: n === "sm" ? "size-5 [&>span]:text-xs" : void 0,
|
|
88
|
+
size: n === "lg" ? "md" : "sm"
|
|
89
|
+
}), /* @__PURE__ */ h("div", {
|
|
90
|
+
className: "flex flex-col gap-1 min-w-0",
|
|
91
|
+
children: [/* @__PURE__ */ m(u, { className: "h-3.5 w-24 rounded-lg" }), a ? null : /* @__PURE__ */ m(u, { className: "h-3 w-32 rounded-lg" })]
|
|
92
|
+
})]
|
|
93
|
+
}) : /* @__PURE__ */ h("div", {
|
|
94
|
+
className: f("flex items-center gap-2 min-w-0", e),
|
|
95
|
+
...s,
|
|
96
|
+
children: [/* @__PURE__ */ m(b, {
|
|
97
|
+
className: n === "sm" ? "size-5 [&>span]:text-xs" : void 0,
|
|
98
|
+
user: p,
|
|
99
|
+
size: n === "lg" ? "md" : "sm"
|
|
100
|
+
}), /* @__PURE__ */ h("div", {
|
|
101
|
+
className: "flex flex-col min-w-0",
|
|
102
|
+
children: [/* @__PURE__ */ m("p", {
|
|
103
|
+
className: "text-foreground text-sm font-medium leading-tight truncate",
|
|
104
|
+
children: p?.displayUsername || p?.name || p?.email
|
|
105
|
+
}), !a && (p?.displayUsername || p?.name) ? /* @__PURE__ */ m("p", {
|
|
106
|
+
className: "text-muted text-xs leading-tight truncate overflow-x-hidden",
|
|
107
|
+
children: p?.email
|
|
108
|
+
}) : null]
|
|
109
|
+
})]
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
//#endregion
|
|
113
|
+
export { _ as a, v as i, b as n, y as r, x as t };
|
package/package.json
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@better-auth-ui/heroui",
|
|
3
|
-
"version": "1.6.
|
|
3
|
+
"version": "1.6.10",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"scripts": {
|
|
6
|
-
"build": "
|
|
7
|
-
"styles": "mkdir -p dist && cp src/styles.css dist/styles.css",
|
|
6
|
+
"build": "vite build",
|
|
8
7
|
"test": "vitest"
|
|
9
8
|
},
|
|
10
9
|
"files": [
|
|
@@ -1,137 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { resolveInputType } from "@better-auth-ui/core";
|
|
3
|
-
import { useAuth } from "@better-auth-ui/react";
|
|
4
|
-
import { Check, Copy } from "@gravity-ui/icons";
|
|
5
|
-
import { Button, Calendar, Checkbox, ComboBox, DateField, DatePicker, FieldError, Input, InputGroup, Label, ListBox, NumberField, Select, Slider, Switch, TextArea, TextField, TimeField, toast } from "@heroui/react";
|
|
6
|
-
import { fromDate, getLocalTimeZone, parseDate, parseDateTime, toCalendarDate, toCalendarDateTime } from "@internationalized/date";
|
|
7
|
-
import { useRef, useState } from "react";
|
|
8
|
-
/** Convert a `defaultValue` into a `CalendarDate` for HeroUI date inputs. */
|
|
9
|
-
function toDateValue(value) {
|
|
10
|
-
if (value instanceof Date) {
|
|
11
|
-
return toCalendarDate(fromDate(value, getLocalTimeZone()));
|
|
12
|
-
}
|
|
13
|
-
if (typeof value === "string") {
|
|
14
|
-
try {
|
|
15
|
-
return parseDate(value.slice(0, 10));
|
|
16
|
-
}
|
|
17
|
-
catch {
|
|
18
|
-
return undefined;
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
return undefined;
|
|
22
|
-
}
|
|
23
|
-
/** Convert a `defaultValue` into a `CalendarDateTime` for datetime inputs. */
|
|
24
|
-
function toDateTimeValue(value) {
|
|
25
|
-
if (value instanceof Date) {
|
|
26
|
-
return toCalendarDateTime(fromDate(value, getLocalTimeZone()));
|
|
27
|
-
}
|
|
28
|
-
if (typeof value === "string") {
|
|
29
|
-
try {
|
|
30
|
-
// Strip trailing `Z` or timezone offset for `parseDateTime`.
|
|
31
|
-
return parseDateTime(value.replace(/(Z|[+-]\d{2}:?\d{2})$/, ""));
|
|
32
|
-
}
|
|
33
|
-
catch {
|
|
34
|
-
return undefined;
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
return undefined;
|
|
38
|
-
}
|
|
39
|
-
/**
|
|
40
|
-
* Icon-only copy button used as an `InputGroup.Suffix`. `getValue` is invoked
|
|
41
|
-
* lazily on click so the button copies the input's *live* value rather than a
|
|
42
|
-
* stale snapshot — important when paired with editable inputs.
|
|
43
|
-
*/
|
|
44
|
-
function CopyButton({ getValue, isDisabled }) {
|
|
45
|
-
const { localization } = useAuth();
|
|
46
|
-
const [copied, setCopied] = useState(false);
|
|
47
|
-
async function handleCopy() {
|
|
48
|
-
const value = getValue();
|
|
49
|
-
if (!value)
|
|
50
|
-
return;
|
|
51
|
-
try {
|
|
52
|
-
await navigator.clipboard.writeText(value);
|
|
53
|
-
setCopied(true);
|
|
54
|
-
setTimeout(() => setCopied(false), 1500);
|
|
55
|
-
}
|
|
56
|
-
catch (error) {
|
|
57
|
-
toast.danger(error instanceof Error ? error.message : String(error));
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
return (_jsx(Button, { isIconOnly: true, "aria-label": localization.settings.copyToClipboard, size: "sm", variant: "ghost", isDisabled: isDisabled, onPress: handleCopy, children: copied ? _jsx(Check, { className: "size-4" }) : _jsx(Copy, { className: "size-4" }) }));
|
|
61
|
-
}
|
|
62
|
-
/** Renders a single additional user field via HeroUI v3 components. */
|
|
63
|
-
export function AdditionalField({ name, field, isPending, variant }) {
|
|
64
|
-
const { localization } = useAuth();
|
|
65
|
-
const inputType = resolveInputType(field);
|
|
66
|
-
const inputVariant = variant === "transparent" ? "primary" : "secondary";
|
|
67
|
-
// Used by `inputType: "input"` with `copyable: true` so the copy button
|
|
68
|
-
// reads the input's *live* value rather than a stale `defaultValue`.
|
|
69
|
-
const inputRef = useRef(null);
|
|
70
|
-
if (field.render) {
|
|
71
|
-
return _jsx(_Fragment, { children: field.render({ name, field, isPending, variant }) });
|
|
72
|
-
}
|
|
73
|
-
if (inputType === "hidden") {
|
|
74
|
-
return (_jsx("input", { type: "hidden", name: name, value: field.defaultValue == null
|
|
75
|
-
? ""
|
|
76
|
-
: field.defaultValue instanceof Date
|
|
77
|
-
? field.defaultValue.toISOString()
|
|
78
|
-
: String(field.defaultValue) }));
|
|
79
|
-
}
|
|
80
|
-
if (inputType === "textarea") {
|
|
81
|
-
return (_jsxs(TextField, { name: name, defaultValue: field.defaultValue == null ? undefined : String(field.defaultValue), isDisabled: isPending, isReadOnly: field.readOnly, children: [_jsx(Label, { children: field.label }), _jsx(TextArea, { placeholder: field.placeholder, required: field.required, variant: inputVariant }), _jsx(FieldError, {})] }));
|
|
82
|
-
}
|
|
83
|
-
if (inputType === "number") {
|
|
84
|
-
const maxFractionDigits = field.formatOptions?.maximumFractionDigits;
|
|
85
|
-
return (_jsxs(NumberField, { name: name, defaultValue: typeof field.defaultValue === "number"
|
|
86
|
-
? field.defaultValue
|
|
87
|
-
: field.defaultValue != null && field.defaultValue !== ""
|
|
88
|
-
? Number(field.defaultValue)
|
|
89
|
-
: undefined, minValue: field.min, maxValue: field.max, step: field.step ?? (maxFractionDigits ? 1 / 10 ** maxFractionDigits : 1), formatOptions: field.formatOptions, isDisabled: isPending, isReadOnly: field.readOnly, variant: inputVariant, children: [_jsx(Label, { children: field.label }), _jsxs(NumberField.Group, { children: [_jsx(NumberField.DecrementButton, {}), _jsx(NumberField.Input, { placeholder: field.placeholder, required: field.required }), _jsx(NumberField.IncrementButton, {})] }), _jsx(FieldError, {})] }));
|
|
90
|
-
}
|
|
91
|
-
if (inputType === "slider") {
|
|
92
|
-
const maxFractionDigits = field.formatOptions?.maximumFractionDigits;
|
|
93
|
-
return (_jsxs(Slider, { defaultValue: typeof field.defaultValue === "number"
|
|
94
|
-
? field.defaultValue
|
|
95
|
-
: field.defaultValue != null
|
|
96
|
-
? Number(field.defaultValue)
|
|
97
|
-
: undefined, minValue: field.min ?? 0, maxValue: field.max ?? 100, step: field.step ?? (maxFractionDigits ? 1 / 10 ** maxFractionDigits : 1), formatOptions: field.formatOptions, isDisabled: isPending || field.readOnly, className: "flex flex-col gap-2", children: [_jsxs("div", { className: "flex items-center justify-between gap-2", children: [_jsx(Label, { children: field.label }), _jsx(Slider.Output, { className: "text-sm text-muted" })] }), _jsxs(Slider.Track, { children: [_jsx(Slider.Fill, {}), _jsx(Slider.Thumb, { name: name })] })] }));
|
|
98
|
-
}
|
|
99
|
-
if (inputType === "switch") {
|
|
100
|
-
return (_jsxs(Switch, { name: name, defaultSelected: field.defaultValue === true || field.defaultValue === "true", isDisabled: isPending, isReadOnly: field.readOnly, children: [_jsx(Switch.Control, { children: _jsx(Switch.Thumb, {}) }), _jsx(Label, { children: field.label })] }));
|
|
101
|
-
}
|
|
102
|
-
if (inputType === "checkbox") {
|
|
103
|
-
return (_jsxs(Checkbox, { name: name, defaultSelected: field.defaultValue === true || field.defaultValue === "true", isDisabled: isPending, isReadOnly: field.readOnly, isRequired: field.required, variant: inputVariant, children: [_jsx(Checkbox.Control, { children: _jsx(Checkbox.Indicator, {}) }), _jsx(Checkbox.Content, { children: _jsx(Label, { children: field.label }) })] }));
|
|
104
|
-
}
|
|
105
|
-
if (inputType === "select") {
|
|
106
|
-
return (_jsxs(Select, { className: "[&[data-required=true]>.label]:after:content-none", name: name, defaultValue: field.defaultValue != null ? String(field.defaultValue) : undefined, placeholder: field.placeholder, isDisabled: isPending || field.readOnly, isRequired: field.required, variant: inputVariant, fullWidth: true, children: [_jsx(Label, { children: field.label }), _jsxs(Select.Trigger, { children: [_jsx(Select.Value, {}), _jsx(Select.Indicator, {})] }), _jsx(Select.Popover, { children: _jsx(ListBox, { children: field.options?.map((option) => (_jsxs(ListBox.Item, { id: option.value, textValue: typeof option.label === "string" ? option.label : option.value, children: [option.label, _jsx(ListBox.ItemIndicator, {})] }, option.value))) }) }), _jsx(FieldError, {})] }));
|
|
107
|
-
}
|
|
108
|
-
if (inputType === "combobox") {
|
|
109
|
-
return (_jsxs(ComboBox, { className: "[&[data-required=true]>.label]:after:content-none", name: name, defaultSelectedKey: field.defaultValue != null ? String(field.defaultValue) : undefined, isDisabled: isPending, isReadOnly: field.readOnly, isRequired: field.required, variant: inputVariant, fullWidth: true, children: [_jsx(Label, { children: field.label }), _jsxs(ComboBox.InputGroup, { children: [_jsx(Input, { placeholder: field.placeholder }), _jsx(ComboBox.Trigger, {})] }), _jsx(ComboBox.Popover, { children: _jsx(ListBox, { children: field.options?.map((option) => (_jsxs(ListBox.Item, { id: option.value, textValue: typeof option.label === "string" ? option.label : option.value, children: [option.label, _jsx(ListBox.ItemIndicator, {})] }, option.value))) }) }), _jsx(FieldError, {})] }));
|
|
110
|
-
}
|
|
111
|
-
if (inputType === "date" || inputType === "datetime") {
|
|
112
|
-
const isDateTime = inputType === "datetime";
|
|
113
|
-
const defaultValue = isDateTime
|
|
114
|
-
? toDateTimeValue(field.defaultValue)
|
|
115
|
-
: toDateValue(field.defaultValue);
|
|
116
|
-
return (_jsx(DatePicker, { className: "w-full [&[data-required=true]>.label]:after:content-none", name: name, defaultValue: defaultValue, granularity: isDateTime ? "minute" : "day", isDisabled: isPending, isReadOnly: field.readOnly, isRequired: field.required, children: ({ state }) => (_jsxs(_Fragment, { children: [_jsx(Label, { children: field.label }), _jsxs(DateField.Group, { variant: inputVariant, fullWidth: true, children: [_jsx(DateField.Input, { children: (segment) => _jsx(DateField.Segment, { segment: segment }) }), _jsx(DateField.Suffix, { children: _jsx(DatePicker.Trigger, { children: _jsx(DatePicker.TriggerIndicator, {}) }) })] }), _jsx(FieldError, {}), _jsxs(DatePicker.Popover, { className: "flex flex-col gap-3", children: [_jsxs(Calendar, { "aria-label": typeof field.label === "string" ? field.label : name, children: [_jsxs(Calendar.Header, { children: [_jsxs(Calendar.YearPickerTrigger, { children: [_jsx(Calendar.YearPickerTriggerHeading, {}), _jsx(Calendar.YearPickerTriggerIndicator, {})] }), _jsx(Calendar.NavButton, { slot: "previous" }), _jsx(Calendar.NavButton, { slot: "next" })] }), _jsxs(Calendar.Grid, { children: [_jsx(Calendar.GridHeader, { children: (day) => _jsx(Calendar.HeaderCell, { children: day }) }), _jsx(Calendar.GridBody, { children: (date) => _jsx(Calendar.Cell, { date: date }) })] }), _jsx(Calendar.YearPickerGrid, { children: _jsx(Calendar.YearPickerGridBody, { children: ({ year }) => _jsx(Calendar.YearPickerCell, { year: year }) }) })] }), isDateTime && (_jsxs("div", { className: "flex items-center justify-between", children: [_jsx(Label, { children: localization.settings.time }), _jsx(TimeField, { "aria-label": localization.settings.time, granularity: "minute", value: state.timeValue, onChange: (v) => state.setTimeValue(v), children: _jsx(TimeField.Group, { variant: "secondary", children: _jsx(TimeField.Input, { children: (segment) => _jsx(TimeField.Segment, { segment: segment }) }) }) })] }))] })] })) }));
|
|
117
|
-
}
|
|
118
|
-
// inputType === "input"
|
|
119
|
-
const hasPrefix = field.prefix != null;
|
|
120
|
-
const hasSuffix = field.suffix != null || field.copyable;
|
|
121
|
-
// When `inputType: "input"` is paired with `type: "number"`, restrict the
|
|
122
|
-
// native input to numbers. `formatOptions.maximumFractionDigits` enables
|
|
123
|
-
// fractional input via `step`.
|
|
124
|
-
const isNumeric = field.type === "number";
|
|
125
|
-
const maxFractionDigits = field.formatOptions?.maximumFractionDigits;
|
|
126
|
-
const nativeInputType = isNumeric ? "number" : undefined;
|
|
127
|
-
const nativeInputMode = isNumeric
|
|
128
|
-
? maxFractionDigits
|
|
129
|
-
? "decimal"
|
|
130
|
-
: "numeric"
|
|
131
|
-
: undefined;
|
|
132
|
-
const nativeStep = maxFractionDigits ? 1 / 10 ** maxFractionDigits : undefined;
|
|
133
|
-
if (hasPrefix || hasSuffix) {
|
|
134
|
-
return (_jsxs(TextField, { name: name, defaultValue: field.defaultValue == null ? undefined : String(field.defaultValue), isDisabled: isPending, isReadOnly: field.readOnly, children: [_jsx(Label, { children: field.label }), _jsxs(InputGroup, { variant: inputVariant, children: [hasPrefix && _jsx(InputGroup.Prefix, { children: field.prefix }), _jsx(InputGroup.Input, { ref: inputRef, placeholder: field.placeholder, required: field.required, type: nativeInputType, inputMode: nativeInputMode, step: nativeStep }), field.copyable ? (_jsx(InputGroup.Suffix, { className: "px-0", children: _jsx(CopyButton, { getValue: () => inputRef.current?.value, isDisabled: isPending }) })) : (field.suffix != null && (_jsx(InputGroup.Suffix, { children: field.suffix })))] }), _jsx(FieldError, {})] }));
|
|
135
|
-
}
|
|
136
|
-
return (_jsxs(TextField, { name: name, defaultValue: field.defaultValue == null ? undefined : String(field.defaultValue), isDisabled: isPending, isReadOnly: field.readOnly, children: [_jsx(Label, { children: field.label }), _jsx(Input, { placeholder: field.placeholder, required: field.required, variant: inputVariant, type: nativeInputType, inputMode: nativeInputMode, step: nativeStep }), _jsx(FieldError, {})] }));
|
|
137
|
-
}
|
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { Skeleton } from "@heroui/react";
|
|
3
|
-
export function ApiKeySkeleton() {
|
|
4
|
-
return (_jsx("div", { className: "flex items-center justify-between", children: _jsxs("div", { className: "flex items-center gap-3", children: [_jsx(Skeleton, { className: "size-10 rounded-xl" }), _jsxs("div", { className: "flex flex-col gap-1", children: [_jsx(Skeleton, { className: "h-4 w-28 rounded-lg" }), _jsx(Skeleton, { className: "h-3 w-36 rounded-lg" }), _jsx(Skeleton, { className: "h-3 w-32 rounded-lg" })] })] }) }));
|
|
5
|
-
}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import { useAuth, useAuthPlugin } from "@better-auth-ui/react";
|
|
3
|
-
import { Key, Xmark } from "@gravity-ui/icons";
|
|
4
|
-
import { Button } from "@heroui/react";
|
|
5
|
-
import { useState } from "react";
|
|
6
|
-
import { apiKeyPlugin } from "../../../lib/auth/api-key-plugin";
|
|
7
|
-
import { DeleteApiKeyDialog } from "./delete-api-key-dialog";
|
|
8
|
-
export function ApiKey({ apiKey, hideDelete, organizationId }) {
|
|
9
|
-
const { localization } = useAuth();
|
|
10
|
-
const { localization: apiKeyLocalization } = useAuthPlugin(apiKeyPlugin);
|
|
11
|
-
const [deleteOpen, setDeleteOpen] = useState(false);
|
|
12
|
-
const preview = `${apiKey.start}${"*".repeat(16)}`;
|
|
13
|
-
return (_jsxs("div", { className: "flex items-center gap-3", children: [_jsx("div", { className: "flex size-10 shrink-0 items-center justify-center rounded-xl bg-surface-secondary", children: _jsx(Key, { className: "size-4.5" }) }), _jsxs("div", { className: "flex flex-col min-w-0", children: [_jsx("span", { className: "text-sm font-medium leading-tight truncate", children: apiKey.name || apiKeyLocalization.apiKey }), _jsx("span", { className: "text-xs text-muted font-mono truncate", children: preview }), _jsx("span", { className: "text-xs text-muted", children: new Date(apiKey.createdAt).toLocaleString(undefined, {
|
|
14
|
-
dateStyle: "medium",
|
|
15
|
-
timeStyle: "short"
|
|
16
|
-
}) })] }), !hideDelete && (_jsxs(_Fragment, { children: [_jsxs(Button, { className: "ml-auto shrink-0", variant: "outline", size: "sm", onPress: () => setDeleteOpen(true), "aria-label": apiKeyLocalization.deleteApiKey, children: [_jsx(Xmark, {}), localization.settings.delete] }), _jsx(DeleteApiKeyDialog, { isOpen: deleteOpen, onOpenChange: setDeleteOpen, apiKey: apiKey, organizationId: organizationId })] }))] }));
|
|
17
|
-
}
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useAuthPlugin } from "@better-auth-ui/react";
|
|
3
|
-
import { Key } from "@gravity-ui/icons";
|
|
4
|
-
import { Button } from "@heroui/react";
|
|
5
|
-
import { apiKeyPlugin } from "../../../lib/auth/api-key-plugin";
|
|
6
|
-
export function ApiKeysEmpty({ onCreatePress, hideCreate }) {
|
|
7
|
-
const { localization: apiKeyLocalization } = useAuthPlugin(apiKeyPlugin);
|
|
8
|
-
return (_jsxs("div", { className: "flex flex-col items-center justify-center gap-4", children: [_jsx("div", { className: "flex size-10 items-center justify-center rounded-xl bg-surface-secondary", children: _jsx(Key, { className: "size-4.5" }) }), _jsxs("div", { className: "flex flex-col items-center justify-center gap-1 text-center", children: [_jsx("p", { className: "text-sm font-semibold", children: apiKeyLocalization.noApiKeys }), _jsx("p", { className: "text-muted text-xs", children: apiKeyLocalization.apiKeysDescription })] }), !hideCreate && (_jsx(Button, { size: "sm", onPress: onCreatePress, children: apiKeyLocalization.createApiKey }))] }));
|
|
9
|
-
}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useAuth, useAuthPlugin, useListApiKeys } from "@better-auth-ui/react";
|
|
3
|
-
import { Button, Card, cn } from "@heroui/react";
|
|
4
|
-
import { useState } from "react";
|
|
5
|
-
import { apiKeyPlugin } from "../../../lib/auth/api-key-plugin";
|
|
6
|
-
import { ApiKey } from "./api-key";
|
|
7
|
-
import { ApiKeySkeleton } from "./api-key-skeleton";
|
|
8
|
-
import { ApiKeysEmpty } from "./api-keys-empty";
|
|
9
|
-
import { CreateApiKeyDialog } from "./create-api-key-dialog";
|
|
10
|
-
export function ApiKeys({ className, variant, organizationId, isPending: isPendingProp, hideCreate, hideDelete }) {
|
|
11
|
-
const { authClient } = useAuth();
|
|
12
|
-
const { localization: apiKeyLocalization } = useAuthPlugin(apiKeyPlugin);
|
|
13
|
-
const { data: listData, isPending: isListPending } = useListApiKeys(authClient, {
|
|
14
|
-
enabled: !isPendingProp,
|
|
15
|
-
...(organizationId
|
|
16
|
-
? { query: { organizationId, configId: "organization" } }
|
|
17
|
-
: {})
|
|
18
|
-
});
|
|
19
|
-
const isPending = isPendingProp || isListPending;
|
|
20
|
-
const [createOpen, setCreateOpen] = useState(false);
|
|
21
|
-
return (_jsxs("div", { className: cn("flex flex-col gap-3", className), children: [_jsxs("div", { className: "flex items-end justify-between gap-3", children: [_jsx("h2", { className: "text-sm font-semibold truncate", children: apiKeyLocalization.apiKeys }), !hideCreate && (_jsx(Button, { className: "shrink-0", size: "sm", isDisabled: isPending, onPress: () => setCreateOpen(true), children: apiKeyLocalization.createApiKey }))] }), _jsx(Card, { variant: variant, children: _jsx(Card.Content, { children: isPending ? (_jsx(ApiKeySkeleton, {})) : !listData?.apiKeys.length ? (_jsx(ApiKeysEmpty, { onCreatePress: () => setCreateOpen(true), hideCreate: hideCreate })) : (listData?.apiKeys.map((key, index) => (_jsxs("div", { children: [index > 0 && (_jsx("div", { className: "border-b border-dashed -mx-4 my-4" })), _jsx(ApiKey, { apiKey: key, hideDelete: hideDelete, organizationId: organizationId })] }, key.id)))) }) }), !hideCreate && (_jsx(CreateApiKeyDialog, { isOpen: createOpen, onOpenChange: setCreateOpen, organizationId: organizationId }))] }));
|
|
22
|
-
}
|
|
@@ -1,44 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
-
import { useAuth, useAuthPlugin, useCreateApiKey } from "@better-auth-ui/react";
|
|
3
|
-
import { Key } from "@gravity-ui/icons";
|
|
4
|
-
import { AlertDialog, Button, FieldError, Form, Input, Label, Spinner, TextField } from "@heroui/react";
|
|
5
|
-
import { useState } from "react";
|
|
6
|
-
import { apiKeyPlugin } from "../../../lib/auth/api-key-plugin";
|
|
7
|
-
import { NewApiKeyDialog } from "./new-api-key-dialog";
|
|
8
|
-
export function CreateApiKeyDialog({ isOpen, onOpenChange, organizationId }) {
|
|
9
|
-
const { authClient, localization } = useAuth();
|
|
10
|
-
const { localization: apiKeyLocalization } = useAuthPlugin(apiKeyPlugin);
|
|
11
|
-
const { mutate: createApiKey, isPending: isCreating } = useCreateApiKey(authClient);
|
|
12
|
-
const [isNewKeyDialogOpen, setIsNewKeyDialogOpen] = useState(false);
|
|
13
|
-
const [keyName, setKeyName] = useState(null);
|
|
14
|
-
const [secretKey, setSecretKey] = useState(null);
|
|
15
|
-
const handleOpenChange = (open) => {
|
|
16
|
-
if (!open) {
|
|
17
|
-
setKeyName(null);
|
|
18
|
-
setSecretKey(null);
|
|
19
|
-
}
|
|
20
|
-
onOpenChange(open);
|
|
21
|
-
};
|
|
22
|
-
const handleSubmit = (e) => {
|
|
23
|
-
e.preventDefault();
|
|
24
|
-
const formData = new FormData(e.target);
|
|
25
|
-
const name = formData.get("name")?.trim();
|
|
26
|
-
const payload = name || organizationId
|
|
27
|
-
? {
|
|
28
|
-
...(name ? { name } : {}),
|
|
29
|
-
...(organizationId
|
|
30
|
-
? { organizationId, configId: "organization" }
|
|
31
|
-
: {})
|
|
32
|
-
}
|
|
33
|
-
: undefined;
|
|
34
|
-
createApiKey(payload, {
|
|
35
|
-
onSuccess: (result) => {
|
|
36
|
-
handleOpenChange(false);
|
|
37
|
-
setKeyName(name);
|
|
38
|
-
setSecretKey(result.key);
|
|
39
|
-
setIsNewKeyDialogOpen(true);
|
|
40
|
-
}
|
|
41
|
-
});
|
|
42
|
-
};
|
|
43
|
-
return (_jsxs(_Fragment, { children: [_jsx(AlertDialog.Backdrop, { isOpen: isOpen, onOpenChange: handleOpenChange, 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(Key, {}) }), _jsx(AlertDialog.Heading, { children: apiKeyLocalization.createApiKey })] }), _jsxs(AlertDialog.Body, { className: "overflow-visible", children: [_jsx("p", { className: "text-muted text-sm", children: apiKeyLocalization.apiKeysDescription }), _jsxs(TextField, { className: "mt-4", id: "name", name: "name", isDisabled: isCreating, children: [_jsx(Label, { children: apiKeyLocalization.name }), _jsx(Input, { autoFocus: true, placeholder: localization.settings.optional, variant: "secondary" }), _jsx(FieldError, {})] })] }), _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" }), apiKeyLocalization.createApiKey] })] })] }) }) }) }), _jsx(NewApiKeyDialog, { isOpen: isNewKeyDialogOpen, onOpenChange: setIsNewKeyDialogOpen, secretKey: secretKey, name: keyName })] }));
|
|
44
|
-
}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useAuth, useAuthPlugin, useDeleteApiKey } from "@better-auth-ui/react";
|
|
3
|
-
import { Key } from "@gravity-ui/icons";
|
|
4
|
-
import { AlertDialog, Button, Input, Label, Spinner, TextField } from "@heroui/react";
|
|
5
|
-
import { apiKeyPlugin } from "../../../lib/auth/api-key-plugin";
|
|
6
|
-
export function DeleteApiKeyDialog({ isOpen, onOpenChange, apiKey, organizationId }) {
|
|
7
|
-
const { authClient, localization } = useAuth();
|
|
8
|
-
const { localization: apiKeyLocalization } = useAuthPlugin(apiKeyPlugin);
|
|
9
|
-
const preview = `${apiKey.start}${"*".repeat(16)}`;
|
|
10
|
-
const { mutate: deleteApiKey, isPending: isDeleting } = useDeleteApiKey(authClient, {
|
|
11
|
-
onSuccess: () => onOpenChange(false)
|
|
12
|
-
});
|
|
13
|
-
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(Key, {}) }), _jsx(AlertDialog.Heading, { children: apiKeyLocalization.deleteApiKey })] }), _jsxs(AlertDialog.Body, { className: "flex flex-col gap-4 overflow-visible", children: [_jsx("p", { className: "text-muted text-sm", children: apiKeyLocalization.deleteApiKeyWarning }), _jsxs(TextField, { value: preview, className: "font-mono text-xs", variant: "secondary", children: [_jsx(Label, { children: apiKey.name || apiKeyLocalization.apiKey }), _jsx(Input, { readOnly: true, className: "font-mono text-xs" })] })] }), _jsxs(AlertDialog.Footer, { children: [_jsx(Button, { slot: "close", variant: "tertiary", isDisabled: isDeleting, children: localization.settings.cancel }), _jsxs(Button, { variant: "danger", onPress: () => deleteApiKey({
|
|
14
|
-
keyId: apiKey.id,
|
|
15
|
-
...(organizationId ? { configId: "organization" } : {})
|
|
16
|
-
}), isPending: isDeleting, children: [isDeleting && _jsx(Spinner, { color: "current", size: "sm" }), apiKeyLocalization.deleteApiKey] })] })] }) }) }));
|
|
17
|
-
}
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useAuth, useAuthPlugin } from "@better-auth-ui/react";
|
|
3
|
-
import { Check, Copy, Key } from "@gravity-ui/icons";
|
|
4
|
-
import { AlertDialog, Button, InputGroup, Label, TextField, toast } from "@heroui/react";
|
|
5
|
-
import { useState } from "react";
|
|
6
|
-
import { apiKeyPlugin } from "../../../lib/auth/api-key-plugin";
|
|
7
|
-
export function NewApiKeyDialog({ isOpen, onOpenChange, name, secretKey }) {
|
|
8
|
-
const { localization } = useAuth();
|
|
9
|
-
const { localization: apiKeyLocalization } = useAuthPlugin(apiKeyPlugin);
|
|
10
|
-
const [copied, setCopied] = useState(false);
|
|
11
|
-
const copySecretKey = async () => {
|
|
12
|
-
if (!secretKey)
|
|
13
|
-
return;
|
|
14
|
-
try {
|
|
15
|
-
await navigator.clipboard.writeText(secretKey);
|
|
16
|
-
setCopied(true);
|
|
17
|
-
setTimeout(() => setCopied(false), 1500);
|
|
18
|
-
}
|
|
19
|
-
catch (error) {
|
|
20
|
-
toast.danger(error instanceof Error ? error.message : String(error));
|
|
21
|
-
}
|
|
22
|
-
};
|
|
23
|
-
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: "warning", children: _jsx(Key, {}) }), _jsx(AlertDialog.Heading, { children: apiKeyLocalization.newApiKey })] }), _jsxs(AlertDialog.Body, { className: "flex flex-col gap-4 overflow-visible", children: [_jsx("p", { className: "text-muted text-sm", children: apiKeyLocalization.newApiKeyWarning }), _jsxs(TextField, { value: secretKey ?? "", className: "font-mono text-xs", children: [_jsx(Label, { children: name || apiKeyLocalization.apiKey }), _jsxs(InputGroup, { variant: "secondary", children: [_jsx(InputGroup.Input, { readOnly: true, className: "font-mono text-xs" }), _jsx(InputGroup.Suffix, { className: "px-0", children: _jsx(Button, { isIconOnly: true, "aria-label": localization.settings.copyToClipboard, size: "sm", variant: "ghost", onPress: copySecretKey, children: copied ? _jsx(Check, {}) : _jsx(Copy, {}) }) })] })] })] }), _jsx(AlertDialog.Footer, { children: _jsx(Button, { slot: "close", children: apiKeyLocalization.dismissNewKey }) })] }) }) }));
|
|
24
|
-
}
|
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { useActiveOrganization, useAuth, useListOrganizationMembers, useSession } from "@better-auth-ui/react";
|
|
3
|
-
import { ApiKeys } from "./api-keys";
|
|
4
|
-
/**
|
|
5
|
-
* {@link ApiKeys} scoped to the active organization.
|
|
6
|
-
*
|
|
7
|
-
* Hidden for members whose role isn't `owner`. Better Auth's
|
|
8
|
-
* `/organization/has-permission` endpoint isn't usable for `apiKey:*` checks
|
|
9
|
-
* (it doesn't pass `allowCreatorAllPermissions` and the default org AC has no
|
|
10
|
-
* `apiKey` statements), so we gate on role directly.
|
|
11
|
-
*/
|
|
12
|
-
export function OrganizationApiKeys({ className, variant }) {
|
|
13
|
-
const { authClient } = useAuth();
|
|
14
|
-
const { data: session } = useSession(authClient);
|
|
15
|
-
const { data: activeOrganization, isPending: activeOrganizationPending } = useActiveOrganization(authClient);
|
|
16
|
-
const { data: membersData } = useListOrganizationMembers(authClient);
|
|
17
|
-
const canManageApiKeys = membersData?.members.some((member) => member.role === "owner" && member.userId === session?.user.id);
|
|
18
|
-
if (!canManageApiKeys) {
|
|
19
|
-
return null;
|
|
20
|
-
}
|
|
21
|
-
return (_jsx(ApiKeys, { className: className, variant: variant, organizationId: activeOrganization?.id, isPending: activeOrganizationPending }));
|
|
22
|
-
}
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { AuthProvider as AuthProviderPrimitive } from "@better-auth-ui/react";
|
|
3
|
-
import { RouterProvider } from "@heroui/react";
|
|
4
|
-
import { ErrorToaster } from "./error-toaster";
|
|
5
|
-
/**
|
|
6
|
-
* Heroui-flavored `AuthProvider`. Wraps the primitive provider with a
|
|
7
|
-
* heroui `RouterProvider` and the heroui `ErrorToaster`.
|
|
8
|
-
*/
|
|
9
|
-
export function AuthProvider({ children, navigate, ...config }) {
|
|
10
|
-
return (_jsx(AuthProviderPrimitive, { navigate: navigate, ...config, children: _jsxs(RouterProvider, { navigate: (path) => navigate({ to: path }), children: [children, _jsx(ErrorToaster, {})] }) }));
|
|
11
|
-
}
|
|
@@ -1,88 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
-
import { useAuth } from "@better-auth-ui/react";
|
|
3
|
-
import { useEffect } from "react";
|
|
4
|
-
import { ForgotPassword } from "./forgot-password";
|
|
5
|
-
import { ResetPassword } from "./reset-password";
|
|
6
|
-
import { SignIn } from "./sign-in";
|
|
7
|
-
import { SignOut } from "./sign-out";
|
|
8
|
-
import { SignUp } from "./sign-up";
|
|
9
|
-
/**
|
|
10
|
-
* Built-in views that only make sense when email + password auth is enabled.
|
|
11
|
-
* When it's disabled, the `<Auth>` router redirects these to `signIn` so a
|
|
12
|
-
* plugin's `fallbackViews.auth.signIn` (e.g. magic link) takes over.
|
|
13
|
-
*/
|
|
14
|
-
const PASSWORD_ONLY_VIEWS = ["signUp", "forgotPassword", "resetPassword"];
|
|
15
|
-
const AUTH_VIEWS = {
|
|
16
|
-
signIn: SignIn,
|
|
17
|
-
signOut: SignOut,
|
|
18
|
-
signUp: SignUp,
|
|
19
|
-
forgotPassword: ForgotPassword,
|
|
20
|
-
resetPassword: ResetPassword
|
|
21
|
-
};
|
|
22
|
-
/**
|
|
23
|
-
* Render the appropriate authentication view based on the provided `view` or `path`.
|
|
24
|
-
*
|
|
25
|
-
* @param path - Route path used to resolve an auth view when `view` is not provided
|
|
26
|
-
* @param socialLayout - Social layout to apply to sign-in/sign-up/magic-link views
|
|
27
|
-
* @param socialPosition - Position for social buttons ("top" or "bottom")
|
|
28
|
-
* @param variant - Variant to apply to the card
|
|
29
|
-
* @param view - Explicit auth view to render (e.g., "signIn", "signUp")
|
|
30
|
-
* @returns The React element for the resolved authentication view
|
|
31
|
-
*/
|
|
32
|
-
export function Auth({ path, socialLayout, socialPosition, view, ...props }) {
|
|
33
|
-
const { basePaths, emailAndPassword, plugins, viewPaths, navigate } = useAuth();
|
|
34
|
-
if (!view && !path) {
|
|
35
|
-
throw new Error("[Better Auth UI] Either `view` or `path` must be provided");
|
|
36
|
-
}
|
|
37
|
-
const authView = view ||
|
|
38
|
-
Object.keys(viewPaths.auth).find((key) => viewPaths.auth[key] === path);
|
|
39
|
-
// When email + password auth is disabled, password-only views (signUp,
|
|
40
|
-
// forgotPassword, resetPassword) have no meaning. Redirect them to signIn,
|
|
41
|
-
// where a plugin's `fallbackViews.auth.signIn` (e.g. magic link) takes
|
|
42
|
-
// over as the primary entry point.
|
|
43
|
-
const shouldRedirectToSignIn = !emailAndPassword?.enabled &&
|
|
44
|
-
authView &&
|
|
45
|
-
PASSWORD_ONLY_VIEWS.includes(authView);
|
|
46
|
-
useEffect(() => {
|
|
47
|
-
if (shouldRedirectToSignIn) {
|
|
48
|
-
navigate({
|
|
49
|
-
to: `${basePaths.auth}/${viewPaths.auth.signIn}`,
|
|
50
|
-
replace: true
|
|
51
|
-
});
|
|
52
|
-
}
|
|
53
|
-
}, [shouldRedirectToSignIn, navigate, basePaths.auth, viewPaths.auth.signIn]);
|
|
54
|
-
if (shouldRedirectToSignIn) {
|
|
55
|
-
return null;
|
|
56
|
-
}
|
|
57
|
-
// 1. Plugin overrides (`views.auth[currentView]`) — first plugin wins,
|
|
58
|
-
// including over built-in views. Resolves the view key from `view`,
|
|
59
|
-
// then `authView` (built-in path match), then plugin-introduced paths
|
|
60
|
-
// (e.g. `magicLink` → `/auth/magic-link`).
|
|
61
|
-
for (const plugin of plugins) {
|
|
62
|
-
const pluginAuthPaths = plugin.viewPaths?.auth;
|
|
63
|
-
const pluginView = view ??
|
|
64
|
-
authView ??
|
|
65
|
-
(pluginAuthPaths &&
|
|
66
|
-
Object.keys(pluginAuthPaths).find((key) => pluginAuthPaths[key] === path));
|
|
67
|
-
if (!pluginView)
|
|
68
|
-
continue;
|
|
69
|
-
const PluginView = plugin.views?.auth?.[pluginView];
|
|
70
|
-
if (!PluginView)
|
|
71
|
-
continue;
|
|
72
|
-
return (_jsx(PluginView, { socialLayout: socialLayout, socialPosition: socialPosition, ...props }));
|
|
73
|
-
}
|
|
74
|
-
// 2. Plugin fallbacks — only when the built-in `signIn` isn't viable
|
|
75
|
-
// (password auth is off). Used by `magicLinkPlugin` to render the
|
|
76
|
-
// magic-link form as the primary passwordless sign-in surface.
|
|
77
|
-
if (authView === "signIn" && !emailAndPassword?.enabled) {
|
|
78
|
-
const Fallback = plugins.find((plugin) => plugin.fallbackViews?.auth?.signIn)?.fallbackViews?.auth?.signIn;
|
|
79
|
-
if (Fallback) {
|
|
80
|
-
return (_jsx(Fallback, { socialLayout: socialLayout, socialPosition: socialPosition, ...props }));
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
const AuthView = authView ? AUTH_VIEWS[authView] : undefined;
|
|
84
|
-
if (!AuthView) {
|
|
85
|
-
throw new Error(`[Better Auth UI] Unknown view "${authView}". Valid views are: ${Object.keys(AUTH_VIEWS).join(", ")}`);
|
|
86
|
-
}
|
|
87
|
-
return (_jsx(AuthView, { socialLayout: socialLayout, socialPosition: socialPosition, ...props }));
|
|
88
|
-
}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useAuth } from "@better-auth-ui/react";
|
|
3
|
-
import { cn } from "@heroui/react";
|
|
4
|
-
import { DeleteAccount } from "./delete-account";
|
|
5
|
-
/**
|
|
6
|
-
* Renders the danger zone heading and {@link DeleteAccount}.
|
|
7
|
-
* Registered as a `securityCard` by `deleteUserPlugin()`; gate by registering the plugin.
|
|
8
|
-
*/
|
|
9
|
-
export function DangerZone({ className, variant, ...props }) {
|
|
10
|
-
const { localization } = useAuth();
|
|
11
|
-
return (_jsxs("div", { className: cn("flex w-full flex-col", className), ...props, children: [_jsx("h2", { className: cn("text-sm font-semibold mb-3 text-danger"), children: localization.settings.dangerZone }), _jsx(DeleteAccount, { variant: variant })] }));
|
|
12
|
-
}
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { authQueryKeys } from "@better-auth-ui/core";
|
|
3
|
-
import { useAuth, useAuthPlugin, useDeleteUser, useListAccounts } from "@better-auth-ui/react";
|
|
4
|
-
import { TriangleExclamation } from "@gravity-ui/icons";
|
|
5
|
-
import { AlertDialog, Button, Card, FieldError, Form, Input, Label, Spinner, TextField, toast } from "@heroui/react";
|
|
6
|
-
import { useQueryClient } from "@tanstack/react-query";
|
|
7
|
-
import { useState } from "react";
|
|
8
|
-
import { deleteUserPlugin } from "../../../lib/auth/delete-user-plugin";
|
|
9
|
-
/**
|
|
10
|
-
* Danger-zone card to delete the authenticated account, with a confirmation dialog and toasts.
|
|
11
|
-
*/
|
|
12
|
-
export function DeleteAccount({ className, variant, ...props }) {
|
|
13
|
-
const { authClient, basePaths, localization, navigate, viewPaths } = useAuth();
|
|
14
|
-
const { localization: deleteUserLocalization, sendDeleteAccountVerification } = useAuthPlugin(deleteUserPlugin);
|
|
15
|
-
const { data: accounts } = useListAccounts(authClient);
|
|
16
|
-
const queryClient = useQueryClient();
|
|
17
|
-
const [confirmOpen, setConfirmOpen] = useState(false);
|
|
18
|
-
const [password, setPassword] = useState("");
|
|
19
|
-
const hasCredentialAccount = accounts?.some((account) => account.providerId === "credential");
|
|
20
|
-
const needsPassword = !sendDeleteAccountVerification && hasCredentialAccount;
|
|
21
|
-
const { mutate: deleteUser, isPending } = useDeleteUser(authClient);
|
|
22
|
-
const handleDialogOpenChange = (open) => {
|
|
23
|
-
setConfirmOpen(open);
|
|
24
|
-
setPassword("");
|
|
25
|
-
};
|
|
26
|
-
const handleSubmit = async (e) => {
|
|
27
|
-
e.preventDefault();
|
|
28
|
-
const params = {
|
|
29
|
-
...(needsPassword ? { password } : {})
|
|
30
|
-
};
|
|
31
|
-
deleteUser(params, {
|
|
32
|
-
onSuccess: () => {
|
|
33
|
-
setConfirmOpen(false);
|
|
34
|
-
setPassword("");
|
|
35
|
-
if (sendDeleteAccountVerification) {
|
|
36
|
-
toast.success(deleteUserLocalization.deleteUserVerificationSent);
|
|
37
|
-
}
|
|
38
|
-
else {
|
|
39
|
-
toast.success(deleteUserLocalization.deleteUserSuccess);
|
|
40
|
-
queryClient.removeQueries({ queryKey: authQueryKeys.all });
|
|
41
|
-
navigate({
|
|
42
|
-
to: `${basePaths.auth}/${viewPaths.auth.signIn}`,
|
|
43
|
-
replace: true
|
|
44
|
-
});
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
});
|
|
48
|
-
};
|
|
49
|
-
return (_jsx(Card, { className: className, variant: variant, ...props, children: _jsxs(Card.Content, { 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: deleteUserLocalization.deleteAccount }), _jsx("p", { className: "text-muted text-xs mt-0.5", children: deleteUserLocalization.deleteAccountDescription })] }), _jsxs(AlertDialog, { children: [_jsx(Button, { isDisabled: !accounts, size: "sm", variant: "danger-soft", onPress: () => setConfirmOpen(true), children: deleteUserLocalization.deleteAccount }), _jsx(AlertDialog.Backdrop, { isOpen: confirmOpen, onOpenChange: handleDialogOpenChange, 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: deleteUserLocalization.deleteAccount })] }), _jsxs(AlertDialog.Body, { className: "overflow-visible", children: [_jsx("p", { className: "text-muted text-sm", children: deleteUserLocalization.deleteAccountDescription }), needsPassword && (_jsxs(TextField, { className: "mt-4", name: "password", type: "password", isDisabled: isPending, value: password, onChange: setPassword, children: [_jsx(Label, { children: localization.auth.password }), _jsx(Input, { autoComplete: "current-password", placeholder: localization.auth.passwordPlaceholder, required: true, variant: "secondary" }), _jsx(FieldError, {})] }))] }), _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" }), deleteUserLocalization.deleteAccount] })] })] }) }) }) })] })] }) }));
|
|
50
|
-
}
|