@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.
@@ -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, ShowMore as S } from "@burdenoff/fe-libs/ui";
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 C = m`
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
- `, w = m`
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
- `, T = m`
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
- `, E = m`
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
- `, D = m`
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
- `, O = m`
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
- `, k = m`
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
- `, A = m`
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
- `, j = m`
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 M(e) {
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 N = new Intl.DateTimeFormat(void 0, {
153
+ var P = new Intl.DateTimeFormat(void 0, {
154
154
  dateStyle: "medium",
155
155
  timeStyle: "short"
156
156
  });
157
- function P(e) {
158
- return N.format(new Date(e));
157
+ function F(e) {
158
+ return P.format(new Date(e));
159
159
  }
160
- function F({ children: e }) {
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 I() {
167
- let n = t(), { workspaceId: a } = e(), { data: o, loading: s, error: c, refetch: l } = g(C, {
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(w, { onCompleted: () => void l() }), m = o?.messageReports.edges.map((e) => e.node) ?? [];
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__ */ _(F, { children: e.status }),
223
- e.message.isHidden && /* @__PURE__ */ _(F, { children: "Hidden" }),
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 ", P(e.createdAt)]
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: M(e.message.content)
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 L() {
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(T, {
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
- }), [S, { data: C, loading: w }] = h(E, { onCompleted: () => {
289
+ }), [C, { data: w, loading: T }] = h(D, { onCompleted: () => {
283
290
  c(/* @__PURE__ */ new Set()), b();
284
- } }), D = f?.conversations.edges.map((e) => e.node) ?? [], O = (e) => {
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 || w,
323
- onClick: () => void S({ variables: { input: {
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
- C?.bulkUpdateConversations && /* @__PURE__ */ v(x, {
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: C.bulkUpdateConversations.successCount
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: C.bulkUpdateConversations.failureCount
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: D.map((e) => /* @__PURE__ */ v("label", {
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: () => O(e.id)
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
- P(e.updatedAt)
402
+ F(e.updatedAt)
388
403
  ]
389
404
  })]
390
405
  })]
@@ -393,8 +408,8 @@ function L() {
393
408
  ]
394
409
  });
395
410
  }
396
- function R() {
397
- let n = t(), { workspaceId: a } = e(), { data: o, loading: s, error: l } = g(D, {
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: P(e.timestamp)
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 z() {
463
- let r = t(), { workspaceId: c } = e(), [l, u] = s(""), [f, m] = s("{}"), [C, w] = s({}), [T, E] = s(null), { data: D, loading: M, error: N, refetch: I } = g(O, {
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
- }), [L, { loading: R }] = h(k, { onCompleted: () => void I() }), [z, { loading: B }] = h(A, { onCompleted: () => void I() }), [V, { loading: H }] = h(j, { onCompleted: () => void I() }), U = o(() => D?.appInstallations ?? [], [D?.appInstallations]);
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
- w((e) => {
469
- if (U.every((t) => e[t.id])) return e;
490
+ T((e) => {
491
+ if (W.every((t) => e[t.id])) return e;
470
492
  let t = { ...e };
471
- for (let e of U) t[e.id] || (t[e.id] = JSON.stringify(e.settings ?? {}, null, 2));
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
- }, [U]);
475
- let W = (e) => {
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("div", {
496
- 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]",
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: R,
546
+ disabled: z,
516
547
  onClick: () => {
517
- let e = W(f);
548
+ let e = G(f);
518
549
  if (!l.trim() || !e) {
519
- E("Enter an app ID and a valid JSON settings object.");
550
+ D("Enter an app ID and a valid JSON settings object.");
520
551
  return;
521
552
  }
522
- E(null), L({ variables: { input: {
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
- }), T && /* @__PURE__ */ _("p", {
562
+ }), E && /* @__PURE__ */ _("p", {
532
563
  className: "text-sm text-status-error-text",
533
- children: T
564
+ children: E
534
565
  })]
535
566
  }),
536
- M && /* @__PURE__ */ _("p", {
567
+ N && /* @__PURE__ */ _("p", {
537
568
  className: "text-sm text-text-secondary",
538
569
  children: "Loading app installations…"
539
570
  }),
540
- N && /* @__PURE__ */ _("p", {
571
+ P && /* @__PURE__ */ _("p", {
541
572
  className: "text-sm text-status-error-text",
542
- children: N.message
573
+ children: P.message
543
574
  }),
544
575
  /* @__PURE__ */ _("div", {
545
576
  className: "grid gap-3",
546
- children: U.map((e) => /* @__PURE__ */ v(x, {
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__ */ _(F, { children: e.state })]
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
- P(e.installedAt)
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: H,
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.`) && V({ variables: { id: e.id } });
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__ */ _(S, {
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: C[e.id] ?? "{}",
597
- onChange: (t) => w((n) => ({
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: B,
637
+ disabled: V,
607
638
  className: "w-fit",
608
639
  onClick: () => {
609
- let t = W(C[e.id] ?? "{}");
640
+ let t = G(w[e.id] ?? "{}");
610
641
  if (!t) {
611
- E("Installation settings must be a valid JSON object.");
642
+ D("Installation settings must be a valid JSON object.");
612
643
  return;
613
644
  }
614
- E(null), z({ variables: {
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 { z as AppInstallationsPage, L as BulkOperationsPage, R as ConversationAuditLogPage, I as ModerationQueuePage };
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":";;;;;;;;;;;;AAuBA,IAAM,IAAsB,CAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BzB,IAAyB,CAAG;;;;;;;;;;;;;;;GAiB5B,IAAyB,CAAG;;;;;;;;;;;;;;;;;GAmB5B,IAA4B,CAAG;;;;;;;;;;;;;;;;GAkB/B,IAA6B,CAAG;;;;;;;;;;;;GAchC,IAAwB,CAAG;;;;;;;;;;;;;;GAgB3B,IAAc,CAAG;;;;;;;;;;GAYjB,IAAsB,CAAG;;;;;;;;GAUzB,IAAgB,CAAG;;;;;;;;AAqEzB,SAAS,EAAc,GAA0B;AAC/C,KAAI,OAAO,KAAY,SAAU,QAAO;AACxC,KAAI,KAAW,OAAO,KAAY,YAAY,UAAU,GAAS;EAC/D,IAAM,IAAQ,EAA+B;AAC7C,MAAI,OAAO,KAAS,SAAU,QAAO;;AAEvC,KAAI;AACF,SAAO,KAAK,UAAU,EAAQ;SACxB;AACN,SAAO;;;AAIX,IAAM,IAAiB,IAAI,KAAK,eAAe,KAAA,GAAW;CACxD,WAAW;CACX,WAAW;CACZ,CAAC;AAEF,SAAS,EAAW,GAAuB;AACzC,QAAO,EAAe,OAAO,IAAI,KAAK,EAAM,CAAC;;AAG/C,SAAS,EAAW,EAAE,eAAkC;AACtD,QACE,kBAAC,QAAD;EAAM,WAAU;EACb;EACI,CAAA;;AAIX,SAAgB,IAAsB;CACpC,IAAM,IAAK,GAAO,EACZ,EAAE,mBAAgB,GAAkB,EACpC,EAAE,SAAM,YAAS,UAAO,eAAY,EAEvC,GAAqB;EACtB,WAAW;GAAE,OAAO;GAAI,QAAQ,EAAE,UAAU,CAAC,WAAW,YAAY,EAAE;GAAE;EACxE,MAAM,CAAC;EACR,CAAC,EACI,CAAC,GAAe,EAAE,SAAS,OAAe,EAC9C,GACA,EACE,mBAAmB,KAAK,GAAS,EAClC,CACF,EAEK,IAAU,GAAM,eAAe,MAAM,KAAK,MAAS,EAAK,KAAK,IAAI,EAAE;AAEzE,QACE,kBAAC,GAAD;EACE,OAAO,EAAG,2CAA2C,mBAAmB;EACxE,UAAU,EACR,iDACA,0DACD;EACD,MAAM,kBAAC,GAAD;GAAa,WAAU;GAAS,eAAY;GAAS,CAAA;EAC3D,UAAS;YAPX;GASE,kBAAC,GAAD;IAAa,MAAM,kBAAC,GAAD;KAAa,WAAU;KAAS,eAAY;KAAS,CAAA;cACrE,EACC,yCACA,mOACD;IACW,CAAA;GACb,CAAC,KACA,kBAAC,GAAD;IACE,OAAO,EACL,uCACA,qBACD;IACD,aAAa,EACX,4DACA,+DACD;IACD,MAAM,kBAAC,GAAD;KAAa,WAAU;KAAS,eAAY;KAAS,CAAA;IAC3D,cAAa;IACb,CAAA;GAEH,KAAe,KACd,kBAAC,KAAD;IAAG,WAAU;cAA8B;IAAoB,CAAA;GAEhE,KAAe,KACd,kBAAC,KAAD;IAAG,WAAU;cAAkC,EAAM;IAAY,CAAA;GAElE,KAAe,CAAC,KAAW,CAAC,KAAS,EAAQ,WAAW,KACvD,kBAAC,GAAD;IACE,OAAM;IACN,aAAY;IACZ,MAAM,kBAAC,GAAD;KAAc,WAAU;KAAS,eAAY;KAAS,CAAA;IAC5D,cAAa;IACb,CAAA;GAEJ,kBAAC,OAAD;IAAK,WAAU;cACZ,EAAQ,KAAK,MACZ,kBAAC,GAAD;KAEE,WAAU;KACV,WAAU;eAEV,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,OAAD;OAAK,WAAU;iBAAf;QACE,kBAAC,OAAD;SAAK,WAAU;mBAAf;UACE,kBAAC,GAAD,EAAA,UAAa,EAAO,QAAoB,CAAA;UACvC,EAAO,QAAQ,YAAY,kBAAC,GAAD,EAAA,UAAY,UAAmB,CAAA;UAC3D,kBAAC,QAAD;WAAM,WAAU;qBAAhB,CAA8C,aAClC,EAAW,EAAO,UAAU,CACjC;;UACH;;QACN,kBAAC,KAAD;SAAG,WAAU;mBACV,EAAO;SACN,CAAA;QACJ,kBAAC,KAAD;SAAG,WAAU;mBACV,EAAc,EAAO,QAAQ,QAAQ;SACpC,CAAA;QACJ,kBAAC,KAAD;SAAG,WAAU;mBAAb;UAA2C;UAChC,EAAO;UAAU;UAAiB;UAC1C,EAAO,QAAQ;UAAe;UAAK,EAAO,QAAQ;UAAS;UAC/C,EAAO;UAClB;;QACA;UAGN,kBAAC,GAAD;OACE,WAAU;OACV,SAAS;QACP,OAAO;QACP,UAAU;QACV,gBACE,KAAK,EAAc,EACjB,WAAW,EACT,OAAO;SAAE,UAAU,EAAO;SAAI,QAAQ;SAAgB,EACvD,EACF,CAAC;QACL;OACD,SAAS,CACP;QACE,OAAO;QACP,UAAU;QACV,gBACE,KAAK,EAAc,EACjB,WAAW,EACT,OAAO;SAAE,UAAU,EAAO;SAAI,QAAQ;SAAW,EAClD,EACF,CAAC;QACL,CACF;OACD,CAAA,CACE;;KACI,EArDL,EAAO,GAqDF,CACZ;IACE,CAAA;GACK;;;AAIjB,SAAgB,IAAqB;CACnC,IAAM,IAAK,GAAO,EACZ,EAAE,mBAAgB,GAAkB,EACpC,CAAC,GAAa,KAAkB,wBAA4B,IAAI,KAAK,CAAC,EACtE,CAAC,GAAQ,KAAa,EAC1B,UACD,EACK,EAAE,SAAM,YAAS,UAAO,eAAY,EAEvC,GAAwB;EACzB,WAAW,EAAE,OAAO,KAAK;EACzB,MAAM,CAAC;EACR,CAAC,EACI,CAAC,GAAY,EAAE,MAAM,GAAY,SAAS,OAAc,EAG5D,GAA2B,EAC3B,mBAAmB;AAEZ,EADL,kBAAe,IAAI,KAAK,CAAC,EACpB,GAAS;IAEjB,CAAC,EACI,IACJ,GAAM,cAAc,MAAM,KAAK,MAAS,EAAK,KAAK,IAAI,EAAE,EAEpD,KAAU,MAAe;AAC7B,KAAgB,MAAY;GAC1B,IAAM,IAAO,IAAI,IAAI,EAAQ;AAG7B,UAFI,EAAK,IAAI,EAAG,GAAE,EAAK,OAAO,EAAG,GAC5B,EAAK,IAAI,EAAG,EACV;IACP;;AAGJ,QACE,kBAAC,GAAD;EACE,OAAO,EACL,+CACA,kBACD;EACD,UAAU,EACR,qDACA,qFACD;EACD,MAAM,kBAAC,GAAD;GAAS,WAAU;GAAS,eAAY;GAAS,CAAA;EACvD,UAAS;EACT,eACE,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,UAAD;IACE,OAAO;IACP,WAAW,MAAU,EAAU,EAAM,OAAO,MAAuB;IACnE,cAAW;IACX,WAAU;cAJZ;KAME,kBAAC,UAAD;MAAQ,OAAM;gBAAU;MAAgB,CAAA;KACxC,kBAAC,UAAD;MAAQ,OAAM;gBAAY;MAAkB,CAAA;KAC5C,kBAAC,UAAD;MAAQ,OAAM;gBAAS;MAAe,CAAA;KAC/B;OACT,kBAAC,GAAD;IACE,MAAK;IACL,MAAK;IACL,UAAU,EAAY,SAAS,KAAK;IACpC,eACE,KAAK,EAAW,EACd,WAAW,EACT,OAAO;KAAE,iBAAiB,MAAM,KAAK,EAAY;KAAE;KAAQ,EAC5D,EACF,CAAC;cATN,CAWC,aACW,EAAY,KACf;MACL;;YArCV;GAwCE,kBAAC,GAAD;IAAa,MAAM,kBAAC,GAAD;KAAS,WAAU;KAAS,eAAY;KAAS,CAAA;cACjE,EACC,mCACA,8PACD;IACW,CAAA;GACb,CAAC,KACA,kBAAC,GAAD;IACE,OAAO,EACL,uCACA,qBACD;IACD,aAAa,EACX,sDACA,sDACD;IACD,MAAM,kBAAC,GAAD;KAAS,WAAU;KAAS,eAAY;KAAS,CAAA;IACvD,cAAa;IACb,CAAA;GAEH,KAAe,KACd,kBAAC,KAAD;IAAG,WAAU;cAA8B;IAA0B,CAAA;GAEtE,KAAe,KACd,kBAAC,KAAD;IAAG,WAAU;cAAkC,EAAM;IAAY,CAAA;GAElE,GAAY,2BACX,kBAAC,GAAD;IACE,WAAU;IACV,MAAA;IACA,WAAU;cAHZ,CAKE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;KAAG,WAAU;eACV,EAAW,wBAAwB;KAClC,CAAA,EACJ,kBAAC,KAAD;KAAG,WAAU;eAA8B;KAAa,CAAA,CACpD,EAAA,CAAA,EACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;KAAG,WAAU;eACV,EAAW,wBAAwB;KAClC,CAAA,EACJ,kBAAC,KAAD;KAAG,WAAU;eAA8B;KAAU,CAAA,CACjD,EAAA,CAAA,CACI;;GAEb,KACC,kBAAC,OAAD;IAAK,WAAU;cACZ,EAAc,KAAK,MAClB,kBAAC,SAAD;KAEE,WAAU;eAFZ,CAIE,kBAAC,SAAD;MACE,MAAK;MACL,cAAY,UAAU,EAAa,SAAS;MAC5C,SAAS,EAAY,IAAI,EAAa,GAAG;MACzC,gBAAgB,EAAO,EAAa,GAAG;MACvC,CAAA,EACF,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,KAAD;OAAG,WAAU;iBACV,EAAa,SAAS;OACrB,CAAA,EACJ,kBAAC,KAAD;OAAG,WAAU;iBAAb;QACG,EAAa;QAAK;QAAI,EAAa;QAAM;QAAW;QACpD,EAAW,EAAa,UAAU;QACjC;SACA;QACA;OAlBD,EAAa,GAkBZ,CACR;IACE,CAAA;GAEG;;;AAIjB,SAAgB,IAA2B;CACzC,IAAM,IAAK,GAAO,EACZ,EAAE,mBAAgB,GAAkB,EACpC,EAAE,SAAM,YAAS,aAAU,EAE9B,GAA4B;EAC7B,WAAW,EACT,QAAQ;GAAE;GAAa,cAAc;GAAgB,OAAO;GAAI,EACjE;EACD,MAAM,CAAC;EACR,CAAC,EACI,IAAO,GAAM,iBAAiB,EAAE;AAEtC,QACE,kBAAC,GAAD;EACE,OAAO,EAAG,yCAAyC,YAAY;EAC/D,UAAU,EACR,+CACA,8EACD;EACD,MAAM,kBAAC,GAAD;GAAU,WAAU;GAAS,eAAY;GAAS,CAAA;EACxD,UAAS;YAPX;GASE,kBAAC,GAAD;IAAa,MAAM,kBAAC,GAAD;KAAU,WAAU;KAAS,eAAY;KAAS,CAAA;cAClE,EACC,oCACA,4NACD;IACW,CAAA;GACb,KACC,kBAAC,KAAD;IAAG,WAAU;cAA8B;IAAqB,CAAA;GAEjE,KACC,kBAAC,KAAD;IAAG,WAAU;cAAkC,EAAM;IAAY,CAAA;GAElE,CAAC,KAAW,CAAC,KAAS,EAAK,WAAW,KACrC,kBAAC,GAAD;IACE,OAAM;IACN,aAAY;IACZ,cAAa;IACb,CAAA;GAEJ,kBAAC,OAAD;IAAK,WAAU;cACZ,EAAK,KAAK,MACT,kBAAC,GAAD;KAEE,WAAU;KACV,WAAU;eAHZ;MAKE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,KAAD;QAAG,WAAU;kBACV,EAAI;QACH,CAAA,EACJ,kBAAC,QAAD;QAAM,WAAU;kBACb,EAAW,EAAI,UAAU;QACrB,CAAA,CACH;;MACN,kBAAC,KAAD;OAAG,WAAU;iBACV,EAAI,eAAe;OAClB,CAAA;MACJ,kBAAC,KAAD;OAAG,WAAU;iBAAb;QAAgD;QACxC,EAAI;QAAO;QAAa,EAAI,cAAc;QAC9C;;MACM;OAlBL,EAAI,GAkBC,CACZ;IACE,CAAA;GACK;;;AAIjB,SAAgB,IAAuB;CACrC,IAAM,IAAK,GAAO,EACZ,EAAE,mBAAgB,GAAkB,EACpC,CAAC,GAAO,KAAY,EAAS,GAAG,EAChC,CAAC,GAAe,KAAoB,EAAS,KAAK,EAClD,CAAC,GAAgB,KAAqB,EAC1C,EAAE,CACH,EACK,CAAC,GAAW,KAAgB,EAAwB,KAAK,EACzD,EAAE,SAAM,YAAS,UAAO,eAAY,EAEvC,GAAuB;EACxB,WAAW,EAAE,gBAAa;EAC1B,MAAM,CAAC;EACR,CAAC,EACI,CAAC,GAAY,EAAE,SAAS,OAAgB,EAAY,GAAa,EACrE,mBAAmB,KAAK,GAAS,EAClC,CAAC,EACI,CAAC,GAAgB,EAAE,SAAS,OAAY,EAC5C,GACA,EAAE,mBAAmB,KAAK,GAAS,EAAE,CACtC,EACK,CAAC,GAAc,EAAE,SAAS,OAAkB,EAAY,GAAe,EAC3E,mBAAmB,KAAK,GAAS,EAClC,CAAC,EACI,IAAgB,QACd,GAAM,oBAAoB,EAAE,EAClC,CAAC,GAAM,iBAAiB,CACzB;AAED,SAAgB;AACd,KAAmB,MAAY;AAC7B,OAAI,EAAc,OAAO,MAAiB,EAAQ,EAAa,IAAI,CACjE,QAAO;GAET,IAAM,IAAO,EAAE,GAAG,GAAS;AAC3B,QAAK,IAAM,KAAgB,EACzB,CAAK,EAAK,EAAa,QACrB,EAAK,EAAa,MAAM,KAAK,UAC3B,EAAa,YAAY,EAAE,EAC3B,MACA,EACD;AAGL,UAAO;IACP;IACD,CAAC,EAAc,CAAC;CAEnB,IAAM,KAAkB,MAAgD;AACtE,MAAI;GACF,IAAM,IAAS,KAAK,MAAM,EAAI;AAC9B,UAAO,KAAU,OAAO,KAAW,YAAY,CAAC,MAAM,QAAQ,EAAO,GAChE,IACD;UACE;AACN,UAAO;;;AAgBX,QACE,kBAAC,GAAD;EACE,OAAO,EACL,iDACA,oBACD;EACD,UAAU,EACR,uDACA,qEACD;EACD,MAAM,kBAAC,GAAD;GAAS,WAAU;GAAS,eAAY;GAAS,CAAA;EACvD,UAAS;YAVX;GAYE,kBAAC,GAAD;IAAa,MAAM,kBAAC,GAAD;KAAS,WAAU;KAAS,eAAY;KAAS,CAAA;cACjE,EACC,mCACA,iMACD;IACW,CAAA;GACd,kBAAC,GAAD;IACE,OAAM;IACN,aAAY;cAFd,CAME,kBAAC,GAAD;KACE,WAAU;KACV,MAAA;KACA,WAAU;eAHZ;MAKE,kBAAC,SAAD;OACE,OAAO;OACP,WAAW,MAAU,EAAS,EAAM,OAAO,MAAM;OACjD,aAAY;OACZ,cAAW;OACX,WAAU;OACV,CAAA;MACF,kBAAC,YAAD;OACE,OAAO;OACP,WAAW,MAAU,EAAiB,EAAM,OAAO,MAAM;OACzD,MAAM;OACN,cAAW;OACX,WAAU;OACV,CAAA;MACF,kBAAC,GAAD;OACE,MAAK;OACL,MAAK;OACL,UAAU;OACV,eA5DkB;QAC1B,IAAM,IAAW,EAAe,EAAc;AAC9C,YAAI,CAAC,EAAM,MAAM,IAAI,CAAC,GAAU;AAC9B,WAAa,oDAAoD;AACjE;;AAGG,QADL,EAAa,KAAK,EACb,EAAW,EACd,WAAW,EAAE,OAAO;SAAE,OAAO,EAAM,MAAM;SAAE;SAAa;SAAU,EAAE,EACrE,CAAC;;iBAoDK;OAEQ,CAAA;MACC;QACX,KACC,kBAAC,KAAD;KAAG,WAAU;eAAkC;KAAc,CAAA,CAEnD;;GACb,KACC,kBAAC,KAAD;IAAG,WAAU;cAA8B;IAEvC,CAAA;GAEL,KACC,kBAAC,KAAD;IAAG,WAAU;cAAkC,EAAM;IAAY,CAAA;GAEnE,kBAAC,OAAD;IAAK,WAAU;cACZ,EAAc,KAAK,MAClB,kBAAC,GAAD;KAEE,WAAU;KACV,WAAU;eAHZ,CAKE,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,OAAD;QAAK,WAAU;kBAAf,CACE,kBAAC,MAAD;SAAI,WAAU;mBACX,EAAa;SACX,CAAA,EACL,kBAAC,GAAD,EAAA,UAAa,EAAa,OAAmB,CAAA,CACzC;WACN,kBAAC,KAAD;QAAG,WAAU;kBAAb;SAA2C;SAChC,EAAa;SAAW;SAAgB;SAChD,EAAa;SAAY;SAAI;SAC7B,EAAW,EAAa,YAAY;SACnC;UACA;UAGN,kBAAC,GAAD;OACE,WAAU;OACV,SAAS,CACP;QACE,OAAO;QACP,QAAQ;QACR,UAAU;QACV,MAAM,kBAAC,GAAD;SAAQ,WAAU;SAAS,eAAY;SAAS,CAAA;QACtD,gBAAgB;AAEX,gBAAO,QACN,aAAa,EAAa,MAAM,8DACjC,IAIE,EAAa,EAChB,WAAW,EAAE,IAAI,EAAa,IAAI,EACnC,CAAC;;QAEL,CACF;OACD,CAAA,CACE;SAGN,kBAAC,GAAD;MACE,WAAU;MACV,iBAAiB;MACjB,OAAM;gBAEN,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,YAAD;QACE,MAAM;QACN,cAAY,gBAAgB,EAAa;QACzC,OAAO,EAAe,EAAa,OAAO;QAC1C,WAAW,MACT,GAAmB,OAAa;SAC9B,GAAG;UACF,EAAa,KAAK,EAAM,OAAO;SACjC,EAAE;QAEL,WAAU;QACV,CAAA,EACF,kBAAC,GAAD;QACE,MAAK;QACL,SAAQ;QACR,MAAK;QACL,UAAU;QACV,WAAU;QACV,eAAe;SACb,IAAM,IAAW,EACf,EAAe,EAAa,OAAO,KACpC;AACD,aAAI,CAAC,GAAU;AACb,YACE,qDACD;AACD;;AAGG,SADL,EAAa,KAAK,EACb,EAAe,EAClB,WAAW;UAAE,IAAI,EAAa;UAAI;UAAU,EAC7C,CAAC;;kBAEL;QAEQ,CAAA,CACL;;MACG,CAAA,CACD;OA1FL,EAAa,GA0FR,CACZ;IACE,CAAA;GACK"}