@protobase/ui 0.22.0 → 0.22.1

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.
Files changed (33) hide show
  1. package/dist/app/pages/action-href.js +1 -1
  2. package/dist/app/pages/render-node.js +3 -1
  3. package/dist/app/pages/use-run-action.js +3 -3
  4. package/dist/invitations/invite-form.js +56 -0
  5. package/dist/invitations/link-notice.js +12 -0
  6. package/dist/invitations/pending-invitation-row.js +54 -0
  7. package/dist/lists/paged-search-list.js +42 -0
  8. package/dist/{organizations/use-all-organizations.js → lists/use-paged-search.js} +11 -10
  9. package/dist/organizations/all-organizations.js +24 -45
  10. package/dist/organizations/app-roles-picker.js +5 -2
  11. package/dist/organizations/invitation-page.js +59 -16
  12. package/dist/organizations/invitations-page.js +41 -116
  13. package/dist/organizations/members-page.js +40 -54
  14. package/dist/package.js +1 -1
  15. package/dist/people/person-row.js +26 -0
  16. package/dist/shell/assets/app-xO_xPXCq.js +69 -0
  17. package/dist/shell/assets/{index-DB2bNsqa.js → index-BvrIX29L.js} +1 -1
  18. package/dist/shell/assets/{maplibre-DRs-NHQ9.js → maplibre-DEGWdV6B.js} +1 -1
  19. package/dist/shell/assets/{markdown-content-obHEqNW8.js → markdown-content-DdB7hUqJ.js} +1 -1
  20. package/dist/shell/assets/{record-map-zJ2TD-WG.js → record-map-C_dz1z7c.js} +1 -1
  21. package/dist/shell/assets/{records-map-BVaFY5HA.js → records-map-ByiKJWnW.js} +1 -1
  22. package/dist/shell/assets/{shared-protobase-ui-DWzrRp3Z.js → shared-protobase-ui-Di_9bo0v.js} +1 -1
  23. package/dist/shell/index.html +3 -3
  24. package/dist/shell/protobase.shell.json +1 -1
  25. package/dist/users/app-invitations.js +63 -0
  26. package/dist/users/create-user-form.js +86 -0
  27. package/dist/users/role-names.js +6 -0
  28. package/dist/users/use-user-roles.js +20 -0
  29. package/dist/users/user-list.js +34 -0
  30. package/dist/users/user-row.js +62 -0
  31. package/dist/users/users-block.js +30 -0
  32. package/package.json +4 -4
  33. package/dist/shell/assets/app-DFxg-FwV.js +0 -69
@@ -1,142 +1,67 @@
1
- import { Input } from "../primitives/input.js";
2
1
  import { PageHeader } from "../app-shell/page-header.js";
3
2
  import { useAuth } from "../auth/auth-provider.js";
4
- import { Button } from "../primitives/button.js";
5
- import { AccountSection, SectionError } from "../account/account-section.js";
3
+ import { AccountSection } from "../account/account-section.js";
6
4
  import { grantableRoles, organizationRoleLabels, roleLabel } from "./organization-paths.js";
7
- import { useAction } from "./use-action.js";
5
+ import { InviteForm } from "../invitations/invite-form.js";
6
+ import { PendingInvitationRow } from "../invitations/pending-invitation-row.js";
8
7
  import { AppRolesPicker } from "./app-roles-picker.js";
9
8
  import { assignableOrganizationRoles } from "./members-page.js";
10
9
  import { useCallback, useEffect, useState } from "react";
11
10
  import { jsx, jsxs } from "react/jsx-runtime";
12
11
  //#region src/organizations/invitations-page.tsx
13
- const LinkNotice = ({ link }) => link ? /* @__PURE__ */ jsxs("p", {
14
- className: "mt-3 break-all rounded-md border bg-muted px-3 py-2 text-xs",
15
- children: ["This app cannot send email, so pass this link on yourself. It works once, until the invitation expires: ", /* @__PURE__ */ jsx("span", {
16
- className: "font-mono",
17
- children: link
18
- })]
19
- }) : null;
20
- const InviteForm = ({ current, onSent }) => {
12
+ const InviteToOrganization = ({ current, onSent }) => {
21
13
  const { session, organizations } = useAuth();
22
14
  const roles = organizations?.roles ?? [];
23
- const [email, setEmail] = useState("");
24
15
  const [role, setRole] = useState("member");
25
16
  const [appRoles, setAppRoles] = useState([]);
26
- const [link, setLink] = useState();
27
- const { busy, error, run } = useAction();
28
- const invite = () => run(async () => {
17
+ const invite = async (email) => {
29
18
  const sent = await session.organizations.invite(current.organization.id, {
30
- email: email.trim(),
19
+ email,
31
20
  role,
32
21
  appRoles
33
22
  });
34
- setLink(sent.link);
35
- setEmail("");
36
23
  setAppRoles([]);
37
24
  onSent();
38
- });
39
- return /* @__PURE__ */ jsxs(AccountSection, {
25
+ return sent;
26
+ };
27
+ return /* @__PURE__ */ jsx(InviteForm, {
40
28
  title: "Invite someone",
41
29
  description: "They get an email with a link to join, or to make an account first. You can give the app roles you hold, and those your roles grant.",
42
- children: [
43
- /* @__PURE__ */ jsx(SectionError, { message: error }),
44
- /* @__PURE__ */ jsxs("form", {
45
- className: "flex flex-col gap-3",
46
- onSubmit: (event) => {
47
- event.preventDefault();
48
- invite();
49
- },
50
- children: [/* @__PURE__ */ jsxs("div", {
51
- className: "flex flex-col gap-2 sm:flex-row",
52
- children: [
53
- /* @__PURE__ */ jsx(Input, {
54
- "aria-label": "Email",
55
- type: "email",
56
- placeholder: "name@example.com",
57
- value: email,
58
- onChange: (event) => setEmail(event.target.value),
59
- wrapperClassName: "flex-1"
60
- }),
61
- /* @__PURE__ */ jsx("select", {
62
- "aria-label": "Organization role",
63
- value: role,
64
- onChange: (event) => setRole(event.target.value),
65
- className: "h-8 rounded-md border border-border-strong bg-background px-2",
66
- children: assignableOrganizationRoles(current).map((option) => /* @__PURE__ */ jsx("option", {
67
- value: option,
68
- children: organizationRoleLabels[option]
69
- }, option))
70
- }),
71
- /* @__PURE__ */ jsx(Button, {
72
- type: "submit",
73
- variant: "primary",
74
- loading: busy,
75
- disabled: !email.trim(),
76
- children: "Invite"
77
- })
78
- ]
79
- }), /* @__PURE__ */ jsx(AppRolesPicker, {
80
- roles,
81
- selected: appRoles,
82
- grantable: grantableRoles(roles, current),
83
- onChange: setAppRoles
84
- })]
85
- }),
86
- /* @__PURE__ */ jsx(LinkNotice, { link })
87
- ]
30
+ onInvite: invite,
31
+ beside: /* @__PURE__ */ jsx("select", {
32
+ "aria-label": "Organization role",
33
+ value: role,
34
+ onChange: (event) => setRole(event.target.value),
35
+ className: "h-8 rounded-md border border-border-strong bg-background px-2",
36
+ children: assignableOrganizationRoles(current).map((option) => /* @__PURE__ */ jsx("option", {
37
+ value: option,
38
+ children: organizationRoleLabels[option]
39
+ }, option))
40
+ }),
41
+ children: /* @__PURE__ */ jsx(AppRolesPicker, {
42
+ roles: roles.filter((candidate) => candidate.membership),
43
+ selected: appRoles,
44
+ grantable: grantableRoles(roles, current),
45
+ onChange: setAppRoles
46
+ })
88
47
  });
89
48
  };
90
49
  const PendingRow = ({ invitation, organizationId, onChanged }) => {
91
50
  const { session, organizations } = useAuth();
92
- const [link, setLink] = useState();
93
- const { busy, error, run } = useAction();
94
- const expired = new Date(invitation.expiresAt).getTime() < Date.now();
95
- return /* @__PURE__ */ jsxs("li", {
96
- className: "px-3 py-2.5",
97
- children: [
98
- /* @__PURE__ */ jsxs("div", {
99
- className: "flex flex-wrap items-center gap-2",
100
- children: [
101
- /* @__PURE__ */ jsxs("span", {
102
- className: "min-w-0 flex-1",
103
- children: [/* @__PURE__ */ jsx("span", {
104
- className: "block truncate font-medium",
105
- children: invitation.email
106
- }), /* @__PURE__ */ jsxs("span", {
107
- className: "block text-xs text-muted-foreground",
108
- children: [
109
- organizationRoleLabels[invitation.role],
110
- invitation.appRoles.length > 0 && ` · ${invitation.appRoles.map((name) => roleLabel(organizations?.roles ?? [], name)).join(", ")}`,
111
- " · ",
112
- expired ? "Expired" : `Until ${new Date(invitation.expiresAt).toLocaleDateString()}`
113
- ]
114
- })]
115
- }),
116
- /* @__PURE__ */ jsx(Button, {
117
- size: "sm",
118
- loading: busy,
119
- onClick: () => void run(async () => {
120
- setLink((await session.organizations.resend(organizationId, invitation)).link);
121
- onChanged();
122
- }),
123
- children: "Send again"
124
- }),
125
- /* @__PURE__ */ jsx(Button, {
126
- size: "sm",
127
- variant: "danger",
128
- loading: busy,
129
- onClick: () => void run(async () => {
130
- await session.organizations.revoke(invitation.id);
131
- onChanged();
132
- }),
133
- children: "Revoke"
134
- })
135
- ]
136
- }),
137
- /* @__PURE__ */ jsx(SectionError, { message: error }),
138
- /* @__PURE__ */ jsx(LinkNotice, { link })
139
- ]
51
+ const appRoles = invitation.appRoles.map((name) => roleLabel(organizations?.roles ?? [], name));
52
+ return /* @__PURE__ */ jsx(PendingInvitationRow, {
53
+ email: invitation.email,
54
+ gives: `${organizationRoleLabels[invitation.role]}${appRoles.length > 0 ? ` · ${appRoles.join(", ")}` : ""}`,
55
+ expiresAt: invitation.expiresAt,
56
+ onResend: async () => {
57
+ const sent = await session.organizations.resend(organizationId, invitation);
58
+ onChanged();
59
+ return sent;
60
+ },
61
+ onRevoke: async () => {
62
+ await session.organizations.revoke(invitation.id);
63
+ onChanged();
64
+ }
140
65
  });
141
66
  };
142
67
  /** Inviting people to the organization one works in, and the invitations not yet accepted, for its owners and admins. */
@@ -156,7 +81,7 @@ const InvitationsPage = ({ current }) => {
156
81
  title: "Invitations",
157
82
  subtitle: `People invited to ${current.organization.name}.`
158
83
  }),
159
- /* @__PURE__ */ jsx(InviteForm, {
84
+ /* @__PURE__ */ jsx(InviteToOrganization, {
160
85
  current,
161
86
  onSent: load
162
87
  }),
@@ -7,9 +7,10 @@ import { AccountSection, SectionError } from "../account/account-section.js";
7
7
  import { grantableRoles, organizationRoleLabels } from "./organization-paths.js";
8
8
  import { useAction } from "./use-action.js";
9
9
  import { AppRolesPicker } from "./app-roles-picker.js";
10
+ import { PersonRow } from "../people/person-row.js";
10
11
  import { useCallback, useEffect, useState } from "react";
11
12
  import { AuthError } from "@protobase/client";
12
- import { jsx, jsxs } from "react/jsx-runtime";
13
+ import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
13
14
  //#region src/organizations/members-page.tsx
14
15
  /** The organization roles the person may set: owners every one, admins those below owner. */
15
16
  const assignableOrganizationRoles = (current) => current?.role === "owner" ? [
@@ -25,61 +26,46 @@ const MemberRow = ({ member, current, onChanged }) => {
25
26
  const self = member.id === current.memberId;
26
27
  const organizationRoles = assignableOrganizationRoles(current);
27
28
  const roleLocked = member.role === "owner" && current.role !== "owner";
28
- return /* @__PURE__ */ jsxs("li", {
29
- className: "flex flex-col gap-2 px-3 py-3",
30
- children: [
31
- /* @__PURE__ */ jsxs("div", {
32
- className: "flex flex-wrap items-center gap-2",
33
- children: [
34
- /* @__PURE__ */ jsxs("span", {
35
- className: "min-w-0 flex-1",
36
- children: [/* @__PURE__ */ jsxs("span", {
37
- className: "block truncate font-medium",
38
- children: [member.name, self && " (you)"]
39
- }), /* @__PURE__ */ jsx("span", {
40
- className: "block truncate text-xs text-muted-foreground",
41
- children: member.email
42
- })]
43
- }),
44
- /* @__PURE__ */ jsx("select", {
45
- "aria-label": `Organization role of ${member.name}`,
46
- value: member.role,
47
- disabled: busy || roleLocked,
48
- onChange: (event) => void run(async () => {
49
- await session.organizations.setRole(organizationId, member.id, event.target.value);
50
- onChanged();
51
- }),
52
- className: "h-7 rounded-md border border-border-strong bg-background px-2 text-xs",
53
- children: (roleLocked ? [member.role] : organizationRoles).map((role) => /* @__PURE__ */ jsx("option", {
54
- value: role,
55
- children: organizationRoleLabels[role]
56
- }, role))
57
- }),
58
- !self && /* @__PURE__ */ jsx(Button, {
59
- size: "sm",
60
- variant: "danger",
61
- loading: busy,
62
- disabled: roleLocked,
63
- onClick: () => void run(async () => {
64
- await session.organizations.removeMember(organizationId, member.id);
65
- onChanged();
66
- }),
67
- children: "Remove"
68
- })
69
- ]
29
+ const membershipRoles = roles.filter((role) => role.membership);
30
+ return /* @__PURE__ */ jsx(PersonRow, {
31
+ name: member.name,
32
+ email: member.email,
33
+ self,
34
+ error,
35
+ actions: /* @__PURE__ */ jsxs(Fragment$1, { children: [/* @__PURE__ */ jsx("select", {
36
+ "aria-label": `Organization role of ${member.name}`,
37
+ value: member.role,
38
+ disabled: busy || roleLocked,
39
+ onChange: (event) => void run(async () => {
40
+ await session.organizations.setRole(organizationId, member.id, event.target.value);
41
+ onChanged();
70
42
  }),
71
- /* @__PURE__ */ jsx(AppRolesPicker, {
72
- roles,
73
- selected: member.appRoles,
74
- grantable: grantableRoles(roles, current),
75
- disabled: busy,
76
- onChange: (selected) => void run(async () => {
77
- await session.organizations.setAppRoles(member.id, selected);
78
- onChanged();
79
- })
43
+ className: "h-7 rounded-md border border-border-strong bg-background px-2 text-xs",
44
+ children: (roleLocked ? [member.role] : organizationRoles).map((role) => /* @__PURE__ */ jsx("option", {
45
+ value: role,
46
+ children: organizationRoleLabels[role]
47
+ }, role))
48
+ }), !self && /* @__PURE__ */ jsx(Button, {
49
+ size: "sm",
50
+ variant: "danger",
51
+ loading: busy,
52
+ disabled: roleLocked,
53
+ onClick: () => void run(async () => {
54
+ await session.organizations.removeMember(organizationId, member.id);
55
+ onChanged();
80
56
  }),
81
- /* @__PURE__ */ jsx(SectionError, { message: error })
82
- ]
57
+ children: "Remove"
58
+ })] }),
59
+ children: /* @__PURE__ */ jsx(AppRolesPicker, {
60
+ roles: membershipRoles,
61
+ selected: member.appRoles,
62
+ grantable: grantableRoles(roles, current),
63
+ disabled: busy,
64
+ onChange: (selected) => void run(async () => {
65
+ await session.organizations.setAppRoles(member.id, selected);
66
+ onChanged();
67
+ })
68
+ })
83
69
  });
84
70
  };
85
71
  /**
package/dist/package.js CHANGED
@@ -1,5 +1,5 @@
1
1
  //#region package.json
2
- var version = "0.22.0";
2
+ var version = "0.22.1";
3
3
  var protobase = { "pluginApi": 1 };
4
4
  //#endregion
5
5
  export { protobase, version };
@@ -0,0 +1,26 @@
1
+ import { SectionError } from "../account/account-section.js";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ //#region src/people/person-row.tsx
4
+ /** Someone in a list of people: their name, " (you)" on one's own row, their address, what can be done and their roles. */
5
+ const PersonRow = ({ name, email, self, actions, children, error }) => /* @__PURE__ */ jsxs("li", {
6
+ className: "flex flex-col gap-2 px-3 py-3",
7
+ children: [
8
+ /* @__PURE__ */ jsxs("div", {
9
+ className: "flex flex-wrap items-center gap-2",
10
+ children: [/* @__PURE__ */ jsxs("span", {
11
+ className: "min-w-0 flex-1",
12
+ children: [/* @__PURE__ */ jsxs("span", {
13
+ className: "block truncate font-medium",
14
+ children: [name, self && " (you)"]
15
+ }), /* @__PURE__ */ jsx("span", {
16
+ className: "block truncate text-xs text-muted-foreground",
17
+ children: email
18
+ })]
19
+ }), actions]
20
+ }),
21
+ children,
22
+ /* @__PURE__ */ jsx(SectionError, { message: error })
23
+ ]
24
+ });
25
+ //#endregion
26
+ export { PersonRow };