@burdenoff/microfe-conversations 2026.525.3 → 2026.527.1

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 (38) hide show
  1. package/dist/conversations/ConversationsUserRoutes.js +4 -3
  2. package/dist/conversations/ConversationsUserRoutes.js.map +1 -1
  3. package/dist/conversations/components/EmptyState.js +27 -0
  4. package/dist/conversations/components/EmptyState.js.map +1 -0
  5. package/dist/conversations/components/LazyPageBoundary.js +26 -42
  6. package/dist/conversations/components/LazyPageBoundary.js.map +1 -1
  7. package/dist/conversations/components/MessageBubble.js +3 -1
  8. package/dist/conversations/components/MessageBubble.js.map +1 -1
  9. package/dist/conversations/components/PageLayout.js +3 -39
  10. package/dist/conversations/components/PageLayout.js.map +1 -1
  11. package/dist/conversations/components/PageSection.js +25 -0
  12. package/dist/conversations/components/PageSection.js.map +1 -0
  13. package/dist/conversations/components/PermissionGuard.js +1 -1
  14. package/dist/conversations/components/PermissionGuard.js.map +1 -1
  15. package/dist/conversations/components/lazyRetry.js +23 -0
  16. package/dist/conversations/components/lazyRetry.js.map +1 -0
  17. package/dist/conversations/constants/permissions.js +0 -1
  18. package/dist/conversations/constants/permissions.js.map +1 -1
  19. package/dist/conversations/hooks/useMessageQueries.js.map +1 -1
  20. package/dist/conversations/hooks/useMessageSubscriptions.js.map +1 -1
  21. package/dist/conversations/pages/AdminConversationsLandingPage.js +6 -6
  22. package/dist/conversations/pages/AdminConversationsLandingPage.js.map +1 -1
  23. package/dist/conversations/pages/ConversationAdminPages.js +56 -49
  24. package/dist/conversations/pages/ConversationAdminPages.js.map +1 -1
  25. package/dist/conversations/pages/ConversationDetailPage.js +3 -2
  26. package/dist/conversations/pages/ConversationDetailPage.js.map +1 -1
  27. package/dist/conversations/pages/ConversationListPage.js +133 -136
  28. package/dist/conversations/pages/ConversationListPage.js.map +1 -1
  29. package/dist/conversations/pages/ConversationsLandingPage.js +50 -57
  30. package/dist/conversations/pages/ConversationsLandingPage.js.map +1 -1
  31. package/dist/conversations/pages/ConversationsSettingsPage.js +8 -5
  32. package/dist/conversations/pages/ConversationsSettingsPage.js.map +1 -1
  33. package/dist/conversations/pages/NewConversationPage.js +87 -88
  34. package/dist/conversations/pages/NewConversationPage.js.map +1 -1
  35. package/dist/conversations/providers/ConversationsProvider.js +1 -1
  36. package/dist/conversations/providers/ConversationsProvider.js.map +1 -1
  37. package/dist/conversations/utils/mappers.js.map +1 -1
  38. package/package.json +1 -1
@@ -1,87 +1,84 @@
1
1
  import { useConversations as e } from "../providers/ConversationsProvider.js";
2
2
  import { joinPath as t } from "../utils/navigation.js";
3
- import { cn as n } from "../utils/cn.js";
4
- import { PageLayout as r } from "../components/PageLayout.js";
5
- import { useCreateConversation as ee } from "../hooks/useConversationQueries.js";
6
- import { PermissionGuard as i } from "../components/PermissionGuard.js";
7
- import { CONVERSATIONS_PERMISSIONS as a } from "../constants/permissions.js";
8
- import { nativeImpact as o, nativeNotify as s } from "../../utils/nativeBridge.js";
9
- import { useCallback as c, useMemo as l, useState as u } from "react";
10
- import { ArrowLeft as d, Globe as f, Hash as p, Lock as m, Megaphone as h, MessageCircle as g, Plus as _, Shield as v, Users as y, X as b } from "lucide-react";
11
- import { useNavigate as x } from "react-router-dom";
12
- import { useEventBus as S } from "@burdenoff/fe-libs/shared/events";
13
- import { jsx as C, jsxs as w } from "react/jsx-runtime";
14
- import { useI18n as T } from "@burdenoff/fe-libs/shared/providers/shell/I18nProvider";
3
+ import { useTr as n } from "../utils/i18n.js";
4
+ import { cn as r } from "../utils/cn.js";
5
+ import { PageLayout as i } from "../components/PageLayout.js";
6
+ import { useCreateConversation as a } from "../hooks/useConversationQueries.js";
7
+ import { PermissionGuard as o } from "../components/PermissionGuard.js";
8
+ import { CONVERSATIONS_PERMISSIONS as s } from "../constants/permissions.js";
9
+ import { nativeImpact as c, nativeNotify as l } from "../../utils/nativeBridge.js";
10
+ import { useCallback as u, useMemo as d, useState as f } from "react";
11
+ import { ArrowLeft as p, Globe as m, Hash as h, Lock as g, Megaphone as _, MessageCircle as v, Plus as ee, Shield as y, Users as b, X as x } from "lucide-react";
12
+ import { useNavigate as S } from "react-router-dom";
13
+ import { useEventBus as C } from "@burdenoff/fe-libs/shared/events";
14
+ import { jsx as w, jsxs as T } from "react/jsx-runtime";
15
15
  //#region src/conversations/pages/NewConversationPage.tsx
16
- function te(e) {
16
+ function E(e) {
17
17
  if ("graphQLErrors" in e) {
18
18
  let t = e.graphQLErrors;
19
19
  if (t && t.length > 0 && t[0]?.message) return t[0].message;
20
20
  }
21
21
  return e.message;
22
22
  }
23
- function E() {
24
- let E = x(), { basePath: D } = e(), { createConversation: O, loading: k, error: A } = ee(), j = S(), { t: M } = T(), N = (e, t) => {
25
- let n = M(e);
26
- return n === e ? t : n;
27
- }, P = l(() => [
23
+ function D() {
24
+ let D = S(), { basePath: O } = e(), { createConversation: k, loading: A, error: j } = a(), M = C(), N = n(), P = d(() => [
28
25
  {
29
26
  type: "DIRECT",
30
27
  label: N("conversations.directMessage", "Direct Message"),
31
28
  description: N("conversations.directMessageDescription", "One-on-one private conversation"),
32
- icon: /* @__PURE__ */ C(g, { size: 20 })
29
+ icon: /* @__PURE__ */ w(v, { size: 20 })
33
30
  },
34
31
  {
35
32
  type: "GROUP",
36
33
  label: N("conversations.groupChat", "Group Chat"),
37
34
  description: N("conversations.groupChatDescription", "Invite multiple people to a group conversation"),
38
- icon: /* @__PURE__ */ C(y, { size: 20 })
35
+ icon: /* @__PURE__ */ w(b, { size: 20 })
39
36
  },
40
37
  {
41
38
  type: "CHANNEL",
42
39
  label: N("conversations.channel", "Channel"),
43
40
  description: N("conversations.channelDescription", "A topic-based channel that anyone can join"),
44
- icon: /* @__PURE__ */ C(p, { size: 20 })
41
+ icon: /* @__PURE__ */ w(h, { size: 20 })
45
42
  },
46
43
  {
47
44
  type: "ANNOUNCEMENT",
48
45
  label: N("conversations.announcement", "Announcement"),
49
46
  description: N("conversations.announcementDescription", "Broadcast messages to a large audience"),
50
- icon: /* @__PURE__ */ C(h, { size: 20 })
47
+ icon: /* @__PURE__ */ w(_, { size: 20 })
51
48
  }
52
- ], [M]), F = l(() => [
49
+ ], [N]), F = d(() => [
53
50
  {
54
51
  value: "PUBLIC",
55
52
  label: N("conversations.public", "Public"),
56
53
  description: N("conversations.publicDescription", "Anyone in the workspace can discover and join"),
57
- icon: /* @__PURE__ */ C(f, { size: 16 })
54
+ icon: /* @__PURE__ */ w(m, { size: 16 })
58
55
  },
59
56
  {
60
57
  value: "PRIVATE",
61
58
  label: N("conversations.private", "Private"),
62
59
  description: N("conversations.privateDescription", "Only invited members can access"),
63
- icon: /* @__PURE__ */ C(m, { size: 16 })
60
+ icon: /* @__PURE__ */ w(g, { size: 16 })
64
61
  },
65
62
  {
66
63
  value: "PROTECTED",
67
64
  label: N("conversations.protected", "Protected"),
68
65
  description: N("conversations.protectedDescription", "Discoverable but requires approval to join"),
69
- icon: /* @__PURE__ */ C(v, { size: 16 })
66
+ icon: /* @__PURE__ */ w(y, { size: 16 })
70
67
  }
71
- ], [M]), [I, L] = u("GROUP"), [R, z] = u(""), [B, V] = u(""), [H, U] = u("PRIVATE"), [W, G] = u(""), [K, q] = u([]), J = I === "DIRECT", Y = !J, X = c(() => {
68
+ ], [N]), [I, L] = f("GROUP"), [R, z] = f(""), [B, V] = f(""), [H, U] = f("PRIVATE"), [W, G] = f(""), [K, q] = f([]), J = I === "DIRECT", Y = !J, X = u(() => {
72
69
  let e = W.trim();
73
70
  !e || K.includes(e) || (q(J ? [e] : (t) => [...t, e]), G(""));
74
71
  }, [
75
72
  W,
76
73
  K,
77
74
  J
78
- ]), Z = c((e) => {
75
+ ]), Z = u((e) => {
79
76
  q((t) => t.filter((t) => t !== e));
80
- }, []), Q = I === "DIRECT" || I === "GROUP", ne = c(async () => {
77
+ }, []), Q = I === "DIRECT" || I === "GROUP", te = u(async () => {
81
78
  if (!(Q && K.length === 0) && !(Y && !R.trim())) {
82
- o("light");
79
+ c("light");
83
80
  try {
84
- let e = await O({
81
+ let e = await k({
85
82
  type: I,
86
83
  title: Y ? R.trim() : void 0,
87
84
  description: B.trim() || void 0,
@@ -89,111 +86,111 @@ function E() {
89
86
  visibility: H
90
87
  });
91
88
  if (!e) {
92
- s("error");
89
+ l("error");
93
90
  return;
94
91
  }
95
- s("success"), j.emit("conversations.conversation.created", {
92
+ l("success"), M.emit("conversations.conversation.created", {
96
93
  conversationId: e.id,
97
94
  conversationType: I,
98
95
  source: "new-conversation-page"
99
- }), E(t(D || "/conversations", e.id));
96
+ }), D(t(O || "/conversations", e.id));
100
97
  } catch (e) {
101
- throw s("error"), e;
98
+ throw l("error"), e;
102
99
  }
103
100
  }
104
101
  }, [
105
- D,
106
- j,
102
+ O,
103
+ M,
107
104
  Q,
108
105
  K,
109
106
  Y,
110
107
  R,
111
- O,
108
+ k,
112
109
  I,
113
110
  B,
114
111
  H,
115
- E
112
+ D
116
113
  ]), $ = (!Q || K.length > 0) && (!Y || R.trim().length > 0);
117
- return /* @__PURE__ */ C(i, {
118
- permission: a.CONVERSATION_CREATE,
114
+ return /* @__PURE__ */ w(o, {
115
+ permission: s.CONVERSATION_CREATE,
119
116
  allowUnavailablePermissions: !0,
120
- children: /* @__PURE__ */ w(r, {
117
+ children: /* @__PURE__ */ T(i, {
121
118
  title: N("conversations.newConversation", "New Conversation"),
122
119
  subtitle: N("conversations.newConversationSubtitle", "Start a new conversation with your team"),
123
- icon: /* @__PURE__ */ C(_, { size: 24 }),
120
+ icon: /* @__PURE__ */ w(ee, { size: 24 }),
124
121
  maxWidth: "lg",
125
- headerActions: /* @__PURE__ */ w("button", {
122
+ headerActions: /* @__PURE__ */ T("button", {
126
123
  type: "button",
127
- onClick: () => E(D || "/conversations"),
124
+ onClick: () => D(O || "/conversations"),
128
125
  className: "inline-flex items-center gap-2 px-3 py-2 text-sm font-medium rounded-lg text-muted-foreground hover:text-foreground hover:bg-muted/50 transition-colors",
129
- children: [/* @__PURE__ */ C(d, { size: 16 }), N("conversations.back", "Back")]
126
+ children: [/* @__PURE__ */ w(p, { size: 16 }), N("conversations.back", "Back")]
130
127
  }),
131
128
  children: [
132
- /* @__PURE__ */ w("section", {
129
+ /* @__PURE__ */ T("section", {
133
130
  "data-tour": "conversation-type-selector",
134
131
  className: "space-y-3",
135
- children: [/* @__PURE__ */ C("h3", {
132
+ children: [/* @__PURE__ */ w("h3", {
136
133
  className: "text-sm font-semibold text-foreground",
137
134
  children: N("conversations.conversationType", "Conversation Type")
138
- }), /* @__PURE__ */ C("div", {
135
+ }), /* @__PURE__ */ w("div", {
139
136
  className: "grid grid-cols-1 sm:grid-cols-2 gap-3",
140
- children: P.map((e) => /* @__PURE__ */ w("button", {
137
+ children: P.map((e) => /* @__PURE__ */ T("button", {
141
138
  type: "button",
142
139
  onClick: () => {
143
140
  L(e.type), e.type === "DIRECT" && q((e) => e.slice(0, 1));
144
141
  },
145
- className: n("flex items-start gap-3 p-4 rounded-xl border text-left transition-all duration-150", I === e.type ? "border-primary bg-primary/5 ring-1 ring-primary/20" : "border-border hover:border-muted-foreground/30 hover:bg-muted/30"),
146
- children: [/* @__PURE__ */ C("div", {
147
- className: n("p-2 rounded-lg", I === e.type ? "bg-primary/10 text-primary" : "bg-muted text-muted-foreground"),
142
+ className: r("flex items-start gap-3 p-4 rounded-xl border text-left transition-all duration-150", I === e.type ? "border-primary bg-primary/5 ring-1 ring-primary/20" : "border-border hover:border-muted-foreground/30 hover:bg-muted/30"),
143
+ children: [/* @__PURE__ */ w("div", {
144
+ className: r("p-2 rounded-lg", I === e.type ? "bg-primary/10 text-primary" : "bg-muted text-muted-foreground"),
148
145
  children: e.icon
149
- }), /* @__PURE__ */ w("div", { children: [/* @__PURE__ */ C("p", {
146
+ }), /* @__PURE__ */ T("div", { children: [/* @__PURE__ */ w("p", {
150
147
  className: "text-sm font-medium text-foreground",
151
148
  children: e.label
152
- }), /* @__PURE__ */ C("p", {
149
+ }), /* @__PURE__ */ w("p", {
153
150
  className: "text-xs text-muted-foreground mt-0.5",
154
151
  children: e.description
155
152
  })] })]
156
153
  }, e.type))
157
154
  })]
158
155
  }),
159
- !J && /* @__PURE__ */ w("section", {
156
+ !J && /* @__PURE__ */ T("section", {
160
157
  "data-tour": "conversation-visibility-selector",
161
158
  className: "space-y-3",
162
159
  children: [
163
- /* @__PURE__ */ C("h3", {
160
+ /* @__PURE__ */ w("h3", {
164
161
  className: "text-sm font-semibold text-foreground",
165
162
  children: N("conversations.visibility", "Visibility")
166
163
  }),
167
- /* @__PURE__ */ C("div", {
164
+ /* @__PURE__ */ w("div", {
168
165
  className: "flex gap-2",
169
- children: F.map((e) => /* @__PURE__ */ w("button", {
166
+ children: F.map((e) => /* @__PURE__ */ T("button", {
170
167
  type: "button",
171
168
  onClick: () => U(e.value),
172
- className: n("flex items-center gap-2 px-4 py-2.5 rounded-lg border text-sm transition-all", H === e.value ? "border-primary bg-primary/5 text-primary font-medium" : "border-border text-muted-foreground hover:text-foreground hover:bg-muted/30"),
169
+ className: r("flex items-center gap-2 px-4 py-2.5 rounded-lg border text-sm transition-all", H === e.value ? "border-primary bg-primary/5 text-primary font-medium" : "border-border text-muted-foreground hover:text-foreground hover:bg-muted/30"),
173
170
  children: [e.icon, e.label]
174
171
  }, e.value))
175
172
  }),
176
- /* @__PURE__ */ C("p", {
173
+ /* @__PURE__ */ w("p", {
177
174
  className: "text-xs text-muted-foreground",
178
175
  children: F.find((e) => e.value === H)?.description
179
176
  })
180
177
  ]
181
178
  }),
182
- Y && /* @__PURE__ */ w("section", {
179
+ Y && /* @__PURE__ */ T("section", {
183
180
  "data-tour": "conversation-create-details",
184
181
  className: "space-y-3",
185
- children: [/* @__PURE__ */ w("div", { children: [/* @__PURE__ */ w("label", {
182
+ children: [/* @__PURE__ */ T("div", { children: [/* @__PURE__ */ T("label", {
186
183
  htmlFor: "conv-title",
187
184
  className: "text-sm font-semibold text-foreground",
188
185
  children: [
189
186
  N("conversations.name", "Name"),
190
187
  " ",
191
- /* @__PURE__ */ C("span", {
188
+ /* @__PURE__ */ w("span", {
192
189
  className: "text-destructive",
193
190
  children: "*"
194
191
  })
195
192
  ]
196
- }), /* @__PURE__ */ C("input", {
193
+ }), /* @__PURE__ */ w("input", {
197
194
  id: "conv-title",
198
195
  type: "text",
199
196
  value: R,
@@ -201,11 +198,11 @@ function E() {
201
198
  placeholder: I === "CHANNEL" ? N("conversations.channelNamePlaceholder", "e.g., engineering-general") : N("conversations.conversationNamePlaceholder", "e.g., Product Launch Q1"),
202
199
  maxLength: 200,
203
200
  className: "mt-1.5 w-full px-3 py-2 text-sm rounded-lg border border-border bg-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring"
204
- })] }), /* @__PURE__ */ w("div", { children: [/* @__PURE__ */ C("label", {
201
+ })] }), /* @__PURE__ */ T("div", { children: [/* @__PURE__ */ w("label", {
205
202
  htmlFor: "conv-desc",
206
203
  className: "text-sm font-semibold text-foreground",
207
204
  children: N("conversations.description", "Description")
208
- }), /* @__PURE__ */ C("textarea", {
205
+ }), /* @__PURE__ */ w("textarea", {
209
206
  id: "conv-desc",
210
207
  value: B,
211
208
  onChange: (e) => V(e.target.value),
@@ -215,13 +212,13 @@ function E() {
215
212
  className: "mt-1.5 w-full px-3 py-2 text-sm rounded-lg border border-border bg-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring resize-none"
216
213
  })] })]
217
214
  }),
218
- /* @__PURE__ */ w("section", {
215
+ /* @__PURE__ */ T("section", {
219
216
  "data-tour": "conversation-participants",
220
217
  className: "space-y-3",
221
218
  children: [
222
- /* @__PURE__ */ w("h3", {
219
+ /* @__PURE__ */ T("h3", {
223
220
  className: "text-sm font-semibold text-foreground",
224
- children: [J ? N("conversations.participantId", "Participant ID") : N("conversations.participantIds", "Participant IDs"), !Q && /* @__PURE__ */ w("span", {
221
+ children: [J ? N("conversations.participantId", "Participant ID") : N("conversations.participantIds", "Participant IDs"), !Q && /* @__PURE__ */ T("span", {
225
222
  className: "ml-1.5 text-xs font-normal text-muted-foreground",
226
223
  children: [
227
224
  "(",
@@ -230,22 +227,24 @@ function E() {
230
227
  ]
231
228
  })]
232
229
  }),
233
- K.length > 0 && /* @__PURE__ */ C("div", {
230
+ K.length > 0 && /* @__PURE__ */ w("div", {
234
231
  className: "flex flex-wrap gap-2",
235
- children: K.map((e) => /* @__PURE__ */ w("span", {
232
+ children: K.map((e) => /* @__PURE__ */ T("span", {
236
233
  className: "inline-flex items-center gap-1.5 px-2.5 py-1 rounded-full bg-primary/10 text-primary text-xs font-medium",
237
- children: [e, /* @__PURE__ */ C("button", {
234
+ children: [e, /* @__PURE__ */ w("button", {
238
235
  type: "button",
236
+ "aria-label": N("conversations.removeParticipant", "Remove participant"),
239
237
  onClick: () => Z(e),
240
238
  className: "p-0.5 rounded-full hover:bg-primary/20",
241
- children: /* @__PURE__ */ C(b, { size: 12 })
239
+ children: /* @__PURE__ */ w(x, { size: 12 })
242
240
  })]
243
241
  }, e))
244
242
  }),
245
- /* @__PURE__ */ w("div", {
243
+ /* @__PURE__ */ T("div", {
246
244
  className: "flex items-center gap-2",
247
- children: [/* @__PURE__ */ C("input", {
245
+ children: [/* @__PURE__ */ w("input", {
248
246
  type: "text",
247
+ "aria-label": N("conversations.participantInputLabel", "Add participant"),
249
248
  value: W,
250
249
  onChange: (e) => G(e.target.value),
251
250
  onKeyDown: (e) => {
@@ -253,7 +252,7 @@ function E() {
253
252
  },
254
253
  placeholder: N("conversations.participantInputPlaceholder", "Enter user ID and press Enter"),
255
254
  className: "w-full px-3 py-2 text-sm rounded-lg border border-border bg-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring"
256
- }), /* @__PURE__ */ C("button", {
255
+ }), /* @__PURE__ */ w("button", {
257
256
  type: "button",
258
257
  onClick: X,
259
258
  className: "px-3 py-2 text-sm rounded-lg border border-border hover:bg-muted/50",
@@ -262,23 +261,23 @@ function E() {
262
261
  })
263
262
  ]
264
263
  }),
265
- A && /* @__PURE__ */ C("div", {
264
+ j && /* @__PURE__ */ w("div", {
266
265
  className: "text-xs text-destructive rounded-lg border border-destructive/30 bg-destructive/10 px-3 py-2",
267
- children: te(A)
266
+ children: E(j)
268
267
  }),
269
- /* @__PURE__ */ w("div", {
268
+ /* @__PURE__ */ T("div", {
270
269
  className: "flex justify-end gap-3 pt-4 border-t border-border",
271
- children: [/* @__PURE__ */ C("button", {
270
+ children: [/* @__PURE__ */ w("button", {
272
271
  type: "button",
273
- onClick: () => E(".."),
272
+ onClick: () => D(".."),
274
273
  className: "px-4 py-2 text-sm font-medium rounded-lg text-muted-foreground hover:text-foreground hover:bg-muted/50 transition-colors",
275
274
  children: N("conversations.cancel", "Cancel")
276
- }), /* @__PURE__ */ C("button", {
275
+ }), /* @__PURE__ */ w("button", {
277
276
  type: "button",
278
- onClick: () => void ne(),
279
- disabled: !$ || k,
280
- className: n("inline-flex items-center gap-2 px-5 py-2 text-sm font-medium rounded-lg transition-all", $ && !k ? "bg-primary text-primary-foreground hover:bg-primary/90 shadow-sm" : "bg-muted text-muted-foreground cursor-not-allowed"),
281
- children: k ? N("conversations.creating", "Creating...") : N("conversations.createConversation", "Create Conversation")
277
+ onClick: () => void te(),
278
+ disabled: !$ || A,
279
+ className: r("inline-flex items-center gap-2 px-5 py-2 text-sm font-medium rounded-lg transition-all", $ && !A ? "bg-primary text-primary-foreground hover:bg-primary/90 shadow-sm" : "bg-muted text-muted-foreground cursor-not-allowed"),
280
+ children: A ? N("conversations.creating", "Creating...") : N("conversations.createConversation", "Create Conversation")
282
281
  })]
283
282
  })
284
283
  ]
@@ -286,6 +285,6 @@ function E() {
286
285
  });
287
286
  }
288
287
  //#endregion
289
- export { E as NewConversationPage };
288
+ export { D as NewConversationPage };
290
289
 
291
290
  //# sourceMappingURL=NewConversationPage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"NewConversationPage.js","names":[],"sources":["../../../src/conversations/pages/NewConversationPage.tsx"],"sourcesContent":["import { useState, useCallback, useMemo } from \"react\";\nimport { useI18n } from \"@burdenoff/fe-libs/shared/providers/shell/I18nProvider\";\nimport { useEventBus } from \"@burdenoff/fe-libs/shared/events\";\nimport { useNavigate } from \"react-router-dom\";\nimport {\n ArrowLeft,\n Globe,\n Hash,\n Lock,\n Megaphone,\n MessageCircle,\n Plus,\n Shield,\n Users,\n X,\n} from \"lucide-react\";\nimport { cn } from \"../utils/cn\";\nimport { PageLayout } from \"../components/PageLayout\";\nimport { useCreateConversation } from \"../hooks/useConversationQueries\";\nimport { useConversations } from \"../providers/ConversationsProvider\";\nimport type { ConversationType, ConversationVisibility } from \"../types\";\nimport { PermissionGuard } from \"../components/PermissionGuard\";\nimport { CONVERSATIONS_PERMISSIONS } from \"../constants/permissions\";\nimport { joinPath } from \"../utils/navigation\";\nimport { nativeImpact, nativeNotify } from \"../../utils/nativeBridge\";\n\n/** Extract the first meaningful GraphQL error message from an ApolloError. */\nfunction getErrorMessage(error: Error): string {\n if (\"graphQLErrors\" in error) {\n const gqlErrors = (error as { graphQLErrors?: Array<{ message?: string }> })\n .graphQLErrors;\n if (gqlErrors && gqlErrors.length > 0 && gqlErrors[0]?.message) {\n return gqlErrors[0].message;\n }\n }\n return error.message;\n}\n\ninterface ConversationTypeOption {\n type: ConversationType;\n label: string;\n description: string;\n icon: React.ReactNode;\n}\n\nexport function NewConversationPage() {\n const navigate = useNavigate();\n const { basePath } = useConversations();\n const { createConversation, loading, error } = useCreateConversation();\n const bus = useEventBus();\n const { t } = useI18n();\n const tr = (key: string, fallback: string): string => {\n const value = t(key);\n return value === key ? fallback : value;\n };\n\n const CONVERSATION_TYPES: ConversationTypeOption[] = useMemo(\n () => [\n {\n type: \"DIRECT\" as ConversationType,\n label: tr(\"conversations.directMessage\", \"Direct Message\"),\n description: tr(\n \"conversations.directMessageDescription\",\n \"One-on-one private conversation\",\n ),\n icon: <MessageCircle size={20} />,\n },\n {\n type: \"GROUP\" as ConversationType,\n label: tr(\"conversations.groupChat\", \"Group Chat\"),\n description: tr(\n \"conversations.groupChatDescription\",\n \"Invite multiple people to a group conversation\",\n ),\n icon: <Users size={20} />,\n },\n {\n type: \"CHANNEL\" as ConversationType,\n label: tr(\"conversations.channel\", \"Channel\"),\n description: tr(\n \"conversations.channelDescription\",\n \"A topic-based channel that anyone can join\",\n ),\n icon: <Hash size={20} />,\n },\n {\n type: \"ANNOUNCEMENT\" as ConversationType,\n label: tr(\"conversations.announcement\", \"Announcement\"),\n description: tr(\n \"conversations.announcementDescription\",\n \"Broadcast messages to a large audience\",\n ),\n icon: <Megaphone size={20} />,\n },\n ],\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [t],\n );\n\n const VISIBILITY_OPTIONS: Array<{\n value: ConversationVisibility;\n label: string;\n description: string;\n icon: React.ReactNode;\n }> = useMemo(\n () => [\n {\n value: \"PUBLIC\" as ConversationVisibility,\n label: tr(\"conversations.public\", \"Public\"),\n description: tr(\n \"conversations.publicDescription\",\n \"Anyone in the workspace can discover and join\",\n ),\n icon: <Globe size={16} />,\n },\n {\n value: \"PRIVATE\" as ConversationVisibility,\n label: tr(\"conversations.private\", \"Private\"),\n description: tr(\n \"conversations.privateDescription\",\n \"Only invited members can access\",\n ),\n icon: <Lock size={16} />,\n },\n {\n value: \"PROTECTED\" as ConversationVisibility,\n label: tr(\"conversations.protected\", \"Protected\"),\n description: tr(\n \"conversations.protectedDescription\",\n \"Discoverable but requires approval to join\",\n ),\n icon: <Shield size={16} />,\n },\n ],\n // eslint-disable-next-line react-hooks/exhaustive-deps\n [t],\n );\n\n const [selectedType, setSelectedType] = useState<ConversationType>(\"GROUP\");\n const [title, setTitle] = useState(\"\");\n const [description, setDescription] = useState(\"\");\n const [visibility, setVisibility] =\n useState<ConversationVisibility>(\"PRIVATE\");\n const [participantInput, setParticipantInput] = useState(\"\");\n const [participantIds, setParticipantIds] = useState<string[]>([]);\n\n const isDirectMessage = selectedType === \"DIRECT\";\n const needsTitle = !isDirectMessage;\n\n const addParticipant = useCallback(() => {\n const value = participantInput.trim();\n if (!value || participantIds.includes(value)) return;\n if (isDirectMessage) {\n setParticipantIds([value]);\n } else {\n setParticipantIds((prev) => [...prev, value]);\n }\n setParticipantInput(\"\");\n }, [participantInput, participantIds, isDirectMessage]);\n\n const removeParticipant = useCallback((participantId: string) => {\n setParticipantIds((prev) => prev.filter((id) => id !== participantId));\n }, []);\n\n const requiresParticipants =\n selectedType === \"DIRECT\" || selectedType === \"GROUP\";\n\n const handleCreate = useCallback(async () => {\n if (requiresParticipants && participantIds.length === 0) return;\n if (needsTitle && !title.trim()) return;\n\n nativeImpact(\"light\");\n try {\n const conversation = await createConversation({\n type: selectedType,\n title: needsTitle ? title.trim() : undefined,\n description: description.trim() || undefined,\n participantIds,\n visibility,\n });\n\n if (!conversation) {\n nativeNotify(\"error\");\n return;\n }\n nativeNotify(\"success\");\n bus.emit(\"conversations.conversation.created\", {\n conversationId: conversation.id,\n conversationType: selectedType,\n source: \"new-conversation-page\",\n });\n navigate(joinPath(basePath || \"/conversations\", conversation.id));\n } catch (error) {\n nativeNotify(\"error\");\n throw error;\n }\n }, [\n basePath,\n bus,\n requiresParticipants,\n participantIds,\n needsTitle,\n title,\n createConversation,\n selectedType,\n description,\n visibility,\n navigate,\n ]);\n\n const canCreate =\n (!requiresParticipants || participantIds.length > 0) &&\n (!needsTitle || title.trim().length > 0);\n\n return (\n <PermissionGuard\n permission={CONVERSATIONS_PERMISSIONS.CONVERSATION_CREATE}\n allowUnavailablePermissions\n >\n <PageLayout\n title={tr(\"conversations.newConversation\", \"New Conversation\")}\n subtitle={tr(\n \"conversations.newConversationSubtitle\",\n \"Start a new conversation with your team\",\n )}\n icon={<Plus size={24} />}\n maxWidth=\"lg\"\n headerActions={\n <button\n type=\"button\"\n onClick={() => navigate(basePath || \"/conversations\")}\n className=\"inline-flex items-center gap-2 px-3 py-2 text-sm font-medium rounded-lg text-muted-foreground hover:text-foreground hover:bg-muted/50 transition-colors\"\n >\n <ArrowLeft size={16} />\n {tr(\"conversations.back\", \"Back\")}\n </button>\n }\n >\n <section data-tour=\"conversation-type-selector\" className=\"space-y-3\">\n <h3 className=\"text-sm font-semibold text-foreground\">\n {tr(\"conversations.conversationType\", \"Conversation Type\")}\n </h3>\n <div className=\"grid grid-cols-1 sm:grid-cols-2 gap-3\">\n {CONVERSATION_TYPES.map((option) => (\n <button\n key={option.type}\n type=\"button\"\n onClick={() => {\n setSelectedType(option.type);\n if (option.type === \"DIRECT\") {\n setParticipantIds((prev) => prev.slice(0, 1));\n }\n }}\n className={cn(\n \"flex items-start gap-3 p-4 rounded-xl border text-left transition-all duration-150\",\n selectedType === option.type\n ? \"border-primary bg-primary/5 ring-1 ring-primary/20\"\n : \"border-border hover:border-muted-foreground/30 hover:bg-muted/30\",\n )}\n >\n <div\n className={cn(\n \"p-2 rounded-lg\",\n selectedType === option.type\n ? \"bg-primary/10 text-primary\"\n : \"bg-muted text-muted-foreground\",\n )}\n >\n {option.icon}\n </div>\n <div>\n <p className=\"text-sm font-medium text-foreground\">\n {option.label}\n </p>\n <p className=\"text-xs text-muted-foreground mt-0.5\">\n {option.description}\n </p>\n </div>\n </button>\n ))}\n </div>\n </section>\n\n {!isDirectMessage && (\n <section\n data-tour=\"conversation-visibility-selector\"\n className=\"space-y-3\"\n >\n <h3 className=\"text-sm font-semibold text-foreground\">\n {tr(\"conversations.visibility\", \"Visibility\")}\n </h3>\n <div className=\"flex gap-2\">\n {VISIBILITY_OPTIONS.map((option) => (\n <button\n key={option.value}\n type=\"button\"\n onClick={() => setVisibility(option.value)}\n className={cn(\n \"flex items-center gap-2 px-4 py-2.5 rounded-lg border text-sm transition-all\",\n visibility === option.value\n ? \"border-primary bg-primary/5 text-primary font-medium\"\n : \"border-border text-muted-foreground hover:text-foreground hover:bg-muted/30\",\n )}\n >\n {option.icon}\n {option.label}\n </button>\n ))}\n </div>\n <p className=\"text-xs text-muted-foreground\">\n {\n VISIBILITY_OPTIONS.find((option) => option.value === visibility)\n ?.description\n }\n </p>\n </section>\n )}\n\n {needsTitle && (\n <section\n data-tour=\"conversation-create-details\"\n className=\"space-y-3\"\n >\n <div>\n <label\n htmlFor=\"conv-title\"\n className=\"text-sm font-semibold text-foreground\"\n >\n {tr(\"conversations.name\", \"Name\")}{\" \"}\n <span className=\"text-destructive\">*</span>\n </label>\n <input\n id=\"conv-title\"\n type=\"text\"\n value={title}\n onChange={(event) => setTitle(event.target.value)}\n placeholder={\n selectedType === \"CHANNEL\"\n ? tr(\n \"conversations.channelNamePlaceholder\",\n \"e.g., engineering-general\",\n )\n : tr(\n \"conversations.conversationNamePlaceholder\",\n \"e.g., Product Launch Q1\",\n )\n }\n maxLength={200}\n className=\"mt-1.5 w-full px-3 py-2 text-sm rounded-lg border border-border bg-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring\"\n />\n </div>\n <div>\n <label\n htmlFor=\"conv-desc\"\n className=\"text-sm font-semibold text-foreground\"\n >\n {tr(\"conversations.description\", \"Description\")}\n </label>\n <textarea\n id=\"conv-desc\"\n value={description}\n onChange={(event) => setDescription(event.target.value)}\n placeholder={tr(\n \"conversations.descriptionPlaceholder\",\n \"What is this conversation about?\",\n )}\n rows={2}\n maxLength={1000}\n className=\"mt-1.5 w-full px-3 py-2 text-sm rounded-lg border border-border bg-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring resize-none\"\n />\n </div>\n </section>\n )}\n\n <section data-tour=\"conversation-participants\" className=\"space-y-3\">\n <h3 className=\"text-sm font-semibold text-foreground\">\n {isDirectMessage\n ? tr(\"conversations.participantId\", \"Participant ID\")\n : tr(\"conversations.participantIds\", \"Participant IDs\")}\n {!requiresParticipants && (\n <span className=\"ml-1.5 text-xs font-normal text-muted-foreground\">\n ({tr(\"conversations.optional\", \"optional\")})\n </span>\n )}\n </h3>\n\n {participantIds.length > 0 && (\n <div className=\"flex flex-wrap gap-2\">\n {participantIds.map((participantId) => (\n <span\n key={participantId}\n className=\"inline-flex items-center gap-1.5 px-2.5 py-1 rounded-full bg-primary/10 text-primary text-xs font-medium\"\n >\n {participantId}\n <button\n type=\"button\"\n onClick={() => removeParticipant(participantId)}\n className=\"p-0.5 rounded-full hover:bg-primary/20\"\n >\n <X size={12} />\n </button>\n </span>\n ))}\n </div>\n )}\n\n <div className=\"flex items-center gap-2\">\n <input\n type=\"text\"\n value={participantInput}\n onChange={(event) => setParticipantInput(event.target.value)}\n onKeyDown={(event) => {\n if (event.key !== \"Enter\") return;\n event.preventDefault();\n addParticipant();\n }}\n placeholder={tr(\n \"conversations.participantInputPlaceholder\",\n \"Enter user ID and press Enter\",\n )}\n className=\"w-full px-3 py-2 text-sm rounded-lg border border-border bg-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring\"\n />\n <button\n type=\"button\"\n onClick={addParticipant}\n className=\"px-3 py-2 text-sm rounded-lg border border-border hover:bg-muted/50\"\n >\n {tr(\"conversations.add\", \"Add\")}\n </button>\n </div>\n </section>\n\n {error && (\n <div className=\"text-xs text-destructive rounded-lg border border-destructive/30 bg-destructive/10 px-3 py-2\">\n {getErrorMessage(error)}\n </div>\n )}\n\n <div className=\"flex justify-end gap-3 pt-4 border-t border-border\">\n <button\n type=\"button\"\n onClick={() => navigate(\"..\")}\n className=\"px-4 py-2 text-sm font-medium rounded-lg text-muted-foreground hover:text-foreground hover:bg-muted/50 transition-colors\"\n >\n {tr(\"conversations.cancel\", \"Cancel\")}\n </button>\n <button\n type=\"button\"\n onClick={() => void handleCreate()}\n disabled={!canCreate || loading}\n className={cn(\n \"inline-flex items-center gap-2 px-5 py-2 text-sm font-medium rounded-lg transition-all\",\n canCreate && !loading\n ? \"bg-primary text-primary-foreground hover:bg-primary/90 shadow-sm\"\n : \"bg-muted text-muted-foreground cursor-not-allowed\",\n )}\n >\n {loading\n ? tr(\"conversations.creating\", \"Creating...\")\n : tr(\"conversations.createConversation\", \"Create Conversation\")}\n </button>\n </div>\n </PageLayout>\n </PermissionGuard>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;AA2BA,SAAS,GAAgB,GAAsB;AAC7C,KAAI,mBAAmB,GAAO;EAC5B,IAAM,IAAa,EAChB;AACH,MAAI,KAAa,EAAU,SAAS,KAAK,EAAU,IAAI,QACrD,QAAO,EAAU,GAAG;;AAGxB,QAAO,EAAM;;AAUf,SAAgB,IAAsB;CACpC,IAAM,IAAW,GAAa,EACxB,EAAE,gBAAa,GAAkB,EACjC,EAAE,uBAAoB,YAAS,aAAU,IAAuB,EAChE,IAAM,GAAa,EACnB,EAAE,SAAM,GAAS,EACjB,KAAM,GAAa,MAA6B;EACpD,IAAM,IAAQ,EAAE,EAAI;AACpB,SAAO,MAAU,IAAM,IAAW;IAG9B,IAA+C,QAC7C;EACJ;GACE,MAAM;GACN,OAAO,EAAG,+BAA+B,iBAAiB;GAC1D,aAAa,EACX,0CACA,kCACD;GACD,MAAM,kBAAC,GAAD,EAAe,MAAM,IAAM,CAAA;GAClC;EACD;GACE,MAAM;GACN,OAAO,EAAG,2BAA2B,aAAa;GAClD,aAAa,EACX,sCACA,iDACD;GACD,MAAM,kBAAC,GAAD,EAAO,MAAM,IAAM,CAAA;GAC1B;EACD;GACE,MAAM;GACN,OAAO,EAAG,yBAAyB,UAAU;GAC7C,aAAa,EACX,oCACA,6CACD;GACD,MAAM,kBAAC,GAAD,EAAM,MAAM,IAAM,CAAA;GACzB;EACD;GACE,MAAM;GACN,OAAO,EAAG,8BAA8B,eAAe;GACvD,aAAa,EACX,yCACA,yCACD;GACD,MAAM,kBAAC,GAAD,EAAW,MAAM,IAAM,CAAA;GAC9B;EACF,EAED,CAAC,EAAE,CACJ,EAEK,IAKD,QACG;EACJ;GACE,OAAO;GACP,OAAO,EAAG,wBAAwB,SAAS;GAC3C,aAAa,EACX,mCACA,gDACD;GACD,MAAM,kBAAC,GAAD,EAAO,MAAM,IAAM,CAAA;GAC1B;EACD;GACE,OAAO;GACP,OAAO,EAAG,yBAAyB,UAAU;GAC7C,aAAa,EACX,oCACA,kCACD;GACD,MAAM,kBAAC,GAAD,EAAM,MAAM,IAAM,CAAA;GACzB;EACD;GACE,OAAO;GACP,OAAO,EAAG,2BAA2B,YAAY;GACjD,aAAa,EACX,sCACA,6CACD;GACD,MAAM,kBAAC,GAAD,EAAQ,MAAM,IAAM,CAAA;GAC3B;EACF,EAED,CAAC,EAAE,CACJ,EAEK,CAAC,GAAc,KAAmB,EAA2B,QAAQ,EACrE,CAAC,GAAO,KAAY,EAAS,GAAG,EAChC,CAAC,GAAa,KAAkB,EAAS,GAAG,EAC5C,CAAC,GAAY,KACjB,EAAiC,UAAU,EACvC,CAAC,GAAkB,KAAuB,EAAS,GAAG,EACtD,CAAC,GAAgB,KAAqB,EAAmB,EAAE,CAAC,EAE5D,IAAkB,MAAiB,UACnC,IAAa,CAAC,GAEd,IAAiB,QAAkB;EACvC,IAAM,IAAQ,EAAiB,MAAM;AACjC,GAAC,KAAS,EAAe,SAAS,EAAM,KAE1C,EADE,IACgB,CAAC,EAAM,IAEN,MAAS,CAAC,GAAG,GAAM,EAAM,CAAC,EAE/C,EAAoB,GAAG;IACtB;EAAC;EAAkB;EAAgB;EAAgB,CAAC,EAEjD,IAAoB,GAAa,MAA0B;AAC/D,KAAmB,MAAS,EAAK,QAAQ,MAAO,MAAO,EAAc,CAAC;IACrE,EAAE,CAAC,EAEA,IACJ,MAAiB,YAAY,MAAiB,SAE1C,KAAe,EAAY,YAAY;AACvC,aAAwB,EAAe,WAAW,MAClD,OAAc,CAAC,EAAM,MAAM,GAE/B;KAAa,QAAQ;AACrB,OAAI;IACF,IAAM,IAAe,MAAM,EAAmB;KAC5C,MAAM;KACN,OAAO,IAAa,EAAM,MAAM,GAAG,KAAA;KACnC,aAAa,EAAY,MAAM,IAAI,KAAA;KACnC;KACA;KACD,CAAC;AAEF,QAAI,CAAC,GAAc;AACjB,OAAa,QAAQ;AACrB;;AAQF,IANA,EAAa,UAAU,EACvB,EAAI,KAAK,sCAAsC;KAC7C,gBAAgB,EAAa;KAC7B,kBAAkB;KAClB,QAAQ;KACT,CAAC,EACF,EAAS,EAAS,KAAY,kBAAkB,EAAa,GAAG,CAAC;YAC1D,GAAO;AAEd,UADA,EAAa,QAAQ,EACf;;;IAEP;EACD;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD,CAAC,EAEI,KACH,CAAC,KAAwB,EAAe,SAAS,OACjD,CAAC,KAAc,EAAM,MAAM,CAAC,SAAS;AAExC,QACE,kBAAC,GAAD;EACE,YAAY,EAA0B;EACtC,6BAAA;YAEA,kBAAC,GAAD;GACE,OAAO,EAAG,iCAAiC,mBAAmB;GAC9D,UAAU,EACR,yCACA,0CACD;GACD,MAAM,kBAAC,GAAD,EAAM,MAAM,IAAM,CAAA;GACxB,UAAS;GACT,eACE,kBAAC,UAAD;IACE,MAAK;IACL,eAAe,EAAS,KAAY,iBAAiB;IACrD,WAAU;cAHZ,CAKE,kBAAC,GAAD,EAAW,MAAM,IAAM,CAAA,EACtB,EAAG,sBAAsB,OAAO,CAC1B;;aAhBb;IAmBE,kBAAC,WAAD;KAAS,aAAU;KAA6B,WAAU;eAA1D,CACE,kBAAC,MAAD;MAAI,WAAU;gBACX,EAAG,kCAAkC,oBAAoB;MACvD,CAAA,EACL,kBAAC,OAAD;MAAK,WAAU;gBACZ,EAAmB,KAAK,MACvB,kBAAC,UAAD;OAEE,MAAK;OACL,eAAe;AAEb,QADA,EAAgB,EAAO,KAAK,EACxB,EAAO,SAAS,YAClB,GAAmB,MAAS,EAAK,MAAM,GAAG,EAAE,CAAC;;OAGjD,WAAW,EACT,sFACA,MAAiB,EAAO,OACpB,uDACA,mEACL;iBAdH,CAgBE,kBAAC,OAAD;QACE,WAAW,EACT,kBACA,MAAiB,EAAO,OACpB,+BACA,iCACL;kBAEA,EAAO;QACJ,CAAA,EACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,KAAD;QAAG,WAAU;kBACV,EAAO;QACN,CAAA,EACJ,kBAAC,KAAD;QAAG,WAAU;kBACV,EAAO;QACN,CAAA,CACA,EAAA,CAAA,CACC;SAjCF,EAAO,KAiCL,CACT;MACE,CAAA,CACE;;IAET,CAAC,KACA,kBAAC,WAAD;KACE,aAAU;KACV,WAAU;eAFZ;MAIE,kBAAC,MAAD;OAAI,WAAU;iBACX,EAAG,4BAA4B,aAAa;OAC1C,CAAA;MACL,kBAAC,OAAD;OAAK,WAAU;iBACZ,EAAmB,KAAK,MACvB,kBAAC,UAAD;QAEE,MAAK;QACL,eAAe,EAAc,EAAO,MAAM;QAC1C,WAAW,EACT,gFACA,MAAe,EAAO,QAClB,yDACA,8EACL;kBATH,CAWG,EAAO,MACP,EAAO,MACD;UAZF,EAAO,MAYL,CACT;OACE,CAAA;MACN,kBAAC,KAAD;OAAG,WAAU;iBAET,EAAmB,MAAM,MAAW,EAAO,UAAU,EAAW,EAC5D;OAEJ,CAAA;MACI;;IAGX,KACC,kBAAC,WAAD;KACE,aAAU;KACV,WAAU;eAFZ,CAIE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;MACE,SAAQ;MACR,WAAU;gBAFZ;OAIG,EAAG,sBAAsB,OAAO;OAAE;OACnC,kBAAC,QAAD;QAAM,WAAU;kBAAmB;QAAQ,CAAA;OACrC;SACR,kBAAC,SAAD;MACE,IAAG;MACH,MAAK;MACL,OAAO;MACP,WAAW,MAAU,EAAS,EAAM,OAAO,MAAM;MACjD,aACE,MAAiB,YACb,EACE,wCACA,4BACD,GACD,EACE,6CACA,0BACD;MAEP,WAAW;MACX,WAAU;MACV,CAAA,CACE,EAAA,CAAA,EACN,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,SAAD;MACE,SAAQ;MACR,WAAU;gBAET,EAAG,6BAA6B,cAAc;MACzC,CAAA,EACR,kBAAC,YAAD;MACE,IAAG;MACH,OAAO;MACP,WAAW,MAAU,EAAe,EAAM,OAAO,MAAM;MACvD,aAAa,EACX,wCACA,mCACD;MACD,MAAM;MACN,WAAW;MACX,WAAU;MACV,CAAA,CACE,EAAA,CAAA,CACE;;IAGZ,kBAAC,WAAD;KAAS,aAAU;KAA4B,WAAU;eAAzD;MACE,kBAAC,MAAD;OAAI,WAAU;iBAAd,CACG,IACG,EAAG,+BAA+B,iBAAiB,GACnD,EAAG,gCAAgC,kBAAkB,EACxD,CAAC,KACA,kBAAC,QAAD;QAAM,WAAU;kBAAhB;SAAmE;SAC/D,EAAG,0BAA0B,WAAW;SAAC;SACtC;UAEN;;MAEJ,EAAe,SAAS,KACvB,kBAAC,OAAD;OAAK,WAAU;iBACZ,EAAe,KAAK,MACnB,kBAAC,QAAD;QAEE,WAAU;kBAFZ,CAIG,GACD,kBAAC,UAAD;SACE,MAAK;SACL,eAAe,EAAkB,EAAc;SAC/C,WAAU;mBAEV,kBAAC,GAAD,EAAG,MAAM,IAAM,CAAA;SACR,CAAA,CACJ;UAXA,EAWA,CACP;OACE,CAAA;MAGR,kBAAC,OAAD;OAAK,WAAU;iBAAf,CACE,kBAAC,SAAD;QACE,MAAK;QACL,OAAO;QACP,WAAW,MAAU,EAAoB,EAAM,OAAO,MAAM;QAC5D,YAAY,MAAU;AAChB,WAAM,QAAQ,YAClB,EAAM,gBAAgB,EACtB,GAAgB;;QAElB,aAAa,EACX,6CACA,gCACD;QACD,WAAU;QACV,CAAA,EACF,kBAAC,UAAD;QACE,MAAK;QACL,SAAS;QACT,WAAU;kBAET,EAAG,qBAAqB,MAAM;QACxB,CAAA,CACL;;MACE;;IAET,KACC,kBAAC,OAAD;KAAK,WAAU;eACZ,GAAgB,EAAM;KACnB,CAAA;IAGR,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,UAAD;MACE,MAAK;MACL,eAAe,EAAS,KAAK;MAC7B,WAAU;gBAET,EAAG,wBAAwB,SAAS;MAC9B,CAAA,EACT,kBAAC,UAAD;MACE,MAAK;MACL,eAAe,KAAK,IAAc;MAClC,UAAU,CAAC,KAAa;MACxB,WAAW,EACT,0FACA,KAAa,CAAC,IACV,qEACA,oDACL;gBAEA,IACG,EAAG,0BAA0B,cAAc,GAC3C,EAAG,oCAAoC,sBAAsB;MAC1D,CAAA,CACL;;IACK;;EACG,CAAA"}
1
+ {"version":3,"file":"NewConversationPage.js","names":[],"sources":["../../../src/conversations/pages/NewConversationPage.tsx"],"sourcesContent":["import { useState, useCallback, useMemo } from \"react\";\nimport { useTr } from \"../utils/i18n\";\nimport { useEventBus } from \"@burdenoff/fe-libs/shared/events\";\nimport { useNavigate } from \"react-router-dom\";\nimport {\n ArrowLeft,\n Globe,\n Hash,\n Lock,\n Megaphone,\n MessageCircle,\n Plus,\n Shield,\n Users,\n X,\n} from \"lucide-react\";\nimport { cn } from \"../utils/cn\";\nimport { PageLayout } from \"../components/PageLayout\";\nimport { useCreateConversation } from \"../hooks/useConversationQueries\";\nimport { useConversations } from \"../providers/ConversationsProvider\";\nimport type { ConversationType, ConversationVisibility } from \"../types\";\nimport { PermissionGuard } from \"../components/PermissionGuard\";\nimport { CONVERSATIONS_PERMISSIONS } from \"../constants/permissions\";\nimport { joinPath } from \"../utils/navigation\";\nimport { nativeImpact, nativeNotify } from \"../../utils/nativeBridge\";\n\n/** Extract the first meaningful GraphQL error message from an ApolloError. */\nfunction getErrorMessage(error: Error): string {\n if (\"graphQLErrors\" in error) {\n const gqlErrors = (error as { graphQLErrors?: Array<{ message?: string }> })\n .graphQLErrors;\n if (gqlErrors && gqlErrors.length > 0 && gqlErrors[0]?.message) {\n return gqlErrors[0].message;\n }\n }\n return error.message;\n}\n\ninterface ConversationTypeOption {\n type: ConversationType;\n label: string;\n description: string;\n icon: React.ReactNode;\n}\n\nexport function NewConversationPage() {\n const navigate = useNavigate();\n const { basePath } = useConversations();\n const { createConversation, loading, error } = useCreateConversation();\n const bus = useEventBus();\n const tr = useTr();\n\n const CONVERSATION_TYPES: ConversationTypeOption[] = useMemo(\n () => [\n {\n type: \"DIRECT\" as ConversationType,\n label: tr(\"conversations.directMessage\", \"Direct Message\"),\n description: tr(\n \"conversations.directMessageDescription\",\n \"One-on-one private conversation\",\n ),\n icon: <MessageCircle size={20} />,\n },\n {\n type: \"GROUP\" as ConversationType,\n label: tr(\"conversations.groupChat\", \"Group Chat\"),\n description: tr(\n \"conversations.groupChatDescription\",\n \"Invite multiple people to a group conversation\",\n ),\n icon: <Users size={20} />,\n },\n {\n type: \"CHANNEL\" as ConversationType,\n label: tr(\"conversations.channel\", \"Channel\"),\n description: tr(\n \"conversations.channelDescription\",\n \"A topic-based channel that anyone can join\",\n ),\n icon: <Hash size={20} />,\n },\n {\n type: \"ANNOUNCEMENT\" as ConversationType,\n label: tr(\"conversations.announcement\", \"Announcement\"),\n description: tr(\n \"conversations.announcementDescription\",\n \"Broadcast messages to a large audience\",\n ),\n icon: <Megaphone size={20} />,\n },\n ],\n [tr],\n );\n\n const VISIBILITY_OPTIONS: Array<{\n value: ConversationVisibility;\n label: string;\n description: string;\n icon: React.ReactNode;\n }> = useMemo(\n () => [\n {\n value: \"PUBLIC\" as ConversationVisibility,\n label: tr(\"conversations.public\", \"Public\"),\n description: tr(\n \"conversations.publicDescription\",\n \"Anyone in the workspace can discover and join\",\n ),\n icon: <Globe size={16} />,\n },\n {\n value: \"PRIVATE\" as ConversationVisibility,\n label: tr(\"conversations.private\", \"Private\"),\n description: tr(\n \"conversations.privateDescription\",\n \"Only invited members can access\",\n ),\n icon: <Lock size={16} />,\n },\n {\n value: \"PROTECTED\" as ConversationVisibility,\n label: tr(\"conversations.protected\", \"Protected\"),\n description: tr(\n \"conversations.protectedDescription\",\n \"Discoverable but requires approval to join\",\n ),\n icon: <Shield size={16} />,\n },\n ],\n [tr],\n );\n\n const [selectedType, setSelectedType] = useState<ConversationType>(\"GROUP\");\n const [title, setTitle] = useState(\"\");\n const [description, setDescription] = useState(\"\");\n const [visibility, setVisibility] =\n useState<ConversationVisibility>(\"PRIVATE\");\n const [participantInput, setParticipantInput] = useState(\"\");\n const [participantIds, setParticipantIds] = useState<string[]>([]);\n\n const isDirectMessage = selectedType === \"DIRECT\";\n const needsTitle = !isDirectMessage;\n\n const addParticipant = useCallback(() => {\n const value = participantInput.trim();\n if (!value || participantIds.includes(value)) return;\n if (isDirectMessage) {\n setParticipantIds([value]);\n } else {\n setParticipantIds((prev) => [...prev, value]);\n }\n setParticipantInput(\"\");\n }, [participantInput, participantIds, isDirectMessage]);\n\n const removeParticipant = useCallback((participantId: string) => {\n setParticipantIds((prev) => prev.filter((id) => id !== participantId));\n }, []);\n\n const requiresParticipants =\n selectedType === \"DIRECT\" || selectedType === \"GROUP\";\n\n const handleCreate = useCallback(async () => {\n if (requiresParticipants && participantIds.length === 0) return;\n if (needsTitle && !title.trim()) return;\n\n nativeImpact(\"light\");\n try {\n const conversation = await createConversation({\n type: selectedType,\n title: needsTitle ? title.trim() : undefined,\n description: description.trim() || undefined,\n participantIds,\n visibility,\n });\n\n if (!conversation) {\n nativeNotify(\"error\");\n return;\n }\n nativeNotify(\"success\");\n bus.emit(\"conversations.conversation.created\", {\n conversationId: conversation.id,\n conversationType: selectedType,\n source: \"new-conversation-page\",\n });\n navigate(joinPath(basePath || \"/conversations\", conversation.id));\n } catch (error) {\n nativeNotify(\"error\");\n throw error;\n }\n }, [\n basePath,\n bus,\n requiresParticipants,\n participantIds,\n needsTitle,\n title,\n createConversation,\n selectedType,\n description,\n visibility,\n navigate,\n ]);\n\n const canCreate =\n (!requiresParticipants || participantIds.length > 0) &&\n (!needsTitle || title.trim().length > 0);\n\n return (\n <PermissionGuard\n permission={CONVERSATIONS_PERMISSIONS.CONVERSATION_CREATE}\n allowUnavailablePermissions\n >\n <PageLayout\n title={tr(\"conversations.newConversation\", \"New Conversation\")}\n subtitle={tr(\n \"conversations.newConversationSubtitle\",\n \"Start a new conversation with your team\",\n )}\n icon={<Plus size={24} />}\n maxWidth=\"lg\"\n headerActions={\n <button\n type=\"button\"\n onClick={() => navigate(basePath || \"/conversations\")}\n className=\"inline-flex items-center gap-2 px-3 py-2 text-sm font-medium rounded-lg text-muted-foreground hover:text-foreground hover:bg-muted/50 transition-colors\"\n >\n <ArrowLeft size={16} />\n {tr(\"conversations.back\", \"Back\")}\n </button>\n }\n >\n <section data-tour=\"conversation-type-selector\" className=\"space-y-3\">\n <h3 className=\"text-sm font-semibold text-foreground\">\n {tr(\"conversations.conversationType\", \"Conversation Type\")}\n </h3>\n <div className=\"grid grid-cols-1 sm:grid-cols-2 gap-3\">\n {CONVERSATION_TYPES.map((option) => (\n <button\n key={option.type}\n type=\"button\"\n onClick={() => {\n setSelectedType(option.type);\n if (option.type === \"DIRECT\") {\n setParticipantIds((prev) => prev.slice(0, 1));\n }\n }}\n className={cn(\n \"flex items-start gap-3 p-4 rounded-xl border text-left transition-all duration-150\",\n selectedType === option.type\n ? \"border-primary bg-primary/5 ring-1 ring-primary/20\"\n : \"border-border hover:border-muted-foreground/30 hover:bg-muted/30\",\n )}\n >\n <div\n className={cn(\n \"p-2 rounded-lg\",\n selectedType === option.type\n ? \"bg-primary/10 text-primary\"\n : \"bg-muted text-muted-foreground\",\n )}\n >\n {option.icon}\n </div>\n <div>\n <p className=\"text-sm font-medium text-foreground\">\n {option.label}\n </p>\n <p className=\"text-xs text-muted-foreground mt-0.5\">\n {option.description}\n </p>\n </div>\n </button>\n ))}\n </div>\n </section>\n\n {!isDirectMessage && (\n <section\n data-tour=\"conversation-visibility-selector\"\n className=\"space-y-3\"\n >\n <h3 className=\"text-sm font-semibold text-foreground\">\n {tr(\"conversations.visibility\", \"Visibility\")}\n </h3>\n <div className=\"flex gap-2\">\n {VISIBILITY_OPTIONS.map((option) => (\n <button\n key={option.value}\n type=\"button\"\n onClick={() => setVisibility(option.value)}\n className={cn(\n \"flex items-center gap-2 px-4 py-2.5 rounded-lg border text-sm transition-all\",\n visibility === option.value\n ? \"border-primary bg-primary/5 text-primary font-medium\"\n : \"border-border text-muted-foreground hover:text-foreground hover:bg-muted/30\",\n )}\n >\n {option.icon}\n {option.label}\n </button>\n ))}\n </div>\n <p className=\"text-xs text-muted-foreground\">\n {\n VISIBILITY_OPTIONS.find((option) => option.value === visibility)\n ?.description\n }\n </p>\n </section>\n )}\n\n {needsTitle && (\n <section\n data-tour=\"conversation-create-details\"\n className=\"space-y-3\"\n >\n <div>\n <label\n htmlFor=\"conv-title\"\n className=\"text-sm font-semibold text-foreground\"\n >\n {tr(\"conversations.name\", \"Name\")}{\" \"}\n <span className=\"text-destructive\">*</span>\n </label>\n <input\n id=\"conv-title\"\n type=\"text\"\n value={title}\n onChange={(event) => setTitle(event.target.value)}\n placeholder={\n selectedType === \"CHANNEL\"\n ? tr(\n \"conversations.channelNamePlaceholder\",\n \"e.g., engineering-general\",\n )\n : tr(\n \"conversations.conversationNamePlaceholder\",\n \"e.g., Product Launch Q1\",\n )\n }\n maxLength={200}\n className=\"mt-1.5 w-full px-3 py-2 text-sm rounded-lg border border-border bg-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring\"\n />\n </div>\n <div>\n <label\n htmlFor=\"conv-desc\"\n className=\"text-sm font-semibold text-foreground\"\n >\n {tr(\"conversations.description\", \"Description\")}\n </label>\n <textarea\n id=\"conv-desc\"\n value={description}\n onChange={(event) => setDescription(event.target.value)}\n placeholder={tr(\n \"conversations.descriptionPlaceholder\",\n \"What is this conversation about?\",\n )}\n rows={2}\n maxLength={1000}\n className=\"mt-1.5 w-full px-3 py-2 text-sm rounded-lg border border-border bg-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring resize-none\"\n />\n </div>\n </section>\n )}\n\n <section data-tour=\"conversation-participants\" className=\"space-y-3\">\n <h3 className=\"text-sm font-semibold text-foreground\">\n {isDirectMessage\n ? tr(\"conversations.participantId\", \"Participant ID\")\n : tr(\"conversations.participantIds\", \"Participant IDs\")}\n {!requiresParticipants && (\n <span className=\"ml-1.5 text-xs font-normal text-muted-foreground\">\n ({tr(\"conversations.optional\", \"optional\")})\n </span>\n )}\n </h3>\n\n {participantIds.length > 0 && (\n <div className=\"flex flex-wrap gap-2\">\n {participantIds.map((participantId) => (\n <span\n key={participantId}\n className=\"inline-flex items-center gap-1.5 px-2.5 py-1 rounded-full bg-primary/10 text-primary text-xs font-medium\"\n >\n {participantId}\n <button\n type=\"button\"\n aria-label={tr(\n \"conversations.removeParticipant\",\n \"Remove participant\",\n )}\n onClick={() => removeParticipant(participantId)}\n className=\"p-0.5 rounded-full hover:bg-primary/20\"\n >\n <X size={12} />\n </button>\n </span>\n ))}\n </div>\n )}\n\n <div className=\"flex items-center gap-2\">\n <input\n type=\"text\"\n aria-label={tr(\n \"conversations.participantInputLabel\",\n \"Add participant\",\n )}\n value={participantInput}\n onChange={(event) => setParticipantInput(event.target.value)}\n onKeyDown={(event) => {\n if (event.key !== \"Enter\") return;\n event.preventDefault();\n addParticipant();\n }}\n placeholder={tr(\n \"conversations.participantInputPlaceholder\",\n \"Enter user ID and press Enter\",\n )}\n className=\"w-full px-3 py-2 text-sm rounded-lg border border-border bg-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring\"\n />\n <button\n type=\"button\"\n onClick={addParticipant}\n className=\"px-3 py-2 text-sm rounded-lg border border-border hover:bg-muted/50\"\n >\n {tr(\"conversations.add\", \"Add\")}\n </button>\n </div>\n </section>\n\n {error && (\n <div className=\"text-xs text-destructive rounded-lg border border-destructive/30 bg-destructive/10 px-3 py-2\">\n {getErrorMessage(error)}\n </div>\n )}\n\n <div className=\"flex justify-end gap-3 pt-4 border-t border-border\">\n <button\n type=\"button\"\n onClick={() => navigate(\"..\")}\n className=\"px-4 py-2 text-sm font-medium rounded-lg text-muted-foreground hover:text-foreground hover:bg-muted/50 transition-colors\"\n >\n {tr(\"conversations.cancel\", \"Cancel\")}\n </button>\n <button\n type=\"button\"\n onClick={() => void handleCreate()}\n disabled={!canCreate || loading}\n className={cn(\n \"inline-flex items-center gap-2 px-5 py-2 text-sm font-medium rounded-lg transition-all\",\n canCreate && !loading\n ? \"bg-primary text-primary-foreground hover:bg-primary/90 shadow-sm\"\n : \"bg-muted text-muted-foreground cursor-not-allowed\",\n )}\n >\n {loading\n ? tr(\"conversations.creating\", \"Creating...\")\n : tr(\"conversations.createConversation\", \"Create Conversation\")}\n </button>\n </div>\n </PageLayout>\n </PermissionGuard>\n );\n}\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import { createContext as e, useContext as t, useMemo as n, useRef as r } from "react";
1
+ import { createContext as e, use as t, useMemo as n, useRef as r } from "react";
2
2
  import { MemoryRouter as i } from "react-router-dom";
3
3
  import { QueryClient as a, QueryClientProvider as o } from "@tanstack/react-query";
4
4
  import { ApolloClient as s, HttpLink as c, InMemoryCache as l, Observable as u, from as d } from "@apollo/client";
@@ -1 +1 @@
1
- {"version":3,"file":"ConversationsProvider.js","names":[],"sources":["../../../src/conversations/providers/ConversationsProvider.tsx"],"sourcesContent":["import type { FC, PropsWithChildren } from \"react\";\nimport { createContext, useContext, useMemo, useRef } from \"react\";\nimport { MemoryRouter } from \"react-router-dom\";\nimport { QueryClient, QueryClientProvider } from \"@tanstack/react-query\";\nimport {\n ApolloClient,\n InMemoryCache,\n HttpLink,\n from,\n Observable,\n} from \"@apollo/client\";\nimport { onError } from \"@apollo/client/link/error\";\nimport { ApolloProvider, useApolloClient } from \"@apollo/client/react\";\nimport { PermissionProvider } from \"@burdenoff/fe-libs/shared/providers/shell\";\nimport {\n isAuthNetworkError,\n redirectToLogin,\n} from \"@burdenoff/fe-libs/shared/utils\";\nimport { useEvent } from \"@burdenoff/fe-libs/shared/events\";\nimport type { ConversationsRootProps } from \"../types\";\n\n/**\n * Clears Apollo cache on auth.signed_out so stale conversation data\n * is not retained across sessions. Must render inside ApolloProvider.\n */\nfunction AuthCacheClearer() {\n const client = useApolloClient();\n useEvent(\"auth.signed_out\", () => {\n void client.clearStore();\n });\n return null;\n}\n\nfunction hasSessionAuthErrors(\n errors: ReadonlyArray<{\n message?: string;\n extensions?: Record<string, unknown>;\n }> = [],\n): boolean {\n return errors.some((error) => {\n const code =\n typeof error.extensions?.code === \"string\" ? error.extensions.code : \"\";\n const errorCode =\n typeof error.extensions?.errorCode === \"string\"\n ? error.extensions.errorCode\n : \"\";\n const message = (error.message ?? \"\").toLowerCase();\n\n return (\n code === \"UNAUTHENTICATED\" ||\n errorCode === \"TOKEN_EXPIRED\" ||\n message.includes(\"token_expired\") ||\n message.includes(\"token expired\") ||\n message.includes(\"session expired\") ||\n message.includes(\"unauthorized\") ||\n message.includes(\"unauthenticated\") ||\n message.includes(\"authentication required\") ||\n message.includes(\"workspace context missing\")\n );\n });\n}\n\n/**\n * Context value for Conversations microfrontend\n */\ntype ConversationsContextValue = ConversationsRootProps;\n\nconst ConversationsContext = createContext<ConversationsContextValue | null>(\n null,\n);\n\n/**\n * Hook to access Conversations context\n */\nexport const useConversations = () => {\n const context = useContext(ConversationsContext);\n if (!context) {\n throw new Error(\n \"useConversations must be used within ConversationsProvider\",\n );\n }\n return context;\n};\n\n/**\n * Provider component for Conversations microfrontend\n * Sets up routing, context, Apollo Client, and React Query client\n */\nexport const ConversationsProvider: FC<\n PropsWithChildren<ConversationsRootProps>\n> = ({\n children,\n basePath,\n navigate,\n currentUser,\n workspaceId,\n apiGatewayUrl,\n authToken,\n workspaceToken,\n getWorkspaceToken,\n onConversationSelect,\n onMessageSend,\n defaultView,\n variant,\n}) => {\n const contextValue: ConversationsContextValue = {\n basePath,\n navigate,\n currentUser,\n workspaceId,\n apiGatewayUrl,\n authToken,\n workspaceToken,\n getWorkspaceToken,\n onConversationSelect,\n onMessageSend,\n defaultView,\n variant,\n };\n\n const isRefreshingRef = useRef(false);\n const getWorkspaceTokenRef = useRef(getWorkspaceToken);\n getWorkspaceTokenRef.current = getWorkspaceToken;\n // PermissionProvider requires the shell's AuthProvider — only mount it in integrated mode.\n const isIntegrated = !!basePath;\n\n // Create QueryClient instance (memoized to avoid recreation on re-renders)\n const queryClient = useMemo(\n () =>\n new QueryClient({\n defaultOptions: {\n queries: {\n retry: 1,\n refetchOnWindowFocus: false,\n staleTime: 5 * 60 * 1000, // 5 minutes\n },\n },\n }),\n [],\n );\n\n // Create ApolloClient instance (memoized, recreated when tokens change)\n // The workspace gateway requires dual-token auth:\n // Authorization: Bearer <authToken> (platform identity)\n // X-Workspace-Authorization: Bearer <workspaceToken> (workspace access)\n const apolloClient = useMemo(() => {\n if (isIntegrated) return null;\n\n const isLocalDirectMode =\n typeof window !== \"undefined\" &&\n (window.location.hostname === \"localhost\" ||\n window.location.hostname.endsWith(\".local.burdenoff.com\"));\n const workspaceEndpoint = isLocalDirectMode\n ? `${window.location.origin}/__local/conversations/graphql`\n : apiGatewayUrl\n ? `${apiGatewayUrl}/workspaces/graphql`\n : \"/workspaces/graphql\";\n\n const headers: Record<string, string> = {};\n if (authToken) {\n headers[\"Authorization\"] = `Bearer ${authToken}`;\n }\n if (workspaceToken) {\n headers[\"X-Workspace-Authorization\"] = `Bearer ${workspaceToken}`;\n }\n if (workspaceId) {\n headers[\"x-workspace-id\"] = workspaceId;\n }\n if (isLocalDirectMode && currentUser?.id) {\n headers[\"x-actor-id\"] = currentUser.id;\n headers[\"x-actor-type\"] = \"user\";\n }\n\n const httpLink = new HttpLink({\n uri: workspaceEndpoint,\n headers: Object.keys(headers).length > 0 ? headers : undefined,\n });\n\n const errorLink = onError(({ error, operation, forward }) => {\n if (!error) return;\n\n const errors =\n \"errors\" in error && Array.isArray(error.errors) ? error.errors : [];\n const hasAuthError = hasSessionAuthErrors(errors);\n\n if (\n !getWorkspaceTokenRef.current ||\n !hasAuthError ||\n isRefreshingRef.current\n ) {\n if (error instanceof Error && isAuthNetworkError(error)) {\n redirectToLogin();\n }\n return;\n }\n\n return new Observable((observer) => {\n isRefreshingRef.current = true;\n getWorkspaceTokenRef\n .current?.()\n .then((freshToken) => {\n isRefreshingRef.current = false;\n if (!freshToken) {\n redirectToLogin();\n observer.error(new Error(\"Failed to refresh workspace token\"));\n return;\n }\n\n operation.setContext(\n ({\n headers: existingHeaders = {},\n }: {\n headers?: Record<string, string>;\n }) => ({\n headers: {\n ...existingHeaders,\n \"X-Workspace-Authorization\": `Bearer ${freshToken}`,\n ...(workspaceId ? { \"x-workspace-id\": workspaceId } : {}),\n },\n }),\n );\n\n const subscriber = forward(operation).subscribe(observer);\n return () => subscriber.unsubscribe();\n })\n .catch((refreshError: unknown) => {\n isRefreshingRef.current = false;\n observer.error(refreshError);\n });\n });\n });\n\n return new ApolloClient({\n link: from([errorLink, httpLink]),\n cache: new InMemoryCache(),\n defaultOptions: {\n watchQuery: {\n fetchPolicy: \"cache-and-network\",\n },\n },\n });\n }, [\n apiGatewayUrl,\n authToken,\n currentUser?.id,\n isIntegrated,\n workspaceId,\n workspaceToken,\n ]);\n\n // Wrap content with QueryClientProvider and ConversationsContext.Provider\n const content = (\n <QueryClientProvider client={queryClient}>\n <ConversationsContext.Provider value={contextValue}>\n {isIntegrated ? (\n <PermissionProvider scopeId={workspaceId ?? null} gateway=\"workspace\">\n <AuthCacheClearer />\n {children}\n </PermissionProvider>\n ) : (\n <>\n <AuthCacheClearer />\n {children}\n </>\n )}\n </ConversationsContext.Provider>\n </QueryClientProvider>\n );\n\n // When integrated into a shell (basePath provided), the shell already has a router and ApolloProvider\n // so we don't create another one. When standalone (no basePath), use MemoryRouter and ApolloProvider.\n if (isIntegrated) {\n return content;\n }\n\n if (!apolloClient) {\n return null;\n }\n\n return (\n <ApolloProvider client={apolloClient}>\n <MemoryRouter>{content}</MemoryRouter>\n </ApolloProvider>\n );\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"ConversationsProvider.js","names":[],"sources":["../../../src/conversations/providers/ConversationsProvider.tsx"],"sourcesContent":["import type { FC, PropsWithChildren } from \"react\";\nimport { createContext, use, useMemo, useRef } from \"react\";\nimport { MemoryRouter } from \"react-router-dom\";\nimport { QueryClient, QueryClientProvider } from \"@tanstack/react-query\";\nimport {\n ApolloClient,\n InMemoryCache,\n HttpLink,\n from,\n Observable,\n} from \"@apollo/client\";\nimport { onError } from \"@apollo/client/link/error\";\nimport { ApolloProvider, useApolloClient } from \"@apollo/client/react\";\nimport { PermissionProvider } from \"@burdenoff/fe-libs/shared/providers/shell\";\nimport {\n isAuthNetworkError,\n redirectToLogin,\n} from \"@burdenoff/fe-libs/shared/utils\";\nimport { useEvent } from \"@burdenoff/fe-libs/shared/events\";\nimport type { ConversationsRootProps } from \"../types\";\n\n/**\n * Clears Apollo cache on auth.signed_out so stale conversation data\n * is not retained across sessions. Must render inside ApolloProvider.\n */\nfunction AuthCacheClearer() {\n const client = useApolloClient();\n useEvent(\"auth.signed_out\", () => {\n void client.clearStore();\n });\n return null;\n}\n\nfunction hasSessionAuthErrors(\n errors: ReadonlyArray<{\n message?: string;\n extensions?: Record<string, unknown>;\n }> = [],\n): boolean {\n return errors.some((error) => {\n const code =\n typeof error.extensions?.code === \"string\" ? error.extensions.code : \"\";\n const errorCode =\n typeof error.extensions?.errorCode === \"string\"\n ? error.extensions.errorCode\n : \"\";\n const message = (error.message ?? \"\").toLowerCase();\n\n return (\n code === \"UNAUTHENTICATED\" ||\n errorCode === \"TOKEN_EXPIRED\" ||\n message.includes(\"token_expired\") ||\n message.includes(\"token expired\") ||\n message.includes(\"session expired\") ||\n message.includes(\"unauthorized\") ||\n message.includes(\"unauthenticated\") ||\n message.includes(\"authentication required\") ||\n message.includes(\"workspace context missing\")\n );\n });\n}\n\n/**\n * Context value for Conversations microfrontend\n */\ntype ConversationsContextValue = ConversationsRootProps;\n\nconst ConversationsContext = createContext<ConversationsContextValue | null>(\n null,\n);\n\n/**\n * Hook to access Conversations context\n */\nexport const useConversations = () => {\n const context = use(ConversationsContext);\n if (!context) {\n throw new Error(\n \"useConversations must be used within ConversationsProvider\",\n );\n }\n return context;\n};\n\n/**\n * Provider component for Conversations microfrontend\n * Sets up routing, context, Apollo Client, and React Query client\n */\nexport const ConversationsProvider: FC<\n PropsWithChildren<ConversationsRootProps>\n> = ({\n children,\n basePath,\n navigate,\n currentUser,\n workspaceId,\n apiGatewayUrl,\n authToken,\n workspaceToken,\n getWorkspaceToken,\n onConversationSelect,\n onMessageSend,\n defaultView,\n variant,\n}) => {\n const contextValue: ConversationsContextValue = {\n basePath,\n navigate,\n currentUser,\n workspaceId,\n apiGatewayUrl,\n authToken,\n workspaceToken,\n getWorkspaceToken,\n onConversationSelect,\n onMessageSend,\n defaultView,\n variant,\n };\n\n const isRefreshingRef = useRef(false);\n const getWorkspaceTokenRef = useRef(getWorkspaceToken);\n getWorkspaceTokenRef.current = getWorkspaceToken;\n // PermissionProvider requires the shell's AuthProvider — only mount it in integrated mode.\n const isIntegrated = !!basePath;\n\n // Create QueryClient instance (memoized to avoid recreation on re-renders)\n const queryClient = useMemo(\n () =>\n new QueryClient({\n defaultOptions: {\n queries: {\n retry: 1,\n refetchOnWindowFocus: false,\n staleTime: 5 * 60 * 1000, // 5 minutes\n },\n },\n }),\n [],\n );\n\n // Create ApolloClient instance (memoized, recreated when tokens change)\n // The workspace gateway requires dual-token auth:\n // Authorization: Bearer <authToken> (platform identity)\n // X-Workspace-Authorization: Bearer <workspaceToken> (workspace access)\n const apolloClient = useMemo(() => {\n if (isIntegrated) return null;\n\n const isLocalDirectMode =\n typeof window !== \"undefined\" &&\n (window.location.hostname === \"localhost\" ||\n window.location.hostname.endsWith(\".local.burdenoff.com\"));\n const workspaceEndpoint = isLocalDirectMode\n ? `${window.location.origin}/__local/conversations/graphql`\n : apiGatewayUrl\n ? `${apiGatewayUrl}/workspaces/graphql`\n : \"/workspaces/graphql\";\n\n const headers: Record<string, string> = {};\n if (authToken) {\n headers[\"Authorization\"] = `Bearer ${authToken}`;\n }\n if (workspaceToken) {\n headers[\"X-Workspace-Authorization\"] = `Bearer ${workspaceToken}`;\n }\n if (workspaceId) {\n headers[\"x-workspace-id\"] = workspaceId;\n }\n if (isLocalDirectMode && currentUser?.id) {\n headers[\"x-actor-id\"] = currentUser.id;\n headers[\"x-actor-type\"] = \"user\";\n }\n\n const httpLink = new HttpLink({\n uri: workspaceEndpoint,\n headers: Object.keys(headers).length > 0 ? headers : undefined,\n });\n\n const errorLink = onError(({ error, operation, forward }) => {\n if (!error) return;\n\n const errors =\n \"errors\" in error && Array.isArray(error.errors) ? error.errors : [];\n const hasAuthError = hasSessionAuthErrors(errors);\n\n if (\n !getWorkspaceTokenRef.current ||\n !hasAuthError ||\n isRefreshingRef.current\n ) {\n if (error instanceof Error && isAuthNetworkError(error)) {\n redirectToLogin();\n }\n return;\n }\n\n return new Observable((observer) => {\n isRefreshingRef.current = true;\n getWorkspaceTokenRef\n .current?.()\n .then((freshToken) => {\n isRefreshingRef.current = false;\n if (!freshToken) {\n redirectToLogin();\n observer.error(new Error(\"Failed to refresh workspace token\"));\n return;\n }\n\n operation.setContext(\n ({\n headers: existingHeaders = {},\n }: {\n headers?: Record<string, string>;\n }) => ({\n headers: {\n ...existingHeaders,\n \"X-Workspace-Authorization\": `Bearer ${freshToken}`,\n ...(workspaceId ? { \"x-workspace-id\": workspaceId } : {}),\n },\n }),\n );\n\n const subscriber = forward(operation).subscribe(observer);\n return () => subscriber.unsubscribe();\n })\n .catch((refreshError: unknown) => {\n isRefreshingRef.current = false;\n observer.error(refreshError);\n });\n });\n });\n\n return new ApolloClient({\n link: from([errorLink, httpLink]),\n cache: new InMemoryCache(),\n defaultOptions: {\n watchQuery: {\n fetchPolicy: \"cache-and-network\",\n },\n },\n });\n }, [\n apiGatewayUrl,\n authToken,\n currentUser?.id,\n isIntegrated,\n workspaceId,\n workspaceToken,\n ]);\n\n // Wrap content with QueryClientProvider and ConversationsContext.Provider\n const content = (\n <QueryClientProvider client={queryClient}>\n <ConversationsContext.Provider value={contextValue}>\n {isIntegrated ? (\n <PermissionProvider scopeId={workspaceId ?? null} gateway=\"workspace\">\n <AuthCacheClearer />\n {children}\n </PermissionProvider>\n ) : (\n <>\n <AuthCacheClearer />\n {children}\n </>\n )}\n </ConversationsContext.Provider>\n </QueryClientProvider>\n );\n\n // When integrated into a shell (basePath provided), the shell already has a router and ApolloProvider\n // so we don't create another one. When standalone (no basePath), use MemoryRouter and ApolloProvider.\n if (isIntegrated) {\n return content;\n }\n\n if (!apolloClient) {\n return null;\n }\n\n return (\n <ApolloProvider client={apolloClient}>\n <MemoryRouter>{content}</MemoryRouter>\n </ApolloProvider>\n );\n};\n"],"mappings":"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"}