@protobase/ui 0.21.12 → 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 (35) 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/lists/use-paged-search.js +50 -0
  9. package/dist/organizations/all-organizations.js +41 -0
  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/organizations/organization-row.js +28 -0
  15. package/dist/organizations/organizations-page.js +4 -62
  16. package/dist/package.js +1 -1
  17. package/dist/people/person-row.js +26 -0
  18. package/dist/shell/assets/app-xO_xPXCq.js +69 -0
  19. package/dist/shell/assets/{index-GB5YRjlo.js → index-BvrIX29L.js} +1 -1
  20. package/dist/shell/assets/{maplibre-BLm41BrA.js → maplibre-DEGWdV6B.js} +1 -1
  21. package/dist/shell/assets/{markdown-content-Cia4jbSB.js → markdown-content-DdB7hUqJ.js} +1 -1
  22. package/dist/shell/assets/{record-map-C-Svn7nc.js → record-map-C_dz1z7c.js} +1 -1
  23. package/dist/shell/assets/{records-map-D5LZhC54.js → records-map-ByiKJWnW.js} +1 -1
  24. package/dist/shell/assets/{shared-protobase-ui-D16YbnPr.js → shared-protobase-ui-Di_9bo0v.js} +1 -1
  25. package/dist/shell/index.html +3 -3
  26. package/dist/shell/protobase.shell.json +1 -1
  27. package/dist/users/app-invitations.js +63 -0
  28. package/dist/users/create-user-form.js +86 -0
  29. package/dist/users/role-names.js +6 -0
  30. package/dist/users/use-user-roles.js +20 -0
  31. package/dist/users/user-list.js +34 -0
  32. package/dist/users/user-row.js +62 -0
  33. package/dist/users/users-block.js +30 -0
  34. package/package.json +4 -4
  35. package/dist/shell/assets/app-IuP56Dep.js +0 -69
@@ -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
  /**
@@ -0,0 +1,28 @@
1
+ import { Badge } from "../primitives/badge.js";
2
+ import { Button } from "../primitives/button.js";
3
+ import { jsx, jsxs } from "react/jsx-runtime";
4
+ //#region src/organizations/organization-row.tsx
5
+ /** An organization with Open, or a Current badge when the person works in it. */
6
+ const OrganizationRow = ({ organization, current, busy, onOpen }) => /* @__PURE__ */ jsxs("li", {
7
+ className: "flex items-center gap-3 px-3 py-2.5",
8
+ children: [/* @__PURE__ */ jsxs("span", {
9
+ className: "min-w-0 flex-1",
10
+ children: [/* @__PURE__ */ jsx("span", {
11
+ className: "block truncate font-medium",
12
+ children: organization.name
13
+ }), /* @__PURE__ */ jsx("span", {
14
+ className: "block text-xs text-muted-foreground",
15
+ children: organization.slug
16
+ })]
17
+ }), current ? /* @__PURE__ */ jsx(Badge, {
18
+ tone: "green",
19
+ children: "Current"
20
+ }) : /* @__PURE__ */ jsx(Button, {
21
+ size: "sm",
22
+ loading: busy,
23
+ onClick: onOpen,
24
+ children: "Open"
25
+ })]
26
+ });
27
+ //#endregion
28
+ export { OrganizationRow };
@@ -1,4 +1,3 @@
1
- import { Badge } from "../primitives/badge.js";
2
1
  import { Input } from "../primitives/input.js";
3
2
  import { PageHeader } from "../app-shell/page-header.js";
4
3
  import { useAuth } from "../auth/auth-provider.js";
@@ -8,33 +7,13 @@ import { roleLabel } from "./organization-paths.js";
8
7
  import { useAction } from "./use-action.js";
9
8
  import { isAdmin } from "../auth/user-to-shell.js";
10
9
  import { reopenIn } from "./use-organizations.js";
10
+ import { OrganizationRow } from "./organization-row.js";
11
+ import { AllOrganizations } from "./all-organizations.js";
11
12
  import { useEffect, useState } from "react";
12
- import { Search } from "lucide-react";
13
13
  import { jsx, jsxs } from "react/jsx-runtime";
14
14
  //#region src/organizations/organizations-page.tsx
15
15
  /** A slug from a name: lowercase ASCII words joined by dashes, as the server accepts them. */
16
16
  const slugOf = (name) => name.normalize("NFKD").replace(/[̀-ͯ]/g, "").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 63);
17
- const OrganizationRow = ({ organization, current, busy, onOpen }) => /* @__PURE__ */ jsxs("li", {
18
- className: "flex items-center gap-3 px-3 py-2.5",
19
- children: [/* @__PURE__ */ jsxs("span", {
20
- className: "min-w-0 flex-1",
21
- children: [/* @__PURE__ */ jsx("span", {
22
- className: "block truncate font-medium",
23
- children: organization.name
24
- }), /* @__PURE__ */ jsx("span", {
25
- className: "block text-xs text-muted-foreground",
26
- children: organization.slug
27
- })]
28
- }), current ? /* @__PURE__ */ jsx(Badge, {
29
- tone: "green",
30
- children: "Current"
31
- }) : /* @__PURE__ */ jsx(Button, {
32
- size: "sm",
33
- loading: busy,
34
- onClick: onOpen,
35
- children: "Open"
36
- })]
37
- });
38
17
  const NewOrganization = ({ basePath }) => {
39
18
  const { session } = useAuth();
40
19
  const [name, setName] = useState("");
@@ -82,43 +61,6 @@ const NewOrganization = ({ basePath }) => {
82
61
  })]
83
62
  });
84
63
  };
85
- const FindOrganization = ({ basePath, currentId }) => {
86
- const { session } = useAuth();
87
- const [query, setQuery] = useState("");
88
- const [found, setFound] = useState([]);
89
- const { busy, error, run } = useAction();
90
- useEffect(() => {
91
- const timer = setTimeout(() => void session.organizations.search(query).then(setFound, () => setFound([])), 200);
92
- return () => clearTimeout(timer);
93
- }, [session, query]);
94
- return /* @__PURE__ */ jsxs(AccountSection, {
95
- title: "Find an organization",
96
- description: "Your global roles let you work in any organization, with what those roles allow there. Opening one you are not a member of is written to the audit log.",
97
- children: [
98
- /* @__PURE__ */ jsx(SectionError, { message: error }),
99
- /* @__PURE__ */ jsx(Input, {
100
- "aria-label": "Find an organization",
101
- placeholder: "Name or slug",
102
- value: query,
103
- onChange: (event) => setQuery(event.target.value),
104
- leading: /* @__PURE__ */ jsx(Search, { className: "size-3.5" }),
105
- wrapperClassName: "mb-3"
106
- }),
107
- found.length > 0 && /* @__PURE__ */ jsx("ul", {
108
- className: "divide-y rounded-md border",
109
- children: found.map((organization) => /* @__PURE__ */ jsx(OrganizationRow, {
110
- organization,
111
- current: organization.id === currentId,
112
- busy,
113
- onOpen: () => void run(async () => {
114
- await session.organizations.switchTo(organization.id);
115
- reopenIn(basePath);
116
- })
117
- }, organization.id))
118
- })
119
- ]
120
- });
121
- };
122
64
  const YourInvitations = () => {
123
65
  const { session, organizations } = useAuth();
124
66
  const [invitations, setInvitations] = useState([]);
@@ -152,7 +94,7 @@ const YourInvitations = () => {
152
94
  };
153
95
  /**
154
96
  * The person's organizations: switching between them, making a new one when the app allows it, and for people with a
155
- * global role finding any organization. Someone in none sees their invitations here.
97
+ * global role every organization. Someone in none sees their invitations here.
156
98
  */
157
99
  const OrganizationsPage = ({ state, basePath }) => {
158
100
  const { session, organizations: settings, state: auth } = useAuth();
@@ -185,7 +127,7 @@ const OrganizationsPage = ({ state, basePath }) => {
185
127
  }),
186
128
  /* @__PURE__ */ jsx(YourInvitations, {}),
187
129
  mayCreate && /* @__PURE__ */ jsx(NewOrganization, { basePath }),
188
- (state.current?.globalRoles.length ?? 0) > 0 && /* @__PURE__ */ jsx(FindOrganization, {
130
+ (state.current?.globalRoles.length ?? 0) > 0 && /* @__PURE__ */ jsx(AllOrganizations, {
189
131
  basePath,
190
132
  currentId
191
133
  })
package/dist/package.js CHANGED
@@ -1,5 +1,5 @@
1
1
  //#region package.json
2
- var version = "0.21.12";
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 };