apiblaze 0.18.3 → 0.18.7
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/index.js +583 -507
- package/dist/react/index.js +366 -233
- package/dist/react/index.mjs +366 -233
- package/dist/server/index.d.mts +21 -0
- package/dist/server/index.d.ts +21 -0
- package/dist/server/index.js +29 -8
- package/dist/server/index.mjs +29 -8
- package/package.json +3 -3
package/dist/react/index.mjs
CHANGED
|
@@ -39,14 +39,79 @@ function relTime(iso) {
|
|
|
39
39
|
if (d < 30) return `${d}d ago`;
|
|
40
40
|
return new Date(iso).toLocaleDateString();
|
|
41
41
|
}
|
|
42
|
+
function Typeahead(props) {
|
|
43
|
+
const [q, setQ] = React.useState("");
|
|
44
|
+
const [results, setResults] = React.useState(null);
|
|
45
|
+
const [loading, setLoading] = React.useState(false);
|
|
46
|
+
const seq = React.useRef(0);
|
|
47
|
+
React.useEffect(() => {
|
|
48
|
+
const term = q.trim();
|
|
49
|
+
if (term.length < 2) {
|
|
50
|
+
setResults(null);
|
|
51
|
+
setLoading(false);
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
const my = ++seq.current;
|
|
55
|
+
setLoading(true);
|
|
56
|
+
const h = setTimeout(async () => {
|
|
57
|
+
try {
|
|
58
|
+
const r = await props.search(term);
|
|
59
|
+
if (my === seq.current) {
|
|
60
|
+
setResults(r);
|
|
61
|
+
setLoading(false);
|
|
62
|
+
}
|
|
63
|
+
} catch {
|
|
64
|
+
if (my === seq.current) {
|
|
65
|
+
setResults([]);
|
|
66
|
+
setLoading(false);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}, 250);
|
|
70
|
+
return () => clearTimeout(h);
|
|
71
|
+
}, [q]);
|
|
72
|
+
return /* @__PURE__ */ jsxs("div", { style: { position: "relative", flex: 1, minWidth: 180 }, children: [
|
|
73
|
+
/* @__PURE__ */ jsx(
|
|
74
|
+
"input",
|
|
75
|
+
{
|
|
76
|
+
style: { ...props.s.input, width: "100%" },
|
|
77
|
+
placeholder: props.placeholder,
|
|
78
|
+
value: q,
|
|
79
|
+
onChange: (e) => setQ(e.target.value),
|
|
80
|
+
disabled: props.disabled,
|
|
81
|
+
"aria-label": props.placeholder
|
|
82
|
+
}
|
|
83
|
+
),
|
|
84
|
+
q.trim().length >= 2 && /* @__PURE__ */ jsxs("div", { style: { position: "absolute", zIndex: 5, left: 0, right: 0, top: "100%", marginTop: 4, background: props.s.surface, border: `1px solid ${props.s.border}`, borderRadius: 8, maxHeight: 220, overflowY: "auto", boxShadow: "0 6px 18px rgba(0,0,0,0.12)" }, children: [
|
|
85
|
+
loading && /* @__PURE__ */ jsx("div", { style: { padding: "8px 10px", fontSize: 12, color: props.s.muted }, children: "Searching\u2026" }),
|
|
86
|
+
!loading && results && results.length === 0 && /* @__PURE__ */ jsx("div", { style: { padding: "8px 10px", fontSize: 12, color: props.s.muted }, children: "No matches" }),
|
|
87
|
+
!loading && results && results.map((it) => /* @__PURE__ */ jsx(
|
|
88
|
+
"button",
|
|
89
|
+
{
|
|
90
|
+
onMouseDown: (e) => e.preventDefault(),
|
|
91
|
+
onClick: () => {
|
|
92
|
+
props.onPick(it);
|
|
93
|
+
setQ("");
|
|
94
|
+
setResults(null);
|
|
95
|
+
},
|
|
96
|
+
style: { display: "block", width: "100%", textAlign: "left", padding: "8px 10px", border: "none", borderBottom: `1px solid ${props.s.border}`, background: "transparent", cursor: "pointer", color: props.s.text, fontFamily: "inherit", fontSize: 13 },
|
|
97
|
+
children: props.itemLabel(it)
|
|
98
|
+
},
|
|
99
|
+
props.itemKey(it)
|
|
100
|
+
))
|
|
101
|
+
] })
|
|
102
|
+
] });
|
|
103
|
+
}
|
|
42
104
|
var UsersGroupsWidget = React.forwardRef(function UsersGroupsWidget2({ endpoint = "/api/apiblaze/groups", theme, title = "Users & groups", className }, ref) {
|
|
43
105
|
const t = { ...DEFAULTS, ...theme ?? {} };
|
|
44
106
|
const headerBg = t.headerBackground || t.surface;
|
|
45
107
|
const [access, setAccess] = React.useState("ok");
|
|
46
108
|
const [myEmail, setMyEmail] = React.useState(null);
|
|
47
109
|
const [groups, setGroups] = React.useState(null);
|
|
48
|
-
const [
|
|
110
|
+
const [groupTotal, setGroupTotal] = React.useState(0);
|
|
111
|
+
const [groupQuery, setGroupQuery] = React.useState("");
|
|
49
112
|
const [observed, setObserved] = React.useState([]);
|
|
113
|
+
const [observedNew, setObservedNew] = React.useState(0);
|
|
114
|
+
const [observedLoaded, setObservedLoaded] = React.useState(false);
|
|
50
115
|
const [loadError, setLoadError] = React.useState(null);
|
|
51
116
|
const [open, setOpen] = React.useState(null);
|
|
52
117
|
const [detail, setDetail] = React.useState(null);
|
|
@@ -54,15 +119,13 @@ var UsersGroupsWidget = React.forwardRef(function UsersGroupsWidget2({ endpoint
|
|
|
54
119
|
const [creating, setCreating] = React.useState(false);
|
|
55
120
|
const [newName, setNewName] = React.useState("");
|
|
56
121
|
const [showCreate, setShowCreate] = React.useState(false);
|
|
122
|
+
const [showNewUser, setShowNewUser] = React.useState(false);
|
|
123
|
+
const [newUserRef, setNewUserRef] = React.useState("");
|
|
57
124
|
const [pending, setPending] = React.useState(null);
|
|
58
125
|
const [err, setErr] = React.useState(null);
|
|
59
126
|
const [confirmDelete, setConfirmDelete] = React.useState(null);
|
|
60
|
-
const [showTraffic, setShowTraffic] = React.useState(false);
|
|
61
|
-
const [addSel, setAddSel] = React.useState("");
|
|
62
127
|
const [addRole, setAddRole] = React.useState("member");
|
|
63
|
-
const [nestSel, setNestSel] = React.useState("");
|
|
64
128
|
const [admins, setAdmins] = React.useState([]);
|
|
65
|
-
const [showAdmins, setShowAdmins] = React.useState(false);
|
|
66
129
|
const [newAdmin, setNewAdmin] = React.useState("");
|
|
67
130
|
const call = React.useCallback(async (body) => {
|
|
68
131
|
const res = await fetch(endpoint, body ? { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(body) } : { cache: "no-store" });
|
|
@@ -76,9 +139,11 @@ var UsersGroupsWidget = React.forwardRef(function UsersGroupsWidget2({ endpoint
|
|
|
76
139
|
const d = await call();
|
|
77
140
|
setAccess(d.access === "pending" ? "pending" : "ok");
|
|
78
141
|
setMyEmail(d.email ?? null);
|
|
79
|
-
setGroups(
|
|
80
|
-
|
|
81
|
-
|
|
142
|
+
setGroups(d.groups ?? []);
|
|
143
|
+
setGroupTotal(d.groupTotal ?? (d.groups?.length ?? 0));
|
|
144
|
+
setObservedNew(d.observedNew ?? 0);
|
|
145
|
+
setObservedLoaded(false);
|
|
146
|
+
setGroupQuery("");
|
|
82
147
|
if (d.access !== "pending") {
|
|
83
148
|
const a = await call({ action: "list-admins" });
|
|
84
149
|
setAdmins(a.admin_emails ?? []);
|
|
@@ -92,12 +157,26 @@ var UsersGroupsWidget = React.forwardRef(function UsersGroupsWidget2({ endpoint
|
|
|
92
157
|
load();
|
|
93
158
|
}, [load]);
|
|
94
159
|
React.useImperativeHandle(ref, () => ({ refresh: load }), [load]);
|
|
160
|
+
const searchGroups = React.useCallback(async (q) => {
|
|
161
|
+
setGroupQuery(q);
|
|
162
|
+
const d = await call({ action: "list-groups", q, offset: 0 });
|
|
163
|
+
setGroups(d.groups ?? []);
|
|
164
|
+
setGroupTotal(d.total ?? (d.groups?.length ?? 0));
|
|
165
|
+
}, [call]);
|
|
166
|
+
const loadMoreGroups = React.useCallback(async () => {
|
|
167
|
+
const d = await call({ action: "list-groups", q: groupQuery, offset: groups?.length ?? 0 });
|
|
168
|
+
setGroups((prev) => [...prev ?? [], ...d.groups ?? []]);
|
|
169
|
+
if (typeof d.total === "number") setGroupTotal(d.total);
|
|
170
|
+
}, [call, groupQuery, groups]);
|
|
171
|
+
const loadObserved = React.useCallback(async () => {
|
|
172
|
+
const d = await call({ action: "list-observed", offset: 0 });
|
|
173
|
+
setObserved(d.identities ?? []);
|
|
174
|
+
setObservedLoaded(true);
|
|
175
|
+
}, [call]);
|
|
95
176
|
const loadDetail = React.useCallback(async (groupId) => {
|
|
96
177
|
setDetailLoading(true);
|
|
97
178
|
setDetail(null);
|
|
98
179
|
setErr(null);
|
|
99
|
-
setAddSel("");
|
|
100
|
-
setNestSel("");
|
|
101
180
|
try {
|
|
102
181
|
setDetail(await call({ action: "group-detail", groupId }));
|
|
103
182
|
} catch (e) {
|
|
@@ -106,6 +185,14 @@ var UsersGroupsWidget = React.forwardRef(function UsersGroupsWidget2({ endpoint
|
|
|
106
185
|
setDetailLoading(false);
|
|
107
186
|
}
|
|
108
187
|
}, [call]);
|
|
188
|
+
const reloadAdmins = React.useCallback(async () => {
|
|
189
|
+
const a = await call({ action: "list-admins" });
|
|
190
|
+
setAdmins(a.admin_emails ?? []);
|
|
191
|
+
}, [call]);
|
|
192
|
+
const loadMoreMembers = React.useCallback(async (groupId, offset) => {
|
|
193
|
+
const d = await call({ action: "group-members", groupId, offset });
|
|
194
|
+
setDetail((prev) => prev && prev.id === groupId ? { ...prev, members: [...prev.members, ...d.members ?? []] } : prev);
|
|
195
|
+
}, [call]);
|
|
109
196
|
function toggle(g) {
|
|
110
197
|
if (open === g.id) {
|
|
111
198
|
setOpen(null);
|
|
@@ -120,7 +207,6 @@ var UsersGroupsWidget = React.forwardRef(function UsersGroupsWidget2({ endpoint
|
|
|
120
207
|
setErr(null);
|
|
121
208
|
try {
|
|
122
209
|
await call(body);
|
|
123
|
-
await load();
|
|
124
210
|
if (after) await after();
|
|
125
211
|
} catch (e) {
|
|
126
212
|
setErr(e instanceof Error ? e.message : "The action failed.");
|
|
@@ -137,7 +223,7 @@ var UsersGroupsWidget = React.forwardRef(function UsersGroupsWidget2({ endpoint
|
|
|
137
223
|
await call({ action: "create-group", name });
|
|
138
224
|
setNewName("");
|
|
139
225
|
setShowCreate(false);
|
|
140
|
-
await
|
|
226
|
+
await searchGroups(groupQuery);
|
|
141
227
|
} catch (e) {
|
|
142
228
|
setErr(e instanceof Error ? e.message : "Could not create the group.");
|
|
143
229
|
} finally {
|
|
@@ -147,7 +233,18 @@ var UsersGroupsWidget = React.forwardRef(function UsersGroupsWidget2({ endpoint
|
|
|
147
233
|
async function addAdmin() {
|
|
148
234
|
const email = newAdmin.trim().toLowerCase();
|
|
149
235
|
if (!email) return;
|
|
150
|
-
await act("add-admin", { action: "add-admin", email }, () =>
|
|
236
|
+
await act("add-admin", { action: "add-admin", email }, async () => {
|
|
237
|
+
setNewAdmin("");
|
|
238
|
+
await reloadAdmins();
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
async function createUser() {
|
|
242
|
+
const ref2 = newUserRef.trim();
|
|
243
|
+
if (!ref2) return;
|
|
244
|
+
await act("create-user", { action: "create-user", ref: ref2 }, async () => {
|
|
245
|
+
setNewUserRef("");
|
|
246
|
+
await loadObserved();
|
|
247
|
+
});
|
|
151
248
|
}
|
|
152
249
|
const vars = {
|
|
153
250
|
// @ts-expect-error CSS custom properties
|
|
@@ -188,7 +285,18 @@ var UsersGroupsWidget = React.forwardRef(function UsersGroupsWidget2({ endpoint
|
|
|
188
285
|
/* @__PURE__ */ jsxs("div", { style: card, children: [
|
|
189
286
|
/* @__PURE__ */ jsxs("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center", gap: 12, padding: "14px 16px", borderBottom: `1px solid ${t.border}`, background: headerBg }, children: [
|
|
190
287
|
/* @__PURE__ */ jsx("strong", { style: { fontSize: 15 }, children: title }),
|
|
191
|
-
access === "ok" && /* @__PURE__ */
|
|
288
|
+
access === "ok" && /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: 8 }, children: [
|
|
289
|
+
/* @__PURE__ */ jsx("button", { style: btn("ghost", true), disabled: busy, onClick: () => {
|
|
290
|
+
const nv = !showNewUser;
|
|
291
|
+
setShowNewUser(nv);
|
|
292
|
+
setShowCreate(false);
|
|
293
|
+
if (nv && !observedLoaded) loadObserved();
|
|
294
|
+
}, "aria-label": "Add a new user", children: "+ New user" }),
|
|
295
|
+
/* @__PURE__ */ jsx("button", { style: btn("primary", true), disabled: busy, onClick: () => {
|
|
296
|
+
setShowCreate((v) => !v);
|
|
297
|
+
setShowNewUser(false);
|
|
298
|
+
}, "aria-label": "Create a new group", children: "+ New group" })
|
|
299
|
+
] })
|
|
192
300
|
] }),
|
|
193
301
|
access === "pending" && /* @__PURE__ */ jsxs("div", { style: { padding: "28px 20px", textAlign: "center", color: t.muted, lineHeight: 1.5 }, children: [
|
|
194
302
|
/* @__PURE__ */ jsx("div", { style: { fontWeight: 700, color: t.text, marginBottom: 6, fontSize: 15 }, children: "Admin access required" }),
|
|
@@ -237,13 +345,88 @@ var UsersGroupsWidget = React.forwardRef(function UsersGroupsWidget2({ endpoint
|
|
|
237
345
|
setNewName("");
|
|
238
346
|
}, children: "Cancel" })
|
|
239
347
|
] }),
|
|
348
|
+
access === "ok" && showNewUser && /* @__PURE__ */ jsxs("div", { style: { padding: "12px 16px", borderBottom: `1px solid ${t.border}` }, children: [
|
|
349
|
+
/* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: 8 }, children: [
|
|
350
|
+
/* @__PURE__ */ jsx(
|
|
351
|
+
"input",
|
|
352
|
+
{
|
|
353
|
+
style: { ...inputStyle, flex: 1 },
|
|
354
|
+
placeholder: "Email (or a user id)",
|
|
355
|
+
value: newUserRef,
|
|
356
|
+
onChange: (e) => setNewUserRef(e.target.value),
|
|
357
|
+
onKeyDown: (e) => e.key === "Enter" && createUser(),
|
|
358
|
+
"aria-label": "New user email or id"
|
|
359
|
+
}
|
|
360
|
+
),
|
|
361
|
+
/* @__PURE__ */ jsx("button", { style: btn("primary", true), disabled: busy || !newUserRef.trim(), onClick: createUser, children: "Add" }),
|
|
362
|
+
/* @__PURE__ */ jsx("button", { style: btn("ghost", true), onClick: () => {
|
|
363
|
+
setShowNewUser(false);
|
|
364
|
+
setNewUserRef("");
|
|
365
|
+
}, children: "Cancel" })
|
|
366
|
+
] }),
|
|
367
|
+
/* @__PURE__ */ jsxs("div", { style: { fontSize: 12, color: t.muted, marginTop: 6 }, children: [
|
|
368
|
+
"An ",
|
|
369
|
+
/* @__PURE__ */ jsx("strong", { children: "email" }),
|
|
370
|
+
" is pre-approved \u2014 they join with their real identity the first time they sign in. A ",
|
|
371
|
+
/* @__PURE__ */ jsx("strong", { children: "user id" }),
|
|
372
|
+
" is provisioned now."
|
|
373
|
+
] }),
|
|
374
|
+
/* @__PURE__ */ jsxs("div", { style: { marginTop: 12 }, children: [
|
|
375
|
+
/* @__PURE__ */ jsxs("div", { style: { fontSize: 12, color: t.muted, marginBottom: 6 }, children: [
|
|
376
|
+
"\u2026or add someone already seen in your API traffic",
|
|
377
|
+
observedNew ? ` (${observedNew} new)` : "",
|
|
378
|
+
":"
|
|
379
|
+
] }),
|
|
380
|
+
!observedLoaded && /* @__PURE__ */ jsx("div", { style: { fontSize: 12, color: t.muted }, children: "Loading\u2026" }),
|
|
381
|
+
observedLoaded && observed.filter((o) => !o.provisioned).length === 0 && /* @__PURE__ */ jsx("div", { style: { fontSize: 12, color: t.muted }, children: "No new identities in traffic \u2014 they appear here after their first API call." }),
|
|
382
|
+
/* @__PURE__ */ jsx("ul", { style: { listStyle: "none", margin: 0, padding: 0 }, children: observed.filter((o) => !o.provisioned).map((o) => /* @__PURE__ */ jsxs("li", { style: { display: "flex", gap: 8, alignItems: "center", padding: "5px 0", flexWrap: "wrap" }, children: [
|
|
383
|
+
/* @__PURE__ */ jsx("span", { style: { flex: 1, minWidth: 140, fontFamily: t.monoFontFamily, fontSize: 13 }, children: o.email || o.abz_sub }),
|
|
384
|
+
o.source_label && /* @__PURE__ */ jsx("span", { style: chip, children: o.source_label }),
|
|
385
|
+
o.last_seen && /* @__PURE__ */ jsxs("span", { style: { fontSize: 11, color: t.muted }, children: [
|
|
386
|
+
"seen ",
|
|
387
|
+
relTime(o.last_seen),
|
|
388
|
+
o.seen_count ? ` \xB7 ${o.seen_count}\xD7` : ""
|
|
389
|
+
] }),
|
|
390
|
+
/* @__PURE__ */ jsx(
|
|
391
|
+
"button",
|
|
392
|
+
{
|
|
393
|
+
style: btn("ghost", true),
|
|
394
|
+
disabled: busy,
|
|
395
|
+
"aria-label": `Add ${o.email || o.abz_sub} as a user`,
|
|
396
|
+
onClick: () => act(`provision:${o.abz_sub}`, { action: "provision-observed", consumerUserId: o.abz_sub, email: o.email ?? void 0 }, loadObserved),
|
|
397
|
+
children: "Add"
|
|
398
|
+
}
|
|
399
|
+
)
|
|
400
|
+
] }, o.abz_sub)) })
|
|
401
|
+
] })
|
|
402
|
+
] }),
|
|
240
403
|
access === "ok" && groups === null && !loadError && /* @__PURE__ */ jsx("div", { "aria-busy": "true", style: { padding: 24, color: t.muted }, children: "Loading groups\u2026" }),
|
|
241
404
|
access === "ok" && loadError && /* @__PURE__ */ jsxs("div", { role: "alert", style: { padding: 20, color: t.danger, fontSize: 13 }, children: [
|
|
242
405
|
loadError,
|
|
243
406
|
" ",
|
|
244
407
|
/* @__PURE__ */ jsx("button", { style: { ...btn("ghost", true), marginLeft: 6 }, onClick: load, children: "Retry" })
|
|
245
408
|
] }),
|
|
246
|
-
access === "ok" && groups !== null && groups.length
|
|
409
|
+
access === "ok" && groups !== null && (groupTotal > groups.length || groupQuery) && /* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: 8, alignItems: "center", padding: "10px 16px", borderBottom: `1px solid ${t.border}` }, children: [
|
|
410
|
+
/* @__PURE__ */ jsx(
|
|
411
|
+
"input",
|
|
412
|
+
{
|
|
413
|
+
style: { ...inputStyle, flex: 1 },
|
|
414
|
+
placeholder: "Search groups\u2026",
|
|
415
|
+
defaultValue: groupQuery,
|
|
416
|
+
onChange: (e) => {
|
|
417
|
+
const v = e.target.value;
|
|
418
|
+
searchGroups(v);
|
|
419
|
+
},
|
|
420
|
+
"aria-label": "Search groups"
|
|
421
|
+
}
|
|
422
|
+
),
|
|
423
|
+
/* @__PURE__ */ jsxs("span", { style: { fontSize: 12, color: t.muted, whiteSpace: "nowrap" }, children: [
|
|
424
|
+
groups.length,
|
|
425
|
+
" of ",
|
|
426
|
+
groupTotal
|
|
427
|
+
] })
|
|
428
|
+
] }),
|
|
429
|
+
access === "ok" && groups !== null && groups.length === 0 && /* @__PURE__ */ jsx("div", { style: { padding: "28px 20px", textAlign: "center", color: t.muted }, children: groupQuery ? `No groups match \u201C${groupQuery}\u201D.` : "No groups yet. Create one to start organizing your users." }),
|
|
247
430
|
access === "ok" && groups !== null && groups.length > 0 && /* @__PURE__ */ jsx("ul", { style: { listStyle: "none", margin: 0, padding: 0 }, children: groups.map((g) => {
|
|
248
431
|
const sealed = g.name === SEALED;
|
|
249
432
|
const isOpen = open === g.id;
|
|
@@ -256,9 +439,12 @@ var UsersGroupsWidget = React.forwardRef(function UsersGroupsWidget2({ endpoint
|
|
|
256
439
|
style: { display: "flex", width: "100%", gap: 10, alignItems: "center", padding: "12px 16px", background: "transparent", border: "none", cursor: "pointer", color: t.text, fontFamily: "inherit", fontSize: 14, textAlign: "left" },
|
|
257
440
|
children: [
|
|
258
441
|
/* @__PURE__ */ jsx("span", { "aria-hidden": "true", style: { color: t.muted, fontSize: 11 }, children: isOpen ? "\u25BE" : "\u25B8" }),
|
|
259
|
-
/* @__PURE__ */ jsx("strong", { style: { flex: 1 }, children: g.name }),
|
|
260
|
-
sealed
|
|
261
|
-
|
|
442
|
+
/* @__PURE__ */ jsx("strong", { style: { flex: 1 }, children: sealed ? "Admins" : g.name }),
|
|
443
|
+
sealed ? /* @__PURE__ */ jsxs("span", { style: chip, children: [
|
|
444
|
+
admins.length,
|
|
445
|
+
" admin",
|
|
446
|
+
admins.length === 1 ? "" : "s"
|
|
447
|
+
] }) : /* @__PURE__ */ jsxs("span", { style: chip, children: [
|
|
262
448
|
g.member_count ?? 0,
|
|
263
449
|
" member",
|
|
264
450
|
(g.member_count ?? 0) === 1 ? "" : "s"
|
|
@@ -268,64 +454,66 @@ var UsersGroupsWidget = React.forwardRef(function UsersGroupsWidget2({ endpoint
|
|
|
268
454
|
),
|
|
269
455
|
isOpen && /* @__PURE__ */ jsxs("div", { style: { padding: "0 16px 14px 34px" }, children: [
|
|
270
456
|
detailLoading && /* @__PURE__ */ jsx("div", { style: { color: t.muted, fontSize: 13 }, children: "Loading\u2026" }),
|
|
271
|
-
detail && detail.id === g.id && /* @__PURE__ */
|
|
457
|
+
detail && detail.id === g.id && /* @__PURE__ */ jsx(Fragment, { children: sealed ? (
|
|
458
|
+
/* apiblaze_admins is managed HERE, by email — the seal means
|
|
459
|
+
nobody is added by group membership; adding an admin adds
|
|
460
|
+
their email (they activate on first sign-in). */
|
|
461
|
+
/* @__PURE__ */ jsxs(Fragment, { children: [
|
|
462
|
+
/* @__PURE__ */ jsxs("div", { style: { fontSize: 12, color: t.muted, marginBottom: 10 }, children: [
|
|
463
|
+
"Admins are granted by ",
|
|
464
|
+
/* @__PURE__ */ jsx("strong", { children: "email" }),
|
|
465
|
+
" \u2014 never by adding someone to a group. A new admin gets access the first time they sign in."
|
|
466
|
+
] }),
|
|
467
|
+
/* @__PURE__ */ jsxs("ul", { style: { listStyle: "none", margin: 0, padding: 0 }, children: [
|
|
468
|
+
admins.map((a) => {
|
|
469
|
+
const removable = !a.pinned && admins.length > 1;
|
|
470
|
+
return /* @__PURE__ */ jsxs("li", { style: { display: "flex", gap: 8, alignItems: "center", padding: "6px 0", flexWrap: "wrap" }, children: [
|
|
471
|
+
/* @__PURE__ */ jsx("span", { style: { flex: 1, minWidth: 160 }, children: a.email }),
|
|
472
|
+
a.pinned && /* @__PURE__ */ jsx("span", { style: chip, title: "Set by the API provider \u2014 you can\u2019t remove this one", children: "provider" }),
|
|
473
|
+
/* @__PURE__ */ jsx("span", { style: { ...chip, ...a.active ? { color: t.success, borderColor: t.success } : {} }, children: a.active ? "active" : "pending sign-in" }),
|
|
474
|
+
/* @__PURE__ */ jsx(
|
|
475
|
+
"button",
|
|
476
|
+
{
|
|
477
|
+
style: { ...btn("danger", true), ...removable ? {} : { opacity: 0.4, cursor: "not-allowed" } },
|
|
478
|
+
disabled: busy || !removable,
|
|
479
|
+
title: a.pinned ? "Provider-set admins can only be removed by the API provider" : admins.length <= 1 ? "A tenant must keep at least one admin" : `Remove ${a.email}`,
|
|
480
|
+
onClick: () => act(`rm-admin:${a.email}`, { action: "remove-admin", email: a.email }, reloadAdmins),
|
|
481
|
+
children: "Remove"
|
|
482
|
+
}
|
|
483
|
+
)
|
|
484
|
+
] }, a.email);
|
|
485
|
+
}),
|
|
486
|
+
admins.length === 0 && /* @__PURE__ */ jsx("li", { style: { color: t.muted, fontSize: 13, padding: "6px 0" }, children: "No admins yet." })
|
|
487
|
+
] }),
|
|
488
|
+
/* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: 8, marginTop: 10 }, children: [
|
|
489
|
+
/* @__PURE__ */ jsx(
|
|
490
|
+
"input",
|
|
491
|
+
{
|
|
492
|
+
style: { ...inputStyle, flex: 1 },
|
|
493
|
+
type: "email",
|
|
494
|
+
placeholder: "Add an admin by email",
|
|
495
|
+
value: newAdmin,
|
|
496
|
+
onChange: (e) => setNewAdmin(e.target.value),
|
|
497
|
+
onKeyDown: (e) => e.key === "Enter" && addAdmin(),
|
|
498
|
+
"aria-label": "New admin email"
|
|
499
|
+
}
|
|
500
|
+
),
|
|
501
|
+
/* @__PURE__ */ jsx("button", { style: btn("primary", true), disabled: busy || !newAdmin.trim(), onClick: addAdmin, children: "Add admin" })
|
|
502
|
+
] })
|
|
503
|
+
] })
|
|
504
|
+
) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
272
505
|
/* @__PURE__ */ jsxs("ul", { style: { listStyle: "none", margin: 0, padding: 0 }, children: [
|
|
273
506
|
detail.members.map((m) => /* @__PURE__ */ jsxs("li", { style: { display: "flex", gap: 8, alignItems: "center", padding: "6px 0", flexWrap: "wrap" }, children: [
|
|
274
507
|
/* @__PURE__ */ jsx("span", { style: { flex: 1, minWidth: 140 }, children: userLabel(m) }),
|
|
275
508
|
m.source_label && /* @__PURE__ */ jsx("span", { style: chip, children: m.source_label }),
|
|
276
|
-
sealed ? /* @__PURE__ */ jsx("span", { style: { ...chip, fontWeight: 600 }, children: "admin" }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
277
|
-
/* @__PURE__ */ jsxs(
|
|
278
|
-
"select",
|
|
279
|
-
{
|
|
280
|
-
style: selectStyle,
|
|
281
|
-
value: m.role,
|
|
282
|
-
disabled: busy,
|
|
283
|
-
"aria-label": `Role of ${userLabel(m)}`,
|
|
284
|
-
onChange: (e) => act(`role:${m.abz_sub}`, { action: "change-role", groupId: g.id, abzSub: m.abz_sub, role: e.target.value }, () => loadDetail(g.id)),
|
|
285
|
-
children: [
|
|
286
|
-
/* @__PURE__ */ jsx("option", { value: "member", children: "member" }),
|
|
287
|
-
/* @__PURE__ */ jsx("option", { value: "admin", children: "group admin" })
|
|
288
|
-
]
|
|
289
|
-
}
|
|
290
|
-
),
|
|
291
|
-
/* @__PURE__ */ jsx(
|
|
292
|
-
"button",
|
|
293
|
-
{
|
|
294
|
-
style: btn("danger", true),
|
|
295
|
-
disabled: busy,
|
|
296
|
-
"aria-label": `Remove ${userLabel(m)} from ${g.name}`,
|
|
297
|
-
onClick: () => act(`rm:${m.abz_sub}`, { action: "remove-member", groupId: g.id, abzSub: m.abz_sub }, () => loadDetail(g.id)),
|
|
298
|
-
children: "Remove"
|
|
299
|
-
}
|
|
300
|
-
)
|
|
301
|
-
] })
|
|
302
|
-
] }, m.abz_sub)),
|
|
303
|
-
detail.members.length === 0 && /* @__PURE__ */ jsx("li", { style: { color: t.muted, fontSize: 13, padding: "6px 0" }, children: "No members yet." })
|
|
304
|
-
] }),
|
|
305
|
-
sealed ? /* @__PURE__ */ jsx("div", { style: { fontSize: 12, color: t.muted, marginTop: 8 }, children: "Workspace admins are granted by email on the provider\u2019s admin list \u2014 membership can\u2019t be edited here." }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
306
|
-
/* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: 8, marginTop: 10, flexWrap: "wrap" }, children: [
|
|
307
|
-
/* @__PURE__ */ jsxs(
|
|
308
|
-
"select",
|
|
309
|
-
{
|
|
310
|
-
style: { ...selectStyle, flex: 1, minWidth: 160 },
|
|
311
|
-
value: addSel,
|
|
312
|
-
disabled: busy,
|
|
313
|
-
"aria-label": "User to add",
|
|
314
|
-
onChange: (e) => setAddSel(e.target.value),
|
|
315
|
-
children: [
|
|
316
|
-
/* @__PURE__ */ jsx("option", { value: "", children: "Add a user\u2026" }),
|
|
317
|
-
users.filter((u) => !detail.members.some((m) => m.abz_sub === u.abz_sub)).map((u) => /* @__PURE__ */ jsx("option", { value: u.abz_sub, children: userLabel(u) }, u.abz_sub))
|
|
318
|
-
]
|
|
319
|
-
}
|
|
320
|
-
),
|
|
321
509
|
/* @__PURE__ */ jsxs(
|
|
322
510
|
"select",
|
|
323
511
|
{
|
|
324
512
|
style: selectStyle,
|
|
325
|
-
value:
|
|
513
|
+
value: m.role,
|
|
326
514
|
disabled: busy,
|
|
327
|
-
"aria-label":
|
|
328
|
-
onChange: (e) =>
|
|
515
|
+
"aria-label": `Role of ${userLabel(m)}`,
|
|
516
|
+
onChange: (e) => act(`role:${m.abz_sub}`, { action: "change-role", groupId: g.id, abzSub: m.abz_sub, role: e.target.value }, () => loadDetail(g.id)),
|
|
329
517
|
children: [
|
|
330
518
|
/* @__PURE__ */ jsx("option", { value: "member", children: "member" }),
|
|
331
519
|
/* @__PURE__ */ jsx("option", { value: "admin", children: "group admin" })
|
|
@@ -335,184 +523,129 @@ var UsersGroupsWidget = React.forwardRef(function UsersGroupsWidget2({ endpoint
|
|
|
335
523
|
/* @__PURE__ */ jsx(
|
|
336
524
|
"button",
|
|
337
525
|
{
|
|
338
|
-
style: btn("
|
|
339
|
-
disabled: busy || !addSel,
|
|
340
|
-
onClick: () => act("add-member", { action: "add-member", groupId: g.id, abzSub: addSel, role: addRole }, () => {
|
|
341
|
-
setAddSel("");
|
|
342
|
-
return loadDetail(g.id);
|
|
343
|
-
}),
|
|
344
|
-
children: "Add"
|
|
345
|
-
}
|
|
346
|
-
)
|
|
347
|
-
] }),
|
|
348
|
-
/* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: 8, alignItems: "center", marginTop: 12, flexWrap: "wrap" }, children: [
|
|
349
|
-
/* @__PURE__ */ jsx("span", { style: { fontSize: 12, color: t.muted }, children: "Subgroups:" }),
|
|
350
|
-
detail.subgroups.map((s) => /* @__PURE__ */ jsxs("span", { style: { ...chip, display: "inline-flex", gap: 6, alignItems: "center" }, children: [
|
|
351
|
-
s.name,
|
|
352
|
-
/* @__PURE__ */ jsx(
|
|
353
|
-
"button",
|
|
354
|
-
{
|
|
355
|
-
style: { background: "none", border: "none", cursor: "pointer", color: t.danger, fontSize: 12, padding: 0 },
|
|
356
|
-
disabled: busy,
|
|
357
|
-
"aria-label": `Un-nest ${s.name} from ${g.name}`,
|
|
358
|
-
onClick: () => act("unnest", { action: "remove-subgroup", groupId: g.id, childGroupId: s.id }, () => loadDetail(g.id)),
|
|
359
|
-
children: "\u2715"
|
|
360
|
-
}
|
|
361
|
-
)
|
|
362
|
-
] }, s.id)),
|
|
363
|
-
detail.subgroups.length === 0 && /* @__PURE__ */ jsx("span", { style: { fontSize: 12, color: t.muted }, children: "none" }),
|
|
364
|
-
/* @__PURE__ */ jsxs(
|
|
365
|
-
"select",
|
|
366
|
-
{
|
|
367
|
-
style: selectStyle,
|
|
368
|
-
value: nestSel,
|
|
526
|
+
style: btn("danger", true),
|
|
369
527
|
disabled: busy,
|
|
370
|
-
"aria-label":
|
|
371
|
-
|
|
372
|
-
children:
|
|
373
|
-
/* @__PURE__ */ jsx("option", { value: "", children: "Nest a group\u2026" }),
|
|
374
|
-
groups.filter((x) => x.id !== g.id && x.name !== SEALED && !detail.subgroups.some((s) => s.id === x.id)).map((x) => /* @__PURE__ */ jsx("option", { value: x.id, children: x.name }, x.id))
|
|
375
|
-
]
|
|
376
|
-
}
|
|
377
|
-
),
|
|
378
|
-
/* @__PURE__ */ jsx(
|
|
379
|
-
"button",
|
|
380
|
-
{
|
|
381
|
-
style: btn("ghost", true),
|
|
382
|
-
disabled: busy || !nestSel,
|
|
383
|
-
onClick: () => act("nest", { action: "add-subgroup", groupId: g.id, childGroupId: nestSel }, () => {
|
|
384
|
-
setNestSel("");
|
|
385
|
-
return loadDetail(g.id);
|
|
386
|
-
}),
|
|
387
|
-
children: "Nest"
|
|
528
|
+
"aria-label": `Remove ${userLabel(m)} from ${g.name}`,
|
|
529
|
+
onClick: () => act(`rm:${m.abz_sub}`, { action: "remove-member", groupId: g.id, abzSub: m.abz_sub }, () => loadDetail(g.id)),
|
|
530
|
+
children: "Remove"
|
|
388
531
|
}
|
|
389
532
|
)
|
|
390
|
-
] }),
|
|
391
|
-
/* @__PURE__ */ jsx("
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
533
|
+
] }, m.abz_sub)),
|
|
534
|
+
detail.members.length === 0 && /* @__PURE__ */ jsx("li", { style: { color: t.muted, fontSize: 13, padding: "6px 0" }, children: "No members yet." })
|
|
535
|
+
] }),
|
|
536
|
+
typeof detail.member_count === "number" && detail.members.length < detail.member_count && /* @__PURE__ */ jsxs(
|
|
537
|
+
"button",
|
|
538
|
+
{
|
|
539
|
+
style: { ...btn("ghost", true), marginTop: 6 },
|
|
540
|
+
disabled: busy,
|
|
541
|
+
onClick: () => loadMoreMembers(g.id, detail.members.length),
|
|
542
|
+
children: [
|
|
543
|
+
"Load more (",
|
|
544
|
+
detail.members.length,
|
|
545
|
+
" of ",
|
|
546
|
+
detail.member_count,
|
|
547
|
+
")"
|
|
548
|
+
]
|
|
549
|
+
}
|
|
550
|
+
),
|
|
551
|
+
/* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: 8, marginTop: 10, flexWrap: "wrap", alignItems: "flex-start" }, children: [
|
|
552
|
+
/* @__PURE__ */ jsx(
|
|
553
|
+
Typeahead,
|
|
554
|
+
{
|
|
555
|
+
placeholder: "Search users to add\u2026",
|
|
556
|
+
disabled: busy,
|
|
557
|
+
s: { input: inputStyle, surface: t.surface, border: t.border, muted: t.muted, text: t.text },
|
|
558
|
+
search: async (q) => {
|
|
559
|
+
const d = await call({ action: "search-users", q, limit: 20 });
|
|
560
|
+
return (d.users ?? []).filter((u) => !detail.members.some((m) => m.abz_sub === u.abz_sub));
|
|
561
|
+
},
|
|
562
|
+
itemKey: (u) => u.abz_sub,
|
|
563
|
+
itemLabel: (u) => userLabel(u),
|
|
564
|
+
onPick: (u) => act(`add-member:${u.abz_sub}`, { action: "add-member", groupId: g.id, abzSub: u.abz_sub, role: addRole }, () => loadDetail(g.id))
|
|
565
|
+
}
|
|
566
|
+
),
|
|
567
|
+
/* @__PURE__ */ jsxs(
|
|
568
|
+
"select",
|
|
569
|
+
{
|
|
570
|
+
style: selectStyle,
|
|
571
|
+
value: addRole,
|
|
572
|
+
disabled: busy,
|
|
573
|
+
"aria-label": "Role for the new member",
|
|
574
|
+
onChange: (e) => setAddRole(e.target.value),
|
|
575
|
+
children: [
|
|
576
|
+
/* @__PURE__ */ jsx("option", { value: "member", children: "member" }),
|
|
577
|
+
/* @__PURE__ */ jsx("option", { value: "admin", children: "group admin" })
|
|
578
|
+
]
|
|
579
|
+
}
|
|
580
|
+
)
|
|
581
|
+
] }),
|
|
582
|
+
/* @__PURE__ */ jsxs("div", { style: { display: "flex", gap: 8, alignItems: "center", marginTop: 12, flexWrap: "wrap" }, children: [
|
|
583
|
+
/* @__PURE__ */ jsx("span", { style: { fontSize: 12, color: t.muted }, children: "Subgroups:" }),
|
|
584
|
+
detail.subgroups.map((s) => /* @__PURE__ */ jsxs("span", { style: { ...chip, display: "inline-flex", gap: 6, alignItems: "center" }, children: [
|
|
585
|
+
s.name,
|
|
397
586
|
/* @__PURE__ */ jsx(
|
|
398
587
|
"button",
|
|
399
588
|
{
|
|
400
|
-
style:
|
|
589
|
+
style: { background: "none", border: "none", cursor: "pointer", color: t.danger, fontSize: 12, padding: 0 },
|
|
401
590
|
disabled: busy,
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
setOpen(null);
|
|
406
|
-
setDetail(null);
|
|
407
|
-
});
|
|
408
|
-
},
|
|
409
|
-
children: "Delete group"
|
|
591
|
+
"aria-label": `Un-nest ${s.name} from ${g.name}`,
|
|
592
|
+
onClick: () => act("unnest", { action: "remove-subgroup", groupId: g.id, childGroupId: s.id }, () => loadDetail(g.id)),
|
|
593
|
+
children: "\u2715"
|
|
410
594
|
}
|
|
411
|
-
)
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
] })
|
|
415
|
-
] })
|
|
416
|
-
] })
|
|
417
|
-
] }, g.id);
|
|
418
|
-
}) }),
|
|
419
|
-
access === "ok" && groups !== null && /* @__PURE__ */ jsxs("div", { style: { borderTop: `1px solid ${t.border}` }, children: [
|
|
420
|
-
/* @__PURE__ */ jsxs(
|
|
421
|
-
"button",
|
|
422
|
-
{
|
|
423
|
-
onClick: () => setShowAdmins((v) => !v),
|
|
424
|
-
"aria-expanded": showAdmins,
|
|
425
|
-
style: { display: "flex", width: "100%", gap: 10, alignItems: "center", padding: "12px 16px", background: "transparent", border: "none", cursor: "pointer", color: t.text, fontFamily: "inherit", fontSize: 13, textAlign: "left" },
|
|
426
|
-
children: [
|
|
427
|
-
/* @__PURE__ */ jsx("span", { "aria-hidden": "true", style: { color: t.muted, fontSize: 11 }, children: showAdmins ? "\u25BE" : "\u25B8" }),
|
|
428
|
-
/* @__PURE__ */ jsx("strong", { style: { flex: 1 }, children: "Admins" }),
|
|
429
|
-
/* @__PURE__ */ jsx("span", { style: chip, children: admins.length })
|
|
430
|
-
]
|
|
431
|
-
}
|
|
432
|
-
),
|
|
433
|
-
showAdmins && /* @__PURE__ */ jsxs("div", { style: { padding: "0 16px 14px 34px" }, children: [
|
|
434
|
-
/* @__PURE__ */ jsxs("ul", { style: { listStyle: "none", margin: 0, padding: 0 }, children: [
|
|
435
|
-
admins.map((a) => {
|
|
436
|
-
const removable = !a.pinned && admins.length > 1;
|
|
437
|
-
return /* @__PURE__ */ jsxs("li", { style: { display: "flex", gap: 8, alignItems: "center", padding: "6px 0", flexWrap: "wrap" }, children: [
|
|
438
|
-
/* @__PURE__ */ jsx("span", { style: { flex: 1, minWidth: 160 }, children: a.email }),
|
|
439
|
-
a.pinned && /* @__PURE__ */ jsx("span", { style: chip, title: "Set by the API provider \u2014 you can\u2019t remove this one", children: "provider" }),
|
|
440
|
-
/* @__PURE__ */ jsx("span", { style: { ...chip, ...a.active ? { color: t.success, borderColor: t.success } : {} }, children: a.active ? "active" : "pending sign-in" }),
|
|
595
|
+
)
|
|
596
|
+
] }, s.id)),
|
|
597
|
+
detail.subgroups.length === 0 && /* @__PURE__ */ jsx("span", { style: { fontSize: 12, color: t.muted }, children: "none" }),
|
|
441
598
|
/* @__PURE__ */ jsx(
|
|
442
|
-
|
|
599
|
+
Typeahead,
|
|
443
600
|
{
|
|
444
|
-
|
|
445
|
-
disabled: busy
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
601
|
+
placeholder: "Nest a group\u2026",
|
|
602
|
+
disabled: busy,
|
|
603
|
+
s: { input: inputStyle, surface: t.surface, border: t.border, muted: t.muted, text: t.text },
|
|
604
|
+
search: async (q) => {
|
|
605
|
+
const d = await call({ action: "list-groups", q, offset: 0 });
|
|
606
|
+
return (d.groups ?? []).filter((x) => x.id !== g.id && x.name !== SEALED && !detail.subgroups.some((s) => s.id === x.id));
|
|
607
|
+
},
|
|
608
|
+
itemKey: (x) => x.id,
|
|
609
|
+
itemLabel: (x) => x.name,
|
|
610
|
+
onPick: (x) => act(`nest:${x.id}`, { action: "add-subgroup", groupId: g.id, childGroupId: x.id }, () => loadDetail(g.id))
|
|
449
611
|
}
|
|
450
612
|
)
|
|
451
|
-
] },
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
]
|
|
488
|
-
}
|
|
489
|
-
),
|
|
490
|
-
showTraffic && /* @__PURE__ */ jsxs("ul", { style: { listStyle: "none", margin: 0, padding: "0 16px 14px 34px" }, children: [
|
|
491
|
-
observed.length === 0 && /* @__PURE__ */ jsx("li", { style: { color: t.muted, fontSize: 13 }, children: "No identities observed yet \u2014 they appear here after their first API call." }),
|
|
492
|
-
observed.map((o) => /* @__PURE__ */ jsxs("li", { style: { display: "flex", gap: 8, alignItems: "center", padding: "6px 0", flexWrap: "wrap" }, children: [
|
|
493
|
-
/* @__PURE__ */ jsx("span", { style: { flex: 1, minWidth: 140, fontFamily: t.monoFontFamily, fontSize: 13 }, children: o.email || o.abz_sub }),
|
|
494
|
-
o.source_label && /* @__PURE__ */ jsx("span", { style: chip, children: o.source_label }),
|
|
495
|
-
o.last_seen && /* @__PURE__ */ jsxs("span", { style: { fontSize: 11, color: t.muted }, children: [
|
|
496
|
-
"seen ",
|
|
497
|
-
relTime(o.last_seen),
|
|
498
|
-
o.seen_count ? ` \xB7 ${o.seen_count}\xD7` : ""
|
|
499
|
-
] }),
|
|
500
|
-
o.provisioned ? /* @__PURE__ */ jsxs("span", { style: { ...chip, color: t.success, borderColor: t.success }, children: [
|
|
501
|
-
"user",
|
|
502
|
-
o.groups?.length ? ` \xB7 ${o.groups.join(", ")}` : ""
|
|
503
|
-
] }) : /* @__PURE__ */ jsx(
|
|
504
|
-
"button",
|
|
505
|
-
{
|
|
506
|
-
style: btn("primary", true),
|
|
507
|
-
disabled: busy,
|
|
508
|
-
"aria-label": `Add ${o.email || o.abz_sub} as a user`,
|
|
509
|
-
onClick: () => act("provision", { action: "provision-observed", consumerUserId: o.abz_sub, email: o.email ?? void 0 }),
|
|
510
|
-
children: "Add as user"
|
|
511
|
-
}
|
|
512
|
-
)
|
|
513
|
-
] }, o.abz_sub))
|
|
514
|
-
] })
|
|
515
|
-
] }),
|
|
613
|
+
] }),
|
|
614
|
+
/* @__PURE__ */ jsx("div", { style: { marginTop: 12, display: "flex", gap: 8, alignItems: "center", flexWrap: "wrap" }, children: confirmDelete === g.id ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
615
|
+
/* @__PURE__ */ jsxs("span", { style: { fontSize: 12, color: t.text }, children: [
|
|
616
|
+
"Delete \u201C",
|
|
617
|
+
g.name,
|
|
618
|
+
"\u201D? Members lose this group everywhere it\u2019s used. This can\u2019t be undone."
|
|
619
|
+
] }),
|
|
620
|
+
/* @__PURE__ */ jsx(
|
|
621
|
+
"button",
|
|
622
|
+
{
|
|
623
|
+
style: btn("danger", true),
|
|
624
|
+
disabled: busy,
|
|
625
|
+
onClick: () => {
|
|
626
|
+
setConfirmDelete(null);
|
|
627
|
+
act("del", { action: "delete-group", groupId: g.id }, () => {
|
|
628
|
+
setOpen(null);
|
|
629
|
+
setDetail(null);
|
|
630
|
+
return searchGroups(groupQuery);
|
|
631
|
+
});
|
|
632
|
+
},
|
|
633
|
+
children: "Delete group"
|
|
634
|
+
}
|
|
635
|
+
),
|
|
636
|
+
/* @__PURE__ */ jsx("button", { style: btn("ghost", true), onClick: () => setConfirmDelete(null), children: "Cancel" })
|
|
637
|
+
] }) : /* @__PURE__ */ jsx("button", { style: { ...btn("ghost", true), color: t.danger }, disabled: busy, onClick: () => setConfirmDelete(g.id), children: "Delete group" }) })
|
|
638
|
+
] }) })
|
|
639
|
+
] })
|
|
640
|
+
] }, g.id);
|
|
641
|
+
}) }),
|
|
642
|
+
access === "ok" && groups !== null && groups.length > 0 && groups.length < groupTotal && /* @__PURE__ */ jsx("div", { style: { padding: "10px 16px", borderTop: `1px solid ${t.border}` }, children: /* @__PURE__ */ jsxs("button", { style: btn("ghost", true), disabled: busy, onClick: loadMoreGroups, children: [
|
|
643
|
+
"Load more groups (",
|
|
644
|
+
groups.length,
|
|
645
|
+
" of ",
|
|
646
|
+
groupTotal,
|
|
647
|
+
")"
|
|
648
|
+
] }) }),
|
|
516
649
|
err && /* @__PURE__ */ jsx("div", { role: "alert", style: { padding: "0 16px 14px", fontSize: 12, color: t.danger }, children: err })
|
|
517
650
|
] })
|
|
518
651
|
] });
|