@stackframe/stack 2.8.3 → 2.8.6
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/CHANGELOG.md +28 -0
- package/dist/components-page/account-settings/active-sessions/active-sessions-page.js +151 -0
- package/dist/components-page/account-settings/active-sessions/active-sessions-page.js.map +1 -0
- package/dist/components-page/account-settings/api-keys/api-keys-page.js +70 -0
- package/dist/components-page/account-settings/api-keys/api-keys-page.js.map +1 -0
- package/dist/components-page/account-settings/editable-text.js +75 -0
- package/dist/components-page/account-settings/editable-text.js.map +1 -0
- package/dist/components-page/account-settings/email-and-auth/email-and-auth-page.js +46 -0
- package/dist/components-page/account-settings/email-and-auth/email-and-auth-page.js.map +1 -0
- package/dist/components-page/account-settings/email-and-auth/emails-section.js +188 -0
- package/dist/components-page/account-settings/email-and-auth/emails-section.js.map +1 -0
- package/dist/components-page/account-settings/email-and-auth/mfa-section.js +146 -0
- package/dist/components-page/account-settings/email-and-auth/mfa-section.js.map +1 -0
- package/dist/components-page/account-settings/email-and-auth/otp-section.js +89 -0
- package/dist/components-page/account-settings/email-and-auth/otp-section.js.map +1 -0
- package/dist/components-page/account-settings/email-and-auth/passkey-section.js +92 -0
- package/dist/components-page/account-settings/email-and-auth/passkey-section.js.map +1 -0
- package/dist/components-page/account-settings/email-and-auth/password-section.js +181 -0
- package/dist/components-page/account-settings/email-and-auth/password-section.js.map +1 -0
- package/dist/components-page/account-settings/page-layout.js +34 -0
- package/dist/components-page/account-settings/page-layout.js.map +1 -0
- package/dist/components-page/account-settings/profile-page/profile-page.js +75 -0
- package/dist/components-page/account-settings/profile-page/profile-page.js.map +1 -0
- package/dist/components-page/account-settings/section.js +44 -0
- package/dist/components-page/account-settings/section.js.map +1 -0
- package/dist/components-page/account-settings/settings/delete-account-section.js +87 -0
- package/dist/components-page/account-settings/settings/delete-account-section.js.map +1 -0
- package/dist/components-page/account-settings/settings/settings-page.js +40 -0
- package/dist/components-page/account-settings/settings/settings-page.js.map +1 -0
- package/dist/components-page/account-settings/settings/sign-out-section.js +54 -0
- package/dist/components-page/account-settings/settings/sign-out-section.js.map +1 -0
- package/dist/components-page/account-settings/teams/leave-team-section.js +79 -0
- package/dist/components-page/account-settings/teams/leave-team-section.js.map +1 -0
- package/dist/components-page/account-settings/teams/team-api-keys-section.js +89 -0
- package/dist/components-page/account-settings/teams/team-api-keys-section.js.map +1 -0
- package/dist/components-page/account-settings/teams/team-creation-page.js +92 -0
- package/dist/components-page/account-settings/teams/team-creation-page.js.map +1 -0
- package/dist/components-page/account-settings/teams/team-display-name-section.js +57 -0
- package/dist/components-page/account-settings/teams/team-display-name-section.js.map +1 -0
- package/dist/components-page/account-settings/teams/team-member-invitation-section.js +131 -0
- package/dist/components-page/account-settings/teams/team-member-invitation-section.js.map +1 -0
- package/dist/components-page/account-settings/teams/team-member-list-section.js +61 -0
- package/dist/components-page/account-settings/teams/team-member-list-section.js.map +1 -0
- package/dist/components-page/account-settings/teams/team-page.js +50 -0
- package/dist/components-page/account-settings/teams/team-page.js.map +1 -0
- package/dist/components-page/account-settings/teams/team-profile-image-section.js +59 -0
- package/dist/components-page/account-settings/teams/team-profile-image-section.js.map +1 -0
- package/dist/components-page/account-settings/teams/team-profile-user-section.js +56 -0
- package/dist/components-page/account-settings/teams/team-profile-user-section.js.map +1 -0
- package/dist/components-page/account-settings.js +16 -1173
- package/dist/components-page/account-settings.js.map +1 -1
- package/dist/components-page/section.js +6 -0
- package/dist/components-page/section.js.map +1 -0
- package/dist/esm/components-page/account-settings/active-sessions/active-sessions-page.js +126 -0
- package/dist/esm/components-page/account-settings/active-sessions/active-sessions-page.js.map +1 -0
- package/dist/esm/components-page/account-settings/api-keys/api-keys-page.js +45 -0
- package/dist/esm/components-page/account-settings/api-keys/api-keys-page.js.map +1 -0
- package/dist/esm/components-page/account-settings/editable-text.js +50 -0
- package/dist/esm/components-page/account-settings/editable-text.js.map +1 -0
- package/dist/esm/components-page/account-settings/email-and-auth/email-and-auth-page.js +21 -0
- package/dist/esm/components-page/account-settings/email-and-auth/email-and-auth-page.js.map +1 -0
- package/dist/esm/components-page/account-settings/email-and-auth/emails-section.js +163 -0
- package/dist/esm/components-page/account-settings/email-and-auth/emails-section.js.map +1 -0
- package/dist/esm/components-page/account-settings/email-and-auth/mfa-section.js +111 -0
- package/dist/esm/components-page/account-settings/email-and-auth/mfa-section.js.map +1 -0
- package/dist/esm/components-page/account-settings/email-and-auth/otp-section.js +64 -0
- package/dist/esm/components-page/account-settings/email-and-auth/otp-section.js.map +1 -0
- package/dist/esm/components-page/account-settings/email-and-auth/passkey-section.js +67 -0
- package/dist/esm/components-page/account-settings/email-and-auth/passkey-section.js.map +1 -0
- package/dist/esm/components-page/account-settings/email-and-auth/password-section.js +146 -0
- package/dist/esm/components-page/account-settings/email-and-auth/password-section.js.map +1 -0
- package/dist/esm/components-page/account-settings/page-layout.js +9 -0
- package/dist/esm/components-page/account-settings/page-layout.js.map +1 -0
- package/dist/esm/components-page/account-settings/profile-page/profile-page.js +50 -0
- package/dist/esm/components-page/account-settings/profile-page/profile-page.js.map +1 -0
- package/dist/esm/components-page/account-settings/section.js +19 -0
- package/dist/esm/components-page/account-settings/section.js.map +1 -0
- package/dist/esm/components-page/account-settings/settings/delete-account-section.js +62 -0
- package/dist/esm/components-page/account-settings/settings/delete-account-section.js.map +1 -0
- package/dist/esm/components-page/account-settings/settings/settings-page.js +15 -0
- package/dist/esm/components-page/account-settings/settings/settings-page.js.map +1 -0
- package/dist/esm/components-page/account-settings/settings/sign-out-section.js +29 -0
- package/dist/esm/components-page/account-settings/settings/sign-out-section.js.map +1 -0
- package/dist/esm/components-page/account-settings/teams/leave-team-section.js +54 -0
- package/dist/esm/components-page/account-settings/teams/leave-team-section.js.map +1 -0
- package/dist/esm/components-page/account-settings/teams/team-api-keys-section.js +64 -0
- package/dist/esm/components-page/account-settings/teams/team-api-keys-section.js.map +1 -0
- package/dist/esm/components-page/account-settings/teams/team-creation-page.js +67 -0
- package/dist/esm/components-page/account-settings/teams/team-creation-page.js.map +1 -0
- package/dist/esm/components-page/account-settings/teams/team-display-name-section.js +32 -0
- package/dist/esm/components-page/account-settings/teams/team-display-name-section.js.map +1 -0
- package/dist/esm/components-page/account-settings/teams/team-member-invitation-section.js +106 -0
- package/dist/esm/components-page/account-settings/teams/team-member-invitation-section.js.map +1 -0
- package/dist/esm/components-page/account-settings/teams/team-member-list-section.js +36 -0
- package/dist/esm/components-page/account-settings/teams/team-member-list-section.js.map +1 -0
- package/dist/esm/components-page/account-settings/teams/team-page.js +25 -0
- package/dist/esm/components-page/account-settings/teams/team-page.js.map +1 -0
- package/dist/esm/components-page/account-settings/teams/team-profile-image-section.js +34 -0
- package/dist/esm/components-page/account-settings/teams/team-profile-image-section.js.map +1 -0
- package/dist/esm/components-page/account-settings/teams/team-profile-user-section.js +31 -0
- package/dist/esm/components-page/account-settings/teams/team-profile-user-section.js.map +1 -0
- package/dist/esm/components-page/account-settings.js +14 -1158
- package/dist/esm/components-page/account-settings.js.map +1 -1
- package/dist/esm/components-page/section.js +4 -0
- package/dist/esm/components-page/section.js.map +1 -0
- package/dist/esm/lib/stack-app/apps/implementations/admin-app-impl.js.map +1 -1
- package/dist/esm/lib/stack-app/apps/implementations/client-app-impl.js +79 -0
- package/dist/esm/lib/stack-app/apps/implementations/client-app-impl.js.map +1 -1
- package/dist/esm/lib/stack-app/apps/implementations/common.js +1 -1
- package/dist/esm/lib/stack-app/apps/implementations/common.js.map +1 -1
- package/dist/esm/lib/stack-app/apps/interfaces/client-app.js.map +1 -1
- package/dist/esm/lib/stack-app/projects/index.js.map +1 -1
- package/dist/esm/lib/translations.js +0 -1
- package/dist/esm/lib/translations.js.map +1 -1
- package/dist/index.d.mts +5 -1
- package/dist/index.d.ts +5 -1
- package/dist/lib/stack-app/apps/implementations/admin-app-impl.js.map +1 -1
- package/dist/lib/stack-app/apps/implementations/client-app-impl.js +79 -0
- package/dist/lib/stack-app/apps/implementations/client-app-impl.js.map +1 -1
- package/dist/lib/stack-app/apps/implementations/common.js +1 -1
- package/dist/lib/stack-app/apps/implementations/common.js.map +1 -1
- package/dist/lib/stack-app/apps/interfaces/client-app.js.map +1 -1
- package/dist/lib/stack-app/projects/index.js.map +1 -1
- package/dist/lib/translations.js +0 -1
- package/dist/lib/translations.js.map +1 -1
- package/package.json +4 -4
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
"use strict";
|
|
3
3
|
"use client";
|
|
4
|
-
var __create = Object.create;
|
|
5
4
|
var __defProp = Object.defineProperty;
|
|
6
5
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
7
6
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
8
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
9
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
10
8
|
var __export = (target, all) => {
|
|
11
9
|
for (var name in all)
|
|
@@ -19,51 +17,29 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
19
17
|
}
|
|
20
18
|
return to;
|
|
21
19
|
};
|
|
22
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
23
|
-
// If the importer is in node compatibility mode or this is not an ESM
|
|
24
|
-
// file that has been converted to a CommonJS file using a Babel-
|
|
25
|
-
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
26
|
-
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
27
|
-
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
28
|
-
mod
|
|
29
|
-
));
|
|
30
20
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
31
21
|
|
|
32
22
|
// src/components-page/account-settings.tsx
|
|
33
23
|
var account_settings_exports = {};
|
|
34
24
|
__export(account_settings_exports, {
|
|
35
|
-
AccountSettings: () => AccountSettings
|
|
36
|
-
ApiKeysPage: () => ApiKeysPage,
|
|
37
|
-
EditableText: () => EditableText,
|
|
38
|
-
TeamApiKeysSection: () => TeamApiKeysSection
|
|
25
|
+
AccountSettings: () => AccountSettings
|
|
39
26
|
});
|
|
40
27
|
module.exports = __toCommonJS(account_settings_exports);
|
|
41
|
-
var import_yup = require("@hookform/resolvers/yup");
|
|
42
|
-
var import_otp = require("@oslojs/otp");
|
|
43
|
-
var import_stack_shared = require("@stackframe/stack-shared");
|
|
44
|
-
var import_password = require("@stackframe/stack-shared/dist/helpers/password");
|
|
45
|
-
var import_use_async_callback = require("@stackframe/stack-shared/dist/hooks/use-async-callback");
|
|
46
|
-
var import_schema_fields = require("@stackframe/stack-shared/dist/schema-fields");
|
|
47
|
-
var import_crypto = require("@stackframe/stack-shared/dist/utils/crypto");
|
|
48
|
-
var import_dates = require("@stackframe/stack-shared/dist/utils/dates");
|
|
49
|
-
var import_errors = require("@stackframe/stack-shared/dist/utils/errors");
|
|
50
|
-
var import_promises = require("@stackframe/stack-shared/dist/utils/promises");
|
|
51
28
|
var import_stack_ui = require("@stackframe/stack-ui");
|
|
52
29
|
var import_lucide_react = require("lucide-react");
|
|
53
|
-
var QRCode = __toESM(require("qrcode"));
|
|
54
30
|
var import_react = require("react");
|
|
55
|
-
var import_react_hook_form = require("react-hook-form");
|
|
56
|
-
var yup = __toESM(require("yup"));
|
|
57
31
|
var import__ = require("..");
|
|
58
|
-
var import_api_key_dialogs = require("../components/api-key-dialogs");
|
|
59
|
-
var import_api_key_table = require("../components/api-key-table");
|
|
60
|
-
var import_form_warning = require("../components/elements/form-warning");
|
|
61
32
|
var import_maybe_full_page = require("../components/elements/maybe-full-page");
|
|
62
33
|
var import_sidebar_layout = require("../components/elements/sidebar-layout");
|
|
63
|
-
var import_user_avatar = require("../components/elements/user-avatar");
|
|
64
|
-
var import_profile_image_editor = require("../components/profile-image-editor");
|
|
65
34
|
var import_team_icon = require("../components/team-icon");
|
|
66
35
|
var import_translations = require("../lib/translations");
|
|
36
|
+
var import_active_sessions_page = require("./account-settings/active-sessions/active-sessions-page");
|
|
37
|
+
var import_api_keys_page = require("./account-settings/api-keys/api-keys-page");
|
|
38
|
+
var import_email_and_auth_page = require("./account-settings/email-and-auth/email-and-auth-page");
|
|
39
|
+
var import_profile_page = require("./account-settings/profile-page/profile-page");
|
|
40
|
+
var import_settings_page = require("./account-settings/settings/settings-page");
|
|
41
|
+
var import_team_creation_page = require("./account-settings/teams/team-creation-page");
|
|
42
|
+
var import_team_page = require("./account-settings/teams/team-page");
|
|
67
43
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
68
44
|
var Icon = ({ name }) => {
|
|
69
45
|
const LucideIcon = import_lucide_react.icons[name];
|
|
@@ -84,35 +60,35 @@ function AccountSettings(props) {
|
|
|
84
60
|
type: "item",
|
|
85
61
|
id: "profile",
|
|
86
62
|
icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Icon, { name: "Contact" }),
|
|
87
|
-
content: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ProfilePage, {})
|
|
63
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_profile_page.ProfilePage, {})
|
|
88
64
|
},
|
|
89
65
|
{
|
|
90
66
|
title: t("Emails & Auth"),
|
|
91
67
|
type: "item",
|
|
92
68
|
id: "auth",
|
|
93
69
|
icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Icon, { name: "ShieldCheck" }),
|
|
94
|
-
content: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react.Suspense, { fallback: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(EmailsAndAuthPageSkeleton, {}), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(EmailsAndAuthPage, {}) })
|
|
70
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react.Suspense, { fallback: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(EmailsAndAuthPageSkeleton, {}), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_email_and_auth_page.EmailsAndAuthPage, {}) })
|
|
95
71
|
},
|
|
96
72
|
{
|
|
97
73
|
title: t("Active Sessions"),
|
|
98
74
|
type: "item",
|
|
99
75
|
id: "sessions",
|
|
100
76
|
icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Icon, { name: "Monitor" }),
|
|
101
|
-
content: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react.Suspense, { fallback: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ActiveSessionsPageSkeleton, {}), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ActiveSessionsPage, {}) })
|
|
77
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react.Suspense, { fallback: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ActiveSessionsPageSkeleton, {}), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_active_sessions_page.ActiveSessionsPage, {}) })
|
|
102
78
|
},
|
|
103
79
|
...project.config.allowUserApiKeys ? [{
|
|
104
80
|
title: t("API Keys"),
|
|
105
81
|
type: "item",
|
|
106
82
|
id: "api-keys",
|
|
107
83
|
icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Icon, { name: "Key" }),
|
|
108
|
-
content: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react.Suspense, { fallback: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ApiKeysPageSkeleton, {}), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ApiKeysPage, {}) })
|
|
84
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react.Suspense, { fallback: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ApiKeysPageSkeleton, {}), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_api_keys_page.ApiKeysPage, {}) })
|
|
109
85
|
}] : [],
|
|
110
86
|
{
|
|
111
87
|
title: t("Settings"),
|
|
112
88
|
type: "item",
|
|
113
89
|
id: "settings",
|
|
114
90
|
icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Icon, { name: "Settings" }),
|
|
115
|
-
content: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SettingsPage, {})
|
|
91
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_settings_page.SettingsPage, {})
|
|
116
92
|
},
|
|
117
93
|
...props.extraItems?.map((item) => ({
|
|
118
94
|
title: item.title,
|
|
@@ -140,423 +116,23 @@ function AccountSettings(props) {
|
|
|
140
116
|
] }),
|
|
141
117
|
type: "item",
|
|
142
118
|
id: `team-${team.id}`,
|
|
143
|
-
content: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react.Suspense, { fallback: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TeamPageSkeleton, {}), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TeamPage, { team }) })
|
|
119
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react.Suspense, { fallback: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TeamPageSkeleton, {}), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_team_page.TeamPage, { team }) })
|
|
144
120
|
})),
|
|
145
121
|
...project.config.clientTeamCreationEnabled ? [{
|
|
146
122
|
title: t("Create a team"),
|
|
147
123
|
icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Icon, { name: "CirclePlus" }),
|
|
148
124
|
type: "item",
|
|
149
125
|
id: "team-creation",
|
|
150
|
-
content: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react.Suspense, { fallback: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TeamCreationSkeleton, {}), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
126
|
+
content: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_react.Suspense, { fallback: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TeamCreationSkeleton, {}), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_team_creation_page.TeamCreationPage, {}) })
|
|
151
127
|
}] : []
|
|
152
128
|
].filter((p) => p.type === "divider" || p.content),
|
|
153
129
|
title: t("Account Settings")
|
|
154
130
|
}
|
|
155
131
|
) }) });
|
|
156
132
|
}
|
|
157
|
-
function Section(props) {
|
|
158
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
159
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Separator, {}),
|
|
160
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex flex-col sm:flex-row gap-2", children: [
|
|
161
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "sm:flex-1 flex flex-col justify-center", children: [
|
|
162
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Typography, { className: "font-medium", children: props.title }),
|
|
163
|
-
props.description && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Typography, { variant: "secondary", type: "footnote", children: props.description })
|
|
164
|
-
] }),
|
|
165
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "sm:flex-1 sm:items-end flex flex-col gap-2 ", children: props.children })
|
|
166
|
-
] })
|
|
167
|
-
] });
|
|
168
|
-
}
|
|
169
133
|
function PageLayout(props) {
|
|
170
134
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "flex flex-col gap-6", children: props.children });
|
|
171
135
|
}
|
|
172
|
-
function ApiKeysPage() {
|
|
173
|
-
const { t } = (0, import_translations.useTranslation)();
|
|
174
|
-
const user = (0, import__.useUser)({ or: "redirect" });
|
|
175
|
-
const apiKeys = user.useApiKeys();
|
|
176
|
-
const [isNewApiKeyDialogOpen, setIsNewApiKeyDialogOpen] = (0, import_react.useState)(false);
|
|
177
|
-
const [returnedApiKey, setReturnedApiKey] = (0, import_react.useState)(null);
|
|
178
|
-
const CreateDialog = import_api_key_dialogs.CreateApiKeyDialog;
|
|
179
|
-
const ShowDialog = import_api_key_dialogs.ShowApiKeyDialog;
|
|
180
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(PageLayout, { children: [
|
|
181
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Button, { onClick: () => setIsNewApiKeyDialogOpen(true), children: t("Create API Key") }),
|
|
182
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_api_key_table.ApiKeyTable, { apiKeys }),
|
|
183
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
184
|
-
CreateDialog,
|
|
185
|
-
{
|
|
186
|
-
open: isNewApiKeyDialogOpen,
|
|
187
|
-
onOpenChange: setIsNewApiKeyDialogOpen,
|
|
188
|
-
onKeyCreated: setReturnedApiKey,
|
|
189
|
-
createApiKey: async (data) => {
|
|
190
|
-
const apiKey = await user.createApiKey(data);
|
|
191
|
-
return apiKey;
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
),
|
|
195
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
196
|
-
ShowDialog,
|
|
197
|
-
{
|
|
198
|
-
apiKey: returnedApiKey,
|
|
199
|
-
onClose: () => setReturnedApiKey(null)
|
|
200
|
-
}
|
|
201
|
-
)
|
|
202
|
-
] });
|
|
203
|
-
}
|
|
204
|
-
function TeamApiKeysSection(props) {
|
|
205
|
-
const user = (0, import__.useUser)({ or: "redirect" });
|
|
206
|
-
const team = user.useTeam(props.team.id);
|
|
207
|
-
if (!team) {
|
|
208
|
-
throw new import_errors.StackAssertionError("Team not found");
|
|
209
|
-
}
|
|
210
|
-
const manageApiKeysPermission = user.usePermission(props.team, "$manage_api_keys");
|
|
211
|
-
if (!manageApiKeysPermission) {
|
|
212
|
-
return null;
|
|
213
|
-
}
|
|
214
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TeamApiKeysSectionInner, { team: props.team });
|
|
215
|
-
}
|
|
216
|
-
function TeamApiKeysSectionInner(props) {
|
|
217
|
-
const { t } = (0, import_translations.useTranslation)();
|
|
218
|
-
const [isNewApiKeyDialogOpen, setIsNewApiKeyDialogOpen] = (0, import_react.useState)(false);
|
|
219
|
-
const [returnedApiKey, setReturnedApiKey] = (0, import_react.useState)(null);
|
|
220
|
-
const apiKeys = props.team.useApiKeys();
|
|
221
|
-
const CreateDialog = import_api_key_dialogs.CreateApiKeyDialog;
|
|
222
|
-
const ShowDialog = import_api_key_dialogs.ShowApiKeyDialog;
|
|
223
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
224
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
225
|
-
Section,
|
|
226
|
-
{
|
|
227
|
-
title: t("API Keys"),
|
|
228
|
-
description: t("API keys grant programmatic access to your team."),
|
|
229
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Button, { onClick: () => setIsNewApiKeyDialogOpen(true), children: t("Create API Key") })
|
|
230
|
-
}
|
|
231
|
-
),
|
|
232
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_api_key_table.ApiKeyTable, { apiKeys }),
|
|
233
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
234
|
-
CreateDialog,
|
|
235
|
-
{
|
|
236
|
-
open: isNewApiKeyDialogOpen,
|
|
237
|
-
onOpenChange: setIsNewApiKeyDialogOpen,
|
|
238
|
-
onKeyCreated: setReturnedApiKey,
|
|
239
|
-
createApiKey: async (data) => {
|
|
240
|
-
const apiKey = await props.team.createApiKey(data);
|
|
241
|
-
return apiKey;
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
),
|
|
245
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
246
|
-
ShowDialog,
|
|
247
|
-
{
|
|
248
|
-
apiKey: returnedApiKey,
|
|
249
|
-
onClose: () => setReturnedApiKey(null)
|
|
250
|
-
}
|
|
251
|
-
)
|
|
252
|
-
] });
|
|
253
|
-
}
|
|
254
|
-
function ProfilePage() {
|
|
255
|
-
const { t } = (0, import_translations.useTranslation)();
|
|
256
|
-
const user = (0, import__.useUser)({ or: "redirect" });
|
|
257
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(PageLayout, { children: [
|
|
258
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
259
|
-
Section,
|
|
260
|
-
{
|
|
261
|
-
title: t("User name"),
|
|
262
|
-
description: t("This is a display name and is not used for authentication"),
|
|
263
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
264
|
-
EditableText,
|
|
265
|
-
{
|
|
266
|
-
value: user.displayName || "",
|
|
267
|
-
onSave: async (newDisplayName) => {
|
|
268
|
-
await user.update({ displayName: newDisplayName });
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
)
|
|
272
|
-
}
|
|
273
|
-
),
|
|
274
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
275
|
-
Section,
|
|
276
|
-
{
|
|
277
|
-
title: t("Profile image"),
|
|
278
|
-
description: t("Upload your own image as your avatar"),
|
|
279
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
280
|
-
import_profile_image_editor.ProfileImageEditor,
|
|
281
|
-
{
|
|
282
|
-
user,
|
|
283
|
-
onProfileImageUrlChange: async (profileImageUrl) => {
|
|
284
|
-
await user.update({ profileImageUrl });
|
|
285
|
-
}
|
|
286
|
-
}
|
|
287
|
-
)
|
|
288
|
-
}
|
|
289
|
-
)
|
|
290
|
-
] });
|
|
291
|
-
}
|
|
292
|
-
function EmailsSection() {
|
|
293
|
-
const { t } = (0, import_translations.useTranslation)();
|
|
294
|
-
const user = (0, import__.useUser)({ or: "redirect" });
|
|
295
|
-
const contactChannels = user.useContactChannels();
|
|
296
|
-
const [addingEmail, setAddingEmail] = (0, import_react.useState)(contactChannels.length === 0);
|
|
297
|
-
const [addingEmailLoading, setAddingEmailLoading] = (0, import_react.useState)(false);
|
|
298
|
-
const [addedEmail, setAddedEmail] = (0, import_react.useState)(null);
|
|
299
|
-
const isLastEmail = contactChannels.filter((x) => x.usedForAuth && x.type === "email").length === 1;
|
|
300
|
-
(0, import_react.useEffect)(() => {
|
|
301
|
-
if (addedEmail) {
|
|
302
|
-
(0, import_promises.runAsynchronously)(async () => {
|
|
303
|
-
const cc = contactChannels.find((x) => x.value === addedEmail);
|
|
304
|
-
if (cc && !cc.isVerified) {
|
|
305
|
-
await cc.sendVerificationEmail();
|
|
306
|
-
}
|
|
307
|
-
setAddedEmail(null);
|
|
308
|
-
});
|
|
309
|
-
}
|
|
310
|
-
}, [contactChannels, addedEmail]);
|
|
311
|
-
const emailSchema = (0, import_schema_fields.yupObject)({
|
|
312
|
-
email: (0, import_schema_fields.strictEmailSchema)(t("Please enter a valid email address")).notOneOf(contactChannels.map((x) => x.value), t("Email already exists")).defined().nonEmpty(t("Email is required"))
|
|
313
|
-
});
|
|
314
|
-
const { register, handleSubmit, formState: { errors }, reset } = (0, import_react_hook_form.useForm)({
|
|
315
|
-
resolver: (0, import_yup.yupResolver)(emailSchema)
|
|
316
|
-
});
|
|
317
|
-
const onSubmit = async (data) => {
|
|
318
|
-
setAddingEmailLoading(true);
|
|
319
|
-
try {
|
|
320
|
-
await user.createContactChannel({ type: "email", value: data.email, usedForAuth: false });
|
|
321
|
-
setAddedEmail(data.email);
|
|
322
|
-
} finally {
|
|
323
|
-
setAddingEmailLoading(false);
|
|
324
|
-
}
|
|
325
|
-
setAddingEmail(false);
|
|
326
|
-
reset();
|
|
327
|
-
};
|
|
328
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
|
|
329
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex flex-col md:flex-row justify-between mb-4 gap-4", children: [
|
|
330
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Typography, { className: "font-medium", children: t("Emails") }),
|
|
331
|
-
addingEmail ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
332
|
-
"form",
|
|
333
|
-
{
|
|
334
|
-
onSubmit: (e) => {
|
|
335
|
-
e.preventDefault();
|
|
336
|
-
(0, import_promises.runAsynchronously)(handleSubmit(onSubmit));
|
|
337
|
-
},
|
|
338
|
-
className: "flex flex-col",
|
|
339
|
-
children: [
|
|
340
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex gap-2", children: [
|
|
341
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
342
|
-
import_stack_ui.Input,
|
|
343
|
-
{
|
|
344
|
-
...register("email"),
|
|
345
|
-
placeholder: t("Enter email")
|
|
346
|
-
}
|
|
347
|
-
),
|
|
348
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Button, { type: "submit", loading: addingEmailLoading, children: t("Add") }),
|
|
349
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
350
|
-
import_stack_ui.Button,
|
|
351
|
-
{
|
|
352
|
-
variant: "secondary",
|
|
353
|
-
onClick: () => {
|
|
354
|
-
setAddingEmail(false);
|
|
355
|
-
reset();
|
|
356
|
-
},
|
|
357
|
-
children: t("Cancel")
|
|
358
|
-
}
|
|
359
|
-
)
|
|
360
|
-
] }),
|
|
361
|
-
errors.email && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_form_warning.FormWarningText, { text: errors.email.message })
|
|
362
|
-
]
|
|
363
|
-
}
|
|
364
|
-
) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "flex md:justify-end", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Button, { variant: "secondary", onClick: () => setAddingEmail(true), children: t("Add an email") }) })
|
|
365
|
-
] }),
|
|
366
|
-
contactChannels.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "border rounded-md", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Table, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.TableBody, { children: contactChannels.filter((x) => x.type === "email").sort((a, b) => {
|
|
367
|
-
if (a.isPrimary !== b.isPrimary) return a.isPrimary ? -1 : 1;
|
|
368
|
-
if (a.isVerified !== b.isVerified) return a.isVerified ? -1 : 1;
|
|
369
|
-
return 0;
|
|
370
|
-
}).map((x) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_stack_ui.TableRow, { children: [
|
|
371
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.TableCell, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex flex-col md:flex-row gap-2 md:gap-4", children: [
|
|
372
|
-
x.value,
|
|
373
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex gap-2", children: [
|
|
374
|
-
x.isPrimary ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Badge, { children: t("Primary") }) : null,
|
|
375
|
-
!x.isVerified ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Badge, { variant: "destructive", children: t("Unverified") }) : null,
|
|
376
|
-
x.usedForAuth ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Badge, { variant: "outline", children: t("Used for sign-in") }) : null
|
|
377
|
-
] })
|
|
378
|
-
] }) }),
|
|
379
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.TableCell, { className: "flex justify-end", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.ActionCell, { items: [
|
|
380
|
-
...!x.isVerified ? [{
|
|
381
|
-
item: t("Send verification email"),
|
|
382
|
-
onClick: async () => {
|
|
383
|
-
await x.sendVerificationEmail();
|
|
384
|
-
}
|
|
385
|
-
}] : [],
|
|
386
|
-
...!x.isPrimary && x.isVerified ? [{
|
|
387
|
-
item: t("Set as primary"),
|
|
388
|
-
onClick: async () => {
|
|
389
|
-
await x.update({ isPrimary: true });
|
|
390
|
-
}
|
|
391
|
-
}] : !x.isPrimary ? [{
|
|
392
|
-
item: t("Set as primary"),
|
|
393
|
-
onClick: async () => {
|
|
394
|
-
},
|
|
395
|
-
disabled: true,
|
|
396
|
-
disabledTooltip: t("Please verify your email first")
|
|
397
|
-
}] : [],
|
|
398
|
-
...!x.usedForAuth && x.isVerified ? [{
|
|
399
|
-
item: t("Use for sign-in"),
|
|
400
|
-
onClick: async () => {
|
|
401
|
-
try {
|
|
402
|
-
await x.update({ usedForAuth: true });
|
|
403
|
-
} catch (e) {
|
|
404
|
-
if (e instanceof import_stack_shared.KnownErrors.ContactChannelAlreadyUsedForAuthBySomeoneElse) {
|
|
405
|
-
alert(t("This email is already used for sign-in by another user."));
|
|
406
|
-
}
|
|
407
|
-
}
|
|
408
|
-
}
|
|
409
|
-
}] : [],
|
|
410
|
-
...x.usedForAuth && !isLastEmail ? [{
|
|
411
|
-
item: t("Stop using for sign-in"),
|
|
412
|
-
onClick: async () => {
|
|
413
|
-
await x.update({ usedForAuth: false });
|
|
414
|
-
}
|
|
415
|
-
}] : x.usedForAuth ? [{
|
|
416
|
-
item: t("Stop using for sign-in"),
|
|
417
|
-
onClick: async () => {
|
|
418
|
-
},
|
|
419
|
-
disabled: true,
|
|
420
|
-
disabledTooltip: t("You can not remove your last sign-in email")
|
|
421
|
-
}] : [],
|
|
422
|
-
...!isLastEmail || !x.usedForAuth ? [{
|
|
423
|
-
item: t("Remove"),
|
|
424
|
-
onClick: async () => {
|
|
425
|
-
await x.delete();
|
|
426
|
-
},
|
|
427
|
-
danger: true
|
|
428
|
-
}] : [{
|
|
429
|
-
item: t("Remove"),
|
|
430
|
-
onClick: async () => {
|
|
431
|
-
},
|
|
432
|
-
disabled: true,
|
|
433
|
-
disabledTooltip: t("You can not remove your last sign-in email")
|
|
434
|
-
}]
|
|
435
|
-
] }) })
|
|
436
|
-
] }, x.id)) }) }) }) : null
|
|
437
|
-
] });
|
|
438
|
-
}
|
|
439
|
-
function EmailsAndAuthPage() {
|
|
440
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(PageLayout, { children: [
|
|
441
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(EmailsSection, {}),
|
|
442
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(PasswordSection, {}),
|
|
443
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(PasskeySection, {}),
|
|
444
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(OtpSection, {}),
|
|
445
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(MfaSection, {})
|
|
446
|
-
] });
|
|
447
|
-
}
|
|
448
|
-
function ActiveSessionsPage() {
|
|
449
|
-
const { t } = (0, import_translations.useTranslation)();
|
|
450
|
-
const user = (0, import__.useUser)({ or: "throw" });
|
|
451
|
-
const [isLoading, setIsLoading] = (0, import_react.useState)(true);
|
|
452
|
-
const [isRevokingAll, setIsRevokingAll] = (0, import_react.useState)(false);
|
|
453
|
-
const [sessions, setSessions] = (0, import_react.useState)([]);
|
|
454
|
-
const [showConfirmRevokeAll, setShowConfirmRevokeAll] = (0, import_react.useState)(false);
|
|
455
|
-
(0, import_react.useEffect)(() => {
|
|
456
|
-
(0, import_promises.runAsynchronously)(async () => {
|
|
457
|
-
setIsLoading(true);
|
|
458
|
-
const sessionsData = await user.getActiveSessions();
|
|
459
|
-
const enhancedSessions = sessionsData;
|
|
460
|
-
setSessions(enhancedSessions);
|
|
461
|
-
setIsLoading(false);
|
|
462
|
-
});
|
|
463
|
-
}, [user]);
|
|
464
|
-
const handleRevokeSession = async (sessionId) => {
|
|
465
|
-
try {
|
|
466
|
-
await user.revokeSession(sessionId);
|
|
467
|
-
setSessions(sessions.filter((session) => session.id !== sessionId));
|
|
468
|
-
} catch (error) {
|
|
469
|
-
(0, import_errors.captureError)("Failed to revoke session", { sessionId, error });
|
|
470
|
-
throw error;
|
|
471
|
-
}
|
|
472
|
-
};
|
|
473
|
-
const handleRevokeAllSessions = async () => {
|
|
474
|
-
setIsRevokingAll(true);
|
|
475
|
-
try {
|
|
476
|
-
const deletionPromises = sessions.filter((session) => !session.isCurrentSession).map((session) => user.revokeSession(session.id));
|
|
477
|
-
await Promise.all(deletionPromises);
|
|
478
|
-
setSessions((prevSessions) => prevSessions.filter((session) => session.isCurrentSession));
|
|
479
|
-
} catch (error) {
|
|
480
|
-
(0, import_errors.captureError)("Failed to revoke all sessions", { error, sessionIds: sessions.map((session) => session.id) });
|
|
481
|
-
throw error;
|
|
482
|
-
} finally {
|
|
483
|
-
setIsRevokingAll(false);
|
|
484
|
-
setShowConfirmRevokeAll(false);
|
|
485
|
-
}
|
|
486
|
-
};
|
|
487
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PageLayout, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
|
|
488
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex justify-between items-center mb-2", children: [
|
|
489
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Typography, { className: "font-medium", children: t("Active Sessions") }),
|
|
490
|
-
sessions.filter((s) => !s.isCurrentSession).length > 0 && !isLoading && (showConfirmRevokeAll ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex gap-2", children: [
|
|
491
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
492
|
-
import_stack_ui.Button,
|
|
493
|
-
{
|
|
494
|
-
variant: "destructive",
|
|
495
|
-
size: "sm",
|
|
496
|
-
loading: isRevokingAll,
|
|
497
|
-
onClick: handleRevokeAllSessions,
|
|
498
|
-
children: t("Confirm")
|
|
499
|
-
}
|
|
500
|
-
),
|
|
501
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
502
|
-
import_stack_ui.Button,
|
|
503
|
-
{
|
|
504
|
-
variant: "secondary",
|
|
505
|
-
size: "sm",
|
|
506
|
-
disabled: isRevokingAll,
|
|
507
|
-
onClick: () => setShowConfirmRevokeAll(false),
|
|
508
|
-
children: t("Cancel")
|
|
509
|
-
}
|
|
510
|
-
)
|
|
511
|
-
] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
512
|
-
import_stack_ui.Button,
|
|
513
|
-
{
|
|
514
|
-
variant: "outline",
|
|
515
|
-
size: "sm",
|
|
516
|
-
onClick: () => setShowConfirmRevokeAll(true),
|
|
517
|
-
children: t("Revoke All Other Sessions")
|
|
518
|
-
}
|
|
519
|
-
))
|
|
520
|
-
] }),
|
|
521
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Typography, { variant: "secondary", type: "footnote", className: "mb-4", children: t("These are devices where you're currently logged in. You can revoke access to end a session.") }),
|
|
522
|
-
isLoading ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Skeleton, { className: "h-[300px] w-full rounded-md" }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "border rounded-md", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_stack_ui.Table, { children: [
|
|
523
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.TableHeader, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_stack_ui.TableRow, { children: [
|
|
524
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.TableHead, { className: "w-[200px]", children: t("Session") }),
|
|
525
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.TableHead, { className: "w-[150px]", children: t("IP Address") }),
|
|
526
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.TableHead, { className: "w-[150px]", children: t("Location") }),
|
|
527
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.TableHead, { className: "w-[150px]", children: t("Last used") }),
|
|
528
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.TableHead, { className: "w-[80px]" })
|
|
529
|
-
] }) }),
|
|
530
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.TableBody, { children: sessions.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.TableRow, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.TableCell, { colSpan: 5, className: "text-center py-6", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Typography, { variant: "secondary", children: t("No active sessions found") }) }) }) : sessions.map((session) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_stack_ui.TableRow, { children: [
|
|
531
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.TableCell, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex flex-col", children: [
|
|
532
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Typography, { children: session.isCurrentSession ? t("Current Session") : t("Other Session") }),
|
|
533
|
-
session.isImpersonation && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Badge, { variant: "secondary", className: "w-fit mt-1", children: t("Impersonation") }),
|
|
534
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Typography, { variant: "secondary", type: "footnote", children: t("Signed in {time}", { time: new Date(session.createdAt).toLocaleDateString() }) })
|
|
535
|
-
] }) }),
|
|
536
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.TableCell, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Typography, { children: session.geoInfo?.ip || t("-") }) }),
|
|
537
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.TableCell, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Typography, { children: session.geoInfo?.cityName || t("Unknown") }) }),
|
|
538
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.TableCell, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex flex-col", children: [
|
|
539
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Typography, { children: session.lastUsedAt ? (0, import_dates.fromNow)(new Date(session.lastUsedAt)) : t("Never") }),
|
|
540
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Typography, { variant: "secondary", type: "footnote", title: session.lastUsedAt ? new Date(session.lastUsedAt).toLocaleString() : "", children: session.lastUsedAt ? new Date(session.lastUsedAt).toLocaleDateString() : "" })
|
|
541
|
-
] }) }),
|
|
542
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.TableCell, { align: "right", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
543
|
-
import_stack_ui.ActionCell,
|
|
544
|
-
{
|
|
545
|
-
items: [
|
|
546
|
-
{
|
|
547
|
-
item: t("Revoke"),
|
|
548
|
-
onClick: () => handleRevokeSession(session.id),
|
|
549
|
-
danger: true,
|
|
550
|
-
disabled: session.isCurrentSession,
|
|
551
|
-
disabledTooltip: session.isCurrentSession ? t("You cannot revoke your current session") : void 0
|
|
552
|
-
}
|
|
553
|
-
]
|
|
554
|
-
}
|
|
555
|
-
) })
|
|
556
|
-
] }, session.id)) })
|
|
557
|
-
] }) })
|
|
558
|
-
] }) });
|
|
559
|
-
}
|
|
560
136
|
function EmailsAndAuthPageSkeleton() {
|
|
561
137
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(PageLayout, { children: [
|
|
562
138
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Skeleton, { className: "h-9 w-full mt-1" }),
|
|
@@ -572,736 +148,6 @@ function ActiveSessionsPageSkeleton() {
|
|
|
572
148
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Skeleton, { className: "h-[200px] w-full mt-1 rounded-md" })
|
|
573
149
|
] });
|
|
574
150
|
}
|
|
575
|
-
function PasskeySection() {
|
|
576
|
-
const { t } = (0, import_translations.useTranslation)();
|
|
577
|
-
const user = (0, import__.useUser)({ or: "throw" });
|
|
578
|
-
const stackApp = (0, import__.useStackApp)();
|
|
579
|
-
const project = stackApp.useProject();
|
|
580
|
-
const contactChannels = user.useContactChannels();
|
|
581
|
-
const hasPasskey = user.passkeyAuthEnabled;
|
|
582
|
-
const isLastAuth = user.passkeyAuthEnabled && !user.hasPassword && user.oauthProviders.length === 0 && !user.otpAuthEnabled;
|
|
583
|
-
const [showConfirmationModal, setShowConfirmationModal] = (0, import_react.useState)(false);
|
|
584
|
-
const hasValidEmail = contactChannels.filter((x) => x.type === "email" && x.isVerified && x.usedForAuth).length > 0;
|
|
585
|
-
if (!project.config.passkeyEnabled) {
|
|
586
|
-
return null;
|
|
587
|
-
}
|
|
588
|
-
const handleDeletePasskey = async () => {
|
|
589
|
-
await user.update({ passkeyAuthEnabled: false });
|
|
590
|
-
setShowConfirmationModal(false);
|
|
591
|
-
};
|
|
592
|
-
const handleAddNewPasskey = async () => {
|
|
593
|
-
await user.registerPasskey();
|
|
594
|
-
};
|
|
595
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Section, { title: t("Passkey"), description: hasPasskey ? t("Passkey registered") : t("Register a passkey"), children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex md:justify-end gap-2", children: [
|
|
596
|
-
!hasValidEmail && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Typography, { variant: "secondary", type: "label", children: t("To enable Passkey sign-in, please add a verified sign-in email.") }),
|
|
597
|
-
hasValidEmail && hasPasskey && isLastAuth && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Typography, { variant: "secondary", type: "label", children: t("Passkey sign-in is enabled and cannot be disabled as it is currently the only sign-in method") }),
|
|
598
|
-
!hasPasskey && hasValidEmail && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Button, { onClick: handleAddNewPasskey, variant: "secondary", children: t("Add new passkey") }) }),
|
|
599
|
-
hasValidEmail && hasPasskey && !isLastAuth && !showConfirmationModal && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
600
|
-
import_stack_ui.Button,
|
|
601
|
-
{
|
|
602
|
-
variant: "secondary",
|
|
603
|
-
onClick: () => setShowConfirmationModal(true),
|
|
604
|
-
children: t("Delete Passkey")
|
|
605
|
-
}
|
|
606
|
-
),
|
|
607
|
-
hasValidEmail && hasPasskey && !isLastAuth && showConfirmationModal && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex flex-col gap-2", children: [
|
|
608
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Typography, { variant: "destructive", children: t("Are you sure you want to disable Passkey sign-in? You will not be able to sign in with your passkey anymore.") }),
|
|
609
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex gap-2", children: [
|
|
610
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
611
|
-
import_stack_ui.Button,
|
|
612
|
-
{
|
|
613
|
-
variant: "destructive",
|
|
614
|
-
onClick: handleDeletePasskey,
|
|
615
|
-
children: t("Disable")
|
|
616
|
-
}
|
|
617
|
-
),
|
|
618
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
619
|
-
import_stack_ui.Button,
|
|
620
|
-
{
|
|
621
|
-
variant: "secondary",
|
|
622
|
-
onClick: () => setShowConfirmationModal(false),
|
|
623
|
-
children: t("Cancel")
|
|
624
|
-
}
|
|
625
|
-
)
|
|
626
|
-
] })
|
|
627
|
-
] })
|
|
628
|
-
] }) }) });
|
|
629
|
-
}
|
|
630
|
-
function OtpSection() {
|
|
631
|
-
const { t } = (0, import_translations.useTranslation)();
|
|
632
|
-
const user = (0, import__.useUser)({ or: "throw" });
|
|
633
|
-
const project = (0, import__.useStackApp)().useProject();
|
|
634
|
-
const contactChannels = user.useContactChannels();
|
|
635
|
-
const isLastAuth = user.otpAuthEnabled && !user.hasPassword && user.oauthProviders.length === 0 && !user.passkeyAuthEnabled;
|
|
636
|
-
const [disabling, setDisabling] = (0, import_react.useState)(false);
|
|
637
|
-
const hasValidEmail = contactChannels.filter((x) => x.type === "email" && x.isVerified && x.usedForAuth).length > 0;
|
|
638
|
-
if (!project.config.magicLinkEnabled) {
|
|
639
|
-
return null;
|
|
640
|
-
}
|
|
641
|
-
const handleDisableOTP = async () => {
|
|
642
|
-
await user.update({ otpAuthEnabled: false });
|
|
643
|
-
setDisabling(false);
|
|
644
|
-
};
|
|
645
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Section, { title: t("OTP sign-in"), description: user.otpAuthEnabled ? t("OTP/magic link sign-in is currently enabled.") : t("Enable sign-in via magic link or OTP sent to your sign-in emails."), children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "flex md:justify-end", children: hasValidEmail ? user.otpAuthEnabled ? !isLastAuth ? !disabling ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
646
|
-
import_stack_ui.Button,
|
|
647
|
-
{
|
|
648
|
-
variant: "secondary",
|
|
649
|
-
onClick: () => setDisabling(true),
|
|
650
|
-
children: t("Disable OTP")
|
|
651
|
-
}
|
|
652
|
-
) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex flex-col gap-2", children: [
|
|
653
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Typography, { variant: "destructive", children: t("Are you sure you want to disable OTP sign-in? You will not be able to sign in with only emails anymore.") }),
|
|
654
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex gap-2", children: [
|
|
655
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
656
|
-
import_stack_ui.Button,
|
|
657
|
-
{
|
|
658
|
-
variant: "destructive",
|
|
659
|
-
onClick: handleDisableOTP,
|
|
660
|
-
children: t("Disable")
|
|
661
|
-
}
|
|
662
|
-
),
|
|
663
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
664
|
-
import_stack_ui.Button,
|
|
665
|
-
{
|
|
666
|
-
variant: "secondary",
|
|
667
|
-
onClick: () => setDisabling(false),
|
|
668
|
-
children: t("Cancel")
|
|
669
|
-
}
|
|
670
|
-
)
|
|
671
|
-
] })
|
|
672
|
-
] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Typography, { variant: "secondary", type: "label", children: t("OTP sign-in is enabled and cannot be disabled as it is currently the only sign-in method") }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
673
|
-
import_stack_ui.Button,
|
|
674
|
-
{
|
|
675
|
-
variant: "secondary",
|
|
676
|
-
onClick: async () => {
|
|
677
|
-
await user.update({ otpAuthEnabled: true });
|
|
678
|
-
},
|
|
679
|
-
children: t("Enable OTP")
|
|
680
|
-
}
|
|
681
|
-
) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Typography, { variant: "secondary", type: "label", children: t("To enable OTP sign-in, please add a verified sign-in email.") }) }) });
|
|
682
|
-
}
|
|
683
|
-
function SettingsPage() {
|
|
684
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(PageLayout, { children: [
|
|
685
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(DeleteAccountSection, {}),
|
|
686
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(SignOutSection, {})
|
|
687
|
-
] });
|
|
688
|
-
}
|
|
689
|
-
function PasswordSection() {
|
|
690
|
-
const { t } = (0, import_translations.useTranslation)();
|
|
691
|
-
const user = (0, import__.useUser)({ or: "throw" });
|
|
692
|
-
const contactChannels = user.useContactChannels();
|
|
693
|
-
const [changingPassword, setChangingPassword] = (0, import_react.useState)(false);
|
|
694
|
-
const [loading, setLoading] = (0, import_react.useState)(false);
|
|
695
|
-
const project = (0, import__.useStackApp)().useProject();
|
|
696
|
-
const passwordSchema = (0, import_schema_fields.yupObject)({
|
|
697
|
-
oldPassword: user.hasPassword ? import_schema_fields.passwordSchema.defined().nonEmpty(t("Please enter your old password")) : (0, import_schema_fields.yupString)(),
|
|
698
|
-
newPassword: import_schema_fields.passwordSchema.defined().nonEmpty(t("Please enter your password")).test({
|
|
699
|
-
name: "is-valid-password",
|
|
700
|
-
test: (value, ctx) => {
|
|
701
|
-
const error = (0, import_password.getPasswordError)(value);
|
|
702
|
-
if (error) {
|
|
703
|
-
return ctx.createError({ message: error.message });
|
|
704
|
-
} else {
|
|
705
|
-
return true;
|
|
706
|
-
}
|
|
707
|
-
}
|
|
708
|
-
}),
|
|
709
|
-
newPasswordRepeat: (0, import_schema_fields.yupString)().nullable().oneOf([yup.ref("newPassword"), "", null], t("Passwords do not match")).defined().nonEmpty(t("Please repeat your password"))
|
|
710
|
-
});
|
|
711
|
-
const { register, handleSubmit, setError, formState: { errors }, clearErrors, reset } = (0, import_react_hook_form.useForm)({
|
|
712
|
-
resolver: (0, import_yup.yupResolver)(passwordSchema)
|
|
713
|
-
});
|
|
714
|
-
const hasValidEmail = contactChannels.filter((x) => x.type === "email" && x.usedForAuth).length > 0;
|
|
715
|
-
const onSubmit = async (data) => {
|
|
716
|
-
setLoading(true);
|
|
717
|
-
try {
|
|
718
|
-
const { oldPassword, newPassword } = data;
|
|
719
|
-
const error = user.hasPassword ? await user.updatePassword({ oldPassword, newPassword }) : await user.setPassword({ password: newPassword });
|
|
720
|
-
if (error) {
|
|
721
|
-
setError("oldPassword", { type: "manual", message: t("Incorrect password") });
|
|
722
|
-
} else {
|
|
723
|
-
reset();
|
|
724
|
-
setChangingPassword(false);
|
|
725
|
-
}
|
|
726
|
-
} finally {
|
|
727
|
-
setLoading(false);
|
|
728
|
-
}
|
|
729
|
-
};
|
|
730
|
-
const registerPassword = register("newPassword");
|
|
731
|
-
const registerPasswordRepeat = register("newPasswordRepeat");
|
|
732
|
-
if (!project.config.credentialEnabled) {
|
|
733
|
-
return null;
|
|
734
|
-
}
|
|
735
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
736
|
-
Section,
|
|
737
|
-
{
|
|
738
|
-
title: t("Password"),
|
|
739
|
-
description: user.hasPassword ? t("Update your password") : t("Set a password for your account"),
|
|
740
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "flex flex-col gap-4", children: !changingPassword ? hasValidEmail ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
741
|
-
import_stack_ui.Button,
|
|
742
|
-
{
|
|
743
|
-
variant: "secondary",
|
|
744
|
-
onClick: () => setChangingPassword(true),
|
|
745
|
-
children: user.hasPassword ? t("Update password") : t("Set password")
|
|
746
|
-
}
|
|
747
|
-
) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Typography, { variant: "secondary", type: "label", children: t("To set a password, please add a sign-in email.") }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
748
|
-
"form",
|
|
749
|
-
{
|
|
750
|
-
onSubmit: (e) => (0, import_promises.runAsynchronouslyWithAlert)(handleSubmit(onSubmit)(e)),
|
|
751
|
-
noValidate: true,
|
|
752
|
-
children: [
|
|
753
|
-
user.hasPassword && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
754
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Label, { htmlFor: "old-password", className: "mb-1", children: t("Old password") }),
|
|
755
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
756
|
-
import_stack_ui.Input,
|
|
757
|
-
{
|
|
758
|
-
id: "old-password",
|
|
759
|
-
type: "password",
|
|
760
|
-
autoComplete: "current-password",
|
|
761
|
-
...register("oldPassword")
|
|
762
|
-
}
|
|
763
|
-
),
|
|
764
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_form_warning.FormWarningText, { text: errors.oldPassword?.message?.toString() })
|
|
765
|
-
] }),
|
|
766
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Label, { htmlFor: "new-password", className: "mt-4 mb-1", children: t("New password") }),
|
|
767
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
768
|
-
import_stack_ui.PasswordInput,
|
|
769
|
-
{
|
|
770
|
-
id: "new-password",
|
|
771
|
-
autoComplete: "new-password",
|
|
772
|
-
...registerPassword,
|
|
773
|
-
onChange: (e) => {
|
|
774
|
-
clearErrors("newPassword");
|
|
775
|
-
clearErrors("newPasswordRepeat");
|
|
776
|
-
(0, import_promises.runAsynchronously)(registerPassword.onChange(e));
|
|
777
|
-
}
|
|
778
|
-
}
|
|
779
|
-
),
|
|
780
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_form_warning.FormWarningText, { text: errors.newPassword?.message?.toString() }),
|
|
781
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Label, { htmlFor: "repeat-password", className: "mt-4 mb-1", children: t("Repeat new password") }),
|
|
782
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
783
|
-
import_stack_ui.PasswordInput,
|
|
784
|
-
{
|
|
785
|
-
id: "repeat-password",
|
|
786
|
-
autoComplete: "new-password",
|
|
787
|
-
...registerPasswordRepeat,
|
|
788
|
-
onChange: (e) => {
|
|
789
|
-
clearErrors("newPassword");
|
|
790
|
-
clearErrors("newPasswordRepeat");
|
|
791
|
-
(0, import_promises.runAsynchronously)(registerPasswordRepeat.onChange(e));
|
|
792
|
-
}
|
|
793
|
-
}
|
|
794
|
-
),
|
|
795
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_form_warning.FormWarningText, { text: errors.newPasswordRepeat?.message?.toString() }),
|
|
796
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "mt-6 flex gap-4", children: [
|
|
797
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Button, { type: "submit", loading, children: user.hasPassword ? t("Update Password") : t("Set Password") }),
|
|
798
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
799
|
-
import_stack_ui.Button,
|
|
800
|
-
{
|
|
801
|
-
variant: "secondary",
|
|
802
|
-
onClick: () => {
|
|
803
|
-
setChangingPassword(false);
|
|
804
|
-
reset();
|
|
805
|
-
},
|
|
806
|
-
children: t("Cancel")
|
|
807
|
-
}
|
|
808
|
-
)
|
|
809
|
-
] })
|
|
810
|
-
]
|
|
811
|
-
}
|
|
812
|
-
) })
|
|
813
|
-
}
|
|
814
|
-
);
|
|
815
|
-
}
|
|
816
|
-
function MfaSection() {
|
|
817
|
-
const { t } = (0, import_translations.useTranslation)();
|
|
818
|
-
const project = (0, import__.useStackApp)().useProject();
|
|
819
|
-
const user = (0, import__.useUser)({ or: "throw" });
|
|
820
|
-
const [generatedSecret, setGeneratedSecret] = (0, import_react.useState)(null);
|
|
821
|
-
const [qrCodeUrl, setQrCodeUrl] = (0, import_react.useState)(null);
|
|
822
|
-
const [mfaCode, setMfaCode] = (0, import_react.useState)("");
|
|
823
|
-
const [isMaybeWrong, setIsMaybeWrong] = (0, import_react.useState)(false);
|
|
824
|
-
const isEnabled = user.isMultiFactorRequired;
|
|
825
|
-
const [handleSubmit, isLoading] = (0, import_use_async_callback.useAsyncCallback)(async () => {
|
|
826
|
-
await user.update({
|
|
827
|
-
totpMultiFactorSecret: generatedSecret
|
|
828
|
-
});
|
|
829
|
-
setGeneratedSecret(null);
|
|
830
|
-
setQrCodeUrl(null);
|
|
831
|
-
setMfaCode("");
|
|
832
|
-
}, [generatedSecret, user]);
|
|
833
|
-
(0, import_react.useEffect)(() => {
|
|
834
|
-
setIsMaybeWrong(false);
|
|
835
|
-
(0, import_promises.runAsynchronouslyWithAlert)(async () => {
|
|
836
|
-
if (generatedSecret && (0, import_otp.verifyTOTP)(generatedSecret, 30, 6, mfaCode)) {
|
|
837
|
-
await handleSubmit();
|
|
838
|
-
}
|
|
839
|
-
setIsMaybeWrong(true);
|
|
840
|
-
});
|
|
841
|
-
}, [mfaCode, generatedSecret, handleSubmit]);
|
|
842
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
843
|
-
Section,
|
|
844
|
-
{
|
|
845
|
-
title: t("Multi-factor authentication"),
|
|
846
|
-
description: isEnabled ? t("Multi-factor authentication is currently enabled.") : t("Multi-factor authentication is currently disabled."),
|
|
847
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex flex-col gap-4", children: [
|
|
848
|
-
!isEnabled && generatedSecret && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
849
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Typography, { children: t("Scan this QR code with your authenticator app:") }),
|
|
850
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("img", { width: 200, height: 200, src: qrCodeUrl ?? (0, import_errors.throwErr)("TOTP QR code failed to generate"), alt: t("TOTP multi-factor authentication QR code") }),
|
|
851
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Typography, { children: t("Then, enter your six-digit MFA code:") }),
|
|
852
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
853
|
-
import_stack_ui.Input,
|
|
854
|
-
{
|
|
855
|
-
value: mfaCode,
|
|
856
|
-
onChange: (e) => {
|
|
857
|
-
setIsMaybeWrong(false);
|
|
858
|
-
setMfaCode(e.target.value);
|
|
859
|
-
},
|
|
860
|
-
placeholder: "123456",
|
|
861
|
-
maxLength: 6,
|
|
862
|
-
disabled: isLoading
|
|
863
|
-
}
|
|
864
|
-
),
|
|
865
|
-
isMaybeWrong && mfaCode.length === 6 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Typography, { variant: "destructive", children: t("Incorrect code. Please try again.") }),
|
|
866
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "flex", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
867
|
-
import_stack_ui.Button,
|
|
868
|
-
{
|
|
869
|
-
variant: "secondary",
|
|
870
|
-
onClick: () => {
|
|
871
|
-
setGeneratedSecret(null);
|
|
872
|
-
setQrCodeUrl(null);
|
|
873
|
-
setMfaCode("");
|
|
874
|
-
},
|
|
875
|
-
children: t("Cancel")
|
|
876
|
-
}
|
|
877
|
-
) })
|
|
878
|
-
] }),
|
|
879
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "flex gap-2", children: isEnabled ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
880
|
-
import_stack_ui.Button,
|
|
881
|
-
{
|
|
882
|
-
variant: "secondary",
|
|
883
|
-
onClick: async () => {
|
|
884
|
-
await user.update({
|
|
885
|
-
totpMultiFactorSecret: null
|
|
886
|
-
});
|
|
887
|
-
},
|
|
888
|
-
children: t("Disable MFA")
|
|
889
|
-
}
|
|
890
|
-
) : !generatedSecret && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
891
|
-
import_stack_ui.Button,
|
|
892
|
-
{
|
|
893
|
-
variant: "secondary",
|
|
894
|
-
onClick: async () => {
|
|
895
|
-
const secret = (0, import_crypto.generateRandomValues)(new Uint8Array(20));
|
|
896
|
-
setQrCodeUrl(await generateTotpQrCode(project, user, secret));
|
|
897
|
-
setGeneratedSecret(secret);
|
|
898
|
-
},
|
|
899
|
-
children: t("Enable MFA")
|
|
900
|
-
}
|
|
901
|
-
) })
|
|
902
|
-
] })
|
|
903
|
-
}
|
|
904
|
-
);
|
|
905
|
-
}
|
|
906
|
-
async function generateTotpQrCode(project, user, secret) {
|
|
907
|
-
const uri = (0, import_otp.createTOTPKeyURI)(project.displayName, user.primaryEmail ?? user.id, secret, 30, 6);
|
|
908
|
-
return await QRCode.toDataURL(uri);
|
|
909
|
-
}
|
|
910
|
-
function SignOutSection() {
|
|
911
|
-
const { t } = (0, import_translations.useTranslation)();
|
|
912
|
-
const user = (0, import__.useUser)({ or: "throw" });
|
|
913
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
914
|
-
Section,
|
|
915
|
-
{
|
|
916
|
-
title: t("Sign out"),
|
|
917
|
-
description: t("End your current session"),
|
|
918
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
919
|
-
import_stack_ui.Button,
|
|
920
|
-
{
|
|
921
|
-
variant: "secondary",
|
|
922
|
-
onClick: () => user.signOut(),
|
|
923
|
-
children: t("Sign out")
|
|
924
|
-
}
|
|
925
|
-
) })
|
|
926
|
-
}
|
|
927
|
-
);
|
|
928
|
-
}
|
|
929
|
-
function TeamPage(props) {
|
|
930
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(PageLayout, { children: [
|
|
931
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(TeamUserProfileSection, { team: props.team }, `user-profile-${props.team.id}`),
|
|
932
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(TeamProfileImageSection, { team: props.team }, `profile-image-${props.team.id}`),
|
|
933
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(TeamDisplayNameSection, { team: props.team }, `display-name-${props.team.id}`),
|
|
934
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(MemberListSection, { team: props.team }, `member-list-${props.team.id}`),
|
|
935
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(MemberInvitationSection, { team: props.team }, `member-invitation-${props.team.id}`),
|
|
936
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(TeamApiKeysSection, { team: props.team }, `api-keys-${props.team.id}`),
|
|
937
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(LeaveTeamSection, { team: props.team }, `leave-team-${props.team.id}`)
|
|
938
|
-
] });
|
|
939
|
-
}
|
|
940
|
-
function LeaveTeamSection(props) {
|
|
941
|
-
const { t } = (0, import_translations.useTranslation)();
|
|
942
|
-
const user = (0, import__.useUser)({ or: "redirect" });
|
|
943
|
-
const [leaving, setLeaving] = (0, import_react.useState)(false);
|
|
944
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
945
|
-
Section,
|
|
946
|
-
{
|
|
947
|
-
title: t("Leave Team"),
|
|
948
|
-
description: t("leave this team and remove your team profile"),
|
|
949
|
-
children: !leaving ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
950
|
-
import_stack_ui.Button,
|
|
951
|
-
{
|
|
952
|
-
variant: "secondary",
|
|
953
|
-
onClick: () => setLeaving(true),
|
|
954
|
-
children: t("Leave team")
|
|
955
|
-
}
|
|
956
|
-
) }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex flex-col gap-2", children: [
|
|
957
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Typography, { variant: "destructive", children: t("Are you sure you want to leave the team?") }),
|
|
958
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex gap-2", children: [
|
|
959
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
960
|
-
import_stack_ui.Button,
|
|
961
|
-
{
|
|
962
|
-
variant: "destructive",
|
|
963
|
-
onClick: async () => {
|
|
964
|
-
await user.leaveTeam(props.team);
|
|
965
|
-
window.location.reload();
|
|
966
|
-
},
|
|
967
|
-
children: t("Leave")
|
|
968
|
-
}
|
|
969
|
-
),
|
|
970
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
971
|
-
import_stack_ui.Button,
|
|
972
|
-
{
|
|
973
|
-
variant: "secondary",
|
|
974
|
-
onClick: () => setLeaving(false),
|
|
975
|
-
children: t("Cancel")
|
|
976
|
-
}
|
|
977
|
-
)
|
|
978
|
-
] })
|
|
979
|
-
] })
|
|
980
|
-
}
|
|
981
|
-
);
|
|
982
|
-
}
|
|
983
|
-
function TeamProfileImageSection(props) {
|
|
984
|
-
const { t } = (0, import_translations.useTranslation)();
|
|
985
|
-
const user = (0, import__.useUser)({ or: "redirect" });
|
|
986
|
-
const updateTeamPermission = user.usePermission(props.team, "$update_team");
|
|
987
|
-
if (!updateTeamPermission) {
|
|
988
|
-
return null;
|
|
989
|
-
}
|
|
990
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
991
|
-
Section,
|
|
992
|
-
{
|
|
993
|
-
title: t("Team profile image"),
|
|
994
|
-
description: t("Upload an image for your team"),
|
|
995
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
996
|
-
import_profile_image_editor.ProfileImageEditor,
|
|
997
|
-
{
|
|
998
|
-
user: props.team,
|
|
999
|
-
onProfileImageUrlChange: async (profileImageUrl) => {
|
|
1000
|
-
await props.team.update({ profileImageUrl });
|
|
1001
|
-
}
|
|
1002
|
-
}
|
|
1003
|
-
)
|
|
1004
|
-
}
|
|
1005
|
-
);
|
|
1006
|
-
}
|
|
1007
|
-
function TeamDisplayNameSection(props) {
|
|
1008
|
-
const { t } = (0, import_translations.useTranslation)();
|
|
1009
|
-
const user = (0, import__.useUser)({ or: "redirect" });
|
|
1010
|
-
const updateTeamPermission = user.usePermission(props.team, "$update_team");
|
|
1011
|
-
if (!updateTeamPermission) {
|
|
1012
|
-
return null;
|
|
1013
|
-
}
|
|
1014
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1015
|
-
Section,
|
|
1016
|
-
{
|
|
1017
|
-
title: t("Team display name"),
|
|
1018
|
-
description: t("Change the display name of your team"),
|
|
1019
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1020
|
-
EditableText,
|
|
1021
|
-
{
|
|
1022
|
-
value: props.team.displayName,
|
|
1023
|
-
onSave: async (newDisplayName) => await props.team.update({ displayName: newDisplayName })
|
|
1024
|
-
}
|
|
1025
|
-
)
|
|
1026
|
-
}
|
|
1027
|
-
);
|
|
1028
|
-
}
|
|
1029
|
-
function TeamUserProfileSection(props) {
|
|
1030
|
-
const { t } = (0, import_translations.useTranslation)();
|
|
1031
|
-
const user = (0, import__.useUser)({ or: "redirect" });
|
|
1032
|
-
const profile = user.useTeamProfile(props.team);
|
|
1033
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1034
|
-
Section,
|
|
1035
|
-
{
|
|
1036
|
-
title: t("Team user name"),
|
|
1037
|
-
description: t("Overwrite your user display name in this team"),
|
|
1038
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1039
|
-
EditableText,
|
|
1040
|
-
{
|
|
1041
|
-
value: profile.displayName || "",
|
|
1042
|
-
onSave: async (newDisplayName) => {
|
|
1043
|
-
await profile.update({ displayName: newDisplayName });
|
|
1044
|
-
}
|
|
1045
|
-
}
|
|
1046
|
-
)
|
|
1047
|
-
}
|
|
1048
|
-
);
|
|
1049
|
-
}
|
|
1050
|
-
function MemberInvitationSection(props) {
|
|
1051
|
-
const user = (0, import__.useUser)({ or: "redirect" });
|
|
1052
|
-
const inviteMemberPermission = user.usePermission(props.team, "$invite_members");
|
|
1053
|
-
if (!inviteMemberPermission) {
|
|
1054
|
-
return null;
|
|
1055
|
-
}
|
|
1056
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MemberInvitationSectionInner, { team: props.team });
|
|
1057
|
-
}
|
|
1058
|
-
function MemberInvitationsSectionInvitationsList(props) {
|
|
1059
|
-
const user = (0, import__.useUser)({ or: "redirect" });
|
|
1060
|
-
const { t } = (0, import_translations.useTranslation)();
|
|
1061
|
-
const invitationsToShow = props.team.useInvitations();
|
|
1062
|
-
const removeMemberPermission = user.usePermission(props.team, "$remove_members");
|
|
1063
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_stack_ui.Table, { className: "mt-6", children: [
|
|
1064
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.TableHeader, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_stack_ui.TableRow, { children: [
|
|
1065
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.TableHead, { className: "w-[200px]", children: t("Outstanding invitations") }),
|
|
1066
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.TableHead, { className: "w-[60px]", children: t("Expires") }),
|
|
1067
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.TableHead, { className: "w-[36px] max-w-[36px]" })
|
|
1068
|
-
] }) }),
|
|
1069
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_stack_ui.TableBody, { children: [
|
|
1070
|
-
invitationsToShow.map((invitation, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_stack_ui.TableRow, { children: [
|
|
1071
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.TableCell, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Typography, { children: invitation.recipientEmail }) }),
|
|
1072
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.TableCell, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Typography, { variant: "secondary", children: invitation.expiresAt.toLocaleString() }) }),
|
|
1073
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.TableCell, { align: "right", className: "max-w-[36px]", children: removeMemberPermission && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Button, { onClick: async () => await invitation.revoke(), size: "icon", variant: "ghost", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.Trash, { className: "w-4 h-4" }) }) })
|
|
1074
|
-
] }, invitation.id)),
|
|
1075
|
-
invitationsToShow.length === 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.TableRow, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.TableCell, { colSpan: 3, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Typography, { variant: "secondary", children: t("No outstanding invitations") }) }) })
|
|
1076
|
-
] })
|
|
1077
|
-
] }) });
|
|
1078
|
-
}
|
|
1079
|
-
function MemberInvitationSectionInner(props) {
|
|
1080
|
-
const user = (0, import__.useUser)({ or: "redirect" });
|
|
1081
|
-
const { t } = (0, import_translations.useTranslation)();
|
|
1082
|
-
const readMemberPermission = user.usePermission(props.team, "$read_members");
|
|
1083
|
-
const invitationSchema = (0, import_schema_fields.yupObject)({
|
|
1084
|
-
email: (0, import_schema_fields.strictEmailSchema)(t("Please enter a valid email address")).defined().nonEmpty(t("Please enter an email address"))
|
|
1085
|
-
});
|
|
1086
|
-
const { register, handleSubmit, formState: { errors }, watch } = (0, import_react_hook_form.useForm)({
|
|
1087
|
-
resolver: (0, import_yup.yupResolver)(invitationSchema)
|
|
1088
|
-
});
|
|
1089
|
-
const [loading, setLoading] = (0, import_react.useState)(false);
|
|
1090
|
-
const [invitedEmail, setInvitedEmail] = (0, import_react.useState)(null);
|
|
1091
|
-
const onSubmit = async (data) => {
|
|
1092
|
-
setLoading(true);
|
|
1093
|
-
try {
|
|
1094
|
-
await props.team.inviteUser({ email: data.email });
|
|
1095
|
-
setInvitedEmail(data.email);
|
|
1096
|
-
} finally {
|
|
1097
|
-
setLoading(false);
|
|
1098
|
-
}
|
|
1099
|
-
};
|
|
1100
|
-
(0, import_react.useEffect)(() => {
|
|
1101
|
-
setInvitedEmail(null);
|
|
1102
|
-
}, [watch("email")]);
|
|
1103
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
1104
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1105
|
-
Section,
|
|
1106
|
-
{
|
|
1107
|
-
title: t("Invite member"),
|
|
1108
|
-
description: t("Invite a user to your team through email"),
|
|
1109
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
1110
|
-
"form",
|
|
1111
|
-
{
|
|
1112
|
-
onSubmit: (e) => (0, import_promises.runAsynchronouslyWithAlert)(handleSubmit(onSubmit)(e)),
|
|
1113
|
-
noValidate: true,
|
|
1114
|
-
className: "w-full",
|
|
1115
|
-
children: [
|
|
1116
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex flex-col gap-4 sm:flex-row w-full", children: [
|
|
1117
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1118
|
-
import_stack_ui.Input,
|
|
1119
|
-
{
|
|
1120
|
-
placeholder: t("Email"),
|
|
1121
|
-
...register("email")
|
|
1122
|
-
}
|
|
1123
|
-
),
|
|
1124
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Button, { type: "submit", loading, children: t("Invite User") })
|
|
1125
|
-
] }),
|
|
1126
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_form_warning.FormWarningText, { text: errors.email?.message?.toString() }),
|
|
1127
|
-
invitedEmail && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_stack_ui.Typography, { type: "label", variant: "secondary", children: [
|
|
1128
|
-
"Invited ",
|
|
1129
|
-
invitedEmail
|
|
1130
|
-
] })
|
|
1131
|
-
]
|
|
1132
|
-
}
|
|
1133
|
-
)
|
|
1134
|
-
}
|
|
1135
|
-
),
|
|
1136
|
-
readMemberPermission && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MemberInvitationsSectionInvitationsList, { team: props.team })
|
|
1137
|
-
] });
|
|
1138
|
-
}
|
|
1139
|
-
function MemberListSection(props) {
|
|
1140
|
-
const user = (0, import__.useUser)({ or: "redirect" });
|
|
1141
|
-
const readMemberPermission = user.usePermission(props.team, "$read_members");
|
|
1142
|
-
const inviteMemberPermission = user.usePermission(props.team, "$invite_members");
|
|
1143
|
-
if (!readMemberPermission && !inviteMemberPermission) {
|
|
1144
|
-
return null;
|
|
1145
|
-
}
|
|
1146
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(MemberListSectionInner, { team: props.team });
|
|
1147
|
-
}
|
|
1148
|
-
function MemberListSectionInner(props) {
|
|
1149
|
-
const { t } = (0, import_translations.useTranslation)();
|
|
1150
|
-
const users = props.team.useUsers();
|
|
1151
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
|
|
1152
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Typography, { className: "font-medium mb-2", children: t("Members") }),
|
|
1153
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "border rounded-md", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_stack_ui.Table, { children: [
|
|
1154
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.TableHeader, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_stack_ui.TableRow, { children: [
|
|
1155
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.TableHead, { className: "w-[100px]", children: t("User") }),
|
|
1156
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.TableHead, { className: "w-[200px]", children: t("Name") })
|
|
1157
|
-
] }) }),
|
|
1158
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.TableBody, { children: users.map(({ id, teamProfile }, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_stack_ui.TableRow, { children: [
|
|
1159
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.TableCell, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_user_avatar.UserAvatar, { user: teamProfile }) }),
|
|
1160
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.TableCell, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Typography, { children: teamProfile.displayName }) })
|
|
1161
|
-
] }, id)) })
|
|
1162
|
-
] }) })
|
|
1163
|
-
] });
|
|
1164
|
-
}
|
|
1165
|
-
function TeamCreation() {
|
|
1166
|
-
const { t } = (0, import_translations.useTranslation)();
|
|
1167
|
-
const teamCreationSchema = (0, import_schema_fields.yupObject)({
|
|
1168
|
-
displayName: (0, import_schema_fields.yupString)().defined().nonEmpty(t("Please enter a team name"))
|
|
1169
|
-
});
|
|
1170
|
-
const { register, handleSubmit, formState: { errors } } = (0, import_react_hook_form.useForm)({
|
|
1171
|
-
resolver: (0, import_yup.yupResolver)(teamCreationSchema)
|
|
1172
|
-
});
|
|
1173
|
-
const app = (0, import__.useStackApp)();
|
|
1174
|
-
const project = app.useProject();
|
|
1175
|
-
const user = (0, import__.useUser)({ or: "redirect" });
|
|
1176
|
-
const navigate = app.useNavigate();
|
|
1177
|
-
const [loading, setLoading] = (0, import_react.useState)(false);
|
|
1178
|
-
if (!project.config.clientTeamCreationEnabled) {
|
|
1179
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import__.MessageCard, { title: t("Team creation is not enabled") });
|
|
1180
|
-
}
|
|
1181
|
-
const onSubmit = async (data) => {
|
|
1182
|
-
setLoading(true);
|
|
1183
|
-
let team;
|
|
1184
|
-
try {
|
|
1185
|
-
team = await user.createTeam({ displayName: data.displayName });
|
|
1186
|
-
} finally {
|
|
1187
|
-
setLoading(false);
|
|
1188
|
-
}
|
|
1189
|
-
navigate(`#team-${team.id}`);
|
|
1190
|
-
};
|
|
1191
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PageLayout, { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Section, { title: t("Create a Team"), description: t("Enter a display name for your new team"), children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
1192
|
-
"form",
|
|
1193
|
-
{
|
|
1194
|
-
onSubmit: (e) => (0, import_promises.runAsynchronouslyWithAlert)(handleSubmit(onSubmit)(e)),
|
|
1195
|
-
noValidate: true,
|
|
1196
|
-
className: "flex gap-2 flex-col sm:flex-row",
|
|
1197
|
-
children: [
|
|
1198
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex flex-col flex-1", children: [
|
|
1199
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1200
|
-
import_stack_ui.Input,
|
|
1201
|
-
{
|
|
1202
|
-
id: "displayName",
|
|
1203
|
-
type: "text",
|
|
1204
|
-
...register("displayName")
|
|
1205
|
-
}
|
|
1206
|
-
),
|
|
1207
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_form_warning.FormWarningText, { text: errors.displayName?.message?.toString() })
|
|
1208
|
-
] }),
|
|
1209
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Button, { type: "submit", loading, children: t("Create") })
|
|
1210
|
-
]
|
|
1211
|
-
}
|
|
1212
|
-
) }) });
|
|
1213
|
-
}
|
|
1214
|
-
function DeleteAccountSection() {
|
|
1215
|
-
const { t } = (0, import_translations.useTranslation)();
|
|
1216
|
-
const user = (0, import__.useUser)({ or: "redirect" });
|
|
1217
|
-
const app = (0, import__.useStackApp)();
|
|
1218
|
-
const project = app.useProject();
|
|
1219
|
-
const [deleting, setDeleting] = (0, import_react.useState)(false);
|
|
1220
|
-
if (!project.config.clientUserDeletionEnabled) {
|
|
1221
|
-
return null;
|
|
1222
|
-
}
|
|
1223
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1224
|
-
Section,
|
|
1225
|
-
{
|
|
1226
|
-
title: t("Delete Account"),
|
|
1227
|
-
description: t("Permanently remove your account and all associated data"),
|
|
1228
|
-
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "stack-scope flex flex-col items-stretch", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Accordion, { type: "single", collapsible: true, className: "w-full", children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_stack_ui.AccordionItem, { value: "item-1", children: [
|
|
1229
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.AccordionTrigger, { children: t("Danger zone") }),
|
|
1230
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.AccordionContent, { children: !deleting ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1231
|
-
import_stack_ui.Button,
|
|
1232
|
-
{
|
|
1233
|
-
variant: "destructive",
|
|
1234
|
-
onClick: () => setDeleting(true),
|
|
1235
|
-
children: t("Delete account")
|
|
1236
|
-
}
|
|
1237
|
-
) }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex flex-col gap-2", children: [
|
|
1238
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Typography, { variant: "destructive", children: t("Are you sure you want to delete your account? This action is IRREVERSIBLE and will delete ALL associated data.") }),
|
|
1239
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { className: "flex gap-2", children: [
|
|
1240
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1241
|
-
import_stack_ui.Button,
|
|
1242
|
-
{
|
|
1243
|
-
variant: "destructive",
|
|
1244
|
-
onClick: async () => {
|
|
1245
|
-
await user.delete();
|
|
1246
|
-
await app.redirectToHome();
|
|
1247
|
-
},
|
|
1248
|
-
children: t("Delete Account")
|
|
1249
|
-
}
|
|
1250
|
-
),
|
|
1251
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1252
|
-
import_stack_ui.Button,
|
|
1253
|
-
{
|
|
1254
|
-
variant: "secondary",
|
|
1255
|
-
onClick: () => setDeleting(false),
|
|
1256
|
-
children: t("Cancel")
|
|
1257
|
-
}
|
|
1258
|
-
)
|
|
1259
|
-
] })
|
|
1260
|
-
] }) })
|
|
1261
|
-
] }) }) })
|
|
1262
|
-
}
|
|
1263
|
-
);
|
|
1264
|
-
}
|
|
1265
|
-
function EditableText(props) {
|
|
1266
|
-
const [editing, setEditing] = (0, import_react.useState)(false);
|
|
1267
|
-
const [editingValue, setEditingValue] = (0, import_react.useState)(props.value);
|
|
1268
|
-
const { t } = (0, import_translations.useTranslation)();
|
|
1269
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "flex items-center gap-2", children: editing ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
1270
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1271
|
-
import_stack_ui.Input,
|
|
1272
|
-
{
|
|
1273
|
-
value: editingValue,
|
|
1274
|
-
onChange: (e) => setEditingValue(e.target.value)
|
|
1275
|
-
}
|
|
1276
|
-
),
|
|
1277
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1278
|
-
import_stack_ui.Button,
|
|
1279
|
-
{
|
|
1280
|
-
size: "sm",
|
|
1281
|
-
onClick: async () => {
|
|
1282
|
-
await props.onSave?.(editingValue);
|
|
1283
|
-
setEditing(false);
|
|
1284
|
-
},
|
|
1285
|
-
children: t("Save")
|
|
1286
|
-
}
|
|
1287
|
-
),
|
|
1288
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1289
|
-
import_stack_ui.Button,
|
|
1290
|
-
{
|
|
1291
|
-
size: "sm",
|
|
1292
|
-
variant: "secondary",
|
|
1293
|
-
onClick: () => {
|
|
1294
|
-
setEditingValue(props.value);
|
|
1295
|
-
setEditing(false);
|
|
1296
|
-
},
|
|
1297
|
-
children: t("Cancel")
|
|
1298
|
-
}
|
|
1299
|
-
)
|
|
1300
|
-
] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
|
|
1301
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Typography, { children: props.value }),
|
|
1302
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Button, { onClick: () => setEditing(true), size: "icon", variant: "ghost", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.Edit, { className: "w-4 h-4" }) })
|
|
1303
|
-
] }) });
|
|
1304
|
-
}
|
|
1305
151
|
function ApiKeysPageSkeleton() {
|
|
1306
152
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(PageLayout, { children: [
|
|
1307
153
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_stack_ui.Skeleton, { className: "h-9 w-full mt-1" }),
|
|
@@ -1324,9 +170,6 @@ function TeamCreationSkeleton() {
|
|
|
1324
170
|
}
|
|
1325
171
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1326
172
|
0 && (module.exports = {
|
|
1327
|
-
AccountSettings
|
|
1328
|
-
ApiKeysPage,
|
|
1329
|
-
EditableText,
|
|
1330
|
-
TeamApiKeysSection
|
|
173
|
+
AccountSettings
|
|
1331
174
|
});
|
|
1332
175
|
//# sourceMappingURL=account-settings.js.map
|