@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.
- package/dist/app/pages/action-href.js +1 -1
- package/dist/app/pages/render-node.js +3 -1
- package/dist/app/pages/use-run-action.js +3 -3
- package/dist/invitations/invite-form.js +56 -0
- package/dist/invitations/link-notice.js +12 -0
- package/dist/invitations/pending-invitation-row.js +54 -0
- package/dist/lists/paged-search-list.js +42 -0
- package/dist/lists/use-paged-search.js +50 -0
- package/dist/organizations/all-organizations.js +41 -0
- package/dist/organizations/app-roles-picker.js +5 -2
- package/dist/organizations/invitation-page.js +59 -16
- package/dist/organizations/invitations-page.js +41 -116
- package/dist/organizations/members-page.js +40 -54
- package/dist/organizations/organization-row.js +28 -0
- package/dist/organizations/organizations-page.js +4 -62
- package/dist/package.js +1 -1
- package/dist/people/person-row.js +26 -0
- package/dist/shell/assets/app-xO_xPXCq.js +69 -0
- package/dist/shell/assets/{index-GB5YRjlo.js → index-BvrIX29L.js} +1 -1
- package/dist/shell/assets/{maplibre-BLm41BrA.js → maplibre-DEGWdV6B.js} +1 -1
- package/dist/shell/assets/{markdown-content-Cia4jbSB.js → markdown-content-DdB7hUqJ.js} +1 -1
- package/dist/shell/assets/{record-map-C-Svn7nc.js → record-map-C_dz1z7c.js} +1 -1
- package/dist/shell/assets/{records-map-D5LZhC54.js → records-map-ByiKJWnW.js} +1 -1
- package/dist/shell/assets/{shared-protobase-ui-D16YbnPr.js → shared-protobase-ui-Di_9bo0v.js} +1 -1
- package/dist/shell/index.html +3 -3
- package/dist/shell/protobase.shell.json +1 -1
- package/dist/users/app-invitations.js +63 -0
- package/dist/users/create-user-form.js +86 -0
- package/dist/users/role-names.js +6 -0
- package/dist/users/use-user-roles.js +20 -0
- package/dist/users/user-list.js +34 -0
- package/dist/users/user-row.js +62 -0
- package/dist/users/users-block.js +30 -0
- package/package.json +4 -4
- 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
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
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
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
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
|
-
|
|
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
|
|
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(
|
|
130
|
+
(state.current?.globalRoles.length ?? 0) > 0 && /* @__PURE__ */ jsx(AllOrganizations, {
|
|
189
131
|
basePath,
|
|
190
132
|
currentId
|
|
191
133
|
})
|
package/dist/package.js
CHANGED
|
@@ -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 };
|