@burdenoff/microfe-conversations 2026.626.1 → 2026.731.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.
@@ -2,256 +2,274 @@ import { useConversations as e } from "../providers/ConversationsProvider.js";
2
2
  import { useTr as t } from "../utils/i18n.js";
3
3
  import { cn as n } from "../utils/cn.js";
4
4
  import { useCreateConversation as r } from "../hooks/useConversationQueries.js";
5
- import { nativeImpact as i, nativeNotify as a } from "../../utils/nativeBridge.js";
6
- import { MemberPicker as o } from "./MemberPicker.js";
7
- import { useCallback as s, useMemo as c, useState as l } from "react";
8
- import { Globe as u, Hash as d, Lock as f, Megaphone as p, MessageCircle as m, Shield as h, Users as ee } from "lucide-react";
9
- import { useEventBus as g } from "@burdenoff/fe-libs/shared/events";
10
- import { jsx as _, jsxs as v } from "react/jsx-runtime";
11
- import { Dialog as y, DialogContent as b, DialogDescription as x, DialogFooter as S, DialogHeader as C, DialogTitle as te } from "@burdenoff/fe-libs/ui";
5
+ import { QuotaExhaustedDialog as ee } from "./QuotaExhaustedDialog.js";
6
+ import { nativeImpact as te, nativeNotify as i } from "../../utils/nativeBridge.js";
7
+ import { MemberPicker as a } from "./MemberPicker.js";
8
+ import { useCallback as o, useMemo as s, useState as c } from "react";
9
+ import { Globe as l, Hash as u, Lock as d, Megaphone as ne, MessageCircle as re, Shield as ie, Users as ae } from "lucide-react";
10
+ import { useNavigate as oe } from "react-router-dom";
11
+ import { isQuotaExhaustedError as f } from "@burdenoff/fe-libs/shared/utils";
12
+ import { useEventBus as se } from "@burdenoff/fe-libs/shared/events";
13
+ import { Fragment as p, jsx as m, jsxs as h } from "react/jsx-runtime";
14
+ import { Dialog as g, DialogContent as _, DialogDescription as v, DialogFooter as y, DialogHeader as ce, DialogTitle as le } from "@burdenoff/fe-libs/ui";
12
15
  //#region src/conversations/components/NewConversationDialog.tsx
13
- function w(e) {
16
+ function ue(e) {
14
17
  if ("graphQLErrors" in e) {
15
18
  let t = e.graphQLErrors;
16
19
  if (t && t.length > 0 && t[0]?.message) return t[0].message;
17
20
  }
18
21
  return e.message;
19
22
  }
20
- function T({ open: T, onOpenChange: E, onCreated: D }) {
21
- let { createConversation: O, loading: k, error: A } = r(), { currentUser: j } = e(), M = g(), N = t(), ne = c(() => [
23
+ function b({ open: b, onOpenChange: x, onQuotaExhausted: S, onQuotaDialogClose: C, onCreated: w }) {
24
+ let { createConversation: T, loading: E, error: D } = r(), { currentUser: O } = e(), k = se(), A = t(), j = oe(), [M, N] = c(!1), P = s(() => [
22
25
  {
23
26
  type: "DIRECT",
24
- label: N("conversations.directMessage", "Direct Message"),
25
- description: N("conversations.directMessageDescription", "One-on-one private conversation"),
26
- icon: /* @__PURE__ */ _(m, { size: 20 })
27
+ label: A("conversations.directMessage", "Direct Message"),
28
+ description: A("conversations.directMessageDescription", "One-on-one private conversation"),
29
+ icon: /* @__PURE__ */ m(re, { size: 20 })
27
30
  },
28
31
  {
29
32
  type: "GROUP",
30
- label: N("conversations.groupChat", "Group Chat"),
31
- description: N("conversations.groupChatDescription", "Invite multiple people to a group conversation"),
32
- icon: /* @__PURE__ */ _(ee, { size: 20 })
33
+ label: A("conversations.groupChat", "Group Chat"),
34
+ description: A("conversations.groupChatDescription", "Invite multiple people to a group conversation"),
35
+ icon: /* @__PURE__ */ m(ae, { size: 20 })
33
36
  },
34
37
  {
35
38
  type: "CHANNEL",
36
- label: N("conversations.channel", "Channel"),
37
- description: N("conversations.channelDescription", "A topic-based channel that anyone can join"),
38
- icon: /* @__PURE__ */ _(d, { size: 20 })
39
+ label: A("conversations.channel", "Channel"),
40
+ description: A("conversations.channelDescription", "A topic-based channel that anyone can join"),
41
+ icon: /* @__PURE__ */ m(u, { size: 20 })
39
42
  },
40
43
  {
41
44
  type: "ANNOUNCEMENT",
42
- label: N("conversations.announcement", "Announcement"),
43
- description: N("conversations.announcementDescription", "Broadcast messages to a large audience"),
44
- icon: /* @__PURE__ */ _(p, { size: 20 })
45
+ label: A("conversations.announcement", "Announcement"),
46
+ description: A("conversations.announcementDescription", "Broadcast messages to a large audience"),
47
+ icon: /* @__PURE__ */ m(ne, { size: 20 })
45
48
  }
46
- ], [N]), P = c(() => [
49
+ ], [A]), F = s(() => [
47
50
  {
48
51
  value: "PUBLIC",
49
- label: N("conversations.public", "Public"),
50
- description: N("conversations.publicDescription", "Anyone in the workspace can discover and join"),
51
- icon: /* @__PURE__ */ _(u, { size: 16 })
52
+ label: A("conversations.public", "Public"),
53
+ description: A("conversations.publicDescription", "Anyone in the workspace can discover and join"),
54
+ icon: /* @__PURE__ */ m(l, { size: 16 })
52
55
  },
53
56
  {
54
57
  value: "PRIVATE",
55
- label: N("conversations.private", "Private"),
56
- description: N("conversations.privateDescription", "Only invited members can access"),
57
- icon: /* @__PURE__ */ _(f, { size: 16 })
58
+ label: A("conversations.private", "Private"),
59
+ description: A("conversations.privateDescription", "Only invited members can access"),
60
+ icon: /* @__PURE__ */ m(d, { size: 16 })
58
61
  },
59
62
  {
60
63
  value: "PROTECTED",
61
- label: N("conversations.protected", "Protected"),
62
- description: N("conversations.protectedDescription", "Discoverable but requires approval to join"),
63
- icon: /* @__PURE__ */ _(h, { size: 16 })
64
+ label: A("conversations.protected", "Protected"),
65
+ description: A("conversations.protectedDescription", "Discoverable but requires approval to join"),
66
+ icon: /* @__PURE__ */ m(ie, { size: 16 })
64
67
  }
65
- ], [N]), [F, I] = l("GROUP"), [L, R] = l(""), [z, B] = l(""), [V, H] = l("PRIVATE"), [U, W] = l([]), G = F === "DIRECT", K = !G, q = c(() => U.map((e) => e.id), [U]), J = s(() => {
66
- I("GROUP"), R(""), B(""), H("PRIVATE"), W([]);
67
- }, []), Y = s((e) => {
68
- W((t) => t.some((t) => t.id === e.id) ? t : G ? [e] : [...t, e]);
69
- }, [G]), X = s((e) => {
70
- W((t) => t.filter((t) => t.id !== e));
71
- }, []), Z = F === "DIRECT" || F === "GROUP", re = c(() => j?.id ? [j.id] : [], [j?.id]), Q = s((e) => {
72
- e || J(), E(e);
73
- }, [E, J]), ie = s(async () => {
74
- if (!(Z && q.length === 0) && !(K && !L.trim())) {
75
- i("light");
68
+ ], [A]), [I, L] = c("GROUP"), [R, z] = c(""), [B, V] = c(""), [H, U] = c("PRIVATE"), [W, G] = c([]), K = I === "DIRECT", q = !K, J = s(() => W.map((e) => e.id), [W]), Y = o(() => {
69
+ L("GROUP"), z(""), V(""), U("PRIVATE"), G([]);
70
+ }, []), de = o((e) => {
71
+ G((t) => t.some((t) => t.id === e.id) ? t : K ? [e] : [...t, e]);
72
+ }, [K]), X = o((e) => {
73
+ G((t) => t.filter((t) => t.id !== e));
74
+ }, []), Z = I === "DIRECT" || I === "GROUP", fe = s(() => O?.id ? [O.id] : [], [O?.id]), Q = o((e) => {
75
+ e || Y(), x(e);
76
+ }, [x, Y]), pe = o(async () => {
77
+ if (!(Z && J.length === 0) && !(q && !R.trim())) {
78
+ te("light");
76
79
  try {
77
- let e = await O({
78
- type: F,
79
- title: K ? L.trim() : void 0,
80
- description: z.trim() || void 0,
81
- participantIds: q,
82
- visibility: V
80
+ let e = await T({
81
+ type: I,
82
+ title: q ? R.trim() : void 0,
83
+ description: B.trim() || void 0,
84
+ participantIds: J,
85
+ visibility: H
83
86
  });
84
87
  if (!e) {
85
- a("error");
88
+ i("error");
86
89
  return;
87
90
  }
88
- a("success"), M.emit("conversations.conversation.created", {
91
+ i("success"), k.emit("conversations.conversation.created", {
89
92
  conversationId: e.id,
90
- conversationType: F,
93
+ conversationType: I,
91
94
  source: "new-conversation-dialog"
92
- }), J(), E(!1), D?.(e.id);
95
+ }), Y(), x(!1), w?.(e.id);
93
96
  } catch (e) {
94
- throw a("error"), e;
97
+ if (f(e)) {
98
+ Y(), N(!0), S ? S() : x(!1);
99
+ return;
100
+ }
101
+ throw i("error"), e;
95
102
  }
96
103
  }
97
104
  }, [
98
- M,
105
+ k,
99
106
  Z,
100
- q,
101
- K,
102
- L,
103
- O,
104
- F,
105
- z,
106
- V,
107
107
  J,
108
- E,
109
- D
110
- ]), $ = (!Z || q.length > 0) && (!K || L.trim().length > 0);
111
- return /* @__PURE__ */ _(y, {
112
- open: T,
108
+ q,
109
+ R,
110
+ T,
111
+ I,
112
+ B,
113
+ H,
114
+ Y,
115
+ x,
116
+ S,
117
+ w
118
+ ]), me = o(() => {
119
+ N(!1), C?.();
120
+ }, [C]), $ = (!Z || J.length > 0) && (!q || R.trim().length > 0);
121
+ return /* @__PURE__ */ h(p, { children: [/* @__PURE__ */ m(g, {
122
+ open: b,
113
123
  onOpenChange: Q,
114
- children: /* @__PURE__ */ v(b, {
124
+ children: /* @__PURE__ */ h(_, {
115
125
  className: "sm:max-w-[640px] max-h-[90vh] overflow-y-auto",
116
126
  "data-tour": "new-conversation-dialog",
117
127
  children: [
118
- /* @__PURE__ */ v(C, { children: [/* @__PURE__ */ _(te, { children: N("conversations.newConversation", "New Conversation") }), /* @__PURE__ */ _(x, { children: N("conversations.newConversationSubtitle", "Start a new conversation with your team") })] }),
119
- /* @__PURE__ */ v("div", {
128
+ /* @__PURE__ */ h(ce, { children: [/* @__PURE__ */ m(le, { children: A("conversations.newConversation", "New Conversation") }), /* @__PURE__ */ m(v, { children: A("conversations.newConversationSubtitle", "Start a new conversation with your team") })] }),
129
+ /* @__PURE__ */ h("div", {
120
130
  className: "space-y-6",
121
131
  children: [
122
- /* @__PURE__ */ v("section", {
132
+ /* @__PURE__ */ h("section", {
123
133
  "data-tour": "conversation-type-selector",
124
134
  className: "space-y-3",
125
- children: [/* @__PURE__ */ _("h3", {
135
+ children: [/* @__PURE__ */ m("h3", {
126
136
  className: "text-sm font-semibold text-text-primary",
127
- children: N("conversations.conversationType", "Conversation Type")
128
- }), /* @__PURE__ */ _("div", {
137
+ children: A("conversations.conversationType", "Conversation Type")
138
+ }), /* @__PURE__ */ m("div", {
129
139
  className: "grid grid-cols-1 sm:grid-cols-2 gap-3",
130
- children: ne.map((e) => /* @__PURE__ */ v("button", {
140
+ children: P.map((e) => /* @__PURE__ */ h("button", {
131
141
  type: "button",
132
142
  onClick: () => {
133
- I(e.type), e.type === "DIRECT" && W((e) => e.slice(0, 1));
143
+ L(e.type), e.type === "DIRECT" && G((e) => e.slice(0, 1));
134
144
  },
135
- className: n("flex items-start gap-3 p-4 rounded-xl border text-left transition-all duration-150", F === e.type ? "border-primary bg-action-primary-bg/5 ring-1 ring-primary/20" : "border-border-subtle hover:border-muted-foreground/30 hover:bg-bg-sunken/30"),
136
- children: [/* @__PURE__ */ _("div", {
137
- className: n("p-2 rounded-lg", F === e.type ? "bg-action-primary-bg/10 text-primary" : "bg-bg-sunken text-text-secondary"),
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-action-primary-bg/5 ring-1 ring-primary/20" : "border-border-subtle hover:border-muted-foreground/30 hover:bg-bg-sunken/30"),
146
+ children: [/* @__PURE__ */ m("div", {
147
+ className: n("p-2 rounded-lg", I === e.type ? "bg-action-primary-bg/10 text-primary" : "bg-bg-sunken text-text-secondary"),
138
148
  children: e.icon
139
- }), /* @__PURE__ */ v("div", { children: [/* @__PURE__ */ _("p", {
149
+ }), /* @__PURE__ */ h("div", { children: [/* @__PURE__ */ m("p", {
140
150
  className: "text-sm font-medium text-text-primary",
141
151
  children: e.label
142
- }), /* @__PURE__ */ _("p", {
152
+ }), /* @__PURE__ */ m("p", {
143
153
  className: "text-xs text-text-secondary mt-0.5",
144
154
  children: e.description
145
155
  })] })]
146
156
  }, e.type))
147
157
  })]
148
158
  }),
149
- !G && /* @__PURE__ */ v("section", {
159
+ !K && /* @__PURE__ */ h("section", {
150
160
  "data-tour": "conversation-visibility-selector",
151
161
  className: "space-y-3",
152
162
  children: [
153
- /* @__PURE__ */ _("h3", {
163
+ /* @__PURE__ */ m("h3", {
154
164
  className: "text-sm font-semibold text-text-primary",
155
- children: N("conversations.visibility", "Visibility")
165
+ children: A("conversations.visibility", "Visibility")
156
166
  }),
157
- /* @__PURE__ */ _("div", {
167
+ /* @__PURE__ */ m("div", {
158
168
  className: "flex gap-2 flex-wrap",
159
- children: P.map((e) => /* @__PURE__ */ v("button", {
169
+ children: F.map((e) => /* @__PURE__ */ h("button", {
160
170
  type: "button",
161
- onClick: () => H(e.value),
162
- className: n("flex items-center gap-2 px-4 py-2.5 rounded-lg border text-sm transition-all", V === e.value ? "border-primary bg-action-primary-bg/5 text-primary font-medium" : "border-border-subtle text-text-secondary hover:text-text-primary hover:bg-bg-sunken/30"),
171
+ 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-action-primary-bg/5 text-primary font-medium" : "border-border-subtle text-text-secondary hover:text-text-primary hover:bg-bg-sunken/30"),
163
173
  children: [e.icon, e.label]
164
174
  }, e.value))
165
175
  }),
166
- /* @__PURE__ */ _("p", {
176
+ /* @__PURE__ */ m("p", {
167
177
  className: "text-xs text-text-secondary",
168
- children: P.find((e) => e.value === V)?.description
178
+ children: F.find((e) => e.value === H)?.description
169
179
  })
170
180
  ]
171
181
  }),
172
- K && /* @__PURE__ */ v("section", {
182
+ q && /* @__PURE__ */ h("section", {
173
183
  "data-tour": "conversation-create-details",
174
184
  className: "space-y-3",
175
- children: [/* @__PURE__ */ v("div", { children: [/* @__PURE__ */ v("label", {
185
+ children: [/* @__PURE__ */ h("div", { children: [/* @__PURE__ */ h("label", {
176
186
  htmlFor: "conv-title",
177
187
  className: "text-sm font-semibold text-text-primary",
178
188
  children: [
179
- N("conversations.name", "Name"),
189
+ A("conversations.name", "Name"),
180
190
  " ",
181
- /* @__PURE__ */ _("span", {
191
+ /* @__PURE__ */ m("span", {
182
192
  className: "text-status-error-text",
183
193
  children: "*"
184
194
  })
185
195
  ]
186
- }), /* @__PURE__ */ _("input", {
196
+ }), /* @__PURE__ */ m("input", {
187
197
  id: "conv-title",
188
198
  type: "text",
189
- value: L,
190
- onChange: (e) => R(e.target.value),
191
- placeholder: F === "CHANNEL" ? N("conversations.channelNamePlaceholder", "e.g., engineering-general") : N("conversations.conversationNamePlaceholder", "e.g., Product Launch Q1"),
199
+ value: R,
200
+ onChange: (e) => z(e.target.value),
201
+ placeholder: I === "CHANNEL" ? A("conversations.channelNamePlaceholder", "e.g., engineering-general") : A("conversations.conversationNamePlaceholder", "e.g., Product Launch Q1"),
192
202
  maxLength: 200,
193
203
  className: "mt-1.5 w-full px-3 py-2 text-sm rounded-lg border border-border-subtle bg-bg-surface placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]"
194
- })] }), /* @__PURE__ */ v("div", { children: [/* @__PURE__ */ _("label", {
204
+ })] }), /* @__PURE__ */ h("div", { children: [/* @__PURE__ */ m("label", {
195
205
  htmlFor: "conv-desc",
196
206
  className: "text-sm font-semibold text-text-primary",
197
- children: N("conversations.description", "Description")
198
- }), /* @__PURE__ */ _("textarea", {
207
+ children: A("conversations.description", "Description")
208
+ }), /* @__PURE__ */ m("textarea", {
199
209
  id: "conv-desc",
200
- value: z,
201
- onChange: (e) => B(e.target.value),
202
- placeholder: N("conversations.descriptionPlaceholder", "What is this conversation about?"),
210
+ value: B,
211
+ onChange: (e) => V(e.target.value),
212
+ placeholder: A("conversations.descriptionPlaceholder", "What is this conversation about?"),
203
213
  rows: 2,
204
214
  maxLength: 1e3,
205
215
  className: "mt-1.5 w-full px-3 py-2 text-sm rounded-lg border border-border-subtle bg-bg-surface placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] resize-none"
206
216
  })] })]
207
217
  }),
208
- /* @__PURE__ */ v("section", {
218
+ /* @__PURE__ */ h("section", {
209
219
  "data-tour": "conversation-participants",
210
220
  className: "space-y-3",
211
- children: [/* @__PURE__ */ v("h3", {
221
+ children: [/* @__PURE__ */ h("h3", {
212
222
  className: "text-sm font-semibold text-text-primary",
213
- children: [G ? N("conversations.recipient", "Recipient") : N("conversations.participants", "Participants"), Z ? /* @__PURE__ */ _("span", {
223
+ children: [K ? A("conversations.recipient", "Recipient") : A("conversations.participants", "Participants"), Z ? /* @__PURE__ */ m("span", {
214
224
  className: "ml-1 text-status-error-text",
215
225
  children: "*"
216
- }) : /* @__PURE__ */ v("span", {
226
+ }) : /* @__PURE__ */ h("span", {
217
227
  className: "ml-1.5 text-xs font-normal text-text-secondary",
218
228
  children: [
219
229
  "(",
220
- N("conversations.optional", "optional"),
230
+ A("conversations.optional", "optional"),
221
231
  ")"
222
232
  ]
223
233
  })]
224
- }), /* @__PURE__ */ _(o, {
225
- selected: U,
226
- onAdd: Y,
234
+ }), /* @__PURE__ */ m(a, {
235
+ selected: W,
236
+ onAdd: de,
227
237
  onRemove: X,
228
- single: G,
229
- excludeIds: re
238
+ single: K,
239
+ excludeIds: fe
230
240
  })]
231
241
  }),
232
- A && /* @__PURE__ */ _("div", {
242
+ D && !f(D) && /* @__PURE__ */ m("div", {
233
243
  className: "text-xs text-status-error-text rounded-lg border border-status-error-border/30 bg-status-error-bg/10 px-3 py-2",
234
- children: w(A)
244
+ children: ue(D)
235
245
  })
236
246
  ]
237
247
  }),
238
- /* @__PURE__ */ v(S, { children: [/* @__PURE__ */ _("button", {
248
+ /* @__PURE__ */ h(y, { children: [/* @__PURE__ */ m("button", {
239
249
  type: "button",
240
250
  onClick: () => Q(!1),
241
251
  className: "px-4 py-2 text-sm font-medium rounded-lg text-text-secondary hover:text-text-primary hover:bg-bg-sunken/50 transition-colors",
242
- children: N("conversations.cancel", "Cancel")
243
- }), /* @__PURE__ */ _("button", {
252
+ children: A("conversations.cancel", "Cancel")
253
+ }), /* @__PURE__ */ m("button", {
244
254
  type: "button",
245
- onClick: () => void ie(),
246
- disabled: !$ || k,
247
- className: n("inline-flex items-center gap-2 px-5 py-2 text-sm font-medium rounded-lg transition-all", $ && !k ? "bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg/90 shadow-sm" : "bg-bg-sunken text-text-secondary cursor-not-allowed"),
248
- children: k ? N("conversations.creating", "Creating...") : N("conversations.createConversation", "Create Conversation")
255
+ onClick: () => void pe(),
256
+ disabled: !$ || E,
257
+ className: n("inline-flex items-center gap-2 px-5 py-2 text-sm font-medium rounded-lg transition-all", $ && !E ? "bg-action-primary-bg text-action-primary-text hover:bg-action-primary-bg/90 shadow-sm" : "bg-bg-sunken text-text-secondary cursor-not-allowed"),
258
+ children: E ? A("conversations.creating", "Creating...") : A("conversations.createConversation", "Create Conversation")
249
259
  })] })
250
260
  ]
251
261
  })
252
- });
262
+ }), /* @__PURE__ */ m(ee, {
263
+ open: M,
264
+ title: A("conversations.quota.conversations.title", "Conversation Quota Reached"),
265
+ description: A("conversations.quota.conversations.description", "You've reached the conversation limit for your current plan. View billing to upgrade or add more capacity."),
266
+ onClose: me,
267
+ onConfirm: () => {
268
+ N(!1), j("/billing");
269
+ }
270
+ })] });
253
271
  }
254
272
  //#endregion
255
- export { T as NewConversationDialog };
273
+ export { b as NewConversationDialog };
256
274
 
257
275
  //# sourceMappingURL=NewConversationDialog.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"NewConversationDialog.js","names":[],"sources":["../../../src/conversations/components/NewConversationDialog.tsx"],"sourcesContent":["import { useState, useCallback, useMemo } from \"react\";\nimport { useEventBus } from \"@burdenoff/fe-libs/shared/events\";\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n} from \"@burdenoff/fe-libs/ui\";\nimport {\n Globe,\n Hash,\n Lock,\n Megaphone,\n MessageCircle,\n Shield,\n Users,\n} from \"lucide-react\";\nimport { cn } from \"../utils/cn\";\nimport { useTr } from \"../utils/i18n\";\nimport {\n useCreateConversation,\n type WorkspaceDirectoryMember,\n} from \"../hooks/useConversationQueries\";\nimport { useConversations } from \"../providers/ConversationsProvider\";\nimport { MemberPicker } from \"./MemberPicker\";\nimport type { ConversationType, ConversationVisibility } from \"../types\";\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 interface NewConversationDialogProps {\n /** Whether the dialog is open. */\n open: boolean;\n /** Open-state change handler. Fires with `false` on close (overlay click,\n * Escape, X button, Cancel). */\n onOpenChange: (open: boolean) => void;\n /**\n * Called after a conversation is successfully created. Receives the new\n * conversation id so the parent can navigate to the detail route.\n * The dialog also closes itself (via `onOpenChange(false)`) before this\n * fires, so the parent does not need to close the dialog manually.\n */\n onCreated?: (conversationId: string) => void;\n}\n\n/**\n * NewConversationDialog — modal form for creating a new conversation.\n *\n * Renders inside the canonical `<Dialog>` primitive from\n * `@burdenoff/fe-libs/ui` so it inherits theme tokens, focus management,\n * and overlay behaviour from the shell.\n *\n * Form state is owned locally; on successful submit the dialog clears\n * state, closes itself, and invokes `onCreated` with the new\n * conversation id.\n */\nexport function NewConversationDialog({\n open,\n onOpenChange,\n onCreated,\n}: NewConversationDialogProps) {\n const { createConversation, loading, error } = useCreateConversation();\n const { currentUser } = useConversations();\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 [selectedMembers, setSelectedMembers] = useState<\n WorkspaceDirectoryMember[]\n >([]);\n\n const isDirectMessage = selectedType === \"DIRECT\";\n const needsTitle = !isDirectMessage;\n\n const participantIds = useMemo(\n () => selectedMembers.map((member) => member.id),\n [selectedMembers],\n );\n\n const resetForm = useCallback(() => {\n setSelectedType(\"GROUP\");\n setTitle(\"\");\n setDescription(\"\");\n setVisibility(\"PRIVATE\");\n setSelectedMembers([]);\n }, []);\n\n const addMember = useCallback(\n (member: WorkspaceDirectoryMember) => {\n setSelectedMembers((prev) => {\n if (prev.some((existing) => existing.id === member.id)) return prev;\n // A direct message has exactly one recipient.\n return isDirectMessage ? [member] : [...prev, member];\n });\n },\n [isDirectMessage],\n );\n\n const removeMember = useCallback((userId: string) => {\n setSelectedMembers((prev) => prev.filter((member) => member.id !== userId));\n }, []);\n\n const requiresParticipants =\n selectedType === \"DIRECT\" || selectedType === \"GROUP\";\n\n // Never offer the current user as a participant — they are added implicitly\n // as the conversation creator.\n const excludeIds = useMemo(\n () => (currentUser?.id ? [currentUser.id] : []),\n [currentUser?.id],\n );\n\n const handleOpenChange = useCallback(\n (nextOpen: boolean) => {\n if (!nextOpen) {\n resetForm();\n }\n onOpenChange(nextOpen);\n },\n [onOpenChange, resetForm],\n );\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-dialog\",\n });\n resetForm();\n onOpenChange(false);\n onCreated?.(conversation.id);\n } catch (error) {\n nativeNotify(\"error\");\n throw error;\n }\n }, [\n bus,\n requiresParticipants,\n participantIds,\n needsTitle,\n title,\n createConversation,\n selectedType,\n description,\n visibility,\n resetForm,\n onOpenChange,\n onCreated,\n ]);\n\n const canCreate =\n (!requiresParticipants || participantIds.length > 0) &&\n (!needsTitle || title.trim().length > 0);\n\n return (\n <Dialog open={open} onOpenChange={handleOpenChange}>\n <DialogContent\n className=\"sm:max-w-[640px] max-h-[90vh] overflow-y-auto\"\n data-tour=\"new-conversation-dialog\"\n >\n <DialogHeader>\n <DialogTitle>\n {tr(\"conversations.newConversation\", \"New Conversation\")}\n </DialogTitle>\n <DialogDescription>\n {tr(\n \"conversations.newConversationSubtitle\",\n \"Start a new conversation with your team\",\n )}\n </DialogDescription>\n </DialogHeader>\n\n <div className=\"space-y-6\">\n <section data-tour=\"conversation-type-selector\" className=\"space-y-3\">\n <h3 className=\"text-sm font-semibold text-text-primary\">\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 setSelectedMembers((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-action-primary-bg/5 ring-1 ring-primary/20\"\n : \"border-border-subtle hover:border-muted-foreground/30 hover:bg-bg-sunken/30\",\n )}\n >\n <div\n className={cn(\n \"p-2 rounded-lg\",\n selectedType === option.type\n ? \"bg-action-primary-bg/10 text-primary\"\n : \"bg-bg-sunken text-text-secondary\",\n )}\n >\n {option.icon}\n </div>\n <div>\n <p className=\"text-sm font-medium text-text-primary\">\n {option.label}\n </p>\n <p className=\"text-xs text-text-secondary 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-text-primary\">\n {tr(\"conversations.visibility\", \"Visibility\")}\n </h3>\n <div className=\"flex gap-2 flex-wrap\">\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-action-primary-bg/5 text-primary font-medium\"\n : \"border-border-subtle text-text-secondary hover:text-text-primary hover:bg-bg-sunken/30\",\n )}\n >\n {option.icon}\n {option.label}\n </button>\n ))}\n </div>\n <p className=\"text-xs text-text-secondary\">\n {\n VISIBILITY_OPTIONS.find(\n (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-text-primary\"\n >\n {tr(\"conversations.name\", \"Name\")}{\" \"}\n <span className=\"text-status-error-text\">*</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-subtle bg-bg-surface placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n />\n </div>\n <div>\n <label\n htmlFor=\"conv-desc\"\n className=\"text-sm font-semibold text-text-primary\"\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-subtle bg-bg-surface placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-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-text-primary\">\n {isDirectMessage\n ? tr(\"conversations.recipient\", \"Recipient\")\n : tr(\"conversations.participants\", \"Participants\")}\n {requiresParticipants ? (\n <span className=\"ml-1 text-status-error-text\">*</span>\n ) : (\n <span className=\"ml-1.5 text-xs font-normal text-text-secondary\">\n ({tr(\"conversations.optional\", \"optional\")})\n </span>\n )}\n </h3>\n\n <MemberPicker\n selected={selectedMembers}\n onAdd={addMember}\n onRemove={removeMember}\n single={isDirectMessage}\n excludeIds={excludeIds}\n />\n </section>\n\n {error && (\n <div className=\"text-xs text-status-error-text rounded-lg border border-status-error-border/30 bg-status-error-bg/10 px-3 py-2\">\n {getErrorMessage(error)}\n </div>\n )}\n </div>\n\n <DialogFooter>\n <button\n type=\"button\"\n onClick={() => handleOpenChange(false)}\n className=\"px-4 py-2 text-sm font-medium rounded-lg text-text-secondary hover:text-text-primary hover:bg-bg-sunken/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-action-primary-bg text-action-primary-text hover:bg-action-primary-bg/90 shadow-sm\"\n : \"bg-bg-sunken text-text-secondary cursor-not-allowed\",\n )}\n >\n {loading\n ? tr(\"conversations.creating\", \"Creating...\")\n : tr(\"conversations.createConversation\", \"Create Conversation\")}\n </button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"NewConversationDialog.js","names":[],"sources":["../../../src/conversations/components/NewConversationDialog.tsx"],"sourcesContent":["import { useState, useCallback, useMemo } from \"react\";\nimport { useEventBus } from \"@burdenoff/fe-libs/shared/events\";\nimport { isQuotaExhaustedError } from \"@burdenoff/fe-libs/shared/utils\";\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n} from \"@burdenoff/fe-libs/ui\";\nimport {\n Globe,\n Hash,\n Lock,\n Megaphone,\n MessageCircle,\n Shield,\n Users,\n} from \"lucide-react\";\nimport { useNavigate } from \"react-router-dom\";\nimport { cn } from \"../utils/cn\";\nimport { useTr } from \"../utils/i18n\";\nimport {\n useCreateConversation,\n type WorkspaceDirectoryMember,\n} from \"../hooks/useConversationQueries\";\nimport { useConversations } from \"../providers/ConversationsProvider\";\nimport { MemberPicker } from \"./MemberPicker\";\nimport { QuotaExhaustedDialog } from \"./QuotaExhaustedDialog\";\nimport type { ConversationType, ConversationVisibility } from \"../types\";\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 interface NewConversationDialogProps {\n /** Whether the dialog is open. */\n open: boolean;\n /** Open-state change handler. Fires with `false` on close (overlay click,\n * Escape, X button, Cancel). */\n onOpenChange: (open: boolean) => void;\n /**\n * Optional quota-failure hook for route wrappers that need to close the\n * create modal without running their normal close navigation.\n */\n onQuotaExhausted?: () => void;\n /** Called when the quota dialog is dismissed without opening billing. */\n onQuotaDialogClose?: () => void;\n /**\n * Called after a conversation is successfully created. Receives the new\n * conversation id so the parent can navigate to the detail route.\n * The dialog also closes itself (via `onOpenChange(false)`) before this\n * fires, so the parent does not need to close the dialog manually.\n */\n onCreated?: (conversationId: string) => void;\n}\n\n/**\n * NewConversationDialog — modal form for creating a new conversation.\n *\n * Renders inside the canonical `<Dialog>` primitive from\n * `@burdenoff/fe-libs/ui` so it inherits theme tokens, focus management,\n * and overlay behaviour from the shell.\n *\n * Form state is owned locally; on successful submit the dialog clears\n * state, closes itself, and invokes `onCreated` with the new\n * conversation id.\n */\nexport function NewConversationDialog({\n open,\n onOpenChange,\n onQuotaExhausted,\n onQuotaDialogClose,\n onCreated,\n}: NewConversationDialogProps) {\n const { createConversation, loading, error } = useCreateConversation();\n const { currentUser } = useConversations();\n const bus = useEventBus();\n const tr = useTr();\n const navigate = useNavigate();\n const [showQuotaExhausted, setShowQuotaExhausted] = useState(false);\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 [selectedMembers, setSelectedMembers] = useState<\n WorkspaceDirectoryMember[]\n >([]);\n\n const isDirectMessage = selectedType === \"DIRECT\";\n const needsTitle = !isDirectMessage;\n\n const participantIds = useMemo(\n () => selectedMembers.map((member) => member.id),\n [selectedMembers],\n );\n\n const resetForm = useCallback(() => {\n setSelectedType(\"GROUP\");\n setTitle(\"\");\n setDescription(\"\");\n setVisibility(\"PRIVATE\");\n setSelectedMembers([]);\n }, []);\n\n const addMember = useCallback(\n (member: WorkspaceDirectoryMember) => {\n setSelectedMembers((prev) => {\n if (prev.some((existing) => existing.id === member.id)) return prev;\n // A direct message has exactly one recipient.\n return isDirectMessage ? [member] : [...prev, member];\n });\n },\n [isDirectMessage],\n );\n\n const removeMember = useCallback((userId: string) => {\n setSelectedMembers((prev) => prev.filter((member) => member.id !== userId));\n }, []);\n\n const requiresParticipants =\n selectedType === \"DIRECT\" || selectedType === \"GROUP\";\n\n // Never offer the current user as a participant — they are added implicitly\n // as the conversation creator.\n const excludeIds = useMemo(\n () => (currentUser?.id ? [currentUser.id] : []),\n [currentUser?.id],\n );\n\n const handleOpenChange = useCallback(\n (nextOpen: boolean) => {\n if (!nextOpen) {\n resetForm();\n }\n onOpenChange(nextOpen);\n },\n [onOpenChange, resetForm],\n );\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-dialog\",\n });\n resetForm();\n onOpenChange(false);\n onCreated?.(conversation.id);\n } catch (error) {\n if (isQuotaExhaustedError(error)) {\n resetForm();\n setShowQuotaExhausted(true);\n if (onQuotaExhausted) {\n onQuotaExhausted();\n } else {\n onOpenChange(false);\n }\n return;\n }\n nativeNotify(\"error\");\n throw error;\n }\n }, [\n bus,\n requiresParticipants,\n participantIds,\n needsTitle,\n title,\n createConversation,\n selectedType,\n description,\n visibility,\n resetForm,\n onOpenChange,\n onQuotaExhausted,\n onCreated,\n ]);\n\n const handleQuotaDialogClose = useCallback(() => {\n setShowQuotaExhausted(false);\n onQuotaDialogClose?.();\n }, [onQuotaDialogClose]);\n\n const canCreate =\n (!requiresParticipants || participantIds.length > 0) &&\n (!needsTitle || title.trim().length > 0);\n\n return (\n <>\n <Dialog open={open} onOpenChange={handleOpenChange}>\n <DialogContent\n className=\"sm:max-w-[640px] max-h-[90vh] overflow-y-auto\"\n data-tour=\"new-conversation-dialog\"\n >\n <DialogHeader>\n <DialogTitle>\n {tr(\"conversations.newConversation\", \"New Conversation\")}\n </DialogTitle>\n <DialogDescription>\n {tr(\n \"conversations.newConversationSubtitle\",\n \"Start a new conversation with your team\",\n )}\n </DialogDescription>\n </DialogHeader>\n\n <div className=\"space-y-6\">\n <section\n data-tour=\"conversation-type-selector\"\n className=\"space-y-3\"\n >\n <h3 className=\"text-sm font-semibold text-text-primary\">\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 setSelectedMembers((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-action-primary-bg/5 ring-1 ring-primary/20\"\n : \"border-border-subtle hover:border-muted-foreground/30 hover:bg-bg-sunken/30\",\n )}\n >\n <div\n className={cn(\n \"p-2 rounded-lg\",\n selectedType === option.type\n ? \"bg-action-primary-bg/10 text-primary\"\n : \"bg-bg-sunken text-text-secondary\",\n )}\n >\n {option.icon}\n </div>\n <div>\n <p className=\"text-sm font-medium text-text-primary\">\n {option.label}\n </p>\n <p className=\"text-xs text-text-secondary 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-text-primary\">\n {tr(\"conversations.visibility\", \"Visibility\")}\n </h3>\n <div className=\"flex gap-2 flex-wrap\">\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-action-primary-bg/5 text-primary font-medium\"\n : \"border-border-subtle text-text-secondary hover:text-text-primary hover:bg-bg-sunken/30\",\n )}\n >\n {option.icon}\n {option.label}\n </button>\n ))}\n </div>\n <p className=\"text-xs text-text-secondary\">\n {\n VISIBILITY_OPTIONS.find(\n (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-text-primary\"\n >\n {tr(\"conversations.name\", \"Name\")}{\" \"}\n <span className=\"text-status-error-text\">*</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-subtle bg-bg-surface placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)]\"\n />\n </div>\n <div>\n <label\n htmlFor=\"conv-desc\"\n className=\"text-sm font-semibold text-text-primary\"\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-subtle bg-bg-surface placeholder:text-text-secondary focus:outline-none focus:ring-2 focus:ring-[var(--color-focus-ring)] resize-none\"\n />\n </div>\n </section>\n )}\n\n <section\n data-tour=\"conversation-participants\"\n className=\"space-y-3\"\n >\n <h3 className=\"text-sm font-semibold text-text-primary\">\n {isDirectMessage\n ? tr(\"conversations.recipient\", \"Recipient\")\n : tr(\"conversations.participants\", \"Participants\")}\n {requiresParticipants ? (\n <span className=\"ml-1 text-status-error-text\">*</span>\n ) : (\n <span className=\"ml-1.5 text-xs font-normal text-text-secondary\">\n ({tr(\"conversations.optional\", \"optional\")})\n </span>\n )}\n </h3>\n\n <MemberPicker\n selected={selectedMembers}\n onAdd={addMember}\n onRemove={removeMember}\n single={isDirectMessage}\n excludeIds={excludeIds}\n />\n </section>\n\n {error && !isQuotaExhaustedError(error) && (\n <div className=\"text-xs text-status-error-text rounded-lg border border-status-error-border/30 bg-status-error-bg/10 px-3 py-2\">\n {getErrorMessage(error)}\n </div>\n )}\n </div>\n\n <DialogFooter>\n <button\n type=\"button\"\n onClick={() => handleOpenChange(false)}\n className=\"px-4 py-2 text-sm font-medium rounded-lg text-text-secondary hover:text-text-primary hover:bg-bg-sunken/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-action-primary-bg text-action-primary-text hover:bg-action-primary-bg/90 shadow-sm\"\n : \"bg-bg-sunken text-text-secondary cursor-not-allowed\",\n )}\n >\n {loading\n ? tr(\"conversations.creating\", \"Creating...\")\n : tr(\"conversations.createConversation\", \"Create Conversation\")}\n </button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n <QuotaExhaustedDialog\n open={showQuotaExhausted}\n title={tr(\n \"conversations.quota.conversations.title\",\n \"Conversation Quota Reached\",\n )}\n description={tr(\n \"conversations.quota.conversations.description\",\n \"You've reached the conversation limit for your current plan. View billing to upgrade or add more capacity.\",\n )}\n onClose={handleQuotaDialogClose}\n onConfirm={() => {\n setShowQuotaExhausted(false);\n navigate(\"/billing\");\n }}\n />\n </>\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ import { useTr as e } from "../utils/i18n.js";
2
+ import { jsx as t, jsxs as n } from "react/jsx-runtime";
3
+ import { Button as r, Dialog as i, DialogContent as a, DialogDescription as o, DialogFooter as s, DialogHeader as c, DialogTitle as l } from "@burdenoff/fe-libs/ui";
4
+ //#region src/conversations/components/QuotaExhaustedDialog.tsx
5
+ function u({ open: u, title: d, description: f, onClose: p, onConfirm: m }) {
6
+ let h = e();
7
+ return /* @__PURE__ */ t(i, {
8
+ open: u,
9
+ onOpenChange: (e) => {
10
+ e || p();
11
+ },
12
+ children: /* @__PURE__ */ n(a, {
13
+ className: "sm:max-w-[420px]",
14
+ children: [/* @__PURE__ */ n(c, { children: [/* @__PURE__ */ t(l, { children: d }), /* @__PURE__ */ t(o, { children: f })] }), /* @__PURE__ */ n(s, { children: [/* @__PURE__ */ t(r, {
15
+ type: "button",
16
+ variant: "ghost",
17
+ onClick: p,
18
+ children: h("conversations.quota.dismiss", "Dismiss")
19
+ }), /* @__PURE__ */ t(r, {
20
+ type: "button",
21
+ onClick: m,
22
+ children: h("conversations.quota.viewBilling", "View Billing")
23
+ })] })]
24
+ })
25
+ });
26
+ }
27
+ //#endregion
28
+ export { u as QuotaExhaustedDialog };
29
+
30
+ //# sourceMappingURL=QuotaExhaustedDialog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"QuotaExhaustedDialog.js","names":[],"sources":["../../../src/conversations/components/QuotaExhaustedDialog.tsx"],"sourcesContent":["import {\n Button,\n Dialog,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogTitle,\n} from \"@burdenoff/fe-libs/ui\";\nimport { useTr } from \"../utils/i18n\";\n\ninterface QuotaExhaustedDialogProps {\n open: boolean;\n title: string;\n description: string;\n onClose: () => void;\n onConfirm: () => void;\n}\n\nexport function QuotaExhaustedDialog({\n open,\n title,\n description,\n onClose,\n onConfirm,\n}: QuotaExhaustedDialogProps) {\n const tr = useTr();\n\n return (\n <Dialog\n open={open}\n onOpenChange={(nextOpen) => {\n if (!nextOpen) onClose();\n }}\n >\n <DialogContent className=\"sm:max-w-[420px]\">\n <DialogHeader>\n <DialogTitle>{title}</DialogTitle>\n <DialogDescription>{description}</DialogDescription>\n </DialogHeader>\n <DialogFooter>\n <Button type=\"button\" variant=\"ghost\" onClick={onClose}>\n {tr(\"conversations.quota.dismiss\", \"Dismiss\")}\n </Button>\n <Button type=\"button\" onClick={onConfirm}>\n {tr(\"conversations.quota.viewBilling\", \"View Billing\")}\n </Button>\n </DialogFooter>\n </DialogContent>\n </Dialog>\n );\n}\n"],"mappings":";;;;AAmBA,SAAgB,EAAqB,EACnC,SACA,UACA,gBACA,YACA,gBAC4B;CAC5B,IAAM,IAAK,GAAO;AAElB,QACE,kBAAC,GAAD;EACQ;EACN,eAAe,MAAa;AAC1B,GAAK,KAAU,GAAS;;YAG1B,kBAAC,GAAD;GAAe,WAAU;aAAzB,CACE,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD,EAAA,UAAc,GAAoB,CAAA,EAClC,kBAAC,GAAD,EAAA,UAAoB,GAAgC,CAAA,CACvC,EAAA,CAAA,EACf,kBAAC,GAAD,EAAA,UAAA,CACE,kBAAC,GAAD;IAAQ,MAAK;IAAS,SAAQ;IAAQ,SAAS;cAC5C,EAAG,+BAA+B,UAAU;IACtC,CAAA,EACT,kBAAC,GAAD;IAAQ,MAAK;IAAS,SAAS;cAC5B,EAAG,mCAAmC,eAAe;IAC/C,CAAA,CACI,EAAA,CAAA,CACD;;EACT,CAAA"}
@@ -103,14 +103,16 @@ function T() {
103
103
  createConversation: h(async (e) => {
104
104
  if (!t) throw Error("Workspace context is required to create a conversation");
105
105
  try {
106
- let { data: n } = await r({ variables: { input: {
106
+ let { data: n, error: i } = await r({ variables: { input: {
107
107
  workspaceId: t,
108
108
  type: e.type,
109
109
  title: e.title,
110
110
  description: e.description,
111
111
  participantIds: e.participantIds
112
- } } }), i = n?.createConversation.conversation;
113
- return i ? m(i) : null;
112
+ } } });
113
+ if (i) throw i;
114
+ let a = n?.createConversation.conversation;
115
+ return a ? m(a) : null;
114
116
  } catch (e) {
115
117
  throw e instanceof Error ? e : /* @__PURE__ */ Error("Failed to create conversation");
116
118
  }