@burdenoff/microfe-conversations 2026.625.2 → 2026.625.3
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/conversations/components/InboxLayout.js +85 -78
- package/dist/conversations/components/InboxLayout.js.map +1 -1
- package/dist/conversations/pages/AdminConversationsLandingPage.js +107 -61
- package/dist/conversations/pages/AdminConversationsLandingPage.js.map +1 -1
- package/dist/conversations/pages/ConversationAdminPages.js +100 -69
- package/dist/conversations/pages/ConversationAdminPages.js.map +1 -1
- package/dist/conversations/pages/ConversationDetailPage.js +311 -239
- package/dist/conversations/pages/ConversationDetailPage.js.map +1 -1
- package/dist/conversations/pages/ConversationsSettingsPage.js +64 -56
- package/dist/conversations/pages/ConversationsSettingsPage.js.map +1 -1
- package/package.json +2 -2
|
@@ -8,9 +8,9 @@ import { Activity as c, Archive as l, CheckCircle2 as u, Package as d, ShieldChe
|
|
|
8
8
|
import { gql as m } from "@apollo/client";
|
|
9
9
|
import { useMutation as h, useQuery as g } from "@apollo/client/react";
|
|
10
10
|
import { jsx as _, jsxs as v } from "react/jsx-runtime";
|
|
11
|
-
import { Button as y, CTAOverflowMenu as b, GlassCard as x,
|
|
11
|
+
import { Button as y, CTAOverflowMenu as b, GlassCard as x, PagePurpose as S, ShowMore as C } from "@burdenoff/fe-libs/ui";
|
|
12
12
|
//#region src/conversations/pages/ConversationAdminPages.tsx
|
|
13
|
-
var
|
|
13
|
+
var w = m`
|
|
14
14
|
query ConversationAdminMessageReports(
|
|
15
15
|
$first: Int
|
|
16
16
|
$filter: MessageReportFilter
|
|
@@ -38,7 +38,7 @@ var C = m`
|
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
40
|
}
|
|
41
|
-
`,
|
|
41
|
+
`, T = m`
|
|
42
42
|
mutation ConversationAdminResolveMessageReport(
|
|
43
43
|
$input: ResolveMessageReportInput!
|
|
44
44
|
) {
|
|
@@ -53,7 +53,7 @@ var C = m`
|
|
|
53
53
|
}
|
|
54
54
|
}
|
|
55
55
|
}
|
|
56
|
-
`,
|
|
56
|
+
`, E = m`
|
|
57
57
|
query ConversationAdminBulkConversations(
|
|
58
58
|
$first: Int
|
|
59
59
|
$filter: ConversationFilter
|
|
@@ -70,7 +70,7 @@ var C = m`
|
|
|
70
70
|
}
|
|
71
71
|
}
|
|
72
72
|
}
|
|
73
|
-
`,
|
|
73
|
+
`, D = m`
|
|
74
74
|
mutation ConversationAdminBulkUpdate(
|
|
75
75
|
$input: BulkConversationOperationInput!
|
|
76
76
|
) {
|
|
@@ -86,7 +86,7 @@ var C = m`
|
|
|
86
86
|
}
|
|
87
87
|
}
|
|
88
88
|
}
|
|
89
|
-
`,
|
|
89
|
+
`, O = m`
|
|
90
90
|
query ConversationAdminAuditLog($filter: ActivityLogFilter) {
|
|
91
91
|
getActivities(filter: $filter) {
|
|
92
92
|
id
|
|
@@ -98,7 +98,7 @@ var C = m`
|
|
|
98
98
|
timestamp
|
|
99
99
|
}
|
|
100
100
|
}
|
|
101
|
-
`,
|
|
101
|
+
`, k = m`
|
|
102
102
|
query ConversationAdminAppInstallations($workspaceId: ID) {
|
|
103
103
|
appInstallations(workspaceId: $workspaceId) {
|
|
104
104
|
id
|
|
@@ -112,7 +112,7 @@ var C = m`
|
|
|
112
112
|
updatedAt
|
|
113
113
|
}
|
|
114
114
|
}
|
|
115
|
-
`,
|
|
115
|
+
`, A = m`
|
|
116
116
|
mutation ConversationAdminInstallApp($input: InstallAppInput!) {
|
|
117
117
|
installApp(input: $input) {
|
|
118
118
|
id
|
|
@@ -122,7 +122,7 @@ var C = m`
|
|
|
122
122
|
installedAt
|
|
123
123
|
}
|
|
124
124
|
}
|
|
125
|
-
`,
|
|
125
|
+
`, j = m`
|
|
126
126
|
mutation ConversationAdminUpdateAppSettings($id: ID!, $settings: JSON!) {
|
|
127
127
|
updateAppSettings(id: $id, settings: $settings) {
|
|
128
128
|
id
|
|
@@ -130,7 +130,7 @@ var C = m`
|
|
|
130
130
|
updatedAt
|
|
131
131
|
}
|
|
132
132
|
}
|
|
133
|
-
`,
|
|
133
|
+
`, M = m`
|
|
134
134
|
mutation ConversationAdminUninstallApp($id: ID!) {
|
|
135
135
|
uninstallApp(id: $id) {
|
|
136
136
|
appInstallationId
|
|
@@ -138,7 +138,7 @@ var C = m`
|
|
|
138
138
|
}
|
|
139
139
|
}
|
|
140
140
|
`;
|
|
141
|
-
function
|
|
141
|
+
function N(e) {
|
|
142
142
|
if (typeof e == "string") return e;
|
|
143
143
|
if (e && typeof e == "object" && "text" in e) {
|
|
144
144
|
let t = e.text;
|
|
@@ -150,27 +150,27 @@ function M(e) {
|
|
|
150
150
|
return "Message content unavailable";
|
|
151
151
|
}
|
|
152
152
|
}
|
|
153
|
-
var
|
|
153
|
+
var P = new Intl.DateTimeFormat(void 0, {
|
|
154
154
|
dateStyle: "medium",
|
|
155
155
|
timeStyle: "short"
|
|
156
156
|
});
|
|
157
|
-
function
|
|
158
|
-
return
|
|
157
|
+
function F(e) {
|
|
158
|
+
return P.format(new Date(e));
|
|
159
159
|
}
|
|
160
|
-
function
|
|
160
|
+
function I({ children: e }) {
|
|
161
161
|
return /* @__PURE__ */ _("span", {
|
|
162
162
|
className: "rounded-full border border-border-subtle bg-bg-sunken px-2 py-0.5 text-xs font-medium text-text-secondary",
|
|
163
163
|
children: e
|
|
164
164
|
});
|
|
165
165
|
}
|
|
166
|
-
function
|
|
167
|
-
let n = t(), { workspaceId: a } = e(), { data: o, loading: s, error: c, refetch: l } = g(
|
|
166
|
+
function L() {
|
|
167
|
+
let n = t(), { workspaceId: a } = e(), { data: o, loading: s, error: c, refetch: l } = g(w, {
|
|
168
168
|
variables: {
|
|
169
169
|
first: 50,
|
|
170
170
|
filter: { statuses: ["PENDING", "REVIEWING"] }
|
|
171
171
|
},
|
|
172
172
|
skip: !a
|
|
173
|
-
}), [d, { loading: p }] = h(
|
|
173
|
+
}), [d, { loading: p }] = h(T, { onCompleted: () => void l() }), m = o?.messageReports.edges.map((e) => e.node) ?? [];
|
|
174
174
|
return /* @__PURE__ */ v(i, {
|
|
175
175
|
title: n("conversations.admin.moderationPageTitle", "Moderation queue"),
|
|
176
176
|
subtitle: n("conversations.admin.moderationPageDescription", "Review reported messages and apply moderator decisions."),
|
|
@@ -180,6 +180,13 @@ function I() {
|
|
|
180
180
|
}),
|
|
181
181
|
maxWidth: "2xl",
|
|
182
182
|
children: [
|
|
183
|
+
/* @__PURE__ */ _(S, {
|
|
184
|
+
icon: /* @__PURE__ */ _(f, {
|
|
185
|
+
className: "size-4",
|
|
186
|
+
"aria-hidden": "true"
|
|
187
|
+
}),
|
|
188
|
+
children: n("conversations.admin.moderationPurpose", "Keep conversations safe: people can report messages they find abusive, spammy, or off-topic, and they land here for a moderator to hide the message or dismiss the report. Work the queue down to zero to stay on top of issues.")
|
|
189
|
+
}),
|
|
183
190
|
!a && /* @__PURE__ */ _(r, {
|
|
184
191
|
title: n("conversations.admin.selectWorkspace", "Select a workspace"),
|
|
185
192
|
description: n("conversations.admin.selectWorkspaceModerationDescription", "Choose a workspace to review reported conversation messages."),
|
|
@@ -219,11 +226,11 @@ function I() {
|
|
|
219
226
|
/* @__PURE__ */ v("div", {
|
|
220
227
|
className: "flex flex-wrap items-center gap-2",
|
|
221
228
|
children: [
|
|
222
|
-
/* @__PURE__ */ _(
|
|
223
|
-
e.message.isHidden && /* @__PURE__ */ _(
|
|
229
|
+
/* @__PURE__ */ _(I, { children: e.status }),
|
|
230
|
+
e.message.isHidden && /* @__PURE__ */ _(I, { children: "Hidden" }),
|
|
224
231
|
/* @__PURE__ */ v("span", {
|
|
225
232
|
className: "text-xs text-text-secondary",
|
|
226
|
-
children: ["Reported ",
|
|
233
|
+
children: ["Reported ", F(e.createdAt)]
|
|
227
234
|
})
|
|
228
235
|
]
|
|
229
236
|
}),
|
|
@@ -233,7 +240,7 @@ function I() {
|
|
|
233
240
|
}),
|
|
234
241
|
/* @__PURE__ */ _("p", {
|
|
235
242
|
className: "rounded-md border border-border-seam bg-bg-sunken p-3 text-sm text-text-secondary",
|
|
236
|
-
children:
|
|
243
|
+
children: N(e.message.content)
|
|
237
244
|
}),
|
|
238
245
|
/* @__PURE__ */ v("p", {
|
|
239
246
|
className: "text-xs text-text-secondary",
|
|
@@ -275,13 +282,13 @@ function I() {
|
|
|
275
282
|
]
|
|
276
283
|
});
|
|
277
284
|
}
|
|
278
|
-
function
|
|
279
|
-
let n = t(), { workspaceId: a } = e(), [o, c] = s(() => /* @__PURE__ */ new Set()), [u, d] = s("ARCHIVE"), { data: f, loading: p, error: m, refetch: b } = g(
|
|
285
|
+
function R() {
|
|
286
|
+
let n = t(), { workspaceId: a } = e(), [o, c] = s(() => /* @__PURE__ */ new Set()), [u, d] = s("ARCHIVE"), { data: f, loading: p, error: m, refetch: b } = g(E, {
|
|
280
287
|
variables: { first: 100 },
|
|
281
288
|
skip: !a
|
|
282
|
-
}), [
|
|
289
|
+
}), [C, { data: w, loading: T }] = h(D, { onCompleted: () => {
|
|
283
290
|
c(/* @__PURE__ */ new Set()), b();
|
|
284
|
-
} }),
|
|
291
|
+
} }), O = f?.conversations.edges.map((e) => e.node) ?? [], k = (e) => {
|
|
285
292
|
c((t) => {
|
|
286
293
|
let n = new Set(t);
|
|
287
294
|
return n.has(e) ? n.delete(e) : n.add(e), n;
|
|
@@ -319,8 +326,8 @@ function L() {
|
|
|
319
326
|
}), /* @__PURE__ */ v(y, {
|
|
320
327
|
type: "button",
|
|
321
328
|
size: "sm",
|
|
322
|
-
disabled: o.size === 0 ||
|
|
323
|
-
onClick: () => void
|
|
329
|
+
disabled: o.size === 0 || T,
|
|
330
|
+
onClick: () => void C({ variables: { input: {
|
|
324
331
|
conversationIds: Array.from(o),
|
|
325
332
|
action: u
|
|
326
333
|
} } }),
|
|
@@ -328,6 +335,13 @@ function L() {
|
|
|
328
335
|
})]
|
|
329
336
|
}),
|
|
330
337
|
children: [
|
|
338
|
+
/* @__PURE__ */ _(S, {
|
|
339
|
+
icon: /* @__PURE__ */ _(l, {
|
|
340
|
+
className: "size-4",
|
|
341
|
+
"aria-hidden": "true"
|
|
342
|
+
}),
|
|
343
|
+
children: n("conversations.admin.bulkPurpose", "Clean up at scale instead of one conversation at a time — select many conversations, then archive them to declutter inboxes, unarchive to restore, or delete the ones you no longer need. Handy for closing out finished projects or removing test threads.")
|
|
344
|
+
}),
|
|
331
345
|
!a && /* @__PURE__ */ _(r, {
|
|
332
346
|
title: n("conversations.admin.selectWorkspace", "Select a workspace"),
|
|
333
347
|
description: n("conversations.admin.selectWorkspaceBulkDescription", "Choose a workspace to manage conversations in bulk."),
|
|
@@ -345,18 +359,19 @@ function L() {
|
|
|
345
359
|
className: "text-sm text-status-error-text",
|
|
346
360
|
children: m.message
|
|
347
361
|
}),
|
|
348
|
-
|
|
362
|
+
w?.bulkUpdateConversations && /* @__PURE__ */ v(x, {
|
|
349
363
|
treatment: "metric",
|
|
364
|
+
glow: !0,
|
|
350
365
|
className: "grid grid-cols-2 gap-4 border-border-seam p-4 sm:max-w-md",
|
|
351
366
|
children: [/* @__PURE__ */ v("div", { children: [/* @__PURE__ */ _("p", {
|
|
352
367
|
className: "text-2xl font-semibold tabular-nums text-status-success-text",
|
|
353
|
-
children:
|
|
368
|
+
children: w.bulkUpdateConversations.successCount
|
|
354
369
|
}), /* @__PURE__ */ _("p", {
|
|
355
370
|
className: "text-xs text-text-secondary",
|
|
356
371
|
children: "Succeeded"
|
|
357
372
|
})] }), /* @__PURE__ */ v("div", { children: [/* @__PURE__ */ _("p", {
|
|
358
373
|
className: "text-2xl font-semibold tabular-nums text-text-primary",
|
|
359
|
-
children:
|
|
374
|
+
children: w.bulkUpdateConversations.failureCount
|
|
360
375
|
}), /* @__PURE__ */ _("p", {
|
|
361
376
|
className: "text-xs text-text-secondary",
|
|
362
377
|
children: "Failed"
|
|
@@ -364,13 +379,13 @@ function L() {
|
|
|
364
379
|
}),
|
|
365
380
|
a && /* @__PURE__ */ _("div", {
|
|
366
381
|
className: "overflow-hidden rounded-lg border border-border-seam bg-bg-surface",
|
|
367
|
-
children:
|
|
382
|
+
children: O.map((e) => /* @__PURE__ */ v("label", {
|
|
368
383
|
className: "flex min-h-11 cursor-pointer items-center gap-3 border-b border-border-seam px-4 py-3 last:border-b-0 hover:bg-accent",
|
|
369
384
|
children: [/* @__PURE__ */ _("input", {
|
|
370
385
|
type: "checkbox",
|
|
371
386
|
"aria-label": `Select ${e.title || "untitled conversation"}`,
|
|
372
387
|
checked: o.has(e.id),
|
|
373
|
-
onChange: () =>
|
|
388
|
+
onChange: () => k(e.id)
|
|
374
389
|
}), /* @__PURE__ */ v("div", {
|
|
375
390
|
className: "min-w-0 flex-1",
|
|
376
391
|
children: [/* @__PURE__ */ _("p", {
|
|
@@ -384,7 +399,7 @@ function L() {
|
|
|
384
399
|
e.state,
|
|
385
400
|
" · Updated",
|
|
386
401
|
" ",
|
|
387
|
-
|
|
402
|
+
F(e.updatedAt)
|
|
388
403
|
]
|
|
389
404
|
})]
|
|
390
405
|
})]
|
|
@@ -393,8 +408,8 @@ function L() {
|
|
|
393
408
|
]
|
|
394
409
|
});
|
|
395
410
|
}
|
|
396
|
-
function
|
|
397
|
-
let n = t(), { workspaceId: a } = e(), { data: o, loading: s, error: l } = g(
|
|
411
|
+
function z() {
|
|
412
|
+
let n = t(), { workspaceId: a } = e(), { data: o, loading: s, error: l } = g(O, {
|
|
398
413
|
variables: { filter: {
|
|
399
414
|
workspaceId: a,
|
|
400
415
|
resourceType: "conversation",
|
|
@@ -411,6 +426,13 @@ function R() {
|
|
|
411
426
|
}),
|
|
412
427
|
maxWidth: "2xl",
|
|
413
428
|
children: [
|
|
429
|
+
/* @__PURE__ */ _(S, {
|
|
430
|
+
icon: /* @__PURE__ */ _(c, {
|
|
431
|
+
className: "size-4",
|
|
432
|
+
"aria-hidden": "true"
|
|
433
|
+
}),
|
|
434
|
+
children: n("conversations.admin.auditPurpose", "An accountability trail of who changed what — every admin and moderator action on workspace conversations is recorded here. Use it to investigate an incident, confirm a change was made, or satisfy a compliance review.")
|
|
435
|
+
}),
|
|
414
436
|
s && /* @__PURE__ */ _("p", {
|
|
415
437
|
className: "text-sm text-text-secondary",
|
|
416
438
|
children: "Loading activity…"
|
|
@@ -437,7 +459,7 @@ function R() {
|
|
|
437
459
|
children: e.action
|
|
438
460
|
}), /* @__PURE__ */ _("span", {
|
|
439
461
|
className: "text-xs tabular-nums text-text-secondary",
|
|
440
|
-
children:
|
|
462
|
+
children: F(e.timestamp)
|
|
441
463
|
})]
|
|
442
464
|
}),
|
|
443
465
|
/* @__PURE__ */ _("p", {
|
|
@@ -459,20 +481,20 @@ function R() {
|
|
|
459
481
|
]
|
|
460
482
|
});
|
|
461
483
|
}
|
|
462
|
-
function
|
|
463
|
-
let r = t(), { workspaceId: c } = e(), [l, u] = s(""), [f, m] = s("{}"), [
|
|
484
|
+
function B() {
|
|
485
|
+
let r = t(), { workspaceId: c } = e(), [l, u] = s(""), [f, m] = s("{}"), [w, T] = s({}), [E, D] = s(null), { data: O, loading: N, error: P, refetch: L } = g(k, {
|
|
464
486
|
variables: { workspaceId: c },
|
|
465
487
|
skip: !c
|
|
466
|
-
}), [
|
|
488
|
+
}), [R, { loading: z }] = h(A, { onCompleted: () => void L() }), [B, { loading: V }] = h(j, { onCompleted: () => void L() }), [H, { loading: U }] = h(M, { onCompleted: () => void L() }), W = o(() => O?.appInstallations ?? [], [O?.appInstallations]);
|
|
467
489
|
a(() => {
|
|
468
|
-
|
|
469
|
-
if (
|
|
490
|
+
T((e) => {
|
|
491
|
+
if (W.every((t) => e[t.id])) return e;
|
|
470
492
|
let t = { ...e };
|
|
471
|
-
for (let e of
|
|
493
|
+
for (let e of W) t[e.id] || (t[e.id] = JSON.stringify(e.settings ?? {}, null, 2));
|
|
472
494
|
return t;
|
|
473
495
|
});
|
|
474
|
-
}, [
|
|
475
|
-
let
|
|
496
|
+
}, [W]);
|
|
497
|
+
let G = (e) => {
|
|
476
498
|
try {
|
|
477
499
|
let t = JSON.parse(e);
|
|
478
500
|
return t && typeof t == "object" && !Array.isArray(t) ? t : null;
|
|
@@ -489,11 +511,20 @@ function z() {
|
|
|
489
511
|
}),
|
|
490
512
|
maxWidth: "2xl",
|
|
491
513
|
children: [
|
|
514
|
+
/* @__PURE__ */ _(S, {
|
|
515
|
+
icon: /* @__PURE__ */ _(d, {
|
|
516
|
+
className: "size-4",
|
|
517
|
+
"aria-hidden": "true"
|
|
518
|
+
}),
|
|
519
|
+
children: r("conversations.admin.appsPurpose", "Extend conversations with apps and integrations — install bots, automations, or third-party connectors scoped to this workspace, adjust their settings, and remove the ones you no longer use.")
|
|
520
|
+
}),
|
|
492
521
|
/* @__PURE__ */ v(n, {
|
|
493
522
|
title: "Install app",
|
|
494
523
|
description: "Install a workspace-scoped conversation app by app ID.",
|
|
495
|
-
children: [/* @__PURE__ */ v(
|
|
496
|
-
|
|
524
|
+
children: [/* @__PURE__ */ v(x, {
|
|
525
|
+
treatment: "glass",
|
|
526
|
+
glow: !0,
|
|
527
|
+
className: "grid gap-3 border-border-seam p-4 sm:grid-cols-[minmax(0,1fr)_minmax(0,2fr)_auto]",
|
|
497
528
|
children: [
|
|
498
529
|
/* @__PURE__ */ _("input", {
|
|
499
530
|
value: l,
|
|
@@ -512,14 +543,14 @@ function z() {
|
|
|
512
543
|
/* @__PURE__ */ _(y, {
|
|
513
544
|
type: "button",
|
|
514
545
|
size: "sm",
|
|
515
|
-
disabled:
|
|
546
|
+
disabled: z,
|
|
516
547
|
onClick: () => {
|
|
517
|
-
let e =
|
|
548
|
+
let e = G(f);
|
|
518
549
|
if (!l.trim() || !e) {
|
|
519
|
-
|
|
550
|
+
D("Enter an app ID and a valid JSON settings object.");
|
|
520
551
|
return;
|
|
521
552
|
}
|
|
522
|
-
|
|
553
|
+
D(null), R({ variables: { input: {
|
|
523
554
|
appId: l.trim(),
|
|
524
555
|
workspaceId: c,
|
|
525
556
|
settings: e
|
|
@@ -528,22 +559,22 @@ function z() {
|
|
|
528
559
|
children: "Install"
|
|
529
560
|
})
|
|
530
561
|
]
|
|
531
|
-
}),
|
|
562
|
+
}), E && /* @__PURE__ */ _("p", {
|
|
532
563
|
className: "text-sm text-status-error-text",
|
|
533
|
-
children:
|
|
564
|
+
children: E
|
|
534
565
|
})]
|
|
535
566
|
}),
|
|
536
|
-
|
|
567
|
+
N && /* @__PURE__ */ _("p", {
|
|
537
568
|
className: "text-sm text-text-secondary",
|
|
538
569
|
children: "Loading app installations…"
|
|
539
570
|
}),
|
|
540
|
-
|
|
571
|
+
P && /* @__PURE__ */ _("p", {
|
|
541
572
|
className: "text-sm text-status-error-text",
|
|
542
|
-
children:
|
|
573
|
+
children: P.message
|
|
543
574
|
}),
|
|
544
575
|
/* @__PURE__ */ _("div", {
|
|
545
576
|
className: "grid gap-3",
|
|
546
|
-
children:
|
|
577
|
+
children: W.map((e) => /* @__PURE__ */ v(x, {
|
|
547
578
|
treatment: "glass",
|
|
548
579
|
className: "border-border-seam p-4",
|
|
549
580
|
children: [/* @__PURE__ */ v("div", {
|
|
@@ -555,7 +586,7 @@ function z() {
|
|
|
555
586
|
children: [/* @__PURE__ */ _("h3", {
|
|
556
587
|
className: "text-sm font-semibold text-text-primary",
|
|
557
588
|
children: e.appId
|
|
558
|
-
}), /* @__PURE__ */ _(
|
|
589
|
+
}), /* @__PURE__ */ _(I, { children: e.state })]
|
|
559
590
|
}), /* @__PURE__ */ v("p", {
|
|
560
591
|
className: "text-xs text-text-secondary",
|
|
561
592
|
children: [
|
|
@@ -566,7 +597,7 @@ function z() {
|
|
|
566
597
|
e.installedBy,
|
|
567
598
|
" on",
|
|
568
599
|
" ",
|
|
569
|
-
|
|
600
|
+
F(e.installedAt)
|
|
570
601
|
]
|
|
571
602
|
})]
|
|
572
603
|
}), /* @__PURE__ */ _(b, {
|
|
@@ -574,17 +605,17 @@ function z() {
|
|
|
574
605
|
actions: [{
|
|
575
606
|
label: "Uninstall",
|
|
576
607
|
intent: "destructive",
|
|
577
|
-
disabled:
|
|
608
|
+
disabled: U,
|
|
578
609
|
icon: /* @__PURE__ */ _(p, {
|
|
579
610
|
className: "size-4",
|
|
580
611
|
"aria-hidden": "true"
|
|
581
612
|
}),
|
|
582
613
|
onSelect: () => {
|
|
583
|
-
window.confirm(`Uninstall ${e.appId}? This removes the app and its settings from this workspace.`) &&
|
|
614
|
+
window.confirm(`Uninstall ${e.appId}? This removes the app and its settings from this workspace.`) && H({ variables: { id: e.id } });
|
|
584
615
|
}
|
|
585
616
|
}]
|
|
586
617
|
})]
|
|
587
|
-
}), /* @__PURE__ */ _(
|
|
618
|
+
}), /* @__PURE__ */ _(C, {
|
|
588
619
|
className: "mt-3",
|
|
589
620
|
collapsedHeight: 0,
|
|
590
621
|
label: "Edit settings (JSON)",
|
|
@@ -593,8 +624,8 @@ function z() {
|
|
|
593
624
|
children: [/* @__PURE__ */ _("textarea", {
|
|
594
625
|
rows: 5,
|
|
595
626
|
"aria-label": `Settings for ${e.appId}`,
|
|
596
|
-
value:
|
|
597
|
-
onChange: (t) =>
|
|
627
|
+
value: w[e.id] ?? "{}",
|
|
628
|
+
onChange: (t) => T((n) => ({
|
|
598
629
|
...n,
|
|
599
630
|
[e.id]: t.target.value
|
|
600
631
|
})),
|
|
@@ -603,15 +634,15 @@ function z() {
|
|
|
603
634
|
type: "button",
|
|
604
635
|
variant: "secondary",
|
|
605
636
|
size: "sm",
|
|
606
|
-
disabled:
|
|
637
|
+
disabled: V,
|
|
607
638
|
className: "w-fit",
|
|
608
639
|
onClick: () => {
|
|
609
|
-
let t =
|
|
640
|
+
let t = G(w[e.id] ?? "{}");
|
|
610
641
|
if (!t) {
|
|
611
|
-
|
|
642
|
+
D("Installation settings must be a valid JSON object.");
|
|
612
643
|
return;
|
|
613
644
|
}
|
|
614
|
-
|
|
645
|
+
D(null), B({ variables: {
|
|
615
646
|
id: e.id,
|
|
616
647
|
settings: t
|
|
617
648
|
} });
|
|
@@ -626,6 +657,6 @@ function z() {
|
|
|
626
657
|
});
|
|
627
658
|
}
|
|
628
659
|
//#endregion
|
|
629
|
-
export {
|
|
660
|
+
export { B as AppInstallationsPage, R as BulkOperationsPage, z as ConversationAuditLogPage, L as ModerationQueuePage };
|
|
630
661
|
|
|
631
662
|
//# sourceMappingURL=ConversationAdminPages.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ConversationAdminPages.js","names":[],"sources":["../../../src/conversations/pages/ConversationAdminPages.tsx"],"sourcesContent":["import { useEffect, useMemo, useState } from \"react\";\nimport { gql } from \"@apollo/client\";\nimport { useMutation, useQuery } from \"@apollo/client/react\";\nimport {\n Activity,\n Archive,\n CheckCircle2,\n Package,\n ShieldCheck,\n Trash2,\n} from \"lucide-react\";\n\nimport {\n Button,\n CTAOverflowMenu,\n GlassCard,\n ShowMore,\n} from \"@burdenoff/fe-libs/ui\";\nimport { EmptyState, PageLayout, PageSection } from \"../components/PageLayout\";\nimport { useConversations } from \"../providers/ConversationsProvider\";\nimport { useTr } from \"../utils/i18n\";\n\nconst GET_MESSAGE_REPORTS = gql`\n query ConversationAdminMessageReports(\n $first: Int\n $filter: MessageReportFilter\n ) {\n messageReports(first: $first, filter: $filter) {\n edges {\n node {\n id\n messageId\n reportedBy\n reason\n status\n notes\n createdAt\n reviewedAt\n message {\n id\n conversationId\n authorId\n content\n isHidden\n createdAt\n }\n }\n }\n }\n }\n`;\n\nconst RESOLVE_MESSAGE_REPORT = gql`\n mutation ConversationAdminResolveMessageReport(\n $input: ResolveMessageReportInput!\n ) {\n resolveMessageReport(input: $input) {\n id\n status\n reviewedAt\n notes\n message {\n id\n isHidden\n }\n }\n }\n`;\n\nconst GET_BULK_CONVERSATIONS = gql`\n query ConversationAdminBulkConversations(\n $first: Int\n $filter: ConversationFilter\n ) {\n conversations(first: $first, filter: $filter) {\n edges {\n node {\n id\n title\n type\n state\n updatedAt\n }\n }\n }\n }\n`;\n\nconst BULK_UPDATE_CONVERSATIONS = gql`\n mutation ConversationAdminBulkUpdate(\n $input: BulkConversationOperationInput!\n ) {\n bulkUpdateConversations(input: $input) {\n success\n processedCount\n successCount\n failureCount\n conversationIds\n errors {\n conversationId\n message\n }\n }\n }\n`;\n\nconst GET_CONVERSATION_AUDIT_LOG = gql`\n query ConversationAdminAuditLog($filter: ActivityLogFilter) {\n getActivities(filter: $filter) {\n id\n action\n description\n resourceId\n resourceType\n userId\n timestamp\n }\n }\n`;\n\nconst GET_APP_INSTALLATIONS = gql`\n query ConversationAdminAppInstallations($workspaceId: ID) {\n appInstallations(workspaceId: $workspaceId) {\n id\n appId\n appVersion\n state\n settings\n permissions\n installedBy\n installedAt\n updatedAt\n }\n }\n`;\n\nconst INSTALL_APP = gql`\n mutation ConversationAdminInstallApp($input: InstallAppInput!) {\n installApp(input: $input) {\n id\n appId\n state\n settings\n installedAt\n }\n }\n`;\n\nconst UPDATE_APP_SETTINGS = gql`\n mutation ConversationAdminUpdateAppSettings($id: ID!, $settings: JSON!) {\n updateAppSettings(id: $id, settings: $settings) {\n id\n settings\n updatedAt\n }\n }\n`;\n\nconst UNINSTALL_APP = gql`\n mutation ConversationAdminUninstallApp($id: ID!) {\n uninstallApp(id: $id) {\n appInstallationId\n success\n }\n }\n`;\n\ninterface MessageReportNode {\n id: string;\n messageId: string;\n reportedBy: string;\n reason: string;\n status: string;\n notes?: string | null;\n createdAt: string;\n reviewedAt?: string | null;\n message: {\n id: string;\n conversationId: string;\n authorId: string;\n content: unknown;\n isHidden: boolean;\n createdAt: string;\n };\n}\n\ninterface ConversationNode {\n id: string;\n title?: string | null;\n type: string;\n state: string;\n updatedAt: string;\n}\n\ninterface ActivityLogNode {\n id: string;\n action: string;\n description?: string | null;\n resourceId?: string | null;\n resourceType?: string | null;\n userId: string;\n timestamp: string;\n}\n\ninterface AppInstallationNode {\n id: string;\n appId: string;\n appVersion: string;\n state: string;\n settings: unknown;\n permissions: string[];\n installedBy: string;\n installedAt: string;\n updatedAt: string;\n}\n\ninterface BulkUpdateConversationsResult {\n bulkUpdateConversations: {\n success: boolean;\n processedCount: number;\n successCount: number;\n failureCount: number;\n conversationIds: string[];\n errors: Array<{ conversationId: string; message: string }>;\n };\n}\n\nfunction formatContent(content: unknown): string {\n if (typeof content === \"string\") return content;\n if (content && typeof content === \"object\" && \"text\" in content) {\n const text = (content as { text?: unknown }).text;\n if (typeof text === \"string\") return text;\n }\n try {\n return JSON.stringify(content);\n } catch {\n return \"Message content unavailable\";\n }\n}\n\nconst DATE_FORMATTER = new Intl.DateTimeFormat(undefined, {\n dateStyle: \"medium\",\n timeStyle: \"short\",\n});\n\nfunction formatDate(value: string): string {\n return DATE_FORMATTER.format(new Date(value));\n}\n\nfunction StatusPill({ children }: { children: string }) {\n return (\n <span className=\"rounded-full border border-border-subtle bg-bg-sunken px-2 py-0.5 text-xs font-medium text-text-secondary\">\n {children}\n </span>\n );\n}\n\nexport function ModerationQueuePage() {\n const tr = useTr();\n const { workspaceId } = useConversations();\n const { data, loading, error, refetch } = useQuery<{\n messageReports: { edges: Array<{ node: MessageReportNode }> };\n }>(GET_MESSAGE_REPORTS, {\n variables: { first: 50, filter: { statuses: [\"PENDING\", \"REVIEWING\"] } },\n skip: !workspaceId,\n });\n const [resolveReport, { loading: resolving }] = useMutation(\n RESOLVE_MESSAGE_REPORT,\n {\n onCompleted: () => void refetch(),\n },\n );\n\n const reports = data?.messageReports.edges.map((edge) => edge.node) ?? [];\n\n return (\n <PageLayout\n title={tr(\"conversations.admin.moderationPageTitle\", \"Moderation queue\")}\n subtitle={tr(\n \"conversations.admin.moderationPageDescription\",\n \"Review reported messages and apply moderator decisions.\",\n )}\n icon={<ShieldCheck className=\"size-5\" aria-hidden=\"true\" />}\n maxWidth=\"2xl\"\n >\n {!workspaceId && (\n <EmptyState\n title={tr(\n \"conversations.admin.selectWorkspace\",\n \"Select a workspace\",\n )}\n description={tr(\n \"conversations.admin.selectWorkspaceModerationDescription\",\n \"Choose a workspace to review reported conversation messages.\",\n )}\n icon={<ShieldCheck className=\"size-6\" aria-hidden=\"true\" />}\n illustration=\"empty-generic\"\n />\n )}\n {workspaceId && loading && (\n <p className=\"text-sm text-text-secondary\">Loading reports…</p>\n )}\n {workspaceId && error && (\n <p className=\"text-sm text-status-error-text\">{error.message}</p>\n )}\n {workspaceId && !loading && !error && reports.length === 0 && (\n <EmptyState\n title=\"No reports waiting for review\"\n description=\"Reported messages will appear here as they are submitted.\"\n icon={<CheckCircle2 className=\"size-6\" aria-hidden=\"true\" />}\n illustration=\"empty-notifications\"\n />\n )}\n <div className=\"grid gap-3\">\n {reports.map((report) => (\n <GlassCard\n key={report.id}\n treatment=\"glass\"\n className=\"border-border-seam p-4\"\n >\n <div className=\"flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between\">\n <div className=\"space-y-2 min-w-0\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <StatusPill>{report.status}</StatusPill>\n {report.message.isHidden && <StatusPill>Hidden</StatusPill>}\n <span className=\"text-xs text-text-secondary\">\n Reported {formatDate(report.createdAt)}\n </span>\n </div>\n <p className=\"text-sm font-medium text-text-primary\">\n {report.reason}\n </p>\n <p className=\"rounded-md border border-border-seam bg-bg-sunken p-3 text-sm text-text-secondary\">\n {formatContent(report.message.content)}\n </p>\n <p className=\"text-xs text-text-secondary\">\n Message {report.messageId} in conversation{\" \"}\n {report.message.conversationId} by {report.message.authorId};\n reported by {report.reportedBy}\n </p>\n </div>\n {/* One primary CTA (hide + resolve), the secondary \"Dismiss\"\n collapses into the overflow menu. */}\n <CTAOverflowMenu\n className=\"shrink-0\"\n primary={{\n label: \"Hide and resolve\",\n disabled: resolving,\n onSelect: () =>\n void resolveReport({\n variables: {\n input: { reportId: report.id, action: \"HIDE_MESSAGE\" },\n },\n }),\n }}\n actions={[\n {\n label: \"Dismiss\",\n disabled: resolving,\n onSelect: () =>\n void resolveReport({\n variables: {\n input: { reportId: report.id, action: \"DISMISS\" },\n },\n }),\n },\n ]}\n />\n </div>\n </GlassCard>\n ))}\n </div>\n </PageLayout>\n );\n}\n\nexport function BulkOperationsPage() {\n const tr = useTr();\n const { workspaceId } = useConversations();\n const [selectedIds, setSelectedIds] = useState<Set<string>>(() => new Set());\n const [action, setAction] = useState<\"ARCHIVE\" | \"UNARCHIVE\" | \"DELETE\">(\n \"ARCHIVE\",\n );\n const { data, loading, error, refetch } = useQuery<{\n conversations: { edges: Array<{ node: ConversationNode }> };\n }>(GET_BULK_CONVERSATIONS, {\n variables: { first: 100 },\n skip: !workspaceId,\n });\n const [bulkUpdate, { data: bulkResult, loading: updating }] = useMutation<\n BulkUpdateConversationsResult,\n { input: { conversationIds: string[]; action: typeof action } }\n >(BULK_UPDATE_CONVERSATIONS, {\n onCompleted: () => {\n setSelectedIds(new Set());\n void refetch();\n },\n });\n const conversations =\n data?.conversations.edges.map((edge) => edge.node) ?? [];\n\n const toggle = (id: string) => {\n setSelectedIds((current) => {\n const next = new Set(current);\n if (next.has(id)) next.delete(id);\n else next.add(id);\n return next;\n });\n };\n\n return (\n <PageLayout\n title={tr(\n \"conversations.admin.bulkOperationsPageTitle\",\n \"Bulk operations\",\n )}\n subtitle={tr(\n \"conversations.admin.bulkOperationsPageDescription\",\n \"Select workspace conversations and apply archive, unarchive, or delete operations.\",\n )}\n icon={<Archive className=\"size-5\" aria-hidden=\"true\" />}\n maxWidth=\"2xl\"\n headerActions={\n <div className=\"flex flex-wrap items-center gap-2\">\n <select\n value={action}\n onChange={(event) => setAction(event.target.value as typeof action)}\n aria-label=\"Bulk action\"\n className=\"min-h-11 rounded-md border border-border-seam bg-bg-surface px-3 py-2 text-sm text-text-primary focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)]\"\n >\n <option value=\"ARCHIVE\">Archive</option>\n <option value=\"UNARCHIVE\">Unarchive</option>\n <option value=\"DELETE\">Delete</option>\n </select>\n <Button\n type=\"button\"\n size=\"sm\"\n disabled={selectedIds.size === 0 || updating}\n onClick={() =>\n void bulkUpdate({\n variables: {\n input: { conversationIds: Array.from(selectedIds), action },\n },\n })\n }\n >\n Apply to {selectedIds.size}\n </Button>\n </div>\n }\n >\n {!workspaceId && (\n <EmptyState\n title={tr(\n \"conversations.admin.selectWorkspace\",\n \"Select a workspace\",\n )}\n description={tr(\n \"conversations.admin.selectWorkspaceBulkDescription\",\n \"Choose a workspace to manage conversations in bulk.\",\n )}\n icon={<Archive className=\"size-6\" aria-hidden=\"true\" />}\n illustration=\"empty-generic\"\n />\n )}\n {workspaceId && loading && (\n <p className=\"text-sm text-text-secondary\">Loading conversations…</p>\n )}\n {workspaceId && error && (\n <p className=\"text-sm text-status-error-text\">{error.message}</p>\n )}\n {bulkResult?.bulkUpdateConversations && (\n <GlassCard\n treatment=\"metric\"\n className=\"grid grid-cols-2 gap-4 border-border-seam p-4 sm:max-w-md\"\n >\n <div>\n <p className=\"text-2xl font-semibold tabular-nums text-status-success-text\">\n {bulkResult.bulkUpdateConversations.successCount}\n </p>\n <p className=\"text-xs text-text-secondary\">Succeeded</p>\n </div>\n <div>\n <p className=\"text-2xl font-semibold tabular-nums text-text-primary\">\n {bulkResult.bulkUpdateConversations.failureCount}\n </p>\n <p className=\"text-xs text-text-secondary\">Failed</p>\n </div>\n </GlassCard>\n )}\n {workspaceId && (\n <div className=\"overflow-hidden rounded-lg border border-border-seam bg-bg-surface\">\n {conversations.map((conversation) => (\n <label\n key={conversation.id}\n className=\"flex min-h-11 cursor-pointer items-center gap-3 border-b border-border-seam px-4 py-3 last:border-b-0 hover:bg-accent\"\n >\n <input\n type=\"checkbox\"\n aria-label={`Select ${conversation.title || \"untitled conversation\"}`}\n checked={selectedIds.has(conversation.id)}\n onChange={() => toggle(conversation.id)}\n />\n <div className=\"min-w-0 flex-1\">\n <p className=\"truncate text-sm font-medium text-text-primary\">\n {conversation.title || \"Untitled conversation\"}\n </p>\n <p className=\"text-xs text-text-secondary\">\n {conversation.type} · {conversation.state} · Updated{\" \"}\n {formatDate(conversation.updatedAt)}\n </p>\n </div>\n </label>\n ))}\n </div>\n )}\n </PageLayout>\n );\n}\n\nexport function ConversationAuditLogPage() {\n const tr = useTr();\n const { workspaceId } = useConversations();\n const { data, loading, error } = useQuery<{\n getActivities: ActivityLogNode[];\n }>(GET_CONVERSATION_AUDIT_LOG, {\n variables: {\n filter: { workspaceId, resourceType: \"conversation\", limit: 50 },\n },\n skip: !workspaceId,\n });\n const logs = data?.getActivities ?? [];\n\n return (\n <PageLayout\n title={tr(\"conversations.admin.auditLogPageTitle\", \"Audit log\")}\n subtitle={tr(\n \"conversations.admin.auditLogPageDescription\",\n \"Activity stream for admin and moderator actions on workspace conversations.\",\n )}\n icon={<Activity className=\"size-5\" aria-hidden=\"true\" />}\n maxWidth=\"2xl\"\n >\n {loading && (\n <p className=\"text-sm text-text-secondary\">Loading activity…</p>\n )}\n {error && <p className=\"text-sm text-status-error-text\">{error.message}</p>}\n {!loading && !error && logs.length === 0 && (\n <EmptyState\n title=\"No activity found\"\n description=\"Conversation admin actions will appear here.\"\n illustration=\"empty-data\"\n />\n )}\n <div className=\"grid gap-3\">\n {logs.map((log) => (\n <GlassCard\n key={log.id}\n treatment=\"plain\"\n className=\"border-border-seam p-4\"\n >\n <div className=\"flex flex-wrap items-center justify-between gap-2\">\n <p className=\"text-sm font-medium text-text-primary\">\n {log.action}\n </p>\n <span className=\"text-xs tabular-nums text-text-secondary\">\n {formatDate(log.timestamp)}\n </span>\n </div>\n <p className=\"mt-1 text-sm text-text-secondary\">\n {log.description || \"No description provided\"}\n </p>\n <p className=\"mt-2 text-xs text-text-secondary\">\n User {log.userId} · Resource {log.resourceId ?? \"workspace\"}\n </p>\n </GlassCard>\n ))}\n </div>\n </PageLayout>\n );\n}\n\nexport function AppInstallationsPage() {\n const tr = useTr();\n const { workspaceId } = useConversations();\n const [appId, setAppId] = useState(\"\");\n const [settingsInput, setSettingsInput] = useState(\"{}\");\n const [settingsDrafts, setSettingsDrafts] = useState<Record<string, string>>(\n {},\n );\n const [formError, setFormError] = useState<string | null>(null);\n const { data, loading, error, refetch } = useQuery<{\n appInstallations: AppInstallationNode[];\n }>(GET_APP_INSTALLATIONS, {\n variables: { workspaceId },\n skip: !workspaceId,\n });\n const [installApp, { loading: installing }] = useMutation(INSTALL_APP, {\n onCompleted: () => void refetch(),\n });\n const [updateSettings, { loading: saving }] = useMutation(\n UPDATE_APP_SETTINGS,\n { onCompleted: () => void refetch() },\n );\n const [uninstallApp, { loading: uninstalling }] = useMutation(UNINSTALL_APP, {\n onCompleted: () => void refetch(),\n });\n const installations = useMemo(\n () => data?.appInstallations ?? [],\n [data?.appInstallations],\n );\n\n useEffect(() => {\n setSettingsDrafts((current) => {\n if (installations.every((installation) => current[installation.id])) {\n return current;\n }\n const next = { ...current };\n for (const installation of installations) {\n if (!next[installation.id]) {\n next[installation.id] = JSON.stringify(\n installation.settings ?? {},\n null,\n 2,\n );\n }\n }\n return next;\n });\n }, [installations]);\n\n const parsedSettings = (raw: string): Record<string, unknown> | null => {\n try {\n const parsed = JSON.parse(raw) as unknown;\n return parsed && typeof parsed === \"object\" && !Array.isArray(parsed)\n ? (parsed as Record<string, unknown>)\n : null;\n } catch {\n return null;\n }\n };\n\n const handleInstall = () => {\n const settings = parsedSettings(settingsInput);\n if (!appId.trim() || !settings) {\n setFormError(\"Enter an app ID and a valid JSON settings object.\");\n return;\n }\n setFormError(null);\n void installApp({\n variables: { input: { appId: appId.trim(), workspaceId, settings } },\n });\n };\n\n return (\n <PageLayout\n title={tr(\n \"conversations.admin.appInstallationsPageTitle\",\n \"App installations\",\n )}\n subtitle={tr(\n \"conversations.admin.appInstallationsPageDescription\",\n \"Install, configure, and remove conversation apps and integrations.\",\n )}\n icon={<Package className=\"size-5\" aria-hidden=\"true\" />}\n maxWidth=\"2xl\"\n >\n <PageSection\n title=\"Install app\"\n description=\"Install a workspace-scoped conversation app by app ID.\"\n >\n <div className=\"grid gap-3 rounded-lg border border-border-seam bg-bg-surface p-4 sm:grid-cols-[minmax(0,1fr)_minmax(0,2fr)_auto]\">\n <input\n value={appId}\n onChange={(event) => setAppId(event.target.value)}\n placeholder=\"app-id\"\n aria-label=\"App ID\"\n className=\"min-h-11 rounded-md border border-border-seam bg-bg-surface px-3 py-2 text-sm text-text-primary focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)]\"\n />\n <textarea\n value={settingsInput}\n onChange={(event) => setSettingsInput(event.target.value)}\n rows={3}\n aria-label=\"App settings JSON\"\n className=\"rounded-md border border-border-seam bg-bg-surface px-3 py-2 text-sm text-text-primary focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)]\"\n />\n <Button\n type=\"button\"\n size=\"sm\"\n disabled={installing}\n onClick={handleInstall}\n >\n Install\n </Button>\n </div>\n {formError && <p className=\"text-sm text-status-error-text\">{formError}</p>}\n </PageSection>\n {loading && (\n <p className=\"text-sm text-text-secondary\">\n Loading app installations…\n </p>\n )}\n {error && <p className=\"text-sm text-status-error-text\">{error.message}</p>}\n <div className=\"grid gap-3\">\n {installations.map((installation) => (\n <GlassCard\n key={installation.id}\n treatment=\"glass\"\n className=\"border-border-seam p-4\"\n >\n <div className=\"flex flex-wrap items-start justify-between gap-3\">\n <div className=\"min-w-0\">\n <div className=\"flex items-center gap-2\">\n <h3 className=\"text-sm font-semibold text-text-primary\">\n {installation.appId}\n </h3>\n <StatusPill>{installation.state}</StatusPill>\n </div>\n <p className=\"text-xs text-text-secondary\">\n Version {installation.appVersion} · Installed by{\" \"}\n {installation.installedBy} on{\" \"}\n {formatDate(installation.installedAt)}\n </p>\n </div>\n {/* Destructive uninstall lives behind the overflow menu and\n asks for confirmation before firing the mutation. */}\n <CTAOverflowMenu\n className=\"shrink-0\"\n actions={[\n {\n label: \"Uninstall\",\n intent: \"destructive\",\n disabled: uninstalling,\n icon: <Trash2 className=\"size-4\" aria-hidden=\"true\" />,\n onSelect: () => {\n if (\n !window.confirm(\n `Uninstall ${installation.appId}? This removes the app and its settings from this workspace.`,\n )\n ) {\n return;\n }\n void uninstallApp({\n variables: { id: installation.id },\n });\n },\n },\n ]}\n />\n </div>\n {/* The raw JSON settings editor is an advanced surface — collapse\n it behind progressive disclosure so the card stays calm. */}\n <ShowMore\n className=\"mt-3\"\n collapsedHeight={0}\n label=\"Edit settings (JSON)\"\n >\n <div className=\"grid gap-2 pt-2\">\n <textarea\n rows={5}\n aria-label={`Settings for ${installation.appId}`}\n value={settingsDrafts[installation.id] ?? \"{}\"}\n onChange={(event) =>\n setSettingsDrafts((current) => ({\n ...current,\n [installation.id]: event.target.value,\n }))\n }\n className=\"rounded-md border border-border-seam bg-bg-surface px-3 py-2 font-mono text-xs text-text-primary focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)]\"\n />\n <Button\n type=\"button\"\n variant=\"secondary\"\n size=\"sm\"\n disabled={saving}\n className=\"w-fit\"\n onClick={() => {\n const settings = parsedSettings(\n settingsDrafts[installation.id] ?? \"{}\",\n );\n if (!settings) {\n setFormError(\n \"Installation settings must be a valid JSON object.\",\n );\n return;\n }\n setFormError(null);\n void updateSettings({\n variables: { id: installation.id, settings },\n });\n }}\n >\n Save settings\n </Button>\n </div>\n </ShowMore>\n </GlassCard>\n ))}\n </div>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ConversationAdminPages.js","names":[],"sources":["../../../src/conversations/pages/ConversationAdminPages.tsx"],"sourcesContent":["import { useEffect, useMemo, useState } from \"react\";\nimport { gql } from \"@apollo/client\";\nimport { useMutation, useQuery } from \"@apollo/client/react\";\nimport {\n Activity,\n Archive,\n CheckCircle2,\n Package,\n ShieldCheck,\n Trash2,\n} from \"lucide-react\";\n\nimport {\n Button,\n CTAOverflowMenu,\n GlassCard,\n PagePurpose,\n ShowMore,\n} from \"@burdenoff/fe-libs/ui\";\nimport { EmptyState, PageLayout, PageSection } from \"../components/PageLayout\";\nimport { useConversations } from \"../providers/ConversationsProvider\";\nimport { useTr } from \"../utils/i18n\";\n\nconst GET_MESSAGE_REPORTS = gql`\n query ConversationAdminMessageReports(\n $first: Int\n $filter: MessageReportFilter\n ) {\n messageReports(first: $first, filter: $filter) {\n edges {\n node {\n id\n messageId\n reportedBy\n reason\n status\n notes\n createdAt\n reviewedAt\n message {\n id\n conversationId\n authorId\n content\n isHidden\n createdAt\n }\n }\n }\n }\n }\n`;\n\nconst RESOLVE_MESSAGE_REPORT = gql`\n mutation ConversationAdminResolveMessageReport(\n $input: ResolveMessageReportInput!\n ) {\n resolveMessageReport(input: $input) {\n id\n status\n reviewedAt\n notes\n message {\n id\n isHidden\n }\n }\n }\n`;\n\nconst GET_BULK_CONVERSATIONS = gql`\n query ConversationAdminBulkConversations(\n $first: Int\n $filter: ConversationFilter\n ) {\n conversations(first: $first, filter: $filter) {\n edges {\n node {\n id\n title\n type\n state\n updatedAt\n }\n }\n }\n }\n`;\n\nconst BULK_UPDATE_CONVERSATIONS = gql`\n mutation ConversationAdminBulkUpdate(\n $input: BulkConversationOperationInput!\n ) {\n bulkUpdateConversations(input: $input) {\n success\n processedCount\n successCount\n failureCount\n conversationIds\n errors {\n conversationId\n message\n }\n }\n }\n`;\n\nconst GET_CONVERSATION_AUDIT_LOG = gql`\n query ConversationAdminAuditLog($filter: ActivityLogFilter) {\n getActivities(filter: $filter) {\n id\n action\n description\n resourceId\n resourceType\n userId\n timestamp\n }\n }\n`;\n\nconst GET_APP_INSTALLATIONS = gql`\n query ConversationAdminAppInstallations($workspaceId: ID) {\n appInstallations(workspaceId: $workspaceId) {\n id\n appId\n appVersion\n state\n settings\n permissions\n installedBy\n installedAt\n updatedAt\n }\n }\n`;\n\nconst INSTALL_APP = gql`\n mutation ConversationAdminInstallApp($input: InstallAppInput!) {\n installApp(input: $input) {\n id\n appId\n state\n settings\n installedAt\n }\n }\n`;\n\nconst UPDATE_APP_SETTINGS = gql`\n mutation ConversationAdminUpdateAppSettings($id: ID!, $settings: JSON!) {\n updateAppSettings(id: $id, settings: $settings) {\n id\n settings\n updatedAt\n }\n }\n`;\n\nconst UNINSTALL_APP = gql`\n mutation ConversationAdminUninstallApp($id: ID!) {\n uninstallApp(id: $id) {\n appInstallationId\n success\n }\n }\n`;\n\ninterface MessageReportNode {\n id: string;\n messageId: string;\n reportedBy: string;\n reason: string;\n status: string;\n notes?: string | null;\n createdAt: string;\n reviewedAt?: string | null;\n message: {\n id: string;\n conversationId: string;\n authorId: string;\n content: unknown;\n isHidden: boolean;\n createdAt: string;\n };\n}\n\ninterface ConversationNode {\n id: string;\n title?: string | null;\n type: string;\n state: string;\n updatedAt: string;\n}\n\ninterface ActivityLogNode {\n id: string;\n action: string;\n description?: string | null;\n resourceId?: string | null;\n resourceType?: string | null;\n userId: string;\n timestamp: string;\n}\n\ninterface AppInstallationNode {\n id: string;\n appId: string;\n appVersion: string;\n state: string;\n settings: unknown;\n permissions: string[];\n installedBy: string;\n installedAt: string;\n updatedAt: string;\n}\n\ninterface BulkUpdateConversationsResult {\n bulkUpdateConversations: {\n success: boolean;\n processedCount: number;\n successCount: number;\n failureCount: number;\n conversationIds: string[];\n errors: Array<{ conversationId: string; message: string }>;\n };\n}\n\nfunction formatContent(content: unknown): string {\n if (typeof content === \"string\") return content;\n if (content && typeof content === \"object\" && \"text\" in content) {\n const text = (content as { text?: unknown }).text;\n if (typeof text === \"string\") return text;\n }\n try {\n return JSON.stringify(content);\n } catch {\n return \"Message content unavailable\";\n }\n}\n\nconst DATE_FORMATTER = new Intl.DateTimeFormat(undefined, {\n dateStyle: \"medium\",\n timeStyle: \"short\",\n});\n\nfunction formatDate(value: string): string {\n return DATE_FORMATTER.format(new Date(value));\n}\n\nfunction StatusPill({ children }: { children: string }) {\n return (\n <span className=\"rounded-full border border-border-subtle bg-bg-sunken px-2 py-0.5 text-xs font-medium text-text-secondary\">\n {children}\n </span>\n );\n}\n\nexport function ModerationQueuePage() {\n const tr = useTr();\n const { workspaceId } = useConversations();\n const { data, loading, error, refetch } = useQuery<{\n messageReports: { edges: Array<{ node: MessageReportNode }> };\n }>(GET_MESSAGE_REPORTS, {\n variables: { first: 50, filter: { statuses: [\"PENDING\", \"REVIEWING\"] } },\n skip: !workspaceId,\n });\n const [resolveReport, { loading: resolving }] = useMutation(\n RESOLVE_MESSAGE_REPORT,\n {\n onCompleted: () => void refetch(),\n },\n );\n\n const reports = data?.messageReports.edges.map((edge) => edge.node) ?? [];\n\n return (\n <PageLayout\n title={tr(\"conversations.admin.moderationPageTitle\", \"Moderation queue\")}\n subtitle={tr(\n \"conversations.admin.moderationPageDescription\",\n \"Review reported messages and apply moderator decisions.\",\n )}\n icon={<ShieldCheck className=\"size-5\" aria-hidden=\"true\" />}\n maxWidth=\"2xl\"\n >\n <PagePurpose icon={<ShieldCheck className=\"size-4\" aria-hidden=\"true\" />}>\n {tr(\n \"conversations.admin.moderationPurpose\",\n \"Keep conversations safe: people can report messages they find abusive, spammy, or off-topic, and they land here for a moderator to hide the message or dismiss the report. Work the queue down to zero to stay on top of issues.\",\n )}\n </PagePurpose>\n {!workspaceId && (\n <EmptyState\n title={tr(\n \"conversations.admin.selectWorkspace\",\n \"Select a workspace\",\n )}\n description={tr(\n \"conversations.admin.selectWorkspaceModerationDescription\",\n \"Choose a workspace to review reported conversation messages.\",\n )}\n icon={<ShieldCheck className=\"size-6\" aria-hidden=\"true\" />}\n illustration=\"empty-generic\"\n />\n )}\n {workspaceId && loading && (\n <p className=\"text-sm text-text-secondary\">Loading reports…</p>\n )}\n {workspaceId && error && (\n <p className=\"text-sm text-status-error-text\">{error.message}</p>\n )}\n {workspaceId && !loading && !error && reports.length === 0 && (\n <EmptyState\n title=\"No reports waiting for review\"\n description=\"Reported messages will appear here as they are submitted.\"\n icon={<CheckCircle2 className=\"size-6\" aria-hidden=\"true\" />}\n illustration=\"empty-notifications\"\n />\n )}\n <div className=\"grid gap-3\">\n {reports.map((report) => (\n <GlassCard\n key={report.id}\n treatment=\"glass\"\n className=\"border-border-seam p-4\"\n >\n <div className=\"flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between\">\n <div className=\"space-y-2 min-w-0\">\n <div className=\"flex flex-wrap items-center gap-2\">\n <StatusPill>{report.status}</StatusPill>\n {report.message.isHidden && <StatusPill>Hidden</StatusPill>}\n <span className=\"text-xs text-text-secondary\">\n Reported {formatDate(report.createdAt)}\n </span>\n </div>\n <p className=\"text-sm font-medium text-text-primary\">\n {report.reason}\n </p>\n <p className=\"rounded-md border border-border-seam bg-bg-sunken p-3 text-sm text-text-secondary\">\n {formatContent(report.message.content)}\n </p>\n <p className=\"text-xs text-text-secondary\">\n Message {report.messageId} in conversation{\" \"}\n {report.message.conversationId} by {report.message.authorId};\n reported by {report.reportedBy}\n </p>\n </div>\n {/* One primary CTA (hide + resolve), the secondary \"Dismiss\"\n collapses into the overflow menu. */}\n <CTAOverflowMenu\n className=\"shrink-0\"\n primary={{\n label: \"Hide and resolve\",\n disabled: resolving,\n onSelect: () =>\n void resolveReport({\n variables: {\n input: { reportId: report.id, action: \"HIDE_MESSAGE\" },\n },\n }),\n }}\n actions={[\n {\n label: \"Dismiss\",\n disabled: resolving,\n onSelect: () =>\n void resolveReport({\n variables: {\n input: { reportId: report.id, action: \"DISMISS\" },\n },\n }),\n },\n ]}\n />\n </div>\n </GlassCard>\n ))}\n </div>\n </PageLayout>\n );\n}\n\nexport function BulkOperationsPage() {\n const tr = useTr();\n const { workspaceId } = useConversations();\n const [selectedIds, setSelectedIds] = useState<Set<string>>(() => new Set());\n const [action, setAction] = useState<\"ARCHIVE\" | \"UNARCHIVE\" | \"DELETE\">(\n \"ARCHIVE\",\n );\n const { data, loading, error, refetch } = useQuery<{\n conversations: { edges: Array<{ node: ConversationNode }> };\n }>(GET_BULK_CONVERSATIONS, {\n variables: { first: 100 },\n skip: !workspaceId,\n });\n const [bulkUpdate, { data: bulkResult, loading: updating }] = useMutation<\n BulkUpdateConversationsResult,\n { input: { conversationIds: string[]; action: typeof action } }\n >(BULK_UPDATE_CONVERSATIONS, {\n onCompleted: () => {\n setSelectedIds(new Set());\n void refetch();\n },\n });\n const conversations =\n data?.conversations.edges.map((edge) => edge.node) ?? [];\n\n const toggle = (id: string) => {\n setSelectedIds((current) => {\n const next = new Set(current);\n if (next.has(id)) next.delete(id);\n else next.add(id);\n return next;\n });\n };\n\n return (\n <PageLayout\n title={tr(\n \"conversations.admin.bulkOperationsPageTitle\",\n \"Bulk operations\",\n )}\n subtitle={tr(\n \"conversations.admin.bulkOperationsPageDescription\",\n \"Select workspace conversations and apply archive, unarchive, or delete operations.\",\n )}\n icon={<Archive className=\"size-5\" aria-hidden=\"true\" />}\n maxWidth=\"2xl\"\n headerActions={\n <div className=\"flex flex-wrap items-center gap-2\">\n <select\n value={action}\n onChange={(event) => setAction(event.target.value as typeof action)}\n aria-label=\"Bulk action\"\n className=\"min-h-11 rounded-md border border-border-seam bg-bg-surface px-3 py-2 text-sm text-text-primary focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)]\"\n >\n <option value=\"ARCHIVE\">Archive</option>\n <option value=\"UNARCHIVE\">Unarchive</option>\n <option value=\"DELETE\">Delete</option>\n </select>\n <Button\n type=\"button\"\n size=\"sm\"\n disabled={selectedIds.size === 0 || updating}\n onClick={() =>\n void bulkUpdate({\n variables: {\n input: { conversationIds: Array.from(selectedIds), action },\n },\n })\n }\n >\n Apply to {selectedIds.size}\n </Button>\n </div>\n }\n >\n <PagePurpose icon={<Archive className=\"size-4\" aria-hidden=\"true\" />}>\n {tr(\n \"conversations.admin.bulkPurpose\",\n \"Clean up at scale instead of one conversation at a time — select many conversations, then archive them to declutter inboxes, unarchive to restore, or delete the ones you no longer need. Handy for closing out finished projects or removing test threads.\",\n )}\n </PagePurpose>\n {!workspaceId && (\n <EmptyState\n title={tr(\n \"conversations.admin.selectWorkspace\",\n \"Select a workspace\",\n )}\n description={tr(\n \"conversations.admin.selectWorkspaceBulkDescription\",\n \"Choose a workspace to manage conversations in bulk.\",\n )}\n icon={<Archive className=\"size-6\" aria-hidden=\"true\" />}\n illustration=\"empty-generic\"\n />\n )}\n {workspaceId && loading && (\n <p className=\"text-sm text-text-secondary\">Loading conversations…</p>\n )}\n {workspaceId && error && (\n <p className=\"text-sm text-status-error-text\">{error.message}</p>\n )}\n {bulkResult?.bulkUpdateConversations && (\n <GlassCard\n treatment=\"metric\"\n glow\n className=\"grid grid-cols-2 gap-4 border-border-seam p-4 sm:max-w-md\"\n >\n <div>\n <p className=\"text-2xl font-semibold tabular-nums text-status-success-text\">\n {bulkResult.bulkUpdateConversations.successCount}\n </p>\n <p className=\"text-xs text-text-secondary\">Succeeded</p>\n </div>\n <div>\n <p className=\"text-2xl font-semibold tabular-nums text-text-primary\">\n {bulkResult.bulkUpdateConversations.failureCount}\n </p>\n <p className=\"text-xs text-text-secondary\">Failed</p>\n </div>\n </GlassCard>\n )}\n {workspaceId && (\n <div className=\"overflow-hidden rounded-lg border border-border-seam bg-bg-surface\">\n {conversations.map((conversation) => (\n <label\n key={conversation.id}\n className=\"flex min-h-11 cursor-pointer items-center gap-3 border-b border-border-seam px-4 py-3 last:border-b-0 hover:bg-accent\"\n >\n <input\n type=\"checkbox\"\n aria-label={`Select ${conversation.title || \"untitled conversation\"}`}\n checked={selectedIds.has(conversation.id)}\n onChange={() => toggle(conversation.id)}\n />\n <div className=\"min-w-0 flex-1\">\n <p className=\"truncate text-sm font-medium text-text-primary\">\n {conversation.title || \"Untitled conversation\"}\n </p>\n <p className=\"text-xs text-text-secondary\">\n {conversation.type} · {conversation.state} · Updated{\" \"}\n {formatDate(conversation.updatedAt)}\n </p>\n </div>\n </label>\n ))}\n </div>\n )}\n </PageLayout>\n );\n}\n\nexport function ConversationAuditLogPage() {\n const tr = useTr();\n const { workspaceId } = useConversations();\n const { data, loading, error } = useQuery<{\n getActivities: ActivityLogNode[];\n }>(GET_CONVERSATION_AUDIT_LOG, {\n variables: {\n filter: { workspaceId, resourceType: \"conversation\", limit: 50 },\n },\n skip: !workspaceId,\n });\n const logs = data?.getActivities ?? [];\n\n return (\n <PageLayout\n title={tr(\"conversations.admin.auditLogPageTitle\", \"Audit log\")}\n subtitle={tr(\n \"conversations.admin.auditLogPageDescription\",\n \"Activity stream for admin and moderator actions on workspace conversations.\",\n )}\n icon={<Activity className=\"size-5\" aria-hidden=\"true\" />}\n maxWidth=\"2xl\"\n >\n <PagePurpose icon={<Activity className=\"size-4\" aria-hidden=\"true\" />}>\n {tr(\n \"conversations.admin.auditPurpose\",\n \"An accountability trail of who changed what — every admin and moderator action on workspace conversations is recorded here. Use it to investigate an incident, confirm a change was made, or satisfy a compliance review.\",\n )}\n </PagePurpose>\n {loading && (\n <p className=\"text-sm text-text-secondary\">Loading activity…</p>\n )}\n {error && (\n <p className=\"text-sm text-status-error-text\">{error.message}</p>\n )}\n {!loading && !error && logs.length === 0 && (\n <EmptyState\n title=\"No activity found\"\n description=\"Conversation admin actions will appear here.\"\n illustration=\"empty-data\"\n />\n )}\n <div className=\"grid gap-3\">\n {logs.map((log) => (\n <GlassCard\n key={log.id}\n treatment=\"plain\"\n className=\"border-border-seam p-4\"\n >\n <div className=\"flex flex-wrap items-center justify-between gap-2\">\n <p className=\"text-sm font-medium text-text-primary\">\n {log.action}\n </p>\n <span className=\"text-xs tabular-nums text-text-secondary\">\n {formatDate(log.timestamp)}\n </span>\n </div>\n <p className=\"mt-1 text-sm text-text-secondary\">\n {log.description || \"No description provided\"}\n </p>\n <p className=\"mt-2 text-xs text-text-secondary\">\n User {log.userId} · Resource {log.resourceId ?? \"workspace\"}\n </p>\n </GlassCard>\n ))}\n </div>\n </PageLayout>\n );\n}\n\nexport function AppInstallationsPage() {\n const tr = useTr();\n const { workspaceId } = useConversations();\n const [appId, setAppId] = useState(\"\");\n const [settingsInput, setSettingsInput] = useState(\"{}\");\n const [settingsDrafts, setSettingsDrafts] = useState<Record<string, string>>(\n {},\n );\n const [formError, setFormError] = useState<string | null>(null);\n const { data, loading, error, refetch } = useQuery<{\n appInstallations: AppInstallationNode[];\n }>(GET_APP_INSTALLATIONS, {\n variables: { workspaceId },\n skip: !workspaceId,\n });\n const [installApp, { loading: installing }] = useMutation(INSTALL_APP, {\n onCompleted: () => void refetch(),\n });\n const [updateSettings, { loading: saving }] = useMutation(\n UPDATE_APP_SETTINGS,\n { onCompleted: () => void refetch() },\n );\n const [uninstallApp, { loading: uninstalling }] = useMutation(UNINSTALL_APP, {\n onCompleted: () => void refetch(),\n });\n const installations = useMemo(\n () => data?.appInstallations ?? [],\n [data?.appInstallations],\n );\n\n useEffect(() => {\n setSettingsDrafts((current) => {\n if (installations.every((installation) => current[installation.id])) {\n return current;\n }\n const next = { ...current };\n for (const installation of installations) {\n if (!next[installation.id]) {\n next[installation.id] = JSON.stringify(\n installation.settings ?? {},\n null,\n 2,\n );\n }\n }\n return next;\n });\n }, [installations]);\n\n const parsedSettings = (raw: string): Record<string, unknown> | null => {\n try {\n const parsed = JSON.parse(raw) as unknown;\n return parsed && typeof parsed === \"object\" && !Array.isArray(parsed)\n ? (parsed as Record<string, unknown>)\n : null;\n } catch {\n return null;\n }\n };\n\n const handleInstall = () => {\n const settings = parsedSettings(settingsInput);\n if (!appId.trim() || !settings) {\n setFormError(\"Enter an app ID and a valid JSON settings object.\");\n return;\n }\n setFormError(null);\n void installApp({\n variables: { input: { appId: appId.trim(), workspaceId, settings } },\n });\n };\n\n return (\n <PageLayout\n title={tr(\n \"conversations.admin.appInstallationsPageTitle\",\n \"App installations\",\n )}\n subtitle={tr(\n \"conversations.admin.appInstallationsPageDescription\",\n \"Install, configure, and remove conversation apps and integrations.\",\n )}\n icon={<Package className=\"size-5\" aria-hidden=\"true\" />}\n maxWidth=\"2xl\"\n >\n <PagePurpose icon={<Package className=\"size-4\" aria-hidden=\"true\" />}>\n {tr(\n \"conversations.admin.appsPurpose\",\n \"Extend conversations with apps and integrations — install bots, automations, or third-party connectors scoped to this workspace, adjust their settings, and remove the ones you no longer use.\",\n )}\n </PagePurpose>\n <PageSection\n title=\"Install app\"\n description=\"Install a workspace-scoped conversation app by app ID.\"\n >\n {/* The single emphasis zone on this page — installing a new app is the\n primary action an admin takes here. */}\n <GlassCard\n treatment=\"glass\"\n glow\n className=\"grid gap-3 border-border-seam p-4 sm:grid-cols-[minmax(0,1fr)_minmax(0,2fr)_auto]\"\n >\n <input\n value={appId}\n onChange={(event) => setAppId(event.target.value)}\n placeholder=\"app-id\"\n aria-label=\"App ID\"\n className=\"min-h-11 rounded-md border border-border-seam bg-bg-surface px-3 py-2 text-sm text-text-primary focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)]\"\n />\n <textarea\n value={settingsInput}\n onChange={(event) => setSettingsInput(event.target.value)}\n rows={3}\n aria-label=\"App settings JSON\"\n className=\"rounded-md border border-border-seam bg-bg-surface px-3 py-2 text-sm text-text-primary focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)]\"\n />\n <Button\n type=\"button\"\n size=\"sm\"\n disabled={installing}\n onClick={handleInstall}\n >\n Install\n </Button>\n </GlassCard>\n {formError && (\n <p className=\"text-sm text-status-error-text\">{formError}</p>\n )}\n </PageSection>\n {loading && (\n <p className=\"text-sm text-text-secondary\">\n Loading app installations…\n </p>\n )}\n {error && (\n <p className=\"text-sm text-status-error-text\">{error.message}</p>\n )}\n <div className=\"grid gap-3\">\n {installations.map((installation) => (\n <GlassCard\n key={installation.id}\n treatment=\"glass\"\n className=\"border-border-seam p-4\"\n >\n <div className=\"flex flex-wrap items-start justify-between gap-3\">\n <div className=\"min-w-0\">\n <div className=\"flex items-center gap-2\">\n <h3 className=\"text-sm font-semibold text-text-primary\">\n {installation.appId}\n </h3>\n <StatusPill>{installation.state}</StatusPill>\n </div>\n <p className=\"text-xs text-text-secondary\">\n Version {installation.appVersion} · Installed by{\" \"}\n {installation.installedBy} on{\" \"}\n {formatDate(installation.installedAt)}\n </p>\n </div>\n {/* Destructive uninstall lives behind the overflow menu and\n asks for confirmation before firing the mutation. */}\n <CTAOverflowMenu\n className=\"shrink-0\"\n actions={[\n {\n label: \"Uninstall\",\n intent: \"destructive\",\n disabled: uninstalling,\n icon: <Trash2 className=\"size-4\" aria-hidden=\"true\" />,\n onSelect: () => {\n if (\n !window.confirm(\n `Uninstall ${installation.appId}? This removes the app and its settings from this workspace.`,\n )\n ) {\n return;\n }\n void uninstallApp({\n variables: { id: installation.id },\n });\n },\n },\n ]}\n />\n </div>\n {/* The raw JSON settings editor is an advanced surface — collapse\n it behind progressive disclosure so the card stays calm. */}\n <ShowMore\n className=\"mt-3\"\n collapsedHeight={0}\n label=\"Edit settings (JSON)\"\n >\n <div className=\"grid gap-2 pt-2\">\n <textarea\n rows={5}\n aria-label={`Settings for ${installation.appId}`}\n value={settingsDrafts[installation.id] ?? \"{}\"}\n onChange={(event) =>\n setSettingsDrafts((current) => ({\n ...current,\n [installation.id]: event.target.value,\n }))\n }\n className=\"rounded-md border border-border-seam bg-bg-surface px-3 py-2 font-mono text-xs text-text-primary focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--color-focus-ring)]\"\n />\n <Button\n type=\"button\"\n variant=\"secondary\"\n size=\"sm\"\n disabled={saving}\n className=\"w-fit\"\n onClick={() => {\n const settings = parsedSettings(\n settingsDrafts[installation.id] ?? \"{}\",\n );\n if (!settings) {\n setFormError(\n \"Installation settings must be a valid JSON object.\",\n );\n return;\n }\n setFormError(null);\n void updateSettings({\n variables: { id: installation.id, settings },\n });\n }}\n >\n Save settings\n </Button>\n </div>\n </ShowMore>\n </GlassCard>\n ))}\n </div>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
|