@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.
- package/dist/conversations/components/NewConversationDialog.js +143 -125
- package/dist/conversations/components/NewConversationDialog.js.map +1 -1
- package/dist/conversations/components/QuotaExhaustedDialog.js +30 -0
- package/dist/conversations/components/QuotaExhaustedDialog.js.map +1 -0
- package/dist/conversations/hooks/useConversationQueries.js +5 -3
- package/dist/conversations/hooks/useConversationQueries.js.map +1 -1
- package/dist/conversations/hooks/useMessageQueries.js +5 -3
- package/dist/conversations/hooks/useMessageQueries.js.map +1 -1
- package/dist/conversations/pages/ConversationDetailPage.js +408 -385
- package/dist/conversations/pages/ConversationDetailPage.js.map +1 -1
- package/dist/conversations/pages/ConversationsLandingPage.js +2 -2
- package/dist/conversations/pages/ConversationsLandingPage.js.map +1 -1
- package/dist/conversations/pages/NewConversationPage.js +25 -15
- package/dist/conversations/pages/NewConversationPage.js.map +1 -1
- package/package.json +6 -6
|
@@ -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 {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
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
|
|
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
|
|
21
|
-
let { createConversation:
|
|
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:
|
|
25
|
-
description:
|
|
26
|
-
icon: /* @__PURE__ */
|
|
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:
|
|
31
|
-
description:
|
|
32
|
-
icon: /* @__PURE__ */
|
|
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:
|
|
37
|
-
description:
|
|
38
|
-
icon: /* @__PURE__ */
|
|
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:
|
|
43
|
-
description:
|
|
44
|
-
icon: /* @__PURE__ */
|
|
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
|
-
], [
|
|
49
|
+
], [A]), F = s(() => [
|
|
47
50
|
{
|
|
48
51
|
value: "PUBLIC",
|
|
49
|
-
label:
|
|
50
|
-
description:
|
|
51
|
-
icon: /* @__PURE__ */
|
|
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:
|
|
56
|
-
description:
|
|
57
|
-
icon: /* @__PURE__ */
|
|
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:
|
|
62
|
-
description:
|
|
63
|
-
icon: /* @__PURE__ */
|
|
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
|
-
], [
|
|
66
|
-
|
|
67
|
-
}, []),
|
|
68
|
-
|
|
69
|
-
}, [
|
|
70
|
-
|
|
71
|
-
}, []), Z =
|
|
72
|
-
e ||
|
|
73
|
-
}, [
|
|
74
|
-
if (!(Z &&
|
|
75
|
-
|
|
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
|
|
78
|
-
type:
|
|
79
|
-
title:
|
|
80
|
-
description:
|
|
81
|
-
participantIds:
|
|
82
|
-
visibility:
|
|
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
|
-
|
|
88
|
+
i("error");
|
|
86
89
|
return;
|
|
87
90
|
}
|
|
88
|
-
|
|
91
|
+
i("success"), k.emit("conversations.conversation.created", {
|
|
89
92
|
conversationId: e.id,
|
|
90
|
-
conversationType:
|
|
93
|
+
conversationType: I,
|
|
91
94
|
source: "new-conversation-dialog"
|
|
92
|
-
}),
|
|
95
|
+
}), Y(), x(!1), w?.(e.id);
|
|
93
96
|
} catch (e) {
|
|
94
|
-
|
|
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
|
-
|
|
105
|
+
k,
|
|
99
106
|
Z,
|
|
100
|
-
q,
|
|
101
|
-
K,
|
|
102
|
-
L,
|
|
103
|
-
O,
|
|
104
|
-
F,
|
|
105
|
-
z,
|
|
106
|
-
V,
|
|
107
107
|
J,
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
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__ */
|
|
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__ */
|
|
119
|
-
/* @__PURE__ */
|
|
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__ */
|
|
132
|
+
/* @__PURE__ */ h("section", {
|
|
123
133
|
"data-tour": "conversation-type-selector",
|
|
124
134
|
className: "space-y-3",
|
|
125
|
-
children: [/* @__PURE__ */
|
|
135
|
+
children: [/* @__PURE__ */ m("h3", {
|
|
126
136
|
className: "text-sm font-semibold text-text-primary",
|
|
127
|
-
children:
|
|
128
|
-
}), /* @__PURE__ */
|
|
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:
|
|
140
|
+
children: P.map((e) => /* @__PURE__ */ h("button", {
|
|
131
141
|
type: "button",
|
|
132
142
|
onClick: () => {
|
|
133
|
-
|
|
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",
|
|
136
|
-
children: [/* @__PURE__ */
|
|
137
|
-
className: n("p-2 rounded-lg",
|
|
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__ */
|
|
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__ */
|
|
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
|
-
!
|
|
159
|
+
!K && /* @__PURE__ */ h("section", {
|
|
150
160
|
"data-tour": "conversation-visibility-selector",
|
|
151
161
|
className: "space-y-3",
|
|
152
162
|
children: [
|
|
153
|
-
/* @__PURE__ */
|
|
163
|
+
/* @__PURE__ */ m("h3", {
|
|
154
164
|
className: "text-sm font-semibold text-text-primary",
|
|
155
|
-
children:
|
|
165
|
+
children: A("conversations.visibility", "Visibility")
|
|
156
166
|
}),
|
|
157
|
-
/* @__PURE__ */
|
|
167
|
+
/* @__PURE__ */ m("div", {
|
|
158
168
|
className: "flex gap-2 flex-wrap",
|
|
159
|
-
children:
|
|
169
|
+
children: F.map((e) => /* @__PURE__ */ h("button", {
|
|
160
170
|
type: "button",
|
|
161
|
-
onClick: () =>
|
|
162
|
-
className: n("flex items-center gap-2 px-4 py-2.5 rounded-lg border text-sm transition-all",
|
|
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__ */
|
|
176
|
+
/* @__PURE__ */ m("p", {
|
|
167
177
|
className: "text-xs text-text-secondary",
|
|
168
|
-
children:
|
|
178
|
+
children: F.find((e) => e.value === H)?.description
|
|
169
179
|
})
|
|
170
180
|
]
|
|
171
181
|
}),
|
|
172
|
-
|
|
182
|
+
q && /* @__PURE__ */ h("section", {
|
|
173
183
|
"data-tour": "conversation-create-details",
|
|
174
184
|
className: "space-y-3",
|
|
175
|
-
children: [/* @__PURE__ */
|
|
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
|
-
|
|
189
|
+
A("conversations.name", "Name"),
|
|
180
190
|
" ",
|
|
181
|
-
/* @__PURE__ */
|
|
191
|
+
/* @__PURE__ */ m("span", {
|
|
182
192
|
className: "text-status-error-text",
|
|
183
193
|
children: "*"
|
|
184
194
|
})
|
|
185
195
|
]
|
|
186
|
-
}), /* @__PURE__ */
|
|
196
|
+
}), /* @__PURE__ */ m("input", {
|
|
187
197
|
id: "conv-title",
|
|
188
198
|
type: "text",
|
|
189
|
-
value:
|
|
190
|
-
onChange: (e) =>
|
|
191
|
-
placeholder:
|
|
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__ */
|
|
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:
|
|
198
|
-
}), /* @__PURE__ */
|
|
207
|
+
children: A("conversations.description", "Description")
|
|
208
|
+
}), /* @__PURE__ */ m("textarea", {
|
|
199
209
|
id: "conv-desc",
|
|
200
|
-
value:
|
|
201
|
-
onChange: (e) =>
|
|
202
|
-
placeholder:
|
|
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__ */
|
|
218
|
+
/* @__PURE__ */ h("section", {
|
|
209
219
|
"data-tour": "conversation-participants",
|
|
210
220
|
className: "space-y-3",
|
|
211
|
-
children: [/* @__PURE__ */
|
|
221
|
+
children: [/* @__PURE__ */ h("h3", {
|
|
212
222
|
className: "text-sm font-semibold text-text-primary",
|
|
213
|
-
children: [
|
|
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__ */
|
|
226
|
+
}) : /* @__PURE__ */ h("span", {
|
|
217
227
|
className: "ml-1.5 text-xs font-normal text-text-secondary",
|
|
218
228
|
children: [
|
|
219
229
|
"(",
|
|
220
|
-
|
|
230
|
+
A("conversations.optional", "optional"),
|
|
221
231
|
")"
|
|
222
232
|
]
|
|
223
233
|
})]
|
|
224
|
-
}), /* @__PURE__ */
|
|
225
|
-
selected:
|
|
226
|
-
onAdd:
|
|
234
|
+
}), /* @__PURE__ */ m(a, {
|
|
235
|
+
selected: W,
|
|
236
|
+
onAdd: de,
|
|
227
237
|
onRemove: X,
|
|
228
|
-
single:
|
|
229
|
-
excludeIds:
|
|
238
|
+
single: K,
|
|
239
|
+
excludeIds: fe
|
|
230
240
|
})]
|
|
231
241
|
}),
|
|
232
|
-
|
|
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:
|
|
244
|
+
children: ue(D)
|
|
235
245
|
})
|
|
236
246
|
]
|
|
237
247
|
}),
|
|
238
|
-
/* @__PURE__ */
|
|
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:
|
|
243
|
-
}), /* @__PURE__ */
|
|
252
|
+
children: A("conversations.cancel", "Cancel")
|
|
253
|
+
}), /* @__PURE__ */ m("button", {
|
|
244
254
|
type: "button",
|
|
245
|
-
onClick: () => void
|
|
246
|
-
disabled: !$ ||
|
|
247
|
-
className: n("inline-flex items-center gap-2 px-5 py-2 text-sm font-medium rounded-lg transition-all", $ && !
|
|
248
|
-
children:
|
|
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 {
|
|
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
|
-
} } })
|
|
113
|
-
|
|
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
|
}
|