@protobase/ui 0.53.7 → 0.53.8

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.
@@ -16,11 +16,15 @@ import { jsx } from "react/jsx-runtime";
16
16
  const useAccountPage = () => {
17
17
  const { account } = useAdminMeta();
18
18
  const auth = useAuth();
19
- const { mcp } = auth;
19
+ const { mcp, accountDeletion } = auth;
20
20
  const linkable = linkableProviders(auth).length > 0;
21
21
  return useMemo(() => {
22
22
  if (!account) return void 0;
23
- const hidden = /* @__PURE__ */ new Set([...mcp ? [] : ["ConnectedApps"], ...linkable ? [] : ["ConnectedAccounts"]]);
23
+ const hidden = /* @__PURE__ */ new Set([
24
+ ...mcp ? [] : ["ConnectedApps"],
25
+ ...linkable ? [] : ["ConnectedAccounts"],
26
+ ...accountDeletion ? [] : ["DeleteAccount"]
27
+ ]);
24
28
  return {
25
29
  ...account,
26
30
  tree: shownAccountTree(account.tree, hidden)
@@ -28,7 +32,8 @@ const useAccountPage = () => {
28
32
  }, [
29
33
  account,
30
34
  mcp,
31
- linkable
35
+ linkable,
36
+ accountDeletion
32
37
  ]);
33
38
  };
34
39
  /** `/-/account`, and `/-/account/<tab>` for one of its tabs: the signed-in person's account page. */
@@ -0,0 +1,22 @@
1
+ import { text } from "../messages.js";
2
+ import { useAuth } from "../../auth/auth-provider.js";
3
+ import { AccountLoading } from "./account-loading.js";
4
+ import { useAccountData } from "./use-account-data.js";
5
+ import { DeleteAccountSection } from "../delete-account-section.js";
6
+ import { useCallback } from "react";
7
+ import { jsx } from "react/jsx-runtime";
8
+ //#region src/account/blocks/delete-account-block.tsx
9
+ /** The `DeleteAccount` block: the signed-in person deletes their own account, which signs them out; staff signed in as them cannot. */
10
+ const DeleteAccountBlock = () => {
11
+ const { session, state, deleteAccount } = useAuth();
12
+ const { account } = session;
13
+ const { data: methods, error } = useAccountData(useCallback(() => account.signInMethods(), [account]), text("couldNotReadMethods"));
14
+ if (!methods) return /* @__PURE__ */ jsx(AccountLoading, { error });
15
+ return /* @__PURE__ */ jsx(DeleteAccountSection, {
16
+ hasPassword: methods.account.password,
17
+ staff: state.kind === "signed-in" && Boolean(state.staff),
18
+ onDelete: deleteAccount
19
+ });
20
+ };
21
+ //#endregion
22
+ export { DeleteAccountBlock };
@@ -0,0 +1,71 @@
1
+ import { text } from "./messages.js";
2
+ import { Button } from "../primitives/button.js";
3
+ import { Dialog } from "../primitives/dialog.js";
4
+ import { Input } from "../primitives/input.js";
5
+ import { SectionError } from "./account-section.js";
6
+ import { authMessage } from "../auth/auth-messages.js";
7
+ import { useId, useState } from "react";
8
+ import { AuthError } from "@protobase/client";
9
+ import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
10
+ //#region src/account/delete-account-dialog.tsx
11
+ const deletionMessage = (error) => {
12
+ if (error.code === "LAST_ADMIN") return text("deleteAccountLastAdmin");
13
+ if (error.code === "ACCOUNT_LOCKED") return text("deleteAccountLocked");
14
+ return authMessage(error);
15
+ };
16
+ /** Asks the person to confirm deleting their own account, with their password when it has one. */
17
+ const DeleteAccountDialog = ({ hasPassword, onDelete, onClose }) => {
18
+ const [password, setPassword] = useState("");
19
+ const [busy, setBusy] = useState(false);
20
+ const [error, setError] = useState();
21
+ const formId = useId();
22
+ const confirm = async () => {
23
+ setBusy(true);
24
+ setError(void 0);
25
+ try {
26
+ await onDelete(hasPassword ? password : void 0);
27
+ } catch (failure) {
28
+ if (!(failure instanceof AuthError)) throw failure;
29
+ setError(deletionMessage(failure));
30
+ setBusy(false);
31
+ }
32
+ };
33
+ return /* @__PURE__ */ jsx(Dialog, {
34
+ title: text("deleteAccountQuestion"),
35
+ description: text("deleteAccountWarning"),
36
+ onClose,
37
+ actions: /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx(Button, {
38
+ onClick: onClose,
39
+ children: text("cancel")
40
+ }), /* @__PURE__ */ jsx(Button, {
41
+ variant: "danger",
42
+ type: "submit",
43
+ form: formId,
44
+ loading: busy,
45
+ disabled: hasPassword && !password,
46
+ children: text("deleteAccountConfirm")
47
+ })] }),
48
+ children: /* @__PURE__ */ jsxs("form", {
49
+ id: formId,
50
+ className: "flex flex-col gap-4",
51
+ onSubmit: (event) => {
52
+ event.preventDefault();
53
+ confirm();
54
+ },
55
+ children: [hasPassword && /* @__PURE__ */ jsxs("label", {
56
+ className: "flex flex-col gap-1.5 font-medium",
57
+ children: [text("deleteAccountPassword"), /* @__PURE__ */ jsx(Input, {
58
+ type: "password",
59
+ name: "current-password",
60
+ autoComplete: "current-password",
61
+ autoFocus: true,
62
+ value: password,
63
+ invalid: Boolean(error),
64
+ onChange: (event) => setPassword(event.target.value)
65
+ })]
66
+ }), /* @__PURE__ */ jsx(SectionError, { message: error })]
67
+ })
68
+ });
69
+ };
70
+ //#endregion
71
+ export { DeleteAccountDialog };
@@ -0,0 +1,26 @@
1
+ import { text } from "./messages.js";
2
+ import { Button } from "../primitives/button.js";
3
+ import { AccountSection } from "./account-section.js";
4
+ import { DeleteAccountDialog } from "./delete-account-dialog.js";
5
+ import { useState } from "react";
6
+ import { jsx, jsxs } from "react/jsx-runtime";
7
+ //#region src/account/delete-account-section.tsx
8
+ /** Deletes the signed-in person's own account, once they confirm it in a dialog. */
9
+ const DeleteAccountSection = ({ hasPassword, staff, onDelete }) => {
10
+ const [confirming, setConfirming] = useState(false);
11
+ return /* @__PURE__ */ jsxs(AccountSection, {
12
+ title: text("deleteAccount"),
13
+ description: staff ? text("deleteAccountStaff") : text("deleteAccountDescription"),
14
+ children: [!staff && /* @__PURE__ */ jsx(Button, {
15
+ variant: "danger",
16
+ onClick: () => setConfirming(true),
17
+ children: text("deleteAccount")
18
+ }), confirming && /* @__PURE__ */ jsx(DeleteAccountDialog, {
19
+ hasPassword,
20
+ onDelete,
21
+ onClose: () => setConfirming(false)
22
+ })]
23
+ });
24
+ };
25
+ //#endregion
26
+ export { DeleteAccountSection };
@@ -153,7 +153,16 @@ const text = defineMessages({
153
153
  mcpAddress: "MCP address",
154
154
  mcpAddressDescription: "Add this address to an AI assistant or code editor that speaks MCP, as a custom connector or remote MCP server. It opens this app in your browser, where you sign in and allow it; it then works with your access.",
155
155
  copyMcpAddress: "Copy the MCP address",
156
- addToClaude: "Add to Claude"
156
+ addToClaude: "Add to Claude",
157
+ deleteAccount: "Delete account",
158
+ deleteAccountDescription: "Delete your account and sign out everywhere. You can no longer sign in with it, and this cannot be undone.",
159
+ deleteAccountStaff: "Staff signed in as someone cannot delete their account.",
160
+ deleteAccountQuestion: "Delete your account?",
161
+ deleteAccountWarning: "Your account, its sign-in methods and every device signed in to it are deleted. This cannot be undone.",
162
+ deleteAccountPassword: "Enter your password to confirm.",
163
+ deleteAccountConfirm: "Delete my account",
164
+ deleteAccountLastAdmin: "You are the last admin of this app. Make someone else an admin first.",
165
+ deleteAccountLocked: "This account is locked, so it cannot be deleted."
157
166
  },
158
167
  nl: {
159
168
  account: "Account",
@@ -307,7 +316,16 @@ const text = defineMessages({
307
316
  mcpAddress: "MCP-adres",
308
317
  mcpAddressDescription: "Voeg dit adres toe aan een AI-assistent of code-editor die MCP spreekt, als aangepaste connector of externe MCP-server. Die opent deze app in je browser, waar je inlogt en hem toestaat; daarna werkt hij met jouw toegang.",
309
318
  copyMcpAddress: "Het MCP-adres kopiëren",
310
- addToClaude: "Toevoegen aan Claude"
319
+ addToClaude: "Toevoegen aan Claude",
320
+ deleteAccount: "Account verwijderen",
321
+ deleteAccountDescription: "Verwijder je account en log overal uit. Je kunt er daarna niet meer mee inloggen, en dit kan niet ongedaan worden gemaakt.",
322
+ deleteAccountStaff: "Een medewerker die als iemand anders is ingelogd, kan diens account niet verwijderen.",
323
+ deleteAccountQuestion: "Je account verwijderen?",
324
+ deleteAccountWarning: "Je account, je inlogmethoden en elk apparaat dat ermee is ingelogd worden verwijderd. Dit kan niet ongedaan worden gemaakt.",
325
+ deleteAccountPassword: "Vul je wachtwoord in om te bevestigen.",
326
+ deleteAccountConfirm: "Mijn account verwijderen",
327
+ deleteAccountLastAdmin: "Je bent de laatste beheerder van deze app. Maak eerst iemand anders beheerder.",
328
+ deleteAccountLocked: "Dit account is vergrendeld en kan dus niet worden verwijderd."
311
329
  }
312
330
  });
313
331
  //#endregion
@@ -24,6 +24,7 @@ import { TabBlock, TabsBlock } from "./blocks/tabs.js";
24
24
  import { UsersBlock } from "../../users/users-block.js";
25
25
  import { ConnectedAccountsBlock } from "../../account/blocks/connected-accounts-block.js";
26
26
  import { ConnectedAppsBlock } from "../../account/blocks/connected-apps-block.js";
27
+ import { DeleteAccountBlock } from "../../account/blocks/delete-account-block.js";
27
28
  import { DevicesBlock } from "../../account/blocks/devices-block.js";
28
29
  import { PasskeysBlock } from "../../account/blocks/passkeys-block.js";
29
30
  import { PasswordBlock } from "../../account/blocks/password-block.js";
@@ -61,7 +62,8 @@ const blocks = {
61
62
  TwoFactor: TwoFactorBlock,
62
63
  ConnectedAccounts: ConnectedAccountsBlock,
63
64
  Devices: DevicesBlock,
64
- ConnectedApps: ConnectedAppsBlock
65
+ ConnectedApps: ConnectedAppsBlock,
66
+ DeleteAccount: DeleteAccountBlock
65
67
  };
66
68
  const isInline = (child) => typeof child === "string" || !child.custom && child.type === "Link";
67
69
  /** Text and links run together as one paragraph, like a sentence with a link in it; other elements stand alone. */
@@ -100,6 +100,7 @@ const messages = {
100
100
  PREVIOUSLY_REGISTERED: "passkeyAdded",
101
101
  ERROR_AUTHENTICATOR_PREVIOUSLY_REGISTERED: "passkeyAdded",
102
102
  SESSION_NOT_FRESH: "notFresh",
103
+ SESSION_EXPIRED: "notFresh",
103
104
  PASSWORD_COMPROMISED: "passwordLeaked"
104
105
  };
105
106
  const lookup = (ids, code) => {
@@ -25,6 +25,7 @@ const AuthProvider = ({ session: given, origin, nativeBridge, children }) => {
25
25
  const [signInFirst, setSignInFirst] = useState("password");
26
26
  const [passwordReset, setPasswordReset] = useState(false);
27
27
  const [changeEmail, setChangeEmail] = useState(false);
28
+ const [accountDeletion, setAccountDeletion] = useState(false);
28
29
  const [mcp, setMcp] = useState();
29
30
  const [canSignUp, setCanSignUp] = useState(false);
30
31
  const [passkeyDomains, setPasskeyDomains] = useState([]);
@@ -110,6 +111,7 @@ const AuthProvider = ({ session: given, origin, nativeBridge, children }) => {
110
111
  setSignInFirst(status.signInFirst ?? "password");
111
112
  setPasswordReset(status.passwordReset && !nativeBridge);
112
113
  setChangeEmail(Boolean(status.changeEmail));
114
+ setAccountDeletion(Boolean(status.accountDeletion));
113
115
  setMcp(status.mcp);
114
116
  setCanSignUp(Boolean(status.signUp));
115
117
  setPasskeyDomains(status.passkeyDomains ?? []);
@@ -141,6 +143,7 @@ const AuthProvider = ({ session: given, origin, nativeBridge, children }) => {
141
143
  signInFirst,
142
144
  passwordReset,
143
145
  changeEmail,
146
+ accountDeletion,
144
147
  mcp,
145
148
  canSignUp,
146
149
  passkeyDomains,
@@ -190,6 +193,11 @@ const AuthProvider = ({ session: given, origin, nativeBridge, children }) => {
190
193
  await session.signOut();
191
194
  setState(signedOutState());
192
195
  },
196
+ deleteAccount: async (password) => {
197
+ await session.deleteAccount(password);
198
+ await push.forget();
199
+ setState(signedOutState());
200
+ },
193
201
  push
194
202
  }), [
195
203
  push,
@@ -199,6 +207,7 @@ const AuthProvider = ({ session: given, origin, nativeBridge, children }) => {
199
207
  signInFirst,
200
208
  passwordReset,
201
209
  changeEmail,
210
+ accountDeletion,
202
211
  mcp,
203
212
  canSignUp,
204
213
  passkeyDomains,
package/dist/package.js CHANGED
@@ -1,5 +1,5 @@
1
1
  //#region package.json
2
- var version = "0.53.7";
2
+ var version = "0.53.8";
3
3
  var protobase = { "pluginApi": 1 };
4
4
  //#endregion
5
5
  export { protobase, version };