@burdenoff/microfe-conversations 2026.510.117

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.
Files changed (155) hide show
  1. package/README.md +12 -0
  2. package/dist/__tests__/setup.d.ts +2 -0
  3. package/dist/__tests__/setup.d.ts.map +1 -0
  4. package/dist/conversations/ConversationsAdminRoot.d.ts +19 -0
  5. package/dist/conversations/ConversationsAdminRoot.d.ts.map +1 -0
  6. package/dist/conversations/ConversationsAdminRoot.js +104 -0
  7. package/dist/conversations/ConversationsAdminRoot.js.map +1 -0
  8. package/dist/conversations/ConversationsAdminRoutes.d.ts +18 -0
  9. package/dist/conversations/ConversationsAdminRoutes.d.ts.map +1 -0
  10. package/dist/conversations/ConversationsAdminRoutes.js +42 -0
  11. package/dist/conversations/ConversationsAdminRoutes.js.map +1 -0
  12. package/dist/conversations/ConversationsRoot.d.ts +33 -0
  13. package/dist/conversations/ConversationsRoot.d.ts.map +1 -0
  14. package/dist/conversations/ConversationsRoot.js +100 -0
  15. package/dist/conversations/ConversationsRoot.js.map +1 -0
  16. package/dist/conversations/ConversationsUserRoot.d.ts +17 -0
  17. package/dist/conversations/ConversationsUserRoot.d.ts.map +1 -0
  18. package/dist/conversations/ConversationsUserRoot.js +101 -0
  19. package/dist/conversations/ConversationsUserRoot.js.map +1 -0
  20. package/dist/conversations/ConversationsUserRoutes.d.ts +12 -0
  21. package/dist/conversations/ConversationsUserRoutes.d.ts.map +1 -0
  22. package/dist/conversations/ConversationsUserRoutes.js +45 -0
  23. package/dist/conversations/ConversationsUserRoutes.js.map +1 -0
  24. package/dist/conversations/components/ConversationCard.d.ts +14 -0
  25. package/dist/conversations/components/ConversationCard.d.ts.map +1 -0
  26. package/dist/conversations/components/ConversationCard.js +118 -0
  27. package/dist/conversations/components/ConversationCard.js.map +1 -0
  28. package/dist/conversations/components/LazyPageBoundary.d.ts +25 -0
  29. package/dist/conversations/components/LazyPageBoundary.d.ts.map +1 -0
  30. package/dist/conversations/components/LazyPageBoundary.js +144 -0
  31. package/dist/conversations/components/LazyPageBoundary.js.map +1 -0
  32. package/dist/conversations/components/MessageBubble.d.ts +21 -0
  33. package/dist/conversations/components/MessageBubble.d.ts.map +1 -0
  34. package/dist/conversations/components/MessageBubble.js +242 -0
  35. package/dist/conversations/components/MessageBubble.js.map +1 -0
  36. package/dist/conversations/components/PageLayout.d.ts +55 -0
  37. package/dist/conversations/components/PageLayout.d.ts.map +1 -0
  38. package/dist/conversations/components/PageLayout.js +93 -0
  39. package/dist/conversations/components/PageLayout.js.map +1 -0
  40. package/dist/conversations/components/PermissionGuard.d.ts +21 -0
  41. package/dist/conversations/components/PermissionGuard.d.ts.map +1 -0
  42. package/dist/conversations/components/PermissionGuard.js +25 -0
  43. package/dist/conversations/components/PermissionGuard.js.map +1 -0
  44. package/dist/conversations/constants/featureFlags.d.ts +8 -0
  45. package/dist/conversations/constants/featureFlags.d.ts.map +1 -0
  46. package/dist/conversations/constants/featureFlags.js +12 -0
  47. package/dist/conversations/constants/featureFlags.js.map +1 -0
  48. package/dist/conversations/constants/permissions.d.ts +174 -0
  49. package/dist/conversations/constants/permissions.d.ts.map +1 -0
  50. package/dist/conversations/constants/permissions.js +68 -0
  51. package/dist/conversations/constants/permissions.js.map +1 -0
  52. package/dist/conversations/hooks/useAdminFeatureFlags.d.ts +8 -0
  53. package/dist/conversations/hooks/useAdminFeatureFlags.d.ts.map +1 -0
  54. package/dist/conversations/hooks/useAdminFeatureFlags.js +16 -0
  55. package/dist/conversations/hooks/useAdminFeatureFlags.js.map +1 -0
  56. package/dist/conversations/hooks/useConversationQueries.d.ts +83 -0
  57. package/dist/conversations/hooks/useConversationQueries.d.ts.map +1 -0
  58. package/dist/conversations/hooks/useConversationQueries.js +136 -0
  59. package/dist/conversations/hooks/useConversationQueries.js.map +1 -0
  60. package/dist/conversations/hooks/useExportConversation.d.ts +10 -0
  61. package/dist/conversations/hooks/useExportConversation.d.ts.map +1 -0
  62. package/dist/conversations/hooks/useExportConversation.js +41 -0
  63. package/dist/conversations/hooks/useExportConversation.js.map +1 -0
  64. package/dist/conversations/hooks/useMessageQueries.d.ts +84 -0
  65. package/dist/conversations/hooks/useMessageQueries.d.ts.map +1 -0
  66. package/dist/conversations/hooks/useMessageQueries.js +123 -0
  67. package/dist/conversations/hooks/useMessageQueries.js.map +1 -0
  68. package/dist/conversations/hooks/useMessageSubscriptions.d.ts +24 -0
  69. package/dist/conversations/hooks/useMessageSubscriptions.d.ts.map +1 -0
  70. package/dist/conversations/hooks/useMessageSubscriptions.js +90 -0
  71. package/dist/conversations/hooks/useMessageSubscriptions.js.map +1 -0
  72. package/dist/conversations/hooks/useUserFeatureFlags.d.ts +5 -0
  73. package/dist/conversations/hooks/useUserFeatureFlags.d.ts.map +1 -0
  74. package/dist/conversations/hooks/useUserFeatureFlags.js +10 -0
  75. package/dist/conversations/hooks/useUserFeatureFlags.js.map +1 -0
  76. package/dist/conversations/index.d.ts +31 -0
  77. package/dist/conversations/index.d.ts.map +1 -0
  78. package/dist/conversations/index.js +5 -0
  79. package/dist/conversations/pages/AdminConversationsLandingPage.d.ts +9 -0
  80. package/dist/conversations/pages/AdminConversationsLandingPage.d.ts.map +1 -0
  81. package/dist/conversations/pages/AdminConversationsLandingPage.js +117 -0
  82. package/dist/conversations/pages/AdminConversationsLandingPage.js.map +1 -0
  83. package/dist/conversations/pages/ConversationAdminPages.d.ts +5 -0
  84. package/dist/conversations/pages/ConversationAdminPages.d.ts.map +1 -0
  85. package/dist/conversations/pages/ConversationAdminPages.js +573 -0
  86. package/dist/conversations/pages/ConversationAdminPages.js.map +1 -0
  87. package/dist/conversations/pages/ConversationDetailPage.d.ts +2 -0
  88. package/dist/conversations/pages/ConversationDetailPage.d.ts.map +1 -0
  89. package/dist/conversations/pages/ConversationDetailPage.js +439 -0
  90. package/dist/conversations/pages/ConversationDetailPage.js.map +1 -0
  91. package/dist/conversations/pages/ConversationListPage.d.ts +6 -0
  92. package/dist/conversations/pages/ConversationListPage.d.ts.map +1 -0
  93. package/dist/conversations/pages/ConversationListPage.js +269 -0
  94. package/dist/conversations/pages/ConversationListPage.js.map +1 -0
  95. package/dist/conversations/pages/ConversationsLandingPage.d.ts +9 -0
  96. package/dist/conversations/pages/ConversationsLandingPage.d.ts.map +1 -0
  97. package/dist/conversations/pages/ConversationsLandingPage.js +124 -0
  98. package/dist/conversations/pages/ConversationsLandingPage.js.map +1 -0
  99. package/dist/conversations/pages/ConversationsSettingsPage.d.ts +2 -0
  100. package/dist/conversations/pages/ConversationsSettingsPage.d.ts.map +1 -0
  101. package/dist/conversations/pages/ConversationsSettingsPage.js +215 -0
  102. package/dist/conversations/pages/ConversationsSettingsPage.js.map +1 -0
  103. package/dist/conversations/pages/NewConversationPage.d.ts +2 -0
  104. package/dist/conversations/pages/NewConversationPage.d.ts.map +1 -0
  105. package/dist/conversations/pages/NewConversationPage.js +287 -0
  106. package/dist/conversations/pages/NewConversationPage.js.map +1 -0
  107. package/dist/conversations/providers/ConversationsProvider.d.ts +13 -0
  108. package/dist/conversations/providers/ConversationsProvider.d.ts.map +1 -0
  109. package/dist/conversations/providers/ConversationsProvider.js +109 -0
  110. package/dist/conversations/providers/ConversationsProvider.js.map +1 -0
  111. package/dist/conversations/store/conversationsStore.d.ts +29 -0
  112. package/dist/conversations/store/conversationsStore.d.ts.map +1 -0
  113. package/dist/conversations/store/conversationsStore.js +56 -0
  114. package/dist/conversations/store/conversationsStore.js.map +1 -0
  115. package/dist/conversations/types/index.d.ts +352 -0
  116. package/dist/conversations/types/index.d.ts.map +1 -0
  117. package/dist/conversations/utils/cn.d.ts +8 -0
  118. package/dist/conversations/utils/cn.d.ts.map +1 -0
  119. package/dist/conversations/utils/cn.js +10 -0
  120. package/dist/conversations/utils/cn.js.map +1 -0
  121. package/dist/conversations/utils/i18n.d.ts +15 -0
  122. package/dist/conversations/utils/i18n.d.ts.map +1 -0
  123. package/dist/conversations/utils/i18n.js +14 -0
  124. package/dist/conversations/utils/i18n.js.map +1 -0
  125. package/dist/conversations/utils/mappers.d.ts +32 -0
  126. package/dist/conversations/utils/mappers.d.ts.map +1 -0
  127. package/dist/conversations/utils/mappers.js +164 -0
  128. package/dist/conversations/utils/mappers.js.map +1 -0
  129. package/dist/conversations/utils/navigation.d.ts +18 -0
  130. package/dist/conversations/utils/navigation.d.ts.map +1 -0
  131. package/dist/conversations/utils/navigation.js +8 -0
  132. package/dist/conversations/utils/navigation.js.map +1 -0
  133. package/dist/dev/main.d.ts +2 -0
  134. package/dist/dev/main.d.ts.map +1 -0
  135. package/dist/generated/global-operations.d.ts +2 -0
  136. package/dist/generated/global-operations.d.ts.map +1 -0
  137. package/dist/generated/global-types.d.ts +20716 -0
  138. package/dist/generated/global-types.d.ts.map +1 -0
  139. package/dist/generated/wspace-operations.d.ts +2008 -0
  140. package/dist/generated/wspace-operations.d.ts.map +1 -0
  141. package/dist/generated/wspace-operations.js +997 -0
  142. package/dist/generated/wspace-operations.js.map +1 -0
  143. package/dist/generated/wspace-types.d.ts +53363 -0
  144. package/dist/generated/wspace-types.d.ts.map +1 -0
  145. package/dist/index.d.ts +6 -0
  146. package/dist/index.d.ts.map +1 -0
  147. package/dist/index.js +7 -0
  148. package/dist/node_modules/clsx/dist/clsx.js +18 -0
  149. package/dist/node_modules/clsx/dist/clsx.js.map +1 -0
  150. package/dist/node_modules/tailwind-merge/dist/bundle-mjs.js +1674 -0
  151. package/dist/node_modules/tailwind-merge/dist/bundle-mjs.js.map +1 -0
  152. package/dist/utils/nativeBridge.d.ts +3 -0
  153. package/dist/utils/nativeBridge.d.ts.map +1 -0
  154. package/dist/utils/nativeBridge.js +2 -0
  155. package/package.json +115 -0
@@ -0,0 +1,573 @@
1
+ import { useConversations as e } from "../providers/ConversationsProvider.js";
2
+ import { useTr as t } from "../utils/i18n.js";
3
+ import { EmptyState as n, PageLayout as r, PageSection as i } from "../components/PageLayout.js";
4
+ import { useEffect as a, useState as o } from "react";
5
+ import { Activity as s, Archive as c, CheckCircle2 as l, Package as u, ShieldCheck as d, Trash2 as f } from "lucide-react";
6
+ import { gql as p } from "@apollo/client";
7
+ import { useMutation as m, useQuery as h } from "@apollo/client/react";
8
+ import { jsx as g, jsxs as _ } from "react/jsx-runtime";
9
+ //#region src/conversations/pages/ConversationAdminPages.tsx
10
+ var v = p`
11
+ query ConversationAdminMessageReports(
12
+ $first: Int
13
+ $filter: MessageReportFilter
14
+ ) {
15
+ messageReports(first: $first, filter: $filter) {
16
+ edges {
17
+ node {
18
+ id
19
+ messageId
20
+ reportedBy
21
+ reason
22
+ status
23
+ notes
24
+ createdAt
25
+ reviewedAt
26
+ message {
27
+ id
28
+ conversationId
29
+ authorId
30
+ content
31
+ isHidden
32
+ createdAt
33
+ }
34
+ }
35
+ }
36
+ }
37
+ }
38
+ `, y = p`
39
+ mutation ConversationAdminResolveMessageReport(
40
+ $input: ResolveMessageReportInput!
41
+ ) {
42
+ resolveMessageReport(input: $input) {
43
+ id
44
+ status
45
+ reviewedAt
46
+ notes
47
+ message {
48
+ id
49
+ isHidden
50
+ }
51
+ }
52
+ }
53
+ `, b = p`
54
+ query ConversationAdminBulkConversations(
55
+ $first: Int
56
+ $filter: ConversationFilter
57
+ ) {
58
+ conversations(first: $first, filter: $filter) {
59
+ edges {
60
+ node {
61
+ id
62
+ title
63
+ type
64
+ state
65
+ updatedAt
66
+ }
67
+ }
68
+ }
69
+ }
70
+ `, x = p`
71
+ mutation ConversationAdminBulkUpdate(
72
+ $input: BulkConversationOperationInput!
73
+ ) {
74
+ bulkUpdateConversations(input: $input) {
75
+ success
76
+ processedCount
77
+ successCount
78
+ failureCount
79
+ conversationIds
80
+ errors {
81
+ conversationId
82
+ message
83
+ }
84
+ }
85
+ }
86
+ `, S = p`
87
+ query ConversationAdminAuditLog($filter: ActivityLogFilter) {
88
+ getActivities(filter: $filter) {
89
+ id
90
+ action
91
+ description
92
+ resourceId
93
+ resourceType
94
+ userId
95
+ timestamp
96
+ }
97
+ }
98
+ `, C = p`
99
+ query ConversationAdminAppInstallations($workspaceId: ID) {
100
+ appInstallations(workspaceId: $workspaceId) {
101
+ id
102
+ appId
103
+ appVersion
104
+ state
105
+ settings
106
+ permissions
107
+ installedBy
108
+ installedAt
109
+ updatedAt
110
+ }
111
+ }
112
+ `, w = p`
113
+ mutation ConversationAdminInstallApp($input: InstallAppInput!) {
114
+ installApp(input: $input) {
115
+ id
116
+ appId
117
+ state
118
+ settings
119
+ installedAt
120
+ }
121
+ }
122
+ `, T = p`
123
+ mutation ConversationAdminUpdateAppSettings($id: ID!, $settings: JSON!) {
124
+ updateAppSettings(id: $id, settings: $settings) {
125
+ id
126
+ settings
127
+ updatedAt
128
+ }
129
+ }
130
+ `, E = p`
131
+ mutation ConversationAdminUninstallApp($id: ID!) {
132
+ uninstallApp(id: $id) {
133
+ appInstallationId
134
+ success
135
+ }
136
+ }
137
+ `;
138
+ function D(e) {
139
+ if (typeof e == "string") return e;
140
+ if (e && typeof e == "object" && "text" in e) {
141
+ let t = e.text;
142
+ if (typeof t == "string") return t;
143
+ }
144
+ try {
145
+ return JSON.stringify(e);
146
+ } catch {
147
+ return "Message content unavailable";
148
+ }
149
+ }
150
+ function O(e) {
151
+ return new Intl.DateTimeFormat(void 0, {
152
+ dateStyle: "medium",
153
+ timeStyle: "short"
154
+ }).format(new Date(e));
155
+ }
156
+ function k({ children: e }) {
157
+ return /* @__PURE__ */ g("span", {
158
+ className: "rounded-full border border-border bg-muted px-2 py-0.5 text-xs font-medium text-muted-foreground",
159
+ children: e
160
+ });
161
+ }
162
+ function A() {
163
+ let e = t(), { data: i, loading: a, error: o, refetch: s } = h(v, { variables: {
164
+ first: 50,
165
+ filter: { statuses: ["PENDING", "REVIEWING"] }
166
+ } }), [c, { loading: u }] = m(y, { onCompleted: () => void s() }), f = i?.messageReports.edges.map((e) => e.node) ?? [];
167
+ return /* @__PURE__ */ _(r, {
168
+ title: e("conversations.admin.moderationPageTitle", "Moderation queue"),
169
+ subtitle: e("conversations.admin.moderationPageDescription", "Review reported messages and apply moderator decisions."),
170
+ icon: /* @__PURE__ */ g(d, {
171
+ className: "h-5 w-5",
172
+ "aria-hidden": "true"
173
+ }),
174
+ maxWidth: "2xl",
175
+ children: [
176
+ a && /* @__PURE__ */ g("p", {
177
+ className: "text-sm text-muted-foreground",
178
+ children: "Loading reports..."
179
+ }),
180
+ o && /* @__PURE__ */ g("p", {
181
+ className: "text-sm text-destructive",
182
+ children: o.message
183
+ }),
184
+ !a && !o && f.length === 0 && /* @__PURE__ */ g(n, {
185
+ title: "No reports waiting for review",
186
+ description: "Reported messages will appear here as they are submitted.",
187
+ icon: /* @__PURE__ */ g(l, {
188
+ className: "h-6 w-6",
189
+ "aria-hidden": "true"
190
+ })
191
+ }),
192
+ /* @__PURE__ */ g("div", {
193
+ className: "grid gap-3",
194
+ children: f.map((e) => /* @__PURE__ */ g("article", {
195
+ className: "rounded-lg border border-border bg-card p-4 shadow-sm",
196
+ children: /* @__PURE__ */ _("div", {
197
+ className: "flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between",
198
+ children: [/* @__PURE__ */ _("div", {
199
+ className: "space-y-2",
200
+ children: [
201
+ /* @__PURE__ */ _("div", {
202
+ className: "flex flex-wrap items-center gap-2",
203
+ children: [
204
+ /* @__PURE__ */ g(k, { children: e.status }),
205
+ e.message.isHidden && /* @__PURE__ */ g(k, { children: "Hidden" }),
206
+ /* @__PURE__ */ _("span", {
207
+ className: "text-xs text-muted-foreground",
208
+ children: ["Reported ", O(e.createdAt)]
209
+ })
210
+ ]
211
+ }),
212
+ /* @__PURE__ */ g("p", {
213
+ className: "text-sm font-medium text-foreground",
214
+ children: e.reason
215
+ }),
216
+ /* @__PURE__ */ g("p", {
217
+ className: "rounded-md bg-muted p-3 text-sm text-muted-foreground",
218
+ children: D(e.message.content)
219
+ }),
220
+ /* @__PURE__ */ _("p", {
221
+ className: "text-xs text-muted-foreground",
222
+ children: [
223
+ "Message ",
224
+ e.messageId,
225
+ " in conversation",
226
+ " ",
227
+ e.message.conversationId,
228
+ " by ",
229
+ e.message.authorId,
230
+ "; reported by ",
231
+ e.reportedBy
232
+ ]
233
+ })
234
+ ]
235
+ }), /* @__PURE__ */ _("div", {
236
+ className: "flex flex-wrap gap-2",
237
+ children: [/* @__PURE__ */ g("button", {
238
+ type: "button",
239
+ disabled: u,
240
+ className: "rounded-md border border-border bg-background px-3 py-2 text-sm text-foreground hover:bg-accent disabled:opacity-50",
241
+ onClick: () => void c({ variables: { input: {
242
+ reportId: e.id,
243
+ action: "DISMISS"
244
+ } } }),
245
+ children: "Dismiss"
246
+ }), /* @__PURE__ */ g("button", {
247
+ type: "button",
248
+ disabled: u,
249
+ className: "rounded-md bg-primary px-3 py-2 text-sm text-primary-foreground hover:bg-primary/90 disabled:opacity-50",
250
+ onClick: () => void c({ variables: { input: {
251
+ reportId: e.id,
252
+ action: "HIDE_MESSAGE"
253
+ } } }),
254
+ children: "Hide and resolve"
255
+ })]
256
+ })]
257
+ })
258
+ }, e.id))
259
+ })
260
+ ]
261
+ });
262
+ }
263
+ function j() {
264
+ let e = t(), [n, i] = o(() => /* @__PURE__ */ new Set()), [a, s] = o("ARCHIVE"), { data: l, loading: u, error: d, refetch: f } = h(b, { variables: { first: 100 } }), [p, { data: v, loading: y }] = m(x, { onCompleted: () => {
265
+ i(/* @__PURE__ */ new Set()), f();
266
+ } }), S = l?.conversations.edges.map((e) => e.node) ?? [], C = (e) => {
267
+ i((t) => {
268
+ let n = new Set(t);
269
+ return n.has(e) ? n.delete(e) : n.add(e), n;
270
+ });
271
+ };
272
+ return /* @__PURE__ */ _(r, {
273
+ title: e("conversations.admin.bulkOperationsPageTitle", "Bulk operations"),
274
+ subtitle: e("conversations.admin.bulkOperationsPageDescription", "Select workspace conversations and apply archive, unarchive, or delete operations."),
275
+ icon: /* @__PURE__ */ g(c, {
276
+ className: "h-5 w-5",
277
+ "aria-hidden": "true"
278
+ }),
279
+ maxWidth: "2xl",
280
+ headerActions: /* @__PURE__ */ _("div", {
281
+ className: "flex flex-wrap items-center gap-2",
282
+ children: [/* @__PURE__ */ _("select", {
283
+ value: a,
284
+ onChange: (e) => s(e.target.value),
285
+ className: "rounded-md border border-border bg-background px-3 py-2 text-sm text-foreground",
286
+ children: [
287
+ /* @__PURE__ */ g("option", {
288
+ value: "ARCHIVE",
289
+ children: "Archive"
290
+ }),
291
+ /* @__PURE__ */ g("option", {
292
+ value: "UNARCHIVE",
293
+ children: "Unarchive"
294
+ }),
295
+ /* @__PURE__ */ g("option", {
296
+ value: "DELETE",
297
+ children: "Delete"
298
+ })
299
+ ]
300
+ }), /* @__PURE__ */ _("button", {
301
+ type: "button",
302
+ disabled: n.size === 0 || y,
303
+ className: "rounded-md bg-primary px-3 py-2 text-sm text-primary-foreground hover:bg-primary/90 disabled:opacity-50",
304
+ onClick: () => void p({ variables: { input: {
305
+ conversationIds: Array.from(n),
306
+ action: a
307
+ } } }),
308
+ children: ["Apply to ", n.size]
309
+ })]
310
+ }),
311
+ children: [
312
+ u && /* @__PURE__ */ g("p", {
313
+ className: "text-sm text-muted-foreground",
314
+ children: "Loading conversations..."
315
+ }),
316
+ d && /* @__PURE__ */ g("p", {
317
+ className: "text-sm text-destructive",
318
+ children: d.message
319
+ }),
320
+ v?.bulkUpdateConversations && /* @__PURE__ */ _("div", {
321
+ className: "rounded-lg border border-border bg-muted p-3 text-sm text-muted-foreground",
322
+ children: [
323
+ v.bulkUpdateConversations.successCount,
324
+ " succeeded,",
325
+ " ",
326
+ v.bulkUpdateConversations.failureCount,
327
+ " failed."
328
+ ]
329
+ }),
330
+ /* @__PURE__ */ g("div", {
331
+ className: "overflow-hidden rounded-lg border border-border bg-card",
332
+ children: S.map((e) => /* @__PURE__ */ _("label", {
333
+ className: "flex cursor-pointer items-center gap-3 border-b border-border px-4 py-3 last:border-b-0 hover:bg-accent",
334
+ children: [/* @__PURE__ */ g("input", {
335
+ type: "checkbox",
336
+ checked: n.has(e.id),
337
+ onChange: () => C(e.id)
338
+ }), /* @__PURE__ */ _("div", {
339
+ className: "min-w-0 flex-1",
340
+ children: [/* @__PURE__ */ g("p", {
341
+ className: "truncate text-sm font-medium text-foreground",
342
+ children: e.title || "Untitled conversation"
343
+ }), /* @__PURE__ */ _("p", {
344
+ className: "text-xs text-muted-foreground",
345
+ children: [
346
+ e.type,
347
+ " · ",
348
+ e.state,
349
+ " · Updated",
350
+ " ",
351
+ O(e.updatedAt)
352
+ ]
353
+ })]
354
+ })]
355
+ }, e.id))
356
+ })
357
+ ]
358
+ });
359
+ }
360
+ function M() {
361
+ let i = t(), { workspaceId: a } = e(), { data: o, loading: c, error: l } = h(S, {
362
+ variables: { filter: {
363
+ workspaceId: a,
364
+ resourceType: "conversation",
365
+ limit: 50
366
+ } },
367
+ skip: !a
368
+ }), u = o?.getActivities ?? [];
369
+ return /* @__PURE__ */ _(r, {
370
+ title: i("conversations.admin.auditLogPageTitle", "Audit log"),
371
+ subtitle: i("conversations.admin.auditLogPageDescription", "Activity stream for admin and moderator actions on workspace conversations."),
372
+ icon: /* @__PURE__ */ g(s, {
373
+ className: "h-5 w-5",
374
+ "aria-hidden": "true"
375
+ }),
376
+ maxWidth: "2xl",
377
+ children: [
378
+ c && /* @__PURE__ */ g("p", {
379
+ className: "text-sm text-muted-foreground",
380
+ children: "Loading activity..."
381
+ }),
382
+ l && /* @__PURE__ */ g("p", {
383
+ className: "text-sm text-destructive",
384
+ children: l.message
385
+ }),
386
+ !c && !l && u.length === 0 && /* @__PURE__ */ g(n, {
387
+ title: "No activity found",
388
+ description: "Conversation admin actions will appear here."
389
+ }),
390
+ /* @__PURE__ */ g("div", {
391
+ className: "grid gap-3",
392
+ children: u.map((e) => /* @__PURE__ */ _("article", {
393
+ className: "rounded-lg border border-border bg-card p-4",
394
+ children: [
395
+ /* @__PURE__ */ _("div", {
396
+ className: "flex flex-wrap items-center justify-between gap-2",
397
+ children: [/* @__PURE__ */ g("p", {
398
+ className: "text-sm font-medium text-foreground",
399
+ children: e.action
400
+ }), /* @__PURE__ */ g("span", {
401
+ className: "text-xs text-muted-foreground",
402
+ children: O(e.timestamp)
403
+ })]
404
+ }),
405
+ /* @__PURE__ */ g("p", {
406
+ className: "mt-1 text-sm text-muted-foreground",
407
+ children: e.description || "No description provided"
408
+ }),
409
+ /* @__PURE__ */ _("p", {
410
+ className: "mt-2 text-xs text-muted-foreground",
411
+ children: [
412
+ "User ",
413
+ e.userId,
414
+ " · Resource ",
415
+ e.resourceId ?? "workspace"
416
+ ]
417
+ })
418
+ ]
419
+ }, e.id))
420
+ })
421
+ ]
422
+ });
423
+ }
424
+ function N() {
425
+ let n = t(), { workspaceId: s } = e(), [c, l] = o(""), [d, p] = o("{}"), [v, y] = o({}), [b, x] = o(null), { data: S, loading: D, error: A, refetch: j } = h(C, {
426
+ variables: { workspaceId: s },
427
+ skip: !s
428
+ }), [M, { loading: N }] = m(w, { onCompleted: () => void j() }), [P, { loading: F }] = m(T, { onCompleted: () => void j() }), [I, { loading: L }] = m(E, { onCompleted: () => void j() }), R = S?.appInstallations ?? [];
429
+ a(() => {
430
+ y((e) => {
431
+ let t = { ...e };
432
+ for (let e of R) t[e.id] || (t[e.id] = JSON.stringify(e.settings ?? {}, null, 2));
433
+ return t;
434
+ });
435
+ }, [R]);
436
+ let z = (e) => {
437
+ try {
438
+ let t = JSON.parse(e);
439
+ return t && typeof t == "object" && !Array.isArray(t) ? t : null;
440
+ } catch {
441
+ return null;
442
+ }
443
+ };
444
+ return /* @__PURE__ */ _(r, {
445
+ title: n("conversations.admin.appInstallationsPageTitle", "App installations"),
446
+ subtitle: n("conversations.admin.appInstallationsPageDescription", "Install, configure, and remove conversation apps and integrations."),
447
+ icon: /* @__PURE__ */ g(u, {
448
+ className: "h-5 w-5",
449
+ "aria-hidden": "true"
450
+ }),
451
+ maxWidth: "2xl",
452
+ children: [
453
+ /* @__PURE__ */ _(i, {
454
+ title: "Install app",
455
+ description: "Install a workspace-scoped conversation app by app ID.",
456
+ children: [/* @__PURE__ */ _("div", {
457
+ className: "grid gap-3 rounded-lg border border-border bg-card p-4 sm:grid-cols-[minmax(0,1fr)_minmax(0,2fr)_auto]",
458
+ children: [
459
+ /* @__PURE__ */ g("input", {
460
+ value: c,
461
+ onChange: (e) => l(e.target.value),
462
+ placeholder: "app-id",
463
+ className: "rounded-md border border-border bg-background px-3 py-2 text-sm text-foreground"
464
+ }),
465
+ /* @__PURE__ */ g("textarea", {
466
+ value: d,
467
+ onChange: (e) => p(e.target.value),
468
+ rows: 3,
469
+ className: "rounded-md border border-border bg-background px-3 py-2 text-sm text-foreground"
470
+ }),
471
+ /* @__PURE__ */ g("button", {
472
+ type: "button",
473
+ disabled: N,
474
+ className: "rounded-md bg-primary px-3 py-2 text-sm text-primary-foreground hover:bg-primary/90 disabled:opacity-50",
475
+ onClick: () => {
476
+ let e = z(d);
477
+ if (!c.trim() || !e) {
478
+ x("Enter an app ID and a valid JSON settings object.");
479
+ return;
480
+ }
481
+ x(null), M({ variables: { input: {
482
+ appId: c.trim(),
483
+ workspaceId: s,
484
+ settings: e
485
+ } } });
486
+ },
487
+ children: "Install"
488
+ })
489
+ ]
490
+ }), b && /* @__PURE__ */ g("p", {
491
+ className: "text-sm text-destructive",
492
+ children: b
493
+ })]
494
+ }),
495
+ D && /* @__PURE__ */ g("p", {
496
+ className: "text-sm text-muted-foreground",
497
+ children: "Loading app installations..."
498
+ }),
499
+ A && /* @__PURE__ */ g("p", {
500
+ className: "text-sm text-destructive",
501
+ children: A.message
502
+ }),
503
+ /* @__PURE__ */ g("div", {
504
+ className: "grid gap-3",
505
+ children: R.map((e) => /* @__PURE__ */ _("article", {
506
+ className: "rounded-lg border border-border bg-card p-4",
507
+ children: [/* @__PURE__ */ _("div", {
508
+ className: "flex flex-wrap items-start justify-between gap-3",
509
+ children: [/* @__PURE__ */ _("div", { children: [/* @__PURE__ */ _("div", {
510
+ className: "flex items-center gap-2",
511
+ children: [/* @__PURE__ */ g("h3", {
512
+ className: "text-sm font-semibold text-foreground",
513
+ children: e.appId
514
+ }), /* @__PURE__ */ g(k, { children: e.state })]
515
+ }), /* @__PURE__ */ _("p", {
516
+ className: "text-xs text-muted-foreground",
517
+ children: [
518
+ "Version ",
519
+ e.appVersion,
520
+ " · Installed by",
521
+ " ",
522
+ e.installedBy,
523
+ " on",
524
+ " ",
525
+ O(e.installedAt)
526
+ ]
527
+ })] }), /* @__PURE__ */ _("button", {
528
+ type: "button",
529
+ disabled: L,
530
+ className: "inline-flex items-center gap-2 rounded-md border border-border bg-background px-3 py-2 text-sm text-foreground hover:bg-accent disabled:opacity-50",
531
+ onClick: () => void I({ variables: { id: e.id } }),
532
+ children: [/* @__PURE__ */ g(f, {
533
+ className: "h-4 w-4",
534
+ "aria-hidden": "true"
535
+ }), "Uninstall"]
536
+ })]
537
+ }), /* @__PURE__ */ _("div", {
538
+ className: "mt-3 grid gap-2",
539
+ children: [/* @__PURE__ */ g("textarea", {
540
+ rows: 5,
541
+ value: v[e.id] ?? "{}",
542
+ onChange: (t) => y((n) => ({
543
+ ...n,
544
+ [e.id]: t.target.value
545
+ })),
546
+ className: "rounded-md border border-border bg-background px-3 py-2 font-mono text-xs text-foreground"
547
+ }), /* @__PURE__ */ g("button", {
548
+ type: "button",
549
+ disabled: F,
550
+ className: "w-fit rounded-md bg-secondary px-3 py-2 text-sm text-secondary-foreground hover:bg-secondary/90 disabled:opacity-50",
551
+ onClick: () => {
552
+ let t = z(v[e.id] ?? "{}");
553
+ if (!t) {
554
+ x("Installation settings must be a valid JSON object.");
555
+ return;
556
+ }
557
+ x(null), P({ variables: {
558
+ id: e.id,
559
+ settings: t
560
+ } });
561
+ },
562
+ children: "Save settings"
563
+ })]
564
+ })]
565
+ }, e.id))
566
+ })
567
+ ]
568
+ });
569
+ }
570
+ //#endregion
571
+ export { N as AppInstallationsPage, j as BulkOperationsPage, M as ConversationAuditLogPage, A as ModerationQueuePage };
572
+
573
+ //# sourceMappingURL=ConversationAdminPages.js.map
@@ -0,0 +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 { 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\nfunction formatDate(value: string): string {\n return new Intl.DateTimeFormat(undefined, {\n dateStyle: \"medium\",\n timeStyle: \"short\",\n }).format(new Date(value));\n}\n\nfunction StatusPill({ children }: { children: string }) {\n return (\n <span className=\"rounded-full border border-border bg-muted px-2 py-0.5 text-xs font-medium text-muted-foreground\">\n {children}\n </span>\n );\n}\n\nexport function ModerationQueuePage() {\n const tr = useTr();\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 });\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=\"h-5 w-5\" aria-hidden=\"true\" />}\n maxWidth=\"2xl\"\n >\n {loading && (\n <p className=\"text-sm text-muted-foreground\">Loading reports...</p>\n )}\n {error && <p className=\"text-sm text-destructive\">{error.message}</p>}\n {!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=\"h-6 w-6\" aria-hidden=\"true\" />}\n />\n )}\n <div className=\"grid gap-3\">\n {reports.map((report) => (\n <article\n key={report.id}\n className=\"rounded-lg border border-border bg-card p-4 shadow-sm\"\n >\n <div className=\"flex flex-col gap-3 sm:flex-row sm:items-start sm:justify-between\">\n <div className=\"space-y-2\">\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-muted-foreground\">\n Reported {formatDate(report.createdAt)}\n </span>\n </div>\n <p className=\"text-sm font-medium text-foreground\">\n {report.reason}\n </p>\n <p className=\"rounded-md bg-muted p-3 text-sm text-muted-foreground\">\n {formatContent(report.message.content)}\n </p>\n <p className=\"text-xs text-muted-foreground\">\n Message {report.messageId} in conversation{\" \"}\n {report.message.conversationId} by {report.message.authorId};\n reported by {report.reportedBy}\n </p>\n </div>\n <div className=\"flex flex-wrap gap-2\">\n <button\n type=\"button\"\n disabled={resolving}\n className=\"rounded-md border border-border bg-background px-3 py-2 text-sm text-foreground hover:bg-accent disabled:opacity-50\"\n onClick={() =>\n void resolveReport({\n variables: {\n input: { reportId: report.id, action: \"DISMISS\" },\n },\n })\n }\n >\n Dismiss\n </button>\n <button\n type=\"button\"\n disabled={resolving}\n className=\"rounded-md bg-primary px-3 py-2 text-sm text-primary-foreground hover:bg-primary/90 disabled:opacity-50\"\n onClick={() =>\n void resolveReport({\n variables: {\n input: { reportId: report.id, action: \"HIDE_MESSAGE\" },\n },\n })\n }\n >\n Hide and resolve\n </button>\n </div>\n </div>\n </article>\n ))}\n </div>\n </PageLayout>\n );\n}\n\nexport function BulkOperationsPage() {\n const tr = useTr();\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, { variables: { first: 100 } });\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=\"h-5 w-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 className=\"rounded-md border border-border bg-background px-3 py-2 text-sm text-foreground\"\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 disabled={selectedIds.size === 0 || updating}\n className=\"rounded-md bg-primary px-3 py-2 text-sm text-primary-foreground hover:bg-primary/90 disabled:opacity-50\"\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 {loading && (\n <p className=\"text-sm text-muted-foreground\">\n Loading conversations...\n </p>\n )}\n {error && <p className=\"text-sm text-destructive\">{error.message}</p>}\n {bulkResult?.bulkUpdateConversations && (\n <div className=\"rounded-lg border border-border bg-muted p-3 text-sm text-muted-foreground\">\n {bulkResult.bulkUpdateConversations.successCount} succeeded,{\" \"}\n {bulkResult.bulkUpdateConversations.failureCount} failed.\n </div>\n )}\n <div className=\"overflow-hidden rounded-lg border border-border bg-card\">\n {conversations.map((conversation) => (\n <label\n key={conversation.id}\n className=\"flex cursor-pointer items-center gap-3 border-b border-border px-4 py-3 last:border-b-0 hover:bg-accent\"\n >\n <input\n type=\"checkbox\"\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-foreground\">\n {conversation.title || \"Untitled conversation\"}\n </p>\n <p className=\"text-xs text-muted-foreground\">\n {conversation.type} · {conversation.state} · Updated{\" \"}\n {formatDate(conversation.updatedAt)}\n </p>\n </div>\n </label>\n ))}\n </div>\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=\"h-5 w-5\" aria-hidden=\"true\" />}\n maxWidth=\"2xl\"\n >\n {loading && (\n <p className=\"text-sm text-muted-foreground\">Loading activity...</p>\n )}\n {error && <p className=\"text-sm text-destructive\">{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 />\n )}\n <div className=\"grid gap-3\">\n {logs.map((log) => (\n <article\n key={log.id}\n className=\"rounded-lg border border-border bg-card p-4\"\n >\n <div className=\"flex flex-wrap items-center justify-between gap-2\">\n <p className=\"text-sm font-medium text-foreground\">\n {log.action}\n </p>\n <span className=\"text-xs text-muted-foreground\">\n {formatDate(log.timestamp)}\n </span>\n </div>\n <p className=\"mt-1 text-sm text-muted-foreground\">\n {log.description || \"No description provided\"}\n </p>\n <p className=\"mt-2 text-xs text-muted-foreground\">\n User {log.userId} · Resource {log.resourceId ?? \"workspace\"}\n </p>\n </article>\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 = data?.appInstallations ?? [];\n\n useEffect(() => {\n setSettingsDrafts((current) => {\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=\"h-5 w-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 bg-card 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 className=\"rounded-md border border-border bg-background px-3 py-2 text-sm text-foreground\"\n />\n <textarea\n value={settingsInput}\n onChange={(event) => setSettingsInput(event.target.value)}\n rows={3}\n className=\"rounded-md border border-border bg-background px-3 py-2 text-sm text-foreground\"\n />\n <button\n type=\"button\"\n disabled={installing}\n className=\"rounded-md bg-primary px-3 py-2 text-sm text-primary-foreground hover:bg-primary/90 disabled:opacity-50\"\n onClick={handleInstall}\n >\n Install\n </button>\n </div>\n {formError && <p className=\"text-sm text-destructive\">{formError}</p>}\n </PageSection>\n {loading && (\n <p className=\"text-sm text-muted-foreground\">\n Loading app installations...\n </p>\n )}\n {error && <p className=\"text-sm text-destructive\">{error.message}</p>}\n <div className=\"grid gap-3\">\n {installations.map((installation) => (\n <article\n key={installation.id}\n className=\"rounded-lg border border-border bg-card p-4\"\n >\n <div className=\"flex flex-wrap items-start justify-between gap-3\">\n <div>\n <div className=\"flex items-center gap-2\">\n <h3 className=\"text-sm font-semibold text-foreground\">\n {installation.appId}\n </h3>\n <StatusPill>{installation.state}</StatusPill>\n </div>\n <p className=\"text-xs text-muted-foreground\">\n Version {installation.appVersion} · Installed by{\" \"}\n {installation.installedBy} on{\" \"}\n {formatDate(installation.installedAt)}\n </p>\n </div>\n <button\n type=\"button\"\n disabled={uninstalling}\n className=\"inline-flex items-center gap-2 rounded-md border border-border bg-background px-3 py-2 text-sm text-foreground hover:bg-accent disabled:opacity-50\"\n onClick={() =>\n void uninstallApp({ variables: { id: installation.id } })\n }\n >\n <Trash2 className=\"h-4 w-4\" aria-hidden=\"true\" />\n Uninstall\n </button>\n </div>\n <div className=\"mt-3 grid gap-2\">\n <textarea\n rows={5}\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 bg-background px-3 py-2 font-mono text-xs text-foreground\"\n />\n <button\n type=\"button\"\n disabled={saving}\n className=\"w-fit rounded-md bg-secondary px-3 py-2 text-sm text-secondary-foreground hover:bg-secondary/90 disabled:opacity-50\"\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 </article>\n ))}\n </div>\n </PageLayout>\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export declare function ConversationDetailPage(): import("react/jsx-runtime").JSX.Element;
2
+ //# sourceMappingURL=ConversationDetailPage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ConversationDetailPage.d.ts","sourceRoot":"","sources":["../../../src/conversations/pages/ConversationDetailPage.tsx"],"names":[],"mappings":"AAuCA,wBAAgB,sBAAsB,4CA6nBrC"}