@protobase/ui 0.22.0 → 0.22.2
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/hand-order/hand-order.js +1 -1
- package/dist/app/incoming-relations.js +1 -1
- 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/app/record-page.js +9 -5
- package/dist/app/related-section.js +11 -6
- package/dist/app/related-value.js +14 -0
- 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/{organizations/use-all-organizations.js → lists/use-paged-search.js} +11 -10
- package/dist/organizations/all-organizations.js +24 -45
- 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/package.js +1 -1
- package/dist/people/person-row.js +26 -0
- package/dist/shell/assets/app-ByxJVEUs.js +69 -0
- package/dist/shell/assets/{index-DB2bNsqa.js → index-Dkcn0zHH.js} +1 -1
- package/dist/shell/assets/{maplibre-DRs-NHQ9.js → maplibre-CAXiHBgj.js} +1 -1
- package/dist/shell/assets/{markdown-content-obHEqNW8.js → markdown-content-B7XDhlLn.js} +1 -1
- package/dist/shell/assets/{record-map-zJ2TD-WG.js → record-map-BcLvshZy.js} +1 -1
- package/dist/shell/assets/{records-map-BVaFY5HA.js → records-map-BYeO0Ega.js} +1 -1
- package/dist/shell/assets/{shared-protobase-ui-DWzrRp3Z.js → shared-protobase-ui-CVanihMA.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-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 {
|
|
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 {
|
|
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
|
|
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
|
|
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
|
|
19
|
+
email,
|
|
31
20
|
role,
|
|
32
21
|
appRoles
|
|
33
22
|
});
|
|
34
|
-
setLink(sent.link);
|
|
35
|
-
setEmail("");
|
|
36
23
|
setAppRoles([]);
|
|
37
24
|
onSent();
|
|
38
|
-
|
|
39
|
-
|
|
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
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
children: [
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
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
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
/**
|
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 };
|