organify-ui 0.2.0 → 0.2.2
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/chat-mock-data-DST5Tn_H.d.ts +339 -0
- package/dist/chunk-2Y3W6JSG.js +4143 -0
- package/dist/chunk-2Y3W6JSG.js.map +1 -0
- package/dist/{chunk-7GFTSEVQ.js → chunk-5L6BWKVA.js} +2 -3
- package/dist/chunk-5L6BWKVA.js.map +1 -0
- package/dist/{chunk-YIVDY4T6.js → chunk-AHMA7OV3.js} +69 -4
- package/dist/chunk-AHMA7OV3.js.map +1 -0
- package/dist/chunk-B3N3YGMA.js +456 -0
- package/dist/chunk-B3N3YGMA.js.map +1 -0
- package/dist/chunk-DW4ZGEBH.js +930 -0
- package/dist/chunk-DW4ZGEBH.js.map +1 -0
- package/dist/{chunk-FWI3KZVO.js → chunk-FQA33MF4.js} +2 -4
- package/dist/chunk-FQA33MF4.js.map +1 -0
- package/dist/chunk-WONVWB5R.js +271 -0
- package/dist/chunk-WONVWB5R.js.map +1 -0
- package/dist/components/chat/index.d.ts +55 -0
- package/dist/components/chat/index.js +5 -0
- package/dist/components/chat/index.js.map +1 -0
- package/dist/components/notifications/index.d.ts +204 -0
- package/dist/components/notifications/index.js +5 -0
- package/dist/components/notifications/index.js.map +1 -0
- package/dist/i18n/index.js +1 -1
- package/dist/icons/index.d.ts +10 -1
- package/dist/icons/index.js +1 -1
- package/dist/index.d.ts +1018 -38
- package/dist/index.js +7858 -892
- package/dist/index.js.map +1 -1
- package/dist/providers/theme-provider.js +1 -1
- package/dist/tailwind-preset.d.ts +1 -2
- package/dist/tailwind-preset.js +102 -16
- package/dist/tailwind-preset.js.map +1 -1
- package/dist/tokens/index.d.ts +131 -69
- package/dist/tokens/index.js +1 -1
- package/package.json +25 -3
- package/src/globals.css +591 -257
- package/dist/chunk-7GFTSEVQ.js.map +0 -1
- package/dist/chunk-FWI3KZVO.js.map +0 -1
- package/dist/chunk-WPZJKIZT.js +0 -248
- package/dist/chunk-WPZJKIZT.js.map +0 -1
- package/dist/chunk-YIVDY4T6.js.map +0 -1
|
@@ -0,0 +1,4143 @@
|
|
|
1
|
+
import { cn, TooltipProvider, Tooltip, TooltipTrigger, TooltipContent, ScrollArea, Skeleton, Avatar, AvatarFallback, Badge, useOrganifyApi, useOrganifyUser, useOrganifyWorkspace, useOrganifyGql } from './chunk-B3N3YGMA.js';
|
|
2
|
+
import { OrgPlus, OrgSearch, OrgComment, OrgClose, OrgEdit, OrgTeam, OrgGlobe, OrgLock, OrgCheckCircle, OrgError, OrgWarning, OrgInfo, OrgChevronDown, OrgChevronRight, OrgDoor, OrgTrash, OrgCheck, OrgFolder, OrgChevronLeft } from './chunk-AHMA7OV3.js';
|
|
3
|
+
import * as React7 from 'react';
|
|
4
|
+
import React7__default, { useState, useCallback, useEffect } from 'react';
|
|
5
|
+
import { Slot } from '@radix-ui/react-slot';
|
|
6
|
+
import { cva } from 'class-variance-authority';
|
|
7
|
+
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
8
|
+
import { motion, AnimatePresence } from 'framer-motion';
|
|
9
|
+
import * as DialogPrimitive from '@radix-ui/react-dialog';
|
|
10
|
+
import { ChevronDown, ChevronUp, Check, XIcon } from 'lucide-react';
|
|
11
|
+
import { Drawer as Drawer$1 } from 'vaul';
|
|
12
|
+
import * as TabsPrimitive from '@radix-ui/react-tabs';
|
|
13
|
+
import * as SeparatorPrimitive from '@radix-ui/react-separator';
|
|
14
|
+
import * as SelectPrimitive from '@radix-ui/react-select';
|
|
15
|
+
|
|
16
|
+
var orgLoaderVariants = cva("relative inline-flex items-center justify-center", {
|
|
17
|
+
variants: {
|
|
18
|
+
size: {
|
|
19
|
+
xs: "h-4 w-4",
|
|
20
|
+
sm: "h-6 w-6",
|
|
21
|
+
default: "h-10 w-10",
|
|
22
|
+
lg: "h-16 w-16",
|
|
23
|
+
xl: "h-24 w-24",
|
|
24
|
+
full: "h-32 w-32"
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
defaultVariants: {
|
|
28
|
+
size: "default"
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
var diamondSizes = {
|
|
32
|
+
xs: 3,
|
|
33
|
+
sm: 4,
|
|
34
|
+
default: 6,
|
|
35
|
+
lg: 8,
|
|
36
|
+
xl: 10,
|
|
37
|
+
full: 14
|
|
38
|
+
};
|
|
39
|
+
var strokeWidths = {
|
|
40
|
+
xs: 1.5,
|
|
41
|
+
sm: 1.5,
|
|
42
|
+
default: 2,
|
|
43
|
+
lg: 2,
|
|
44
|
+
xl: 2.5,
|
|
45
|
+
full: 2.5
|
|
46
|
+
};
|
|
47
|
+
function OrgLoader({
|
|
48
|
+
size = "default",
|
|
49
|
+
caption,
|
|
50
|
+
overlay = false,
|
|
51
|
+
className,
|
|
52
|
+
...props
|
|
53
|
+
}) {
|
|
54
|
+
const s = size ?? "default";
|
|
55
|
+
const d = diamondSizes[s];
|
|
56
|
+
const sw = strokeWidths[s];
|
|
57
|
+
const loader = /* @__PURE__ */ jsxs(
|
|
58
|
+
"div",
|
|
59
|
+
{
|
|
60
|
+
className: cn(
|
|
61
|
+
"flex flex-col items-center gap-3",
|
|
62
|
+
overlay && "pointer-events-none",
|
|
63
|
+
className
|
|
64
|
+
),
|
|
65
|
+
role: "status",
|
|
66
|
+
"aria-label": "Loading",
|
|
67
|
+
...props,
|
|
68
|
+
children: [
|
|
69
|
+
/* @__PURE__ */ jsx("div", { className: cn(orgLoaderVariants({ size })), children: /* @__PURE__ */ jsxs("svg", { className: "h-full w-full", viewBox: "0 0 48 48", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [
|
|
70
|
+
/* @__PURE__ */ jsx(
|
|
71
|
+
"circle",
|
|
72
|
+
{
|
|
73
|
+
cx: "24",
|
|
74
|
+
cy: "24",
|
|
75
|
+
r: "18",
|
|
76
|
+
className: "stroke-current opacity-10 dark:opacity-[0.08]",
|
|
77
|
+
strokeWidth: sw
|
|
78
|
+
}
|
|
79
|
+
),
|
|
80
|
+
/* @__PURE__ */ jsx(
|
|
81
|
+
"circle",
|
|
82
|
+
{
|
|
83
|
+
cx: "24",
|
|
84
|
+
cy: "24",
|
|
85
|
+
r: "18",
|
|
86
|
+
stroke: "url(#org-loader-gradient)",
|
|
87
|
+
strokeWidth: sw,
|
|
88
|
+
strokeLinecap: "round",
|
|
89
|
+
strokeDasharray: "85 28.27",
|
|
90
|
+
className: "origin-center",
|
|
91
|
+
style: { animation: "org-loader-spin 1.4s cubic-bezier(0.4, 0, 0.2, 1) infinite" }
|
|
92
|
+
}
|
|
93
|
+
),
|
|
94
|
+
/* @__PURE__ */ jsx("g", { style: { animation: "org-loader-orbit 1.4s cubic-bezier(0.4, 0, 0.2, 1) infinite" }, className: "origin-center", children: /* @__PURE__ */ jsx(
|
|
95
|
+
"rect",
|
|
96
|
+
{
|
|
97
|
+
x: 24 - d / 2,
|
|
98
|
+
y: 6 - d / 2,
|
|
99
|
+
width: d,
|
|
100
|
+
height: d,
|
|
101
|
+
rx: d * 0.15,
|
|
102
|
+
transform: `rotate(45 24 6)`,
|
|
103
|
+
fill: "url(#org-loader-diamond)",
|
|
104
|
+
className: "drop-shadow-[0_0_6px_rgba(79,57,246,0.5)]"
|
|
105
|
+
}
|
|
106
|
+
) }),
|
|
107
|
+
/* @__PURE__ */ jsx(
|
|
108
|
+
"circle",
|
|
109
|
+
{
|
|
110
|
+
cx: "24",
|
|
111
|
+
cy: "24",
|
|
112
|
+
r: "2",
|
|
113
|
+
className: "fill-current opacity-20",
|
|
114
|
+
style: { animation: "org-loader-pulse 1.4s ease-in-out infinite" }
|
|
115
|
+
}
|
|
116
|
+
),
|
|
117
|
+
/* @__PURE__ */ jsxs("defs", { children: [
|
|
118
|
+
/* @__PURE__ */ jsxs("linearGradient", { id: "org-loader-gradient", x1: "6", y1: "6", x2: "42", y2: "42", gradientUnits: "userSpaceOnUse", children: [
|
|
119
|
+
/* @__PURE__ */ jsx("stop", { stopColor: "#4F39F6" }),
|
|
120
|
+
/* @__PURE__ */ jsx("stop", { offset: "0.5", stopColor: "#5b42f2" }),
|
|
121
|
+
/* @__PURE__ */ jsx("stop", { offset: "1", stopColor: "#241979" })
|
|
122
|
+
] }),
|
|
123
|
+
/* @__PURE__ */ jsxs("linearGradient", { id: "org-loader-diamond", x1: "0", y1: "0", x2: "1", y2: "1", children: [
|
|
124
|
+
/* @__PURE__ */ jsx("stop", { stopColor: "#4F39F6" }),
|
|
125
|
+
/* @__PURE__ */ jsx("stop", { offset: "1", stopColor: "#241979" })
|
|
126
|
+
] })
|
|
127
|
+
] })
|
|
128
|
+
] }) }),
|
|
129
|
+
caption && /* @__PURE__ */ jsx("span", { className: cn(
|
|
130
|
+
"font-mono uppercase tracking-widest text-current opacity-40",
|
|
131
|
+
s === "xs" || s === "sm" ? "text-[8px]" : "text-[10px]"
|
|
132
|
+
), children: caption })
|
|
133
|
+
]
|
|
134
|
+
}
|
|
135
|
+
);
|
|
136
|
+
if (overlay) {
|
|
137
|
+
return /* @__PURE__ */ jsx("div", { className: "fixed inset-0 z-50 flex items-center justify-center bg-black/40 backdrop-blur-sm dark:bg-black/60", children: loader });
|
|
138
|
+
}
|
|
139
|
+
return loader;
|
|
140
|
+
}
|
|
141
|
+
function OrgLoaderInline({ className, ...props }) {
|
|
142
|
+
return /* @__PURE__ */ jsxs(
|
|
143
|
+
"svg",
|
|
144
|
+
{
|
|
145
|
+
className: cn("h-4 w-4", className),
|
|
146
|
+
viewBox: "0 0 48 48",
|
|
147
|
+
fill: "none",
|
|
148
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
149
|
+
role: "status",
|
|
150
|
+
"aria-label": "Loading",
|
|
151
|
+
...props,
|
|
152
|
+
children: [
|
|
153
|
+
/* @__PURE__ */ jsx("circle", { cx: "24", cy: "24", r: "18", className: "stroke-current opacity-10", strokeWidth: "2" }),
|
|
154
|
+
/* @__PURE__ */ jsx(
|
|
155
|
+
"circle",
|
|
156
|
+
{
|
|
157
|
+
cx: "24",
|
|
158
|
+
cy: "24",
|
|
159
|
+
r: "18",
|
|
160
|
+
stroke: "currentColor",
|
|
161
|
+
strokeWidth: "2",
|
|
162
|
+
strokeLinecap: "round",
|
|
163
|
+
strokeDasharray: "85 28.27",
|
|
164
|
+
className: "origin-center",
|
|
165
|
+
style: { animation: "org-loader-spin 1.4s cubic-bezier(0.4, 0, 0.2, 1) infinite" }
|
|
166
|
+
}
|
|
167
|
+
),
|
|
168
|
+
/* @__PURE__ */ jsx("g", { style: { animation: "org-loader-orbit 1.4s cubic-bezier(0.4, 0, 0.2, 1) infinite" }, className: "origin-center", children: /* @__PURE__ */ jsx("rect", { x: "21", y: "3", width: "6", height: "6", rx: "1", transform: "rotate(45 24 6)", fill: "currentColor" }) })
|
|
169
|
+
]
|
|
170
|
+
}
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
var buttonVariants = cva(
|
|
174
|
+
"inline-flex items-center justify-center gap-2 whitespace-nowrap text-sm font-medium tracking-wide uppercase rounded-sm transition-all duration-[400ms] ease-[cubic-bezier(0.25,1,0.5,1)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-light disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
|
|
175
|
+
{
|
|
176
|
+
variants: {
|
|
177
|
+
variant: {
|
|
178
|
+
default: "bg-gradient-to-br from-primary-light to-primary !text-white hover:shadow-glow shadow-glow-primary",
|
|
179
|
+
secondary: "bg-transparent border border-glass-border-strong text-org-text hover:border-primary-light hover:bg-glass-highlight",
|
|
180
|
+
ghost: "bg-transparent text-org-text-secondary hover:bg-glass-highlight hover:text-org-text rounded-sm",
|
|
181
|
+
destructive: "bg-error !text-white hover:bg-error/90 shadow-glow-error",
|
|
182
|
+
outline: "bg-transparent border border-glass-border-strong text-org-text hover:bg-glass-highlight hover:border-primary-light",
|
|
183
|
+
link: "text-primary-light underline-offset-4 hover:underline rounded-none",
|
|
184
|
+
// ─── Cream Light theme buttons ─────────────
|
|
185
|
+
cream: "bg-primary text-white hover:shadow-cream-btn-primary hover:translate-y-[-1px] shadow-cream-btn",
|
|
186
|
+
"cream-secondary": "bg-transparent border border-primary text-primary hover:bg-primary/[0.03]"
|
|
187
|
+
},
|
|
188
|
+
size: {
|
|
189
|
+
default: "px-8 py-3",
|
|
190
|
+
sm: "px-4 py-2 text-xs",
|
|
191
|
+
lg: "px-10 py-4 text-base",
|
|
192
|
+
icon: "h-10 w-10",
|
|
193
|
+
"icon-sm": "h-8 w-8",
|
|
194
|
+
"icon-lg": "h-12 w-12"
|
|
195
|
+
}
|
|
196
|
+
},
|
|
197
|
+
defaultVariants: {
|
|
198
|
+
variant: "default",
|
|
199
|
+
size: "default"
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
);
|
|
203
|
+
var Button = React7.forwardRef(
|
|
204
|
+
({ className, variant, size, asChild = false, loading = false, children, disabled, ...props }, ref) => {
|
|
205
|
+
const Comp = asChild ? Slot : "button";
|
|
206
|
+
return /* @__PURE__ */ jsx(
|
|
207
|
+
Comp,
|
|
208
|
+
{
|
|
209
|
+
className: cn(buttonVariants({ variant, size, className })),
|
|
210
|
+
ref,
|
|
211
|
+
disabled: disabled || loading,
|
|
212
|
+
...props,
|
|
213
|
+
children: loading ? /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
214
|
+
/* @__PURE__ */ jsx(OrgLoaderInline, { className: "shrink-0" }),
|
|
215
|
+
children
|
|
216
|
+
] }) : children
|
|
217
|
+
}
|
|
218
|
+
);
|
|
219
|
+
}
|
|
220
|
+
);
|
|
221
|
+
Button.displayName = "Button";
|
|
222
|
+
var inputVariants = cva(
|
|
223
|
+
"flex w-full bg-glass-highlight backdrop-blur-md border border-glass-border rounded-sm px-4 py-3 text-sm font-light text-org-text transition-all duration-[400ms] ease-[cubic-bezier(0.25,1,0.5,1)] file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-org-text-muted focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50",
|
|
224
|
+
{
|
|
225
|
+
variants: {
|
|
226
|
+
variant: {
|
|
227
|
+
default: "focus:border-primary-light/50 focus:shadow-[0_0_0_3px_rgba(79,57,246,0.10),0_0_20px_rgba(79,57,246,0.08)]",
|
|
228
|
+
flat: "rounded-sm focus:border-primary-light/50 focus:shadow-[0_0_0_3px_rgba(79,57,246,0.10)]",
|
|
229
|
+
rounded: "rounded-sm focus:border-primary-light/50 focus:shadow-[0_0_0_3px_rgba(79,57,246,0.10)]",
|
|
230
|
+
error: "border-error/30 bg-error/5 text-red-100 placeholder:text-red-500/30 focus:border-error focus:shadow-[0_0_0_3px_rgba(224,17,95,0.10)]",
|
|
231
|
+
// ─── Cream Light theme (explicit light styling) ─────────────────────
|
|
232
|
+
cream: "bg-white/60 border-[rgba(36,25,121,0.06)] text-neutral-900 placeholder:text-neutral-400 focus:border-primary/40 focus:bg-white/80 focus:shadow-[0_0_0_3px_rgba(79,57,246,0.08)] backdrop-blur-sm"
|
|
233
|
+
}
|
|
234
|
+
},
|
|
235
|
+
defaultVariants: {
|
|
236
|
+
variant: "default"
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
);
|
|
240
|
+
var Input = React7.forwardRef(
|
|
241
|
+
({ className, variant, type, label, error, labelPosition = "left", ...props }, ref) => {
|
|
242
|
+
const id = React7.useId();
|
|
243
|
+
const effectiveVariant = error ? "error" : variant;
|
|
244
|
+
return /* @__PURE__ */ jsxs("div", { className: "relative group", children: [
|
|
245
|
+
label && /* @__PURE__ */ jsx(
|
|
246
|
+
"label",
|
|
247
|
+
{
|
|
248
|
+
htmlFor: id,
|
|
249
|
+
className: cn(
|
|
250
|
+
"absolute -top-3 z-10 px-2 text-label uppercase tracking-widest",
|
|
251
|
+
effectiveVariant === "cream" ? "bg-cream-base" : "bg-surface",
|
|
252
|
+
labelPosition === "left" ? "left-0 ml-2" : "right-0 mr-2",
|
|
253
|
+
error ? "text-error" : "text-primary-light"
|
|
254
|
+
),
|
|
255
|
+
children: label
|
|
256
|
+
}
|
|
257
|
+
),
|
|
258
|
+
/* @__PURE__ */ jsx(
|
|
259
|
+
"input",
|
|
260
|
+
{
|
|
261
|
+
id,
|
|
262
|
+
type,
|
|
263
|
+
className: cn(inputVariants({ variant: effectiveVariant, className })),
|
|
264
|
+
ref,
|
|
265
|
+
"aria-invalid": !!error,
|
|
266
|
+
"aria-describedby": error ? `${id}-error` : void 0,
|
|
267
|
+
...props
|
|
268
|
+
}
|
|
269
|
+
),
|
|
270
|
+
error && /* @__PURE__ */ jsx("div", { id: `${id}-error`, className: "mt-2 flex items-center justify-end gap-2", children: /* @__PURE__ */ jsx("span", { className: "text-mono-xs text-error", children: error }) })
|
|
271
|
+
] });
|
|
272
|
+
}
|
|
273
|
+
);
|
|
274
|
+
Input.displayName = "Input";
|
|
275
|
+
function getRoomIcon(room) {
|
|
276
|
+
if (room.type === "DIRECT") return /* @__PURE__ */ jsx(OrgComment, { className: "w-4 h-4" });
|
|
277
|
+
if (room.visibility === "PRIVATE") return /* @__PURE__ */ jsx(OrgLock, { className: "w-3.5 h-3.5" });
|
|
278
|
+
return /* @__PURE__ */ jsx("span", { className: "text-sm font-medium", children: "#" });
|
|
279
|
+
}
|
|
280
|
+
function ChatSidebar({
|
|
281
|
+
rooms,
|
|
282
|
+
activeRoomId,
|
|
283
|
+
loading,
|
|
284
|
+
onSelectRoom,
|
|
285
|
+
onCreateRoom,
|
|
286
|
+
onMobileClose
|
|
287
|
+
}) {
|
|
288
|
+
const [search, setSearch] = React7.useState("");
|
|
289
|
+
const [channelsOpen, setChannelsOpen] = React7.useState(true);
|
|
290
|
+
const [dmsOpen, setDmsOpen] = React7.useState(true);
|
|
291
|
+
const filtered = React7.useMemo(
|
|
292
|
+
() => search ? rooms.filter(
|
|
293
|
+
(r) => r.name?.toLowerCase().includes(search.toLowerCase()) || r.slug?.toLowerCase().includes(search.toLowerCase())
|
|
294
|
+
) : rooms,
|
|
295
|
+
[rooms, search]
|
|
296
|
+
);
|
|
297
|
+
const channels = filtered.filter((r) => r.type === "CHANNEL");
|
|
298
|
+
const dms = filtered.filter((r) => r.type === "DIRECT");
|
|
299
|
+
const handleSelect = (roomId) => {
|
|
300
|
+
onSelectRoom(roomId);
|
|
301
|
+
onMobileClose?.();
|
|
302
|
+
};
|
|
303
|
+
const renderRoom = (room) => /* @__PURE__ */ jsxs(
|
|
304
|
+
"button",
|
|
305
|
+
{
|
|
306
|
+
onClick: () => handleSelect(room.id),
|
|
307
|
+
className: cn(
|
|
308
|
+
"flex items-center gap-2.5 w-full px-2.5 py-2 rounded-sm text-left text-sm transition-all duration-[400ms] ease-[cubic-bezier(0.25,1,0.5,1)]",
|
|
309
|
+
"active:scale-[0.98]",
|
|
310
|
+
room.id === activeRoomId ? "bg-primary/12 text-primary-light" : "text-theme-secondary hover:bg-theme-subtle hover:text-theme"
|
|
311
|
+
),
|
|
312
|
+
children: [
|
|
313
|
+
/* @__PURE__ */ jsx(
|
|
314
|
+
"span",
|
|
315
|
+
{
|
|
316
|
+
className: cn(
|
|
317
|
+
"flex items-center justify-center h-7 w-7 rounded-sm text-sm shrink-0",
|
|
318
|
+
room.id === activeRoomId ? "bg-primary/20 text-primary-light" : "bg-theme-subtle-10 text-theme-muted"
|
|
319
|
+
),
|
|
320
|
+
children: getRoomIcon(room)
|
|
321
|
+
}
|
|
322
|
+
),
|
|
323
|
+
/* @__PURE__ */ jsx("div", { className: "flex-1 min-w-0", children: /* @__PURE__ */ jsx("div", { className: "font-medium text-[13px] truncate", children: room.name || "Sem nome" }) }),
|
|
324
|
+
room.unreadCount > 0 && /* @__PURE__ */ jsx(
|
|
325
|
+
Badge,
|
|
326
|
+
{
|
|
327
|
+
variant: "primary",
|
|
328
|
+
className: "text-[10px] px-1.5 py-0 min-w-[18px] h-[18px] flex items-center justify-center rounded-full animate-in zoom-in-50 duration-[400ms]",
|
|
329
|
+
children: room.unreadCount > 99 ? "99+" : room.unreadCount
|
|
330
|
+
}
|
|
331
|
+
)
|
|
332
|
+
]
|
|
333
|
+
},
|
|
334
|
+
room.id
|
|
335
|
+
);
|
|
336
|
+
const renderSection = (title, items, isOpen, toggle) => /* @__PURE__ */ jsxs("div", { className: "mb-1", children: [
|
|
337
|
+
/* @__PURE__ */ jsxs(
|
|
338
|
+
"button",
|
|
339
|
+
{
|
|
340
|
+
onClick: toggle,
|
|
341
|
+
className: "flex items-center gap-1 w-full px-2.5 py-1.5 text-[11px] font-semibold uppercase tracking-wider text-theme-muted hover:text-theme-secondary transition-colors",
|
|
342
|
+
children: [
|
|
343
|
+
isOpen ? /* @__PURE__ */ jsx(OrgChevronDown, { className: "w-3 h-3" }) : /* @__PURE__ */ jsx(OrgChevronRight, { className: "w-3 h-3" }),
|
|
344
|
+
/* @__PURE__ */ jsx("span", { children: title }),
|
|
345
|
+
/* @__PURE__ */ jsx("span", { className: "ml-auto text-[10px] font-normal", children: items.length })
|
|
346
|
+
]
|
|
347
|
+
}
|
|
348
|
+
),
|
|
349
|
+
isOpen && /* @__PURE__ */ jsx("div", { className: "space-y-0.5 animate-in fade-in-0 slide-in-from-top-1 duration-[400ms]", children: items.map(renderRoom) })
|
|
350
|
+
] });
|
|
351
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex flex-col h-full border-r border-theme-subtle bg-theme-surface", children: [
|
|
352
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-4 py-3 border-b border-theme-subtle", children: [
|
|
353
|
+
/* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold text-theme", children: "Chat" }),
|
|
354
|
+
/* @__PURE__ */ jsx(TooltipProvider, { children: /* @__PURE__ */ jsxs(Tooltip, { children: [
|
|
355
|
+
/* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
|
|
356
|
+
Button,
|
|
357
|
+
{
|
|
358
|
+
variant: "ghost",
|
|
359
|
+
size: "sm",
|
|
360
|
+
onClick: onCreateRoom,
|
|
361
|
+
className: "h-7 w-7 p-0 text-theme-muted hover:text-primary-light hover:bg-primary/10 transition-colors",
|
|
362
|
+
children: /* @__PURE__ */ jsx(OrgPlus, { className: "w-4 h-4" })
|
|
363
|
+
}
|
|
364
|
+
) }),
|
|
365
|
+
/* @__PURE__ */ jsx(TooltipContent, { children: "Nova conversa" })
|
|
366
|
+
] }) })
|
|
367
|
+
] }),
|
|
368
|
+
/* @__PURE__ */ jsx("div", { className: "px-3 py-2", children: /* @__PURE__ */ jsxs("div", { className: "relative", children: [
|
|
369
|
+
/* @__PURE__ */ jsx(OrgSearch, { className: "absolute left-2.5 top-1/2 -translate-y-1/2 w-3.5 h-3.5 text-theme-muted" }),
|
|
370
|
+
/* @__PURE__ */ jsx(
|
|
371
|
+
Input,
|
|
372
|
+
{
|
|
373
|
+
variant: "flat",
|
|
374
|
+
placeholder: "Pesquisar...",
|
|
375
|
+
value: search,
|
|
376
|
+
onChange: (e) => setSearch(e.target.value),
|
|
377
|
+
className: "h-8 text-xs bg-theme-subtle border-theme-subtle rounded-sm pl-8"
|
|
378
|
+
}
|
|
379
|
+
)
|
|
380
|
+
] }) }),
|
|
381
|
+
/* @__PURE__ */ jsx(ScrollArea, { className: "flex-1", children: /* @__PURE__ */ jsx("div", { className: "px-2 pb-2", children: loading ? /* @__PURE__ */ jsx("div", { className: "space-y-0.5", children: Array.from({ length: 6 }).map((_, i) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 px-3 py-2", children: [
|
|
382
|
+
/* @__PURE__ */ jsx(Skeleton, { variant: "rounded", className: "h-7 w-7 shrink-0" }),
|
|
383
|
+
/* @__PURE__ */ jsx("div", { className: "flex-1 space-y-1.5", children: /* @__PURE__ */ jsx(Skeleton, { className: "h-3 w-24" }) })
|
|
384
|
+
] }, i)) }) : filtered.length === 0 ? /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center py-8 text-theme-muted", children: [
|
|
385
|
+
/* @__PURE__ */ jsx(OrgSearch, { className: "w-6 h-6 mb-2 opacity-40" }),
|
|
386
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs", children: search ? "Sem resultados" : "Sem conversas" }),
|
|
387
|
+
!search && /* @__PURE__ */ jsx(
|
|
388
|
+
"button",
|
|
389
|
+
{
|
|
390
|
+
onClick: onCreateRoom,
|
|
391
|
+
className: "mt-3 text-xs text-primary-light/70 hover:text-primary-light transition-colors",
|
|
392
|
+
children: "Criar primeira conversa \u2192"
|
|
393
|
+
}
|
|
394
|
+
)
|
|
395
|
+
] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
396
|
+
channels.length > 0 && renderSection("Canais", channels, channelsOpen, () => setChannelsOpen(!channelsOpen)),
|
|
397
|
+
dms.length > 0 && renderSection("Mensagens Diretas", dms, dmsOpen, () => setDmsOpen(!dmsOpen))
|
|
398
|
+
] }) }) })
|
|
399
|
+
] });
|
|
400
|
+
}
|
|
401
|
+
var actionBarVariants = {
|
|
402
|
+
hidden: {
|
|
403
|
+
opacity: 0,
|
|
404
|
+
y: 8,
|
|
405
|
+
scale: 0.95
|
|
406
|
+
},
|
|
407
|
+
visible: {
|
|
408
|
+
opacity: 1,
|
|
409
|
+
y: 0,
|
|
410
|
+
scale: 1,
|
|
411
|
+
transition: {
|
|
412
|
+
type: "spring",
|
|
413
|
+
stiffness: 400,
|
|
414
|
+
damping: 25,
|
|
415
|
+
staggerChildren: 0.03
|
|
416
|
+
}
|
|
417
|
+
},
|
|
418
|
+
exit: {
|
|
419
|
+
opacity: 0,
|
|
420
|
+
y: 4,
|
|
421
|
+
scale: 0.98,
|
|
422
|
+
transition: { duration: 0.15 }
|
|
423
|
+
}
|
|
424
|
+
};
|
|
425
|
+
var actionItemVariants = {
|
|
426
|
+
hidden: { opacity: 0, scale: 0.8 },
|
|
427
|
+
visible: {
|
|
428
|
+
opacity: 1,
|
|
429
|
+
scale: 1,
|
|
430
|
+
transition: { type: "spring", stiffness: 500, damping: 30 }
|
|
431
|
+
}
|
|
432
|
+
};
|
|
433
|
+
var replyPreviewVariants = {
|
|
434
|
+
hidden: { opacity: 0, x: -10, height: 0 },
|
|
435
|
+
visible: {
|
|
436
|
+
opacity: 1,
|
|
437
|
+
x: 0,
|
|
438
|
+
height: "auto",
|
|
439
|
+
transition: { duration: 0.2, ease: "easeOut" }
|
|
440
|
+
}
|
|
441
|
+
};
|
|
442
|
+
var reactionVariants = {
|
|
443
|
+
hidden: { scale: 0, opacity: 0 },
|
|
444
|
+
visible: {
|
|
445
|
+
scale: 1,
|
|
446
|
+
opacity: 1,
|
|
447
|
+
transition: { type: "spring", stiffness: 500, damping: 25 }
|
|
448
|
+
},
|
|
449
|
+
tap: { scale: 1.2 }
|
|
450
|
+
};
|
|
451
|
+
function formatTime(iso) {
|
|
452
|
+
return new Date(iso).toLocaleTimeString("pt-BR", {
|
|
453
|
+
hour: "2-digit",
|
|
454
|
+
minute: "2-digit"
|
|
455
|
+
});
|
|
456
|
+
}
|
|
457
|
+
function getInitials(name) {
|
|
458
|
+
if (!name) return "??";
|
|
459
|
+
return name.split(" ").map((w) => w[0]).slice(0, 2).join("").toUpperCase();
|
|
460
|
+
}
|
|
461
|
+
function groupReactions(reactions) {
|
|
462
|
+
const map = /* @__PURE__ */ new Map();
|
|
463
|
+
reactions.forEach((r) => {
|
|
464
|
+
const existing = map.get(r.emoji);
|
|
465
|
+
if (existing) {
|
|
466
|
+
existing.count++;
|
|
467
|
+
existing.users.push(r.userId);
|
|
468
|
+
} else {
|
|
469
|
+
map.set(r.emoji, { count: 1, users: [r.userId] });
|
|
470
|
+
}
|
|
471
|
+
});
|
|
472
|
+
return Array.from(map.entries());
|
|
473
|
+
}
|
|
474
|
+
function truncate(str, len) {
|
|
475
|
+
if (str.length <= len) return str;
|
|
476
|
+
return str.slice(0, len) + "...";
|
|
477
|
+
}
|
|
478
|
+
var quickEmojis = ["\u{1F44D}", "\u2764\uFE0F", "\u{1F602}", "\u{1F62E}", "\u{1F622}", "\u{1F389}"];
|
|
479
|
+
function renderContent(content) {
|
|
480
|
+
const parts = content.split(/(@\w+)/g);
|
|
481
|
+
return parts.map((part, i) => {
|
|
482
|
+
if (part.startsWith("@")) {
|
|
483
|
+
return /* @__PURE__ */ jsx(
|
|
484
|
+
motion.span,
|
|
485
|
+
{
|
|
486
|
+
className: "text-primary-light font-medium hover:underline cursor-pointer",
|
|
487
|
+
whileHover: { scale: 1.02 },
|
|
488
|
+
whileTap: { scale: 0.98 },
|
|
489
|
+
children: part
|
|
490
|
+
},
|
|
491
|
+
i
|
|
492
|
+
);
|
|
493
|
+
}
|
|
494
|
+
return part;
|
|
495
|
+
});
|
|
496
|
+
}
|
|
497
|
+
function ActionBar({
|
|
498
|
+
show,
|
|
499
|
+
onClose,
|
|
500
|
+
onEdit,
|
|
501
|
+
onReply,
|
|
502
|
+
onDelete,
|
|
503
|
+
onReact,
|
|
504
|
+
canEdit,
|
|
505
|
+
canDelete,
|
|
506
|
+
position
|
|
507
|
+
}) {
|
|
508
|
+
return /* @__PURE__ */ jsx(AnimatePresence, { children: show && /* @__PURE__ */ jsxs(
|
|
509
|
+
motion.div,
|
|
510
|
+
{
|
|
511
|
+
variants: actionBarVariants,
|
|
512
|
+
initial: "hidden",
|
|
513
|
+
animate: "visible",
|
|
514
|
+
exit: "exit",
|
|
515
|
+
className: cn(
|
|
516
|
+
"absolute z-10 -top-12 flex items-center gap-1 p-1.5 rounded-sm",
|
|
517
|
+
"bg-theme-surface/95 backdrop-blur-md border border-theme-subtle shadow-lg",
|
|
518
|
+
position === "right" ? "right-0" : "left-0"
|
|
519
|
+
),
|
|
520
|
+
children: [
|
|
521
|
+
onReact && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
522
|
+
quickEmojis.slice(0, 4).map((emoji) => /* @__PURE__ */ jsx(
|
|
523
|
+
motion.button,
|
|
524
|
+
{
|
|
525
|
+
variants: actionItemVariants,
|
|
526
|
+
whileHover: { scale: 1.15 },
|
|
527
|
+
whileTap: { scale: 0.9 },
|
|
528
|
+
onClick: () => {
|
|
529
|
+
onReact(emoji);
|
|
530
|
+
onClose();
|
|
531
|
+
},
|
|
532
|
+
className: "w-8 h-8 flex items-center justify-center rounded-sm hover:bg-theme-highlight text-base transition-colors",
|
|
533
|
+
children: emoji
|
|
534
|
+
},
|
|
535
|
+
emoji
|
|
536
|
+
)),
|
|
537
|
+
/* @__PURE__ */ jsx("div", { className: "w-px h-6 bg-theme-subtle mx-1" })
|
|
538
|
+
] }),
|
|
539
|
+
onReply && /* @__PURE__ */ jsx(TooltipProvider, { children: /* @__PURE__ */ jsxs(Tooltip, { children: [
|
|
540
|
+
/* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
|
|
541
|
+
motion.button,
|
|
542
|
+
{
|
|
543
|
+
variants: actionItemVariants,
|
|
544
|
+
whileHover: { scale: 1.1 },
|
|
545
|
+
whileTap: { scale: 0.9 },
|
|
546
|
+
onClick: () => {
|
|
547
|
+
onReply();
|
|
548
|
+
onClose();
|
|
549
|
+
},
|
|
550
|
+
className: "p-2 rounded-sm text-theme-muted hover:text-theme hover:bg-theme-highlight transition-colors",
|
|
551
|
+
children: /* @__PURE__ */ jsx(OrgComment, { className: "w-4 h-4" })
|
|
552
|
+
}
|
|
553
|
+
) }),
|
|
554
|
+
/* @__PURE__ */ jsx(TooltipContent, { side: "top", children: "Responder" })
|
|
555
|
+
] }) }),
|
|
556
|
+
canEdit && onEdit && /* @__PURE__ */ jsx(TooltipProvider, { children: /* @__PURE__ */ jsxs(Tooltip, { children: [
|
|
557
|
+
/* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
|
|
558
|
+
motion.button,
|
|
559
|
+
{
|
|
560
|
+
variants: actionItemVariants,
|
|
561
|
+
whileHover: { scale: 1.1 },
|
|
562
|
+
whileTap: { scale: 0.9 },
|
|
563
|
+
onClick: () => {
|
|
564
|
+
onEdit();
|
|
565
|
+
onClose();
|
|
566
|
+
},
|
|
567
|
+
className: "p-2 rounded-sm text-theme-muted hover:text-theme hover:bg-theme-highlight transition-colors",
|
|
568
|
+
children: /* @__PURE__ */ jsx(OrgEdit, { className: "w-4 h-4" })
|
|
569
|
+
}
|
|
570
|
+
) }),
|
|
571
|
+
/* @__PURE__ */ jsx(TooltipContent, { side: "top", children: "Editar" })
|
|
572
|
+
] }) }),
|
|
573
|
+
canDelete && onDelete && /* @__PURE__ */ jsx(TooltipProvider, { children: /* @__PURE__ */ jsxs(Tooltip, { children: [
|
|
574
|
+
/* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
|
|
575
|
+
motion.button,
|
|
576
|
+
{
|
|
577
|
+
variants: actionItemVariants,
|
|
578
|
+
whileHover: { scale: 1.1 },
|
|
579
|
+
whileTap: { scale: 0.9 },
|
|
580
|
+
onClick: () => {
|
|
581
|
+
onDelete();
|
|
582
|
+
onClose();
|
|
583
|
+
},
|
|
584
|
+
className: "p-2 rounded-sm text-rose-400 hover:text-rose-300 hover:bg-rose-500/10 transition-colors",
|
|
585
|
+
children: /* @__PURE__ */ jsx(OrgTrash, { className: "w-4 h-4" })
|
|
586
|
+
}
|
|
587
|
+
) }),
|
|
588
|
+
/* @__PURE__ */ jsx(TooltipContent, { side: "top", children: "Excluir" })
|
|
589
|
+
] }) }),
|
|
590
|
+
/* @__PURE__ */ jsx(
|
|
591
|
+
motion.button,
|
|
592
|
+
{
|
|
593
|
+
variants: actionItemVariants,
|
|
594
|
+
whileHover: { scale: 1.1 },
|
|
595
|
+
whileTap: { scale: 0.9 },
|
|
596
|
+
onClick: onClose,
|
|
597
|
+
className: "p-2 rounded-sm text-theme-muted hover:text-theme hover:bg-theme-highlight transition-colors md:hidden",
|
|
598
|
+
children: /* @__PURE__ */ jsx(OrgClose, { className: "w-4 h-4" })
|
|
599
|
+
}
|
|
600
|
+
)
|
|
601
|
+
]
|
|
602
|
+
}
|
|
603
|
+
) });
|
|
604
|
+
}
|
|
605
|
+
function ReactionPill({ emoji, count, onClick }) {
|
|
606
|
+
return /* @__PURE__ */ jsxs(
|
|
607
|
+
motion.button,
|
|
608
|
+
{
|
|
609
|
+
variants: reactionVariants,
|
|
610
|
+
initial: "hidden",
|
|
611
|
+
animate: "visible",
|
|
612
|
+
whileHover: { scale: 1.05 },
|
|
613
|
+
whileTap: "tap",
|
|
614
|
+
onClick,
|
|
615
|
+
className: "inline-flex items-center gap-1 px-2 py-1 rounded-full text-xs bg-theme-subtle border border-theme-subtle hover:bg-primary/10 hover:border-primary/20 transition-colors",
|
|
616
|
+
children: [
|
|
617
|
+
/* @__PURE__ */ jsx(
|
|
618
|
+
motion.span,
|
|
619
|
+
{
|
|
620
|
+
animate: { rotate: [0, -10, 10, 0] },
|
|
621
|
+
transition: { duration: 0.3, delay: 0.1 },
|
|
622
|
+
children: emoji
|
|
623
|
+
}
|
|
624
|
+
),
|
|
625
|
+
/* @__PURE__ */ jsx("span", { className: "text-theme-muted font-medium", children: count })
|
|
626
|
+
]
|
|
627
|
+
}
|
|
628
|
+
);
|
|
629
|
+
}
|
|
630
|
+
function MessageBubble({
|
|
631
|
+
message,
|
|
632
|
+
isCurrentUser,
|
|
633
|
+
replyToMessage,
|
|
634
|
+
onEdit,
|
|
635
|
+
onReply,
|
|
636
|
+
onDelete,
|
|
637
|
+
onReact,
|
|
638
|
+
onScrollToMessage,
|
|
639
|
+
canEdit = false,
|
|
640
|
+
canDelete = false
|
|
641
|
+
}) {
|
|
642
|
+
const [showActions, setShowActions] = React7.useState(false);
|
|
643
|
+
const bubbleRef = React7.useRef(null);
|
|
644
|
+
const isPending = message._status === "pending";
|
|
645
|
+
const isError = message._status === "error";
|
|
646
|
+
React7.useEffect(() => {
|
|
647
|
+
const handleClickOutside = (e) => {
|
|
648
|
+
if (bubbleRef.current && !bubbleRef.current.contains(e.target)) {
|
|
649
|
+
setShowActions(false);
|
|
650
|
+
}
|
|
651
|
+
};
|
|
652
|
+
if (showActions) {
|
|
653
|
+
document.addEventListener("mousedown", handleClickOutside);
|
|
654
|
+
return () => document.removeEventListener("mousedown", handleClickOutside);
|
|
655
|
+
}
|
|
656
|
+
}, [showActions]);
|
|
657
|
+
const handleMessageClick = (e) => {
|
|
658
|
+
if (e.target.closest("button")) return;
|
|
659
|
+
setShowActions((prev) => !prev);
|
|
660
|
+
};
|
|
661
|
+
if (isCurrentUser) {
|
|
662
|
+
return /* @__PURE__ */ jsxs(
|
|
663
|
+
motion.div,
|
|
664
|
+
{
|
|
665
|
+
ref: bubbleRef,
|
|
666
|
+
"data-message-id": message.id,
|
|
667
|
+
initial: { opacity: 0, y: 10, x: 20 },
|
|
668
|
+
animate: { opacity: 1, y: 0, x: 0 },
|
|
669
|
+
transition: { duration: 0.2, ease: "easeOut" },
|
|
670
|
+
className: cn(
|
|
671
|
+
"group flex flex-col items-end px-2 py-1.5 relative",
|
|
672
|
+
isPending && "opacity-60"
|
|
673
|
+
),
|
|
674
|
+
children: [
|
|
675
|
+
/* @__PURE__ */ jsx(AnimatePresence, { children: replyToMessage && /* @__PURE__ */ jsxs(
|
|
676
|
+
motion.div,
|
|
677
|
+
{
|
|
678
|
+
variants: replyPreviewVariants,
|
|
679
|
+
initial: "hidden",
|
|
680
|
+
animate: "visible",
|
|
681
|
+
onClick: (e) => {
|
|
682
|
+
e.stopPropagation();
|
|
683
|
+
if (replyToMessage.id && onScrollToMessage) onScrollToMessage(replyToMessage.id);
|
|
684
|
+
},
|
|
685
|
+
className: cn(
|
|
686
|
+
"flex items-center gap-2 mb-1 mr-2 px-2 py-1 bg-theme-subtle/50 border-l-2 border-primary-light/50 rounded-r text-[11px] overflow-hidden",
|
|
687
|
+
onScrollToMessage && "cursor-pointer hover:bg-theme-subtle/80 transition-colors"
|
|
688
|
+
),
|
|
689
|
+
children: [
|
|
690
|
+
/* @__PURE__ */ jsxs("span", { className: "text-theme-muted", children: [
|
|
691
|
+
"Respondendo a ",
|
|
692
|
+
/* @__PURE__ */ jsx("span", { className: "text-primary-light", children: replyToMessage.authorName || replyToMessage.authorId?.substring(0, 8) }),
|
|
693
|
+
":"
|
|
694
|
+
] }),
|
|
695
|
+
/* @__PURE__ */ jsx("span", { className: "text-theme-muted truncate", children: truncate(replyToMessage.content, 40) })
|
|
696
|
+
]
|
|
697
|
+
}
|
|
698
|
+
) }),
|
|
699
|
+
/* @__PURE__ */ jsxs("div", { className: "max-w-[75%] flex flex-col items-end relative", children: [
|
|
700
|
+
/* @__PURE__ */ jsx(
|
|
701
|
+
ActionBar,
|
|
702
|
+
{
|
|
703
|
+
show: showActions,
|
|
704
|
+
onClose: () => setShowActions(false),
|
|
705
|
+
onEdit: onEdit ? () => onEdit(message) : void 0,
|
|
706
|
+
onReply: onReply ? () => onReply(message) : void 0,
|
|
707
|
+
onDelete: onDelete ? () => onDelete(message.id) : void 0,
|
|
708
|
+
onReact: onReact ? (emoji) => onReact(message.id, emoji) : void 0,
|
|
709
|
+
canEdit,
|
|
710
|
+
canDelete,
|
|
711
|
+
position: "right"
|
|
712
|
+
}
|
|
713
|
+
),
|
|
714
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-baseline gap-2 mb-0.5 justify-end", children: [
|
|
715
|
+
/* @__PURE__ */ jsx("span", { className: "text-[11px] text-theme-muted", children: formatTime(message.createdAt) }),
|
|
716
|
+
message.edited && /* @__PURE__ */ jsx(
|
|
717
|
+
motion.span,
|
|
718
|
+
{
|
|
719
|
+
initial: { opacity: 0 },
|
|
720
|
+
animate: { opacity: 1 },
|
|
721
|
+
className: "text-[10px] text-theme-muted italic",
|
|
722
|
+
children: "(editado)"
|
|
723
|
+
}
|
|
724
|
+
),
|
|
725
|
+
isPending && /* @__PURE__ */ jsx(
|
|
726
|
+
motion.span,
|
|
727
|
+
{
|
|
728
|
+
animate: { opacity: [0.5, 1, 0.5] },
|
|
729
|
+
transition: { duration: 1.5, repeat: Infinity },
|
|
730
|
+
className: "text-[10px] text-theme-muted italic",
|
|
731
|
+
children: "enviando..."
|
|
732
|
+
}
|
|
733
|
+
),
|
|
734
|
+
isError && /* @__PURE__ */ jsx(
|
|
735
|
+
motion.span,
|
|
736
|
+
{
|
|
737
|
+
initial: { scale: 0.8 },
|
|
738
|
+
animate: { scale: 1 },
|
|
739
|
+
className: "text-[10px] text-rose-400 font-medium",
|
|
740
|
+
children: "erro \u2014 toque para reenviar"
|
|
741
|
+
}
|
|
742
|
+
)
|
|
743
|
+
] }),
|
|
744
|
+
/* @__PURE__ */ jsx(
|
|
745
|
+
motion.div,
|
|
746
|
+
{
|
|
747
|
+
onClick: handleMessageClick,
|
|
748
|
+
whileTap: { scale: 0.98 },
|
|
749
|
+
className: cn(
|
|
750
|
+
"rounded-sm rounded-br-none px-4 py-2.5 cursor-pointer select-none",
|
|
751
|
+
"bg-primary !text-white",
|
|
752
|
+
isError && "bg-rose-500/80",
|
|
753
|
+
showActions && "ring-2 ring-primary-light/30"
|
|
754
|
+
),
|
|
755
|
+
children: /* @__PURE__ */ jsx("p", { className: "text-[13px] leading-relaxed break-words whitespace-pre-wrap !text-white", children: renderContent(message.content) })
|
|
756
|
+
}
|
|
757
|
+
),
|
|
758
|
+
message.reactions.length > 0 && /* @__PURE__ */ jsx(
|
|
759
|
+
motion.div,
|
|
760
|
+
{
|
|
761
|
+
initial: { opacity: 0 },
|
|
762
|
+
animate: { opacity: 1 },
|
|
763
|
+
transition: { delay: 0.1 },
|
|
764
|
+
className: "flex flex-wrap gap-1 mt-1.5 justify-end",
|
|
765
|
+
children: groupReactions(message.reactions).map(([emoji, data]) => /* @__PURE__ */ jsx(
|
|
766
|
+
ReactionPill,
|
|
767
|
+
{
|
|
768
|
+
emoji,
|
|
769
|
+
count: data.count,
|
|
770
|
+
onClick: () => onReact?.(message.id, emoji)
|
|
771
|
+
},
|
|
772
|
+
emoji
|
|
773
|
+
))
|
|
774
|
+
}
|
|
775
|
+
)
|
|
776
|
+
] })
|
|
777
|
+
]
|
|
778
|
+
}
|
|
779
|
+
);
|
|
780
|
+
}
|
|
781
|
+
return /* @__PURE__ */ jsxs(
|
|
782
|
+
motion.div,
|
|
783
|
+
{
|
|
784
|
+
ref: bubbleRef,
|
|
785
|
+
"data-message-id": message.id,
|
|
786
|
+
initial: { opacity: 0, y: 10, x: -20 },
|
|
787
|
+
animate: { opacity: 1, y: 0, x: 0 },
|
|
788
|
+
transition: { duration: 0.2, ease: "easeOut" },
|
|
789
|
+
onClick: handleMessageClick,
|
|
790
|
+
className: cn(
|
|
791
|
+
"group flex gap-3 px-2 py-1.5 rounded-sm cursor-pointer select-none relative",
|
|
792
|
+
"transition-colors",
|
|
793
|
+
isPending && "opacity-60",
|
|
794
|
+
isError && "border-l-2 border-rose-500/50",
|
|
795
|
+
showActions && "bg-theme-subtle"
|
|
796
|
+
),
|
|
797
|
+
children: [
|
|
798
|
+
/* @__PURE__ */ jsx(motion.div, { whileHover: { scale: 1.05 }, whileTap: { scale: 0.95 }, children: /* @__PURE__ */ jsx(Avatar, { shape: "circle", size: "sm", className: "mt-0.5 shrink-0", children: /* @__PURE__ */ jsx(AvatarFallback, { className: "bg-primary/20 text-primary-light text-xs font-semibold", children: getInitials(message.authorName || message.authorId) }) }) }),
|
|
799
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0 relative", children: [
|
|
800
|
+
/* @__PURE__ */ jsx(
|
|
801
|
+
ActionBar,
|
|
802
|
+
{
|
|
803
|
+
show: showActions,
|
|
804
|
+
onClose: () => setShowActions(false),
|
|
805
|
+
onEdit: onEdit ? () => onEdit(message) : void 0,
|
|
806
|
+
onReply: onReply ? () => onReply(message) : void 0,
|
|
807
|
+
onDelete: onDelete ? () => onDelete(message.id) : void 0,
|
|
808
|
+
onReact: onReact ? (emoji) => onReact(message.id, emoji) : void 0,
|
|
809
|
+
canEdit,
|
|
810
|
+
canDelete,
|
|
811
|
+
position: "left"
|
|
812
|
+
}
|
|
813
|
+
),
|
|
814
|
+
/* @__PURE__ */ jsx(AnimatePresence, { children: replyToMessage && /* @__PURE__ */ jsxs(
|
|
815
|
+
motion.div,
|
|
816
|
+
{
|
|
817
|
+
variants: replyPreviewVariants,
|
|
818
|
+
initial: "hidden",
|
|
819
|
+
animate: "visible",
|
|
820
|
+
onClick: (e) => {
|
|
821
|
+
e.stopPropagation();
|
|
822
|
+
if (replyToMessage.id && onScrollToMessage) onScrollToMessage(replyToMessage.id);
|
|
823
|
+
},
|
|
824
|
+
className: cn(
|
|
825
|
+
"flex items-center gap-2 mb-1 px-2 py-1 bg-theme-subtle/50 border-l-2 border-primary-light/50 rounded-r text-[11px] overflow-hidden",
|
|
826
|
+
onScrollToMessage && "cursor-pointer hover:bg-theme-subtle/80 transition-colors"
|
|
827
|
+
),
|
|
828
|
+
children: [
|
|
829
|
+
/* @__PURE__ */ jsxs("span", { className: "text-theme-muted", children: [
|
|
830
|
+
"Respondendo a ",
|
|
831
|
+
/* @__PURE__ */ jsx("span", { className: "text-primary-light", children: replyToMessage.authorName }),
|
|
832
|
+
":"
|
|
833
|
+
] }),
|
|
834
|
+
/* @__PURE__ */ jsx("span", { className: "text-theme-muted truncate", children: truncate(replyToMessage.content, 40) })
|
|
835
|
+
]
|
|
836
|
+
}
|
|
837
|
+
) }),
|
|
838
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-baseline gap-2 mb-0.5", children: [
|
|
839
|
+
/* @__PURE__ */ jsx("span", { className: "text-[13px] font-semibold text-theme", children: message.authorName || message.authorId.substring(0, 8) }),
|
|
840
|
+
/* @__PURE__ */ jsx("span", { className: "text-[11px] text-theme-muted", children: formatTime(message.createdAt) }),
|
|
841
|
+
message.edited && /* @__PURE__ */ jsx(
|
|
842
|
+
motion.span,
|
|
843
|
+
{
|
|
844
|
+
initial: { opacity: 0 },
|
|
845
|
+
animate: { opacity: 1 },
|
|
846
|
+
className: "text-[10px] text-theme-muted italic",
|
|
847
|
+
children: "(editado)"
|
|
848
|
+
}
|
|
849
|
+
),
|
|
850
|
+
isPending && /* @__PURE__ */ jsx(
|
|
851
|
+
motion.span,
|
|
852
|
+
{
|
|
853
|
+
animate: { opacity: [0.5, 1, 0.5] },
|
|
854
|
+
transition: { duration: 1.5, repeat: Infinity },
|
|
855
|
+
className: "text-[10px] text-theme-muted italic",
|
|
856
|
+
children: "enviando..."
|
|
857
|
+
}
|
|
858
|
+
),
|
|
859
|
+
isError && /* @__PURE__ */ jsx(
|
|
860
|
+
motion.span,
|
|
861
|
+
{
|
|
862
|
+
initial: { scale: 0.8 },
|
|
863
|
+
animate: { scale: 1 },
|
|
864
|
+
className: "text-[10px] text-rose-400 font-medium",
|
|
865
|
+
children: "erro ao enviar"
|
|
866
|
+
}
|
|
867
|
+
)
|
|
868
|
+
] }),
|
|
869
|
+
/* @__PURE__ */ jsx("p", { className: "text-[13px] text-theme-secondary leading-relaxed break-words whitespace-pre-wrap", children: renderContent(message.content) }),
|
|
870
|
+
message.reactions.length > 0 && /* @__PURE__ */ jsx(
|
|
871
|
+
motion.div,
|
|
872
|
+
{
|
|
873
|
+
initial: { opacity: 0 },
|
|
874
|
+
animate: { opacity: 1 },
|
|
875
|
+
transition: { delay: 0.1 },
|
|
876
|
+
className: "flex flex-wrap gap-1 mt-1.5",
|
|
877
|
+
children: groupReactions(message.reactions).map(([emoji, data]) => /* @__PURE__ */ jsx(
|
|
878
|
+
ReactionPill,
|
|
879
|
+
{
|
|
880
|
+
emoji,
|
|
881
|
+
count: data.count,
|
|
882
|
+
onClick: () => onReact?.(message.id, emoji)
|
|
883
|
+
},
|
|
884
|
+
emoji
|
|
885
|
+
))
|
|
886
|
+
}
|
|
887
|
+
)
|
|
888
|
+
] })
|
|
889
|
+
]
|
|
890
|
+
}
|
|
891
|
+
);
|
|
892
|
+
}
|
|
893
|
+
MessageBubble.displayName = "MessageBubble";
|
|
894
|
+
function getInitials2(name) {
|
|
895
|
+
if (!name) return "?";
|
|
896
|
+
return name.split(" ").map((w) => w[0]).slice(0, 2).join("").toUpperCase();
|
|
897
|
+
}
|
|
898
|
+
function getMentionIcon(type) {
|
|
899
|
+
switch (type) {
|
|
900
|
+
case "user":
|
|
901
|
+
return /* @__PURE__ */ jsx(OrgTeam, { className: "w-4 h-4" });
|
|
902
|
+
case "project":
|
|
903
|
+
return /* @__PURE__ */ jsx(OrgFolder, { className: "w-4 h-4" });
|
|
904
|
+
case "task":
|
|
905
|
+
return /* @__PURE__ */ jsx(OrgCheck, { className: "w-4 h-4" });
|
|
906
|
+
default:
|
|
907
|
+
return null;
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
function getMentionLabel(type) {
|
|
911
|
+
switch (type) {
|
|
912
|
+
case "user":
|
|
913
|
+
return "Usu\xE1rio";
|
|
914
|
+
case "project":
|
|
915
|
+
return "Projeto";
|
|
916
|
+
case "task":
|
|
917
|
+
return "Tarefa";
|
|
918
|
+
default:
|
|
919
|
+
return "";
|
|
920
|
+
}
|
|
921
|
+
}
|
|
922
|
+
function MentionPopover({
|
|
923
|
+
open,
|
|
924
|
+
query,
|
|
925
|
+
options,
|
|
926
|
+
loading = false,
|
|
927
|
+
onSelect,
|
|
928
|
+
onClose,
|
|
929
|
+
position,
|
|
930
|
+
selectedIndex = 0
|
|
931
|
+
}) {
|
|
932
|
+
const listRef = React7.useRef(null);
|
|
933
|
+
const groupedOptions = React7.useMemo(() => {
|
|
934
|
+
const groups = {
|
|
935
|
+
user: [],
|
|
936
|
+
project: [],
|
|
937
|
+
task: []
|
|
938
|
+
};
|
|
939
|
+
options.forEach((opt) => {
|
|
940
|
+
if (groups[opt.type]) {
|
|
941
|
+
groups[opt.type].push(opt);
|
|
942
|
+
}
|
|
943
|
+
});
|
|
944
|
+
return groups;
|
|
945
|
+
}, [options]);
|
|
946
|
+
React7.useEffect(() => {
|
|
947
|
+
if (listRef.current && selectedIndex >= 0) {
|
|
948
|
+
const items = listRef.current.querySelectorAll("[data-mention-item]");
|
|
949
|
+
const item = items[selectedIndex];
|
|
950
|
+
if (item) {
|
|
951
|
+
item.scrollIntoView({ block: "nearest" });
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
}, [selectedIndex]);
|
|
955
|
+
if (!open) return null;
|
|
956
|
+
const hasResults = options.length > 0;
|
|
957
|
+
let flatIndex = -1;
|
|
958
|
+
return /* @__PURE__ */ jsx(
|
|
959
|
+
"div",
|
|
960
|
+
{
|
|
961
|
+
className: cn(
|
|
962
|
+
"absolute z-50 w-72 max-h-64 overflow-y-auto",
|
|
963
|
+
"bg-theme-surface border border-theme-subtle rounded-sm shadow-lg",
|
|
964
|
+
"animate-in fade-in-0 zoom-in-95 duration-[400ms]"
|
|
965
|
+
),
|
|
966
|
+
style: position ? { top: position.top, left: position.left } : void 0,
|
|
967
|
+
children: loading ? /* @__PURE__ */ jsx("div", { className: "px-3 py-4 flex items-center justify-center", children: /* @__PURE__ */ jsx("div", { className: "flex gap-1", children: [0, 1, 2].map((i) => /* @__PURE__ */ jsx(
|
|
968
|
+
"span",
|
|
969
|
+
{
|
|
970
|
+
className: "w-2 h-2 rounded-full bg-primary-light/50 animate-bounce",
|
|
971
|
+
style: { animationDelay: `${i * 100}ms` }
|
|
972
|
+
},
|
|
973
|
+
i
|
|
974
|
+
)) }) }) : !hasResults ? /* @__PURE__ */ jsx("div", { className: "px-3 py-4 text-center", children: /* @__PURE__ */ jsx("p", { className: "text-sm text-theme-muted", children: query ? `Nenhum resultado para "${query}"` : "Digite para pesquisar" }) }) : /* @__PURE__ */ jsx("div", { ref: listRef, className: "py-1", children: ["user", "project", "task"].map((type) => {
|
|
975
|
+
const typeOptions = groupedOptions[type];
|
|
976
|
+
if (typeOptions.length === 0) return null;
|
|
977
|
+
return /* @__PURE__ */ jsxs("div", { children: [
|
|
978
|
+
/* @__PURE__ */ jsxs("div", { className: "px-3 py-1.5 flex items-center gap-2", children: [
|
|
979
|
+
/* @__PURE__ */ jsx("span", { className: "text-theme-muted", children: getMentionIcon(type) }),
|
|
980
|
+
/* @__PURE__ */ jsxs("span", { className: "text-[11px] font-medium text-theme-muted uppercase tracking-wide", children: [
|
|
981
|
+
getMentionLabel(type),
|
|
982
|
+
"s"
|
|
983
|
+
] })
|
|
984
|
+
] }),
|
|
985
|
+
typeOptions.map((option) => {
|
|
986
|
+
flatIndex++;
|
|
987
|
+
const isSelected = flatIndex === selectedIndex;
|
|
988
|
+
return /* @__PURE__ */ jsxs(
|
|
989
|
+
"button",
|
|
990
|
+
{
|
|
991
|
+
"data-mention-item": true,
|
|
992
|
+
onClick: () => onSelect(option),
|
|
993
|
+
className: cn(
|
|
994
|
+
"w-full flex items-center gap-3 px-3 py-2 text-left transition-colors",
|
|
995
|
+
"hover:bg-theme-highlight focus:bg-theme-highlight focus:outline-none",
|
|
996
|
+
isSelected && "bg-theme-highlight"
|
|
997
|
+
),
|
|
998
|
+
children: [
|
|
999
|
+
type === "user" ? /* @__PURE__ */ jsx(Avatar, { shape: "circle", size: "sm", className: "w-7 h-7", children: /* @__PURE__ */ jsx(AvatarFallback, { className: "bg-primary/20 text-primary-light text-[10px] font-semibold", children: getInitials2(option.display) }) }) : /* @__PURE__ */ jsx(
|
|
1000
|
+
"span",
|
|
1001
|
+
{
|
|
1002
|
+
className: cn(
|
|
1003
|
+
"w-7 h-7 rounded flex items-center justify-center",
|
|
1004
|
+
type === "project" && "bg-purple-500/20 text-purple-400",
|
|
1005
|
+
type === "task" && "bg-emerald-500/20 text-emerald-400"
|
|
1006
|
+
),
|
|
1007
|
+
children: getMentionIcon(type)
|
|
1008
|
+
}
|
|
1009
|
+
),
|
|
1010
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
|
|
1011
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm font-medium text-theme truncate", children: option.display }),
|
|
1012
|
+
option.subtitle && /* @__PURE__ */ jsx("p", { className: "text-[11px] text-theme-muted truncate", children: option.subtitle })
|
|
1013
|
+
] })
|
|
1014
|
+
]
|
|
1015
|
+
},
|
|
1016
|
+
option.id
|
|
1017
|
+
);
|
|
1018
|
+
})
|
|
1019
|
+
] }, type);
|
|
1020
|
+
}) })
|
|
1021
|
+
}
|
|
1022
|
+
);
|
|
1023
|
+
}
|
|
1024
|
+
MentionPopover.displayName = "MentionPopover";
|
|
1025
|
+
var bannerVariants = {
|
|
1026
|
+
initial: { opacity: 0, y: -8, height: 0 },
|
|
1027
|
+
animate: { opacity: 1, y: 0, height: "auto" },
|
|
1028
|
+
exit: { opacity: 0, y: -8, height: 0 }
|
|
1029
|
+
};
|
|
1030
|
+
var bannerTransition = {
|
|
1031
|
+
duration: 0.2,
|
|
1032
|
+
ease: [0.4, 0, 0.2, 1]
|
|
1033
|
+
};
|
|
1034
|
+
function truncate2(str, len) {
|
|
1035
|
+
if (str.length <= len) return str;
|
|
1036
|
+
return str.slice(0, len) + "...";
|
|
1037
|
+
}
|
|
1038
|
+
function MessageInput({
|
|
1039
|
+
value,
|
|
1040
|
+
onChange,
|
|
1041
|
+
onSend,
|
|
1042
|
+
onTyping,
|
|
1043
|
+
onStopTyping,
|
|
1044
|
+
disabled = false,
|
|
1045
|
+
placeholder = "Escreva uma mensagem...",
|
|
1046
|
+
editingMessage,
|
|
1047
|
+
onCancelEdit,
|
|
1048
|
+
replyTo,
|
|
1049
|
+
onCancelReply,
|
|
1050
|
+
mentionOptions = [],
|
|
1051
|
+
onMentionSearch,
|
|
1052
|
+
mentionLoading = false
|
|
1053
|
+
}) {
|
|
1054
|
+
const inputRef = React7.useRef(null);
|
|
1055
|
+
const [showMentions, setShowMentions] = React7.useState(false);
|
|
1056
|
+
const [mentionQuery, setMentionQuery] = React7.useState("");
|
|
1057
|
+
const [mentionIndex, setMentionIndex] = React7.useState(0);
|
|
1058
|
+
const [caretPosition, setCaretPosition] = React7.useState(null);
|
|
1059
|
+
const detectMention = (text, cursorPos) => {
|
|
1060
|
+
const beforeCursor = text.slice(0, cursorPos);
|
|
1061
|
+
const match = beforeCursor.match(/@(\w*)$/);
|
|
1062
|
+
if (match) {
|
|
1063
|
+
setShowMentions(true);
|
|
1064
|
+
setMentionQuery(match[1]);
|
|
1065
|
+
setMentionIndex(0);
|
|
1066
|
+
onMentionSearch?.(match[1], null);
|
|
1067
|
+
if (inputRef.current) {
|
|
1068
|
+
inputRef.current.getBoundingClientRect();
|
|
1069
|
+
setCaretPosition({
|
|
1070
|
+
top: -200,
|
|
1071
|
+
// Above input
|
|
1072
|
+
left: 0
|
|
1073
|
+
});
|
|
1074
|
+
}
|
|
1075
|
+
} else {
|
|
1076
|
+
setShowMentions(false);
|
|
1077
|
+
setMentionQuery("");
|
|
1078
|
+
}
|
|
1079
|
+
};
|
|
1080
|
+
const handleChange = (e) => {
|
|
1081
|
+
const newValue = e.target.value;
|
|
1082
|
+
onChange(newValue);
|
|
1083
|
+
onTyping?.();
|
|
1084
|
+
detectMention(newValue, e.target.selectionStart || 0);
|
|
1085
|
+
};
|
|
1086
|
+
const handleKeyDown = (e) => {
|
|
1087
|
+
if (showMentions && mentionOptions.length > 0) {
|
|
1088
|
+
if (e.key === "ArrowDown") {
|
|
1089
|
+
e.preventDefault();
|
|
1090
|
+
setMentionIndex((i) => Math.min(i + 1, mentionOptions.length - 1));
|
|
1091
|
+
return;
|
|
1092
|
+
}
|
|
1093
|
+
if (e.key === "ArrowUp") {
|
|
1094
|
+
e.preventDefault();
|
|
1095
|
+
setMentionIndex((i) => Math.max(i - 1, 0));
|
|
1096
|
+
return;
|
|
1097
|
+
}
|
|
1098
|
+
if (e.key === "Enter" || e.key === "Tab") {
|
|
1099
|
+
e.preventDefault();
|
|
1100
|
+
handleMentionSelect(mentionOptions[mentionIndex]);
|
|
1101
|
+
return;
|
|
1102
|
+
}
|
|
1103
|
+
if (e.key === "Escape") {
|
|
1104
|
+
e.preventDefault();
|
|
1105
|
+
setShowMentions(false);
|
|
1106
|
+
return;
|
|
1107
|
+
}
|
|
1108
|
+
}
|
|
1109
|
+
if (e.key === "Enter" && !e.shiftKey) {
|
|
1110
|
+
e.preventDefault();
|
|
1111
|
+
if (value.trim()) {
|
|
1112
|
+
onSend();
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
};
|
|
1116
|
+
const handleMentionSelect = (option) => {
|
|
1117
|
+
const cursorPos = inputRef.current?.selectionStart || 0;
|
|
1118
|
+
const beforeCursor = value.slice(0, cursorPos);
|
|
1119
|
+
const afterCursor = value.slice(cursorPos);
|
|
1120
|
+
const match = beforeCursor.match(/@(\w*)$/);
|
|
1121
|
+
if (match) {
|
|
1122
|
+
const mentionStart = beforeCursor.length - match[0].length;
|
|
1123
|
+
const newValue = beforeCursor.slice(0, mentionStart) + `@${option.display} ` + afterCursor;
|
|
1124
|
+
onChange(newValue);
|
|
1125
|
+
}
|
|
1126
|
+
setShowMentions(false);
|
|
1127
|
+
setMentionQuery("");
|
|
1128
|
+
inputRef.current?.focus();
|
|
1129
|
+
};
|
|
1130
|
+
const handleBlur = () => {
|
|
1131
|
+
setTimeout(() => {
|
|
1132
|
+
setShowMentions(false);
|
|
1133
|
+
onStopTyping?.();
|
|
1134
|
+
}, 200);
|
|
1135
|
+
};
|
|
1136
|
+
React7.useEffect(() => {
|
|
1137
|
+
if (inputRef.current) {
|
|
1138
|
+
inputRef.current.style.height = "auto";
|
|
1139
|
+
inputRef.current.style.height = `${Math.min(inputRef.current.scrollHeight, 120)}px`;
|
|
1140
|
+
}
|
|
1141
|
+
}, [value]);
|
|
1142
|
+
React7.useEffect(() => {
|
|
1143
|
+
if (editingMessage && inputRef.current) {
|
|
1144
|
+
inputRef.current.focus();
|
|
1145
|
+
}
|
|
1146
|
+
}, [editingMessage]);
|
|
1147
|
+
const isEditing = !!editingMessage;
|
|
1148
|
+
const isReplying = !!replyTo;
|
|
1149
|
+
return /* @__PURE__ */ jsxs("div", { className: "relative", children: [
|
|
1150
|
+
/* @__PURE__ */ jsx(
|
|
1151
|
+
MentionPopover,
|
|
1152
|
+
{
|
|
1153
|
+
open: showMentions,
|
|
1154
|
+
query: mentionQuery,
|
|
1155
|
+
options: mentionOptions,
|
|
1156
|
+
loading: mentionLoading,
|
|
1157
|
+
onSelect: handleMentionSelect,
|
|
1158
|
+
onClose: () => setShowMentions(false),
|
|
1159
|
+
position: caretPosition ?? void 0,
|
|
1160
|
+
selectedIndex: mentionIndex
|
|
1161
|
+
}
|
|
1162
|
+
),
|
|
1163
|
+
/* @__PURE__ */ jsx(AnimatePresence, { children: isReplying && replyTo && /* @__PURE__ */ jsx(
|
|
1164
|
+
motion.div,
|
|
1165
|
+
{
|
|
1166
|
+
variants: bannerVariants,
|
|
1167
|
+
initial: "initial",
|
|
1168
|
+
animate: "animate",
|
|
1169
|
+
exit: "exit",
|
|
1170
|
+
transition: bannerTransition,
|
|
1171
|
+
className: "overflow-hidden",
|
|
1172
|
+
children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 px-3 py-2 mb-2 bg-theme-subtle border border-theme-subtle rounded-sm", children: [
|
|
1173
|
+
/* @__PURE__ */ jsx(OrgComment, { className: "w-4 h-4 text-primary-light shrink-0" }),
|
|
1174
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
|
|
1175
|
+
/* @__PURE__ */ jsxs("p", { className: "text-[11px] font-medium text-primary-light", children: [
|
|
1176
|
+
"Respondendo a ",
|
|
1177
|
+
replyTo.authorName
|
|
1178
|
+
] }),
|
|
1179
|
+
/* @__PURE__ */ jsx("p", { className: "text-[12px] text-theme-muted truncate", children: truncate2(replyTo.content, 60) })
|
|
1180
|
+
] }),
|
|
1181
|
+
/* @__PURE__ */ jsx(
|
|
1182
|
+
"button",
|
|
1183
|
+
{
|
|
1184
|
+
onClick: onCancelReply,
|
|
1185
|
+
className: "p-1 rounded hover:bg-theme-highlight text-theme-muted hover:text-theme transition-colors",
|
|
1186
|
+
children: /* @__PURE__ */ jsx(OrgClose, { className: "w-4 h-4" })
|
|
1187
|
+
}
|
|
1188
|
+
)
|
|
1189
|
+
] })
|
|
1190
|
+
},
|
|
1191
|
+
"reply-banner"
|
|
1192
|
+
) }),
|
|
1193
|
+
/* @__PURE__ */ jsx(AnimatePresence, { children: isEditing && /* @__PURE__ */ jsx(
|
|
1194
|
+
motion.div,
|
|
1195
|
+
{
|
|
1196
|
+
variants: bannerVariants,
|
|
1197
|
+
initial: "initial",
|
|
1198
|
+
animate: "animate",
|
|
1199
|
+
exit: "exit",
|
|
1200
|
+
transition: bannerTransition,
|
|
1201
|
+
className: "overflow-hidden",
|
|
1202
|
+
children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 px-3 py-2 mb-2 bg-amber-500/10 border border-amber-500/30 rounded-sm", children: [
|
|
1203
|
+
/* @__PURE__ */ jsx(OrgEdit, { className: "w-4 h-4 text-amber-400 shrink-0" }),
|
|
1204
|
+
/* @__PURE__ */ jsx("p", { className: "text-[12px] text-amber-300 flex-1", children: "Editando mensagem" }),
|
|
1205
|
+
/* @__PURE__ */ jsx(
|
|
1206
|
+
"button",
|
|
1207
|
+
{
|
|
1208
|
+
onClick: onCancelEdit,
|
|
1209
|
+
className: "p-1 rounded hover:bg-amber-500/20 text-amber-400 hover:text-amber-300 transition-colors",
|
|
1210
|
+
children: /* @__PURE__ */ jsx(OrgClose, { className: "w-4 h-4" })
|
|
1211
|
+
}
|
|
1212
|
+
)
|
|
1213
|
+
] })
|
|
1214
|
+
},
|
|
1215
|
+
"edit-banner"
|
|
1216
|
+
) }),
|
|
1217
|
+
/* @__PURE__ */ jsxs(
|
|
1218
|
+
"div",
|
|
1219
|
+
{
|
|
1220
|
+
className: cn(
|
|
1221
|
+
"flex items-end gap-2 bg-theme-subtle border border-theme-subtle rounded-sm px-3 py-2 transition-colors",
|
|
1222
|
+
"focus-within:border-primary/50",
|
|
1223
|
+
isEditing && "border-amber-500/30 focus-within:border-amber-500/50"
|
|
1224
|
+
),
|
|
1225
|
+
children: [
|
|
1226
|
+
/* @__PURE__ */ jsx(
|
|
1227
|
+
"textarea",
|
|
1228
|
+
{
|
|
1229
|
+
ref: inputRef,
|
|
1230
|
+
value,
|
|
1231
|
+
onChange: handleChange,
|
|
1232
|
+
onKeyDown: handleKeyDown,
|
|
1233
|
+
onBlur: handleBlur,
|
|
1234
|
+
placeholder,
|
|
1235
|
+
disabled,
|
|
1236
|
+
rows: 1,
|
|
1237
|
+
className: cn(
|
|
1238
|
+
"flex-1 bg-transparent border-none outline-none resize-none",
|
|
1239
|
+
"text-[13px] text-theme placeholder:text-theme-muted",
|
|
1240
|
+
"min-h-[24px] max-h-[120px]"
|
|
1241
|
+
)
|
|
1242
|
+
}
|
|
1243
|
+
),
|
|
1244
|
+
/* @__PURE__ */ jsx(
|
|
1245
|
+
Button,
|
|
1246
|
+
{
|
|
1247
|
+
variant: isEditing ? "secondary" : "default",
|
|
1248
|
+
size: "sm",
|
|
1249
|
+
onClick: onSend,
|
|
1250
|
+
disabled: disabled || !value.trim(),
|
|
1251
|
+
className: "h-7 px-3 text-xs shrink-0",
|
|
1252
|
+
children: isEditing ? "Salvar" : "Enviar"
|
|
1253
|
+
}
|
|
1254
|
+
)
|
|
1255
|
+
]
|
|
1256
|
+
}
|
|
1257
|
+
),
|
|
1258
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-1 mt-1", children: [
|
|
1259
|
+
/* @__PURE__ */ jsxs("p", { className: "text-[10px] text-theme-muted", children: [
|
|
1260
|
+
/* @__PURE__ */ jsx("kbd", { className: "px-1 py-0.5 rounded bg-theme-subtle text-[10px]", children: "@" }),
|
|
1261
|
+
" para mencionar"
|
|
1262
|
+
] }),
|
|
1263
|
+
/* @__PURE__ */ jsxs("p", { className: "text-[10px] text-theme-muted", children: [
|
|
1264
|
+
/* @__PURE__ */ jsx("kbd", { className: "px-1 py-0.5 rounded bg-theme-subtle text-[10px]", children: "Enter" }),
|
|
1265
|
+
" para enviar"
|
|
1266
|
+
] })
|
|
1267
|
+
] })
|
|
1268
|
+
] });
|
|
1269
|
+
}
|
|
1270
|
+
MessageInput.displayName = "MessageInput";
|
|
1271
|
+
var chatPanelVariants = {
|
|
1272
|
+
hidden: { opacity: 0, x: 20 },
|
|
1273
|
+
visible: {
|
|
1274
|
+
opacity: 1,
|
|
1275
|
+
x: 0,
|
|
1276
|
+
transition: {
|
|
1277
|
+
type: "spring",
|
|
1278
|
+
stiffness: 300,
|
|
1279
|
+
damping: 30
|
|
1280
|
+
}
|
|
1281
|
+
},
|
|
1282
|
+
exit: {
|
|
1283
|
+
opacity: 0,
|
|
1284
|
+
x: -20,
|
|
1285
|
+
transition: {
|
|
1286
|
+
duration: 0.2
|
|
1287
|
+
}
|
|
1288
|
+
}
|
|
1289
|
+
};
|
|
1290
|
+
function formatDateSeparator(iso) {
|
|
1291
|
+
const date = new Date(iso);
|
|
1292
|
+
const today = /* @__PURE__ */ new Date();
|
|
1293
|
+
const yesterday = new Date(today);
|
|
1294
|
+
yesterday.setDate(yesterday.getDate() - 1);
|
|
1295
|
+
if (date.toDateString() === today.toDateString()) return "Hoje";
|
|
1296
|
+
if (date.toDateString() === yesterday.toDateString()) return "Ontem";
|
|
1297
|
+
return date.toLocaleDateString("pt-BR", { day: "numeric", month: "long" });
|
|
1298
|
+
}
|
|
1299
|
+
function shouldShowDateSeparator(messages, index) {
|
|
1300
|
+
if (index === 0) return true;
|
|
1301
|
+
const curr = new Date(messages[index].createdAt).toDateString();
|
|
1302
|
+
const prev = new Date(messages[index - 1].createdAt).toDateString();
|
|
1303
|
+
return curr !== prev;
|
|
1304
|
+
}
|
|
1305
|
+
function TypingIndicator({ users }) {
|
|
1306
|
+
if (users.length === 0) return null;
|
|
1307
|
+
const text = users.length === 1 ? `${users[0].substring(0, 8)} est\xE1 digitando` : users.length === 2 ? `${users[0].substring(0, 8)} e ${users[1].substring(0, 8)} est\xE3o digitando` : `${users.length} pessoas est\xE3o digitando`;
|
|
1308
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 px-5 py-1 animate-in fade-in-0 duration-[400ms]", children: [
|
|
1309
|
+
/* @__PURE__ */ jsx("div", { className: "flex gap-0.5", children: [0, 1, 2].map((i) => /* @__PURE__ */ jsx(
|
|
1310
|
+
"span",
|
|
1311
|
+
{
|
|
1312
|
+
className: "w-1.5 h-1.5 rounded-full bg-primary-light/50 animate-bounce",
|
|
1313
|
+
style: { animationDelay: `${i * 150}ms` }
|
|
1314
|
+
},
|
|
1315
|
+
i
|
|
1316
|
+
)) }),
|
|
1317
|
+
/* @__PURE__ */ jsx("span", { className: "text-[11px] text-theme-muted", children: text })
|
|
1318
|
+
] });
|
|
1319
|
+
}
|
|
1320
|
+
function ChatMessages({
|
|
1321
|
+
room,
|
|
1322
|
+
messages,
|
|
1323
|
+
loading,
|
|
1324
|
+
typingUsers = [],
|
|
1325
|
+
currentUserId,
|
|
1326
|
+
permissions,
|
|
1327
|
+
onSendMessage,
|
|
1328
|
+
onEditMessage,
|
|
1329
|
+
onDeleteMessage,
|
|
1330
|
+
onReactToMessage,
|
|
1331
|
+
onTyping,
|
|
1332
|
+
onStopTyping,
|
|
1333
|
+
onOpenManagement,
|
|
1334
|
+
onBack,
|
|
1335
|
+
isMobile,
|
|
1336
|
+
mentionUsers = [],
|
|
1337
|
+
mentionProjects = [],
|
|
1338
|
+
hasMore = false,
|
|
1339
|
+
onLoadMore,
|
|
1340
|
+
loadingMore = false
|
|
1341
|
+
}) {
|
|
1342
|
+
const [inputValue, setInputValue] = React7.useState("");
|
|
1343
|
+
const [editingMessage, setEditingMessage] = React7.useState(null);
|
|
1344
|
+
const [replyTo, setReplyTo] = React7.useState(null);
|
|
1345
|
+
const [mentionOptions, setMentionOptions] = React7.useState([]);
|
|
1346
|
+
const scrollRef = React7.useRef(null);
|
|
1347
|
+
const messagesById = React7.useMemo(() => {
|
|
1348
|
+
const map = /* @__PURE__ */ new Map();
|
|
1349
|
+
messages.forEach((m) => map.set(m.id, m));
|
|
1350
|
+
return map;
|
|
1351
|
+
}, [messages]);
|
|
1352
|
+
const isInitialLoad = React7.useRef(true);
|
|
1353
|
+
const prevMessageCount = React7.useRef(messages.length);
|
|
1354
|
+
React7.useEffect(() => {
|
|
1355
|
+
const el = scrollRef.current;
|
|
1356
|
+
if (!el) return;
|
|
1357
|
+
if (isInitialLoad.current || messages.length > prevMessageCount.current) {
|
|
1358
|
+
const nearBottom = isInitialLoad.current || el.scrollHeight - el.scrollTop - el.clientHeight < 120;
|
|
1359
|
+
if (nearBottom) {
|
|
1360
|
+
requestAnimationFrame(() => {
|
|
1361
|
+
el.scrollTop = el.scrollHeight;
|
|
1362
|
+
});
|
|
1363
|
+
}
|
|
1364
|
+
isInitialLoad.current = false;
|
|
1365
|
+
}
|
|
1366
|
+
prevMessageCount.current = messages.length;
|
|
1367
|
+
}, [messages]);
|
|
1368
|
+
React7.useEffect(() => {
|
|
1369
|
+
isInitialLoad.current = true;
|
|
1370
|
+
}, [room?.id]);
|
|
1371
|
+
const handleScroll = React7.useCallback(() => {
|
|
1372
|
+
const el = scrollRef.current;
|
|
1373
|
+
if (!el || !hasMore || loadingMore || !onLoadMore) return;
|
|
1374
|
+
if (el.scrollTop < 60) {
|
|
1375
|
+
onLoadMore();
|
|
1376
|
+
}
|
|
1377
|
+
}, [hasMore, loadingMore, onLoadMore]);
|
|
1378
|
+
React7.useEffect(() => {
|
|
1379
|
+
if (editingMessage) {
|
|
1380
|
+
setInputValue(editingMessage.content);
|
|
1381
|
+
}
|
|
1382
|
+
}, [editingMessage]);
|
|
1383
|
+
const handleSend = () => {
|
|
1384
|
+
if (!inputValue.trim()) return;
|
|
1385
|
+
if (editingMessage) {
|
|
1386
|
+
onEditMessage?.(editingMessage.id, inputValue.trim());
|
|
1387
|
+
setEditingMessage(null);
|
|
1388
|
+
} else {
|
|
1389
|
+
onSendMessage(inputValue.trim(), replyTo?.id);
|
|
1390
|
+
setReplyTo(null);
|
|
1391
|
+
}
|
|
1392
|
+
setInputValue("");
|
|
1393
|
+
};
|
|
1394
|
+
const handleCancelEdit = () => {
|
|
1395
|
+
setEditingMessage(null);
|
|
1396
|
+
setInputValue("");
|
|
1397
|
+
};
|
|
1398
|
+
const handleCancelReply = () => {
|
|
1399
|
+
setReplyTo(null);
|
|
1400
|
+
};
|
|
1401
|
+
const handleEditMessage = (message) => {
|
|
1402
|
+
setEditingMessage(message);
|
|
1403
|
+
setReplyTo(null);
|
|
1404
|
+
};
|
|
1405
|
+
const handleReplyToMessage = (message) => {
|
|
1406
|
+
setReplyTo({
|
|
1407
|
+
id: message.id,
|
|
1408
|
+
authorName: message.authorName || message.authorId.substring(0, 8),
|
|
1409
|
+
content: message.content
|
|
1410
|
+
});
|
|
1411
|
+
setEditingMessage(null);
|
|
1412
|
+
};
|
|
1413
|
+
const handleScrollToMessage = React7.useCallback((messageId) => {
|
|
1414
|
+
const el = scrollRef.current?.querySelector(`[data-message-id="${messageId}"]`);
|
|
1415
|
+
if (el) {
|
|
1416
|
+
el.scrollIntoView({ behavior: "smooth", block: "center" });
|
|
1417
|
+
el.classList.add("bg-primary/10");
|
|
1418
|
+
setTimeout(() => el.classList.remove("bg-primary/10"), 1500);
|
|
1419
|
+
}
|
|
1420
|
+
}, []);
|
|
1421
|
+
const handleMentionSearch = (query) => {
|
|
1422
|
+
const q = query.toLowerCase();
|
|
1423
|
+
const options = [];
|
|
1424
|
+
mentionUsers.filter((u) => u.displayName.toLowerCase().includes(q) || u.email?.toLowerCase().includes(q)).slice(0, 5).forEach((u) => {
|
|
1425
|
+
options.push({
|
|
1426
|
+
id: u.id,
|
|
1427
|
+
type: "user",
|
|
1428
|
+
display: u.displayName,
|
|
1429
|
+
avatarUrl: u.avatarUrl,
|
|
1430
|
+
subtitle: u.email
|
|
1431
|
+
});
|
|
1432
|
+
});
|
|
1433
|
+
mentionProjects.filter((p) => p.name.toLowerCase().includes(q) || p.key?.toLowerCase().includes(q)).slice(0, 3).forEach((p) => {
|
|
1434
|
+
options.push({
|
|
1435
|
+
id: p.id,
|
|
1436
|
+
type: "project",
|
|
1437
|
+
display: p.name,
|
|
1438
|
+
subtitle: p.key
|
|
1439
|
+
});
|
|
1440
|
+
});
|
|
1441
|
+
setMentionOptions(options);
|
|
1442
|
+
};
|
|
1443
|
+
if (!room) {
|
|
1444
|
+
return /* @__PURE__ */ jsxs("div", { className: "flex flex-1 flex-col items-center justify-center text-center px-6", children: [
|
|
1445
|
+
/* @__PURE__ */ jsx("div", { className: "w-16 h-16 rounded-sm bg-primary/10 flex items-center justify-center mb-4", children: /* @__PURE__ */ jsx(OrgComment, { className: "w-8 h-8 text-primary-light/60" }) }),
|
|
1446
|
+
/* @__PURE__ */ jsx("h3", { className: "text-lg font-medium text-theme mb-2", children: "Selecione uma conversa" }),
|
|
1447
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm text-theme-muted max-w-[240px]", children: "Escolha um canal ou mensagem direta para come\xE7ar a conversar." })
|
|
1448
|
+
] });
|
|
1449
|
+
}
|
|
1450
|
+
return /* @__PURE__ */ jsx(AnimatePresence, { mode: "wait", children: /* @__PURE__ */ jsxs(
|
|
1451
|
+
motion.div,
|
|
1452
|
+
{
|
|
1453
|
+
variants: chatPanelVariants,
|
|
1454
|
+
initial: "hidden",
|
|
1455
|
+
animate: "visible",
|
|
1456
|
+
exit: "exit",
|
|
1457
|
+
className: "flex flex-col h-full overflow-hidden relative",
|
|
1458
|
+
children: [
|
|
1459
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 px-4 md:px-5 py-3 border-b border-theme-subtle", children: [
|
|
1460
|
+
isMobile && onBack && /* @__PURE__ */ jsx(
|
|
1461
|
+
"button",
|
|
1462
|
+
{
|
|
1463
|
+
onClick: onBack,
|
|
1464
|
+
className: "p-1.5 rounded-sm text-theme-muted hover:text-theme hover:bg-theme-subtle transition-colors",
|
|
1465
|
+
children: /* @__PURE__ */ jsx("svg", { className: "w-5 h-5", fill: "none", viewBox: "0 0 24 24", stroke: "currentColor", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M15 19l-7-7 7-7" }) })
|
|
1466
|
+
}
|
|
1467
|
+
),
|
|
1468
|
+
/* @__PURE__ */ jsx(
|
|
1469
|
+
"span",
|
|
1470
|
+
{
|
|
1471
|
+
className: cn(
|
|
1472
|
+
"flex items-center justify-center h-8 w-8 rounded-sm text-sm",
|
|
1473
|
+
"bg-primary/15 text-primary-light"
|
|
1474
|
+
),
|
|
1475
|
+
children: room.type === "DIRECT" ? /* @__PURE__ */ jsx(OrgComment, { className: "w-4 h-4" }) : "#"
|
|
1476
|
+
}
|
|
1477
|
+
),
|
|
1478
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
|
|
1479
|
+
/* @__PURE__ */ jsx("h3", { className: "text-[15px] font-semibold text-theme truncate", children: room.name || "Sem nome" }),
|
|
1480
|
+
/* @__PURE__ */ jsxs("p", { className: "text-[11px] text-theme-muted truncate", children: [
|
|
1481
|
+
room.memberCount,
|
|
1482
|
+
" membro",
|
|
1483
|
+
room.memberCount !== 1 ? "s" : ""
|
|
1484
|
+
] })
|
|
1485
|
+
] }),
|
|
1486
|
+
onOpenManagement && /* @__PURE__ */ jsx("div", { className: "flex items-center gap-1", children: /* @__PURE__ */ jsx(TooltipProvider, { children: /* @__PURE__ */ jsxs(Tooltip, { children: [
|
|
1487
|
+
/* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
|
|
1488
|
+
Button,
|
|
1489
|
+
{
|
|
1490
|
+
variant: "ghost",
|
|
1491
|
+
size: "sm",
|
|
1492
|
+
onClick: onOpenManagement,
|
|
1493
|
+
className: "h-8 w-8 p-0 text-theme-muted hover:text-theme",
|
|
1494
|
+
children: /* @__PURE__ */ jsx(OrgTeam, { className: "w-4 h-4" })
|
|
1495
|
+
}
|
|
1496
|
+
) }),
|
|
1497
|
+
/* @__PURE__ */ jsx(TooltipContent, { children: "Detalhes do canal" })
|
|
1498
|
+
] }) }) })
|
|
1499
|
+
] }),
|
|
1500
|
+
/* @__PURE__ */ jsxs(
|
|
1501
|
+
"div",
|
|
1502
|
+
{
|
|
1503
|
+
ref: scrollRef,
|
|
1504
|
+
onScroll: handleScroll,
|
|
1505
|
+
className: "flex-1 overflow-y-auto px-3 md:px-5 py-4 space-y-0.5",
|
|
1506
|
+
children: [
|
|
1507
|
+
loadingMore && /* @__PURE__ */ jsx("div", { className: "flex justify-center py-3", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-xs text-theme-muted", children: [
|
|
1508
|
+
/* @__PURE__ */ jsx("div", { className: "w-4 h-4 border-2 border-primary-light/30 border-t-primary-light rounded-full animate-spin" }),
|
|
1509
|
+
"Carregando mensagens anteriores..."
|
|
1510
|
+
] }) }),
|
|
1511
|
+
hasMore && !loadingMore && /* @__PURE__ */ jsx("div", { className: "flex justify-center py-2", children: /* @__PURE__ */ jsx(
|
|
1512
|
+
"button",
|
|
1513
|
+
{
|
|
1514
|
+
onClick: onLoadMore,
|
|
1515
|
+
className: "text-xs text-primary-light hover:text-primary transition-colors",
|
|
1516
|
+
children: "\u2191 Carregar mais mensagens"
|
|
1517
|
+
}
|
|
1518
|
+
) }),
|
|
1519
|
+
loading ? /* @__PURE__ */ jsx("div", { className: "space-y-4 py-4", children: Array.from({ length: 5 }).map((_, i) => /* @__PURE__ */ jsxs("div", { className: "flex gap-3", children: [
|
|
1520
|
+
/* @__PURE__ */ jsx(Skeleton, { variant: "circular", className: "h-8 w-8 shrink-0" }),
|
|
1521
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-1 space-y-1.5", children: [
|
|
1522
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-3 w-32" }),
|
|
1523
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-3 w-full" }),
|
|
1524
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-3 w-3/4" })
|
|
1525
|
+
] })
|
|
1526
|
+
] }, i)) }) : messages.length === 0 ? /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center h-full", children: [
|
|
1527
|
+
/* @__PURE__ */ jsx("div", { className: "w-12 h-12 rounded-sm bg-primary/10 flex items-center justify-center mb-3", children: /* @__PURE__ */ jsx(OrgComment, { className: "w-6 h-6 text-primary-light/50" }) }),
|
|
1528
|
+
/* @__PURE__ */ jsx("p", { className: "text-sm text-theme-muted", children: "Ainda sem mensagens." }),
|
|
1529
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs text-theme-muted mt-1", children: "Comece a conversa!" })
|
|
1530
|
+
] }) : messages.map((msg, idx) => {
|
|
1531
|
+
const isCurrentUser = msg.authorId === currentUserId;
|
|
1532
|
+
const replyToMessage = msg.replyToId || msg.parentId ? messagesById.get(msg.replyToId || msg.parentId || "") : null;
|
|
1533
|
+
return /* @__PURE__ */ jsxs(React7.Fragment, { children: [
|
|
1534
|
+
shouldShowDateSeparator(messages, idx) && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 py-3", children: [
|
|
1535
|
+
/* @__PURE__ */ jsx("div", { className: "flex-1 h-px bg-theme-subtle-10" }),
|
|
1536
|
+
/* @__PURE__ */ jsx("span", { className: "text-[11px] text-theme-muted font-medium", children: formatDateSeparator(msg.createdAt) }),
|
|
1537
|
+
/* @__PURE__ */ jsx("div", { className: "flex-1 h-px bg-theme-subtle-10" })
|
|
1538
|
+
] }),
|
|
1539
|
+
/* @__PURE__ */ jsx(
|
|
1540
|
+
MessageBubble,
|
|
1541
|
+
{
|
|
1542
|
+
message: msg,
|
|
1543
|
+
isCurrentUser,
|
|
1544
|
+
replyToMessage,
|
|
1545
|
+
onEdit: isCurrentUser ? handleEditMessage : void 0,
|
|
1546
|
+
onReply: handleReplyToMessage,
|
|
1547
|
+
onDelete: isCurrentUser ? (id) => onDeleteMessage?.(id) : void 0,
|
|
1548
|
+
onReact: (id, emoji) => onReactToMessage?.(id, emoji),
|
|
1549
|
+
onScrollToMessage: handleScrollToMessage,
|
|
1550
|
+
canEdit: isCurrentUser,
|
|
1551
|
+
canDelete: isCurrentUser
|
|
1552
|
+
}
|
|
1553
|
+
)
|
|
1554
|
+
] }, msg.id);
|
|
1555
|
+
})
|
|
1556
|
+
]
|
|
1557
|
+
}
|
|
1558
|
+
),
|
|
1559
|
+
/* @__PURE__ */ jsx(TypingIndicator, { users: typingUsers }),
|
|
1560
|
+
/* @__PURE__ */ jsx("div", { className: "px-3 md:px-5 py-3 border-t border-theme-subtle", children: /* @__PURE__ */ jsx(
|
|
1561
|
+
MessageInput,
|
|
1562
|
+
{
|
|
1563
|
+
value: inputValue,
|
|
1564
|
+
onChange: setInputValue,
|
|
1565
|
+
onSend: handleSend,
|
|
1566
|
+
onTyping,
|
|
1567
|
+
onStopTyping,
|
|
1568
|
+
editingMessage,
|
|
1569
|
+
onCancelEdit: handleCancelEdit,
|
|
1570
|
+
replyTo,
|
|
1571
|
+
onCancelReply: handleCancelReply,
|
|
1572
|
+
mentionOptions,
|
|
1573
|
+
onMentionSearch: handleMentionSearch,
|
|
1574
|
+
placeholder: "Escreva uma mensagem..."
|
|
1575
|
+
}
|
|
1576
|
+
) })
|
|
1577
|
+
]
|
|
1578
|
+
},
|
|
1579
|
+
room.id
|
|
1580
|
+
) });
|
|
1581
|
+
}
|
|
1582
|
+
ChatMessages.displayName = "ChatMessages";
|
|
1583
|
+
function Label({ className, ...props }) {
|
|
1584
|
+
return /* @__PURE__ */ jsx(
|
|
1585
|
+
"label",
|
|
1586
|
+
{
|
|
1587
|
+
"data-slot": "label",
|
|
1588
|
+
className: cn(
|
|
1589
|
+
"text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70",
|
|
1590
|
+
className
|
|
1591
|
+
),
|
|
1592
|
+
...props
|
|
1593
|
+
}
|
|
1594
|
+
);
|
|
1595
|
+
}
|
|
1596
|
+
var textareaVariants = cva(
|
|
1597
|
+
"flex w-full min-h-[100px] bg-glass-highlight backdrop-blur-md border border-glass-border rounded-sm px-4 py-3 text-sm font-light text-org-text transition-all duration-[400ms] ease-[cubic-bezier(0.25,1,0.5,1)] placeholder:text-org-text-muted focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50 resize-none",
|
|
1598
|
+
{
|
|
1599
|
+
variants: {
|
|
1600
|
+
variant: {
|
|
1601
|
+
default: "focus:border-primary-light/50 focus:shadow-[0_0_0_3px_rgba(79,57,246,0.10),0_0_20px_rgba(79,57,246,0.08)]",
|
|
1602
|
+
flat: "rounded-sm focus:border-primary-light/50 focus:shadow-[0_0_0_3px_rgba(79,57,246,0.10)]",
|
|
1603
|
+
rounded: "rounded-sm focus:border-primary-light/50 focus:shadow-[0_0_0_3px_rgba(79,57,246,0.10)]",
|
|
1604
|
+
error: "border-error/30 bg-error/5 text-red-100 placeholder:text-red-500/30 focus:border-error focus:shadow-[0_0_0_3px_rgba(224,17,95,0.10)]",
|
|
1605
|
+
cream: "bg-white/60 border-[rgba(36,25,121,0.06)] text-neutral-900 placeholder:text-neutral-400 focus:border-primary/40 focus:bg-white/80 focus:shadow-[0_0_0_3px_rgba(79,57,246,0.08)] backdrop-blur-sm"
|
|
1606
|
+
}
|
|
1607
|
+
},
|
|
1608
|
+
defaultVariants: {
|
|
1609
|
+
variant: "default"
|
|
1610
|
+
}
|
|
1611
|
+
}
|
|
1612
|
+
);
|
|
1613
|
+
var Textarea = React7.forwardRef(
|
|
1614
|
+
({ className, variant, label, error, labelPosition = "left", ...props }, ref) => {
|
|
1615
|
+
const id = React7.useId();
|
|
1616
|
+
const effectiveVariant = error ? "error" : variant;
|
|
1617
|
+
return /* @__PURE__ */ jsxs("div", { className: "relative group", children: [
|
|
1618
|
+
label && /* @__PURE__ */ jsx(
|
|
1619
|
+
"label",
|
|
1620
|
+
{
|
|
1621
|
+
htmlFor: id,
|
|
1622
|
+
className: cn(
|
|
1623
|
+
"absolute -top-3 z-10 px-2 text-label uppercase tracking-widest",
|
|
1624
|
+
effectiveVariant === "cream" ? "bg-cream-base" : "bg-surface",
|
|
1625
|
+
labelPosition === "left" ? "left-0 ml-2" : "right-0 mr-2",
|
|
1626
|
+
error ? "text-error" : "text-primary-light"
|
|
1627
|
+
),
|
|
1628
|
+
children: label
|
|
1629
|
+
}
|
|
1630
|
+
),
|
|
1631
|
+
/* @__PURE__ */ jsx(
|
|
1632
|
+
"textarea",
|
|
1633
|
+
{
|
|
1634
|
+
id,
|
|
1635
|
+
className: cn(textareaVariants({ variant: effectiveVariant, className })),
|
|
1636
|
+
ref,
|
|
1637
|
+
"aria-invalid": !!error,
|
|
1638
|
+
"aria-describedby": error ? `${id}-error` : void 0,
|
|
1639
|
+
...props
|
|
1640
|
+
}
|
|
1641
|
+
),
|
|
1642
|
+
error && /* @__PURE__ */ jsx("div", { id: `${id}-error`, className: "mt-2 flex items-center justify-end gap-2", children: /* @__PURE__ */ jsx("span", { className: "text-mono-xs text-error", children: error }) })
|
|
1643
|
+
] });
|
|
1644
|
+
}
|
|
1645
|
+
);
|
|
1646
|
+
Textarea.displayName = "Textarea";
|
|
1647
|
+
function Dialog({ ...props }) {
|
|
1648
|
+
return /* @__PURE__ */ jsx(DialogPrimitive.Root, { "data-slot": "dialog", ...props });
|
|
1649
|
+
}
|
|
1650
|
+
function DialogTrigger({ ...props }) {
|
|
1651
|
+
return /* @__PURE__ */ jsx(DialogPrimitive.Trigger, { "data-slot": "dialog-trigger", ...props });
|
|
1652
|
+
}
|
|
1653
|
+
function DialogPortal({ ...props }) {
|
|
1654
|
+
return /* @__PURE__ */ jsx(DialogPrimitive.Portal, { "data-slot": "dialog-portal", ...props });
|
|
1655
|
+
}
|
|
1656
|
+
function DialogClose({ ...props }) {
|
|
1657
|
+
return /* @__PURE__ */ jsx(DialogPrimitive.Close, { "data-slot": "dialog-close", ...props });
|
|
1658
|
+
}
|
|
1659
|
+
function DialogOverlay({
|
|
1660
|
+
className,
|
|
1661
|
+
...props
|
|
1662
|
+
}) {
|
|
1663
|
+
return /* @__PURE__ */ jsx(
|
|
1664
|
+
DialogPrimitive.Overlay,
|
|
1665
|
+
{
|
|
1666
|
+
"data-slot": "dialog-overlay",
|
|
1667
|
+
className: cn(
|
|
1668
|
+
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50",
|
|
1669
|
+
className
|
|
1670
|
+
),
|
|
1671
|
+
...props
|
|
1672
|
+
}
|
|
1673
|
+
);
|
|
1674
|
+
}
|
|
1675
|
+
function DialogContent({
|
|
1676
|
+
className,
|
|
1677
|
+
children,
|
|
1678
|
+
showCloseButton = true,
|
|
1679
|
+
...props
|
|
1680
|
+
}) {
|
|
1681
|
+
return /* @__PURE__ */ jsxs(DialogPortal, { "data-slot": "dialog-portal", children: [
|
|
1682
|
+
/* @__PURE__ */ jsx(DialogOverlay, {}),
|
|
1683
|
+
/* @__PURE__ */ jsxs(
|
|
1684
|
+
DialogPrimitive.Content,
|
|
1685
|
+
{
|
|
1686
|
+
"data-slot": "dialog-content",
|
|
1687
|
+
className: cn(
|
|
1688
|
+
"bg-theme-surface data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-sm border border-theme-subtle p-6 shadow-glass backdrop-blur-[40px] saturate-[180%] duration-[400ms] sm:max-w-lg text-theme",
|
|
1689
|
+
className
|
|
1690
|
+
),
|
|
1691
|
+
...props,
|
|
1692
|
+
children: [
|
|
1693
|
+
children,
|
|
1694
|
+
showCloseButton && /* @__PURE__ */ jsxs(
|
|
1695
|
+
DialogPrimitive.Close,
|
|
1696
|
+
{
|
|
1697
|
+
"data-slot": "dialog-close",
|
|
1698
|
+
className: "ring-offset-background focus:ring-ring data-[state=open]:bg-accent data-[state=open]:text-muted-foreground absolute top-4 right-4 rounded-xs opacity-70 transition-opacity hover:opacity-100 focus:ring-2 focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
1699
|
+
children: [
|
|
1700
|
+
/* @__PURE__ */ jsx(XIcon, {}),
|
|
1701
|
+
/* @__PURE__ */ jsx("span", { className: "sr-only", children: "Close" })
|
|
1702
|
+
]
|
|
1703
|
+
}
|
|
1704
|
+
)
|
|
1705
|
+
]
|
|
1706
|
+
}
|
|
1707
|
+
)
|
|
1708
|
+
] });
|
|
1709
|
+
}
|
|
1710
|
+
function DialogHeader({ className, ...props }) {
|
|
1711
|
+
return /* @__PURE__ */ jsx(
|
|
1712
|
+
"div",
|
|
1713
|
+
{
|
|
1714
|
+
"data-slot": "dialog-header",
|
|
1715
|
+
className: cn("flex flex-col gap-2 text-center sm:text-left", className),
|
|
1716
|
+
...props
|
|
1717
|
+
}
|
|
1718
|
+
);
|
|
1719
|
+
}
|
|
1720
|
+
function DialogFooter({ className, ...props }) {
|
|
1721
|
+
return /* @__PURE__ */ jsx(
|
|
1722
|
+
"div",
|
|
1723
|
+
{
|
|
1724
|
+
"data-slot": "dialog-footer",
|
|
1725
|
+
className: cn("flex flex-col-reverse gap-2 sm:flex-row sm:justify-end", className),
|
|
1726
|
+
...props
|
|
1727
|
+
}
|
|
1728
|
+
);
|
|
1729
|
+
}
|
|
1730
|
+
function DialogTitle({
|
|
1731
|
+
className,
|
|
1732
|
+
...props
|
|
1733
|
+
}) {
|
|
1734
|
+
return /* @__PURE__ */ jsx(
|
|
1735
|
+
DialogPrimitive.Title,
|
|
1736
|
+
{
|
|
1737
|
+
"data-slot": "dialog-title",
|
|
1738
|
+
className: cn("text-lg leading-none font-semibold", className),
|
|
1739
|
+
...props
|
|
1740
|
+
}
|
|
1741
|
+
);
|
|
1742
|
+
}
|
|
1743
|
+
function DialogDescription({
|
|
1744
|
+
className,
|
|
1745
|
+
...props
|
|
1746
|
+
}) {
|
|
1747
|
+
return /* @__PURE__ */ jsx(
|
|
1748
|
+
DialogPrimitive.Description,
|
|
1749
|
+
{
|
|
1750
|
+
"data-slot": "dialog-description",
|
|
1751
|
+
className: cn("text-theme-secondary text-sm", className),
|
|
1752
|
+
...props
|
|
1753
|
+
}
|
|
1754
|
+
);
|
|
1755
|
+
}
|
|
1756
|
+
function Drawer({
|
|
1757
|
+
shouldScaleBackground = true,
|
|
1758
|
+
...props
|
|
1759
|
+
}) {
|
|
1760
|
+
return /* @__PURE__ */ jsx(Drawer$1.Root, { shouldScaleBackground, ...props });
|
|
1761
|
+
}
|
|
1762
|
+
var DrawerTrigger = Drawer$1.Trigger;
|
|
1763
|
+
var DrawerPortal = Drawer$1.Portal;
|
|
1764
|
+
var DrawerClose = Drawer$1.Close;
|
|
1765
|
+
function DrawerOverlay({
|
|
1766
|
+
className,
|
|
1767
|
+
...props
|
|
1768
|
+
}) {
|
|
1769
|
+
return /* @__PURE__ */ jsx(
|
|
1770
|
+
Drawer$1.Overlay,
|
|
1771
|
+
{
|
|
1772
|
+
"data-slot": "drawer-overlay",
|
|
1773
|
+
className: cn("fixed inset-0 z-50 bg-black/80", className),
|
|
1774
|
+
...props
|
|
1775
|
+
}
|
|
1776
|
+
);
|
|
1777
|
+
}
|
|
1778
|
+
function DrawerContent({
|
|
1779
|
+
className,
|
|
1780
|
+
children,
|
|
1781
|
+
...props
|
|
1782
|
+
}) {
|
|
1783
|
+
return /* @__PURE__ */ jsxs(DrawerPortal, { children: [
|
|
1784
|
+
/* @__PURE__ */ jsx(DrawerOverlay, {}),
|
|
1785
|
+
/* @__PURE__ */ jsx(
|
|
1786
|
+
Drawer$1.Content,
|
|
1787
|
+
{
|
|
1788
|
+
asChild: true,
|
|
1789
|
+
"data-slot": "drawer-content",
|
|
1790
|
+
...props,
|
|
1791
|
+
children: /* @__PURE__ */ jsxs(
|
|
1792
|
+
motion.div,
|
|
1793
|
+
{
|
|
1794
|
+
initial: { y: "100%", opacity: 0 },
|
|
1795
|
+
animate: { y: 0, opacity: 1 },
|
|
1796
|
+
exit: { y: "100%", opacity: 0 },
|
|
1797
|
+
transition: {
|
|
1798
|
+
type: "spring",
|
|
1799
|
+
stiffness: 400,
|
|
1800
|
+
damping: 40
|
|
1801
|
+
},
|
|
1802
|
+
className: cn(
|
|
1803
|
+
"fixed inset-x-0 bottom-0 z-50 mt-24 flex h-auto flex-col rounded-t-[10px] border border-theme-subtle bg-theme-surface text-theme",
|
|
1804
|
+
className
|
|
1805
|
+
),
|
|
1806
|
+
children: [
|
|
1807
|
+
/* @__PURE__ */ jsx("div", { className: "mx-auto mt-4 h-2 w-[100px] rounded-full bg-theme-subtle-20" }),
|
|
1808
|
+
children
|
|
1809
|
+
]
|
|
1810
|
+
}
|
|
1811
|
+
)
|
|
1812
|
+
}
|
|
1813
|
+
)
|
|
1814
|
+
] });
|
|
1815
|
+
}
|
|
1816
|
+
function DrawerHeader({ className, ...props }) {
|
|
1817
|
+
return /* @__PURE__ */ jsx(
|
|
1818
|
+
"div",
|
|
1819
|
+
{
|
|
1820
|
+
"data-slot": "drawer-header",
|
|
1821
|
+
className: cn("grid gap-1.5 p-4 text-center sm:text-left", className),
|
|
1822
|
+
...props
|
|
1823
|
+
}
|
|
1824
|
+
);
|
|
1825
|
+
}
|
|
1826
|
+
function DrawerFooter({ className, ...props }) {
|
|
1827
|
+
return /* @__PURE__ */ jsx(
|
|
1828
|
+
"div",
|
|
1829
|
+
{
|
|
1830
|
+
"data-slot": "drawer-footer",
|
|
1831
|
+
className: cn("mt-auto flex flex-col gap-2 p-4", className),
|
|
1832
|
+
...props
|
|
1833
|
+
}
|
|
1834
|
+
);
|
|
1835
|
+
}
|
|
1836
|
+
function DrawerTitle({
|
|
1837
|
+
className,
|
|
1838
|
+
...props
|
|
1839
|
+
}) {
|
|
1840
|
+
return /* @__PURE__ */ jsx(
|
|
1841
|
+
Drawer$1.Title,
|
|
1842
|
+
{
|
|
1843
|
+
"data-slot": "drawer-title",
|
|
1844
|
+
className: cn("text-lg font-semibold leading-none tracking-tight", className),
|
|
1845
|
+
...props
|
|
1846
|
+
}
|
|
1847
|
+
);
|
|
1848
|
+
}
|
|
1849
|
+
function DrawerDescription({
|
|
1850
|
+
className,
|
|
1851
|
+
...props
|
|
1852
|
+
}) {
|
|
1853
|
+
return /* @__PURE__ */ jsx(
|
|
1854
|
+
Drawer$1.Description,
|
|
1855
|
+
{
|
|
1856
|
+
"data-slot": "drawer-description",
|
|
1857
|
+
className: cn("text-sm text-theme-secondary", className),
|
|
1858
|
+
...props
|
|
1859
|
+
}
|
|
1860
|
+
);
|
|
1861
|
+
}
|
|
1862
|
+
function useMediaQuery(query) {
|
|
1863
|
+
const [matches, setMatches] = React7.useState(false);
|
|
1864
|
+
React7.useEffect(() => {
|
|
1865
|
+
const mql = window.matchMedia(query);
|
|
1866
|
+
setMatches(mql.matches);
|
|
1867
|
+
const handler = (e) => setMatches(e.matches);
|
|
1868
|
+
mql.addEventListener("change", handler);
|
|
1869
|
+
return () => mql.removeEventListener("change", handler);
|
|
1870
|
+
}, [query]);
|
|
1871
|
+
return matches;
|
|
1872
|
+
}
|
|
1873
|
+
function ResponsiveDialog({
|
|
1874
|
+
open,
|
|
1875
|
+
onOpenChange,
|
|
1876
|
+
title,
|
|
1877
|
+
description,
|
|
1878
|
+
children,
|
|
1879
|
+
footer,
|
|
1880
|
+
desktopQuery = "(min-width: 768px)",
|
|
1881
|
+
className,
|
|
1882
|
+
contentClassName
|
|
1883
|
+
}) {
|
|
1884
|
+
const isDesktop = useMediaQuery(desktopQuery);
|
|
1885
|
+
if (isDesktop) {
|
|
1886
|
+
return /* @__PURE__ */ jsx(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxs(
|
|
1887
|
+
DialogContent,
|
|
1888
|
+
{
|
|
1889
|
+
className: cn(
|
|
1890
|
+
"max-w-2xl max-h-[85vh] overflow-y-auto",
|
|
1891
|
+
"border border-theme-subtle bg-theme-glass-heavy backdrop-blur-2xl",
|
|
1892
|
+
"shadow-glass-xl rounded-sm",
|
|
1893
|
+
contentClassName
|
|
1894
|
+
),
|
|
1895
|
+
children: [
|
|
1896
|
+
(title || description) && /* @__PURE__ */ jsxs(DialogHeader, { children: [
|
|
1897
|
+
title && /* @__PURE__ */ jsx(DialogTitle, { className: "text-lg", children: title }),
|
|
1898
|
+
description && /* @__PURE__ */ jsx(DialogDescription, { className: "text-muted-foreground text-sm", children: description })
|
|
1899
|
+
] }),
|
|
1900
|
+
/* @__PURE__ */ jsx("div", { className, children }),
|
|
1901
|
+
footer && /* @__PURE__ */ jsx(DialogFooter, { children: footer })
|
|
1902
|
+
]
|
|
1903
|
+
}
|
|
1904
|
+
) });
|
|
1905
|
+
}
|
|
1906
|
+
return /* @__PURE__ */ jsx(Drawer, { open, onOpenChange, children: /* @__PURE__ */ jsxs(
|
|
1907
|
+
DrawerContent,
|
|
1908
|
+
{
|
|
1909
|
+
className: cn(
|
|
1910
|
+
"max-h-[90vh]",
|
|
1911
|
+
"border-t border-theme-subtle bg-theme-glass-heavy backdrop-blur-2xl",
|
|
1912
|
+
contentClassName
|
|
1913
|
+
),
|
|
1914
|
+
children: [
|
|
1915
|
+
(title || description) && /* @__PURE__ */ jsxs(DrawerHeader, { className: "text-left", children: [
|
|
1916
|
+
title && /* @__PURE__ */ jsx(DrawerTitle, { className: "text-lg", children: title }),
|
|
1917
|
+
description && /* @__PURE__ */ jsx(DrawerDescription, { className: "text-muted-foreground text-sm", children: description })
|
|
1918
|
+
] }),
|
|
1919
|
+
/* @__PURE__ */ jsx("div", { className: cn("overflow-y-auto px-4 pb-4", className), children }),
|
|
1920
|
+
footer && /* @__PURE__ */ jsx(DrawerFooter, { children: footer })
|
|
1921
|
+
]
|
|
1922
|
+
}
|
|
1923
|
+
) });
|
|
1924
|
+
}
|
|
1925
|
+
ResponsiveDialog.displayName = "ResponsiveDialog";
|
|
1926
|
+
var Tabs = TabsPrimitive.Root;
|
|
1927
|
+
function TabsList({
|
|
1928
|
+
className,
|
|
1929
|
+
...props
|
|
1930
|
+
}) {
|
|
1931
|
+
return /* @__PURE__ */ jsx(
|
|
1932
|
+
TabsPrimitive.List,
|
|
1933
|
+
{
|
|
1934
|
+
"data-slot": "tabs-list",
|
|
1935
|
+
className: cn(
|
|
1936
|
+
"inline-flex h-10 items-center justify-center rounded-sm bg-theme-glass-heavy backdrop-blur-md border border-glass-border p-1 text-theme-muted",
|
|
1937
|
+
className
|
|
1938
|
+
),
|
|
1939
|
+
...props
|
|
1940
|
+
}
|
|
1941
|
+
);
|
|
1942
|
+
}
|
|
1943
|
+
function TabsTrigger({
|
|
1944
|
+
className,
|
|
1945
|
+
...props
|
|
1946
|
+
}) {
|
|
1947
|
+
return /* @__PURE__ */ jsx(
|
|
1948
|
+
TabsPrimitive.Trigger,
|
|
1949
|
+
{
|
|
1950
|
+
"data-slot": "tabs-trigger",
|
|
1951
|
+
className: cn(
|
|
1952
|
+
"inline-flex items-center justify-center whitespace-nowrap rounded-sm px-3 py-1.5 text-sm font-medium ring-offset-background transition-all duration-[400ms] ease-[cubic-bezier(0.25,1,0.5,1)] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-glass-highlight data-[state=active]:text-theme data-[state=active]:shadow-inner-light",
|
|
1953
|
+
className
|
|
1954
|
+
),
|
|
1955
|
+
...props
|
|
1956
|
+
}
|
|
1957
|
+
);
|
|
1958
|
+
}
|
|
1959
|
+
function TabsContent({
|
|
1960
|
+
className,
|
|
1961
|
+
...props
|
|
1962
|
+
}) {
|
|
1963
|
+
return /* @__PURE__ */ jsx(
|
|
1964
|
+
TabsPrimitive.Content,
|
|
1965
|
+
{
|
|
1966
|
+
"data-slot": "tabs-content",
|
|
1967
|
+
className: cn(
|
|
1968
|
+
"mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
|
|
1969
|
+
className
|
|
1970
|
+
),
|
|
1971
|
+
...props
|
|
1972
|
+
}
|
|
1973
|
+
);
|
|
1974
|
+
}
|
|
1975
|
+
function getInitials3(name) {
|
|
1976
|
+
return name.split(" ").map((w) => w[0]).slice(0, 2).join("").toUpperCase();
|
|
1977
|
+
}
|
|
1978
|
+
function CreateRoomDialog({
|
|
1979
|
+
open,
|
|
1980
|
+
onOpenChange,
|
|
1981
|
+
onCreateChannel,
|
|
1982
|
+
onCreateDM,
|
|
1983
|
+
workspaceMembers,
|
|
1984
|
+
loadingMembers
|
|
1985
|
+
}) {
|
|
1986
|
+
const [tab, setTab] = React7.useState("channel");
|
|
1987
|
+
const [creating, setCreating] = React7.useState(false);
|
|
1988
|
+
const [channelName, setChannelName] = React7.useState("");
|
|
1989
|
+
const [channelDesc, setChannelDesc] = React7.useState("");
|
|
1990
|
+
const [visibility, setVisibility] = React7.useState("WORKSPACE");
|
|
1991
|
+
const [selectedMembers, setSelectedMembers] = React7.useState([]);
|
|
1992
|
+
const [showAdvanced, setShowAdvanced] = React7.useState(false);
|
|
1993
|
+
const [dmSearch, setDmSearch] = React7.useState("");
|
|
1994
|
+
const [nameError, setNameError] = React7.useState("");
|
|
1995
|
+
const validateName = (value) => {
|
|
1996
|
+
if (!value.trim()) {
|
|
1997
|
+
setNameError("Nome do canal \xE9 obrigat\xF3rio");
|
|
1998
|
+
return false;
|
|
1999
|
+
}
|
|
2000
|
+
if (value.length < 2) {
|
|
2001
|
+
setNameError("Nome deve ter pelo menos 2 caracteres");
|
|
2002
|
+
return false;
|
|
2003
|
+
}
|
|
2004
|
+
if (!/^[a-zA-Z0-9À-ÿ\s\-_]+$/.test(value)) {
|
|
2005
|
+
setNameError("Apenas letras, n\xFAmeros, espa\xE7os e h\xEDfens");
|
|
2006
|
+
return false;
|
|
2007
|
+
}
|
|
2008
|
+
setNameError("");
|
|
2009
|
+
return true;
|
|
2010
|
+
};
|
|
2011
|
+
const filteredMembers = React7.useMemo(() => {
|
|
2012
|
+
if (!dmSearch) return workspaceMembers;
|
|
2013
|
+
const q = dmSearch.toLowerCase();
|
|
2014
|
+
return workspaceMembers.filter(
|
|
2015
|
+
(m) => m.displayName.toLowerCase().includes(q) || m.email?.toLowerCase().includes(q)
|
|
2016
|
+
);
|
|
2017
|
+
}, [workspaceMembers, dmSearch]);
|
|
2018
|
+
const handleCreateChannel = async () => {
|
|
2019
|
+
if (!validateName(channelName)) return;
|
|
2020
|
+
setCreating(true);
|
|
2021
|
+
try {
|
|
2022
|
+
const result = await onCreateChannel({
|
|
2023
|
+
name: channelName.trim(),
|
|
2024
|
+
description: channelDesc.trim() || void 0,
|
|
2025
|
+
visibility,
|
|
2026
|
+
memberIds: selectedMembers
|
|
2027
|
+
});
|
|
2028
|
+
if (result) {
|
|
2029
|
+
resetForm();
|
|
2030
|
+
onOpenChange(false);
|
|
2031
|
+
}
|
|
2032
|
+
} finally {
|
|
2033
|
+
setCreating(false);
|
|
2034
|
+
}
|
|
2035
|
+
};
|
|
2036
|
+
const handleCreateDM = async (userId) => {
|
|
2037
|
+
setCreating(true);
|
|
2038
|
+
try {
|
|
2039
|
+
const result = await onCreateDM(userId);
|
|
2040
|
+
if (result) {
|
|
2041
|
+
resetForm();
|
|
2042
|
+
onOpenChange(false);
|
|
2043
|
+
}
|
|
2044
|
+
} finally {
|
|
2045
|
+
setCreating(false);
|
|
2046
|
+
}
|
|
2047
|
+
};
|
|
2048
|
+
const toggleMember = (userId) => {
|
|
2049
|
+
setSelectedMembers(
|
|
2050
|
+
(prev) => prev.includes(userId) ? prev.filter((id) => id !== userId) : [...prev, userId]
|
|
2051
|
+
);
|
|
2052
|
+
};
|
|
2053
|
+
const resetForm = () => {
|
|
2054
|
+
setChannelName("");
|
|
2055
|
+
setChannelDesc("");
|
|
2056
|
+
setVisibility("WORKSPACE");
|
|
2057
|
+
setSelectedMembers([]);
|
|
2058
|
+
setShowAdvanced(false);
|
|
2059
|
+
setDmSearch("");
|
|
2060
|
+
setNameError("");
|
|
2061
|
+
setTab("channel");
|
|
2062
|
+
};
|
|
2063
|
+
return /* @__PURE__ */ jsx(
|
|
2064
|
+
ResponsiveDialog,
|
|
2065
|
+
{
|
|
2066
|
+
open,
|
|
2067
|
+
onOpenChange,
|
|
2068
|
+
title: "Nova conversa",
|
|
2069
|
+
description: "Crie um canal para a equipa ou inicie uma conversa direta.",
|
|
2070
|
+
contentClassName: "sm:max-w-md",
|
|
2071
|
+
children: /* @__PURE__ */ jsxs(Tabs, { value: tab, onValueChange: (v) => setTab(v), children: [
|
|
2072
|
+
/* @__PURE__ */ jsxs(TabsList, { className: "grid w-full grid-cols-2 bg-theme-subtle", children: [
|
|
2073
|
+
/* @__PURE__ */ jsxs(
|
|
2074
|
+
TabsTrigger,
|
|
2075
|
+
{
|
|
2076
|
+
value: "channel",
|
|
2077
|
+
className: "text-xs data-[state=active]:bg-primary/20 data-[state=active]:text-primary-light",
|
|
2078
|
+
children: [
|
|
2079
|
+
/* @__PURE__ */ jsx(OrgTeam, { className: "w-3.5 h-3.5 mr-1.5" }),
|
|
2080
|
+
"Canal"
|
|
2081
|
+
]
|
|
2082
|
+
}
|
|
2083
|
+
),
|
|
2084
|
+
/* @__PURE__ */ jsxs(
|
|
2085
|
+
TabsTrigger,
|
|
2086
|
+
{
|
|
2087
|
+
value: "dm",
|
|
2088
|
+
className: "text-xs data-[state=active]:bg-primary/20 data-[state=active]:text-primary-light",
|
|
2089
|
+
children: [
|
|
2090
|
+
/* @__PURE__ */ jsx(OrgComment, { className: "w-3.5 h-3.5 mr-1.5" }),
|
|
2091
|
+
"Mensagem Direta"
|
|
2092
|
+
]
|
|
2093
|
+
}
|
|
2094
|
+
)
|
|
2095
|
+
] }),
|
|
2096
|
+
/* @__PURE__ */ jsxs(TabsContent, { value: "channel", className: "space-y-4 mt-4", children: [
|
|
2097
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
2098
|
+
/* @__PURE__ */ jsx(Label, { className: "text-theme-secondary text-xs", children: "Nome do canal" }),
|
|
2099
|
+
/* @__PURE__ */ jsx(
|
|
2100
|
+
Input,
|
|
2101
|
+
{
|
|
2102
|
+
variant: "flat",
|
|
2103
|
+
placeholder: "ex: equipa-design",
|
|
2104
|
+
value: channelName,
|
|
2105
|
+
onChange: (e) => {
|
|
2106
|
+
setChannelName(e.target.value);
|
|
2107
|
+
if (nameError) validateName(e.target.value);
|
|
2108
|
+
},
|
|
2109
|
+
onBlur: () => channelName && validateName(channelName),
|
|
2110
|
+
className: cn(
|
|
2111
|
+
"h-9 text-sm bg-theme-subtle border-theme-subtle",
|
|
2112
|
+
nameError && "border-rose-500/50 focus:border-rose-500"
|
|
2113
|
+
),
|
|
2114
|
+
autoFocus: true
|
|
2115
|
+
}
|
|
2116
|
+
),
|
|
2117
|
+
nameError && /* @__PURE__ */ jsx("p", { className: "text-[11px] text-rose-400 animate-in fade-in-0 slide-in-from-top-1 duration-[400ms]", children: nameError })
|
|
2118
|
+
] }),
|
|
2119
|
+
/* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
|
|
2120
|
+
/* @__PURE__ */ jsxs(
|
|
2121
|
+
"button",
|
|
2122
|
+
{
|
|
2123
|
+
type: "button",
|
|
2124
|
+
onClick: () => setVisibility("WORKSPACE"),
|
|
2125
|
+
className: cn(
|
|
2126
|
+
"flex-1 flex items-center gap-2 px-3 py-2 rounded-sm text-xs transition-all duration-[400ms] ease-[cubic-bezier(0.25,1,0.5,1)] border",
|
|
2127
|
+
visibility === "WORKSPACE" ? "bg-primary/15 border-primary/30 text-primary-light" : "bg-theme-subtle border-theme-subtle text-theme-muted hover:bg-theme-subtle"
|
|
2128
|
+
),
|
|
2129
|
+
children: [
|
|
2130
|
+
/* @__PURE__ */ jsx(OrgGlobe, { className: "w-3.5 h-3.5" }),
|
|
2131
|
+
"P\xFAblico"
|
|
2132
|
+
]
|
|
2133
|
+
}
|
|
2134
|
+
),
|
|
2135
|
+
/* @__PURE__ */ jsxs(
|
|
2136
|
+
"button",
|
|
2137
|
+
{
|
|
2138
|
+
type: "button",
|
|
2139
|
+
onClick: () => setVisibility("PRIVATE"),
|
|
2140
|
+
className: cn(
|
|
2141
|
+
"flex-1 flex items-center gap-2 px-3 py-2 rounded-sm text-xs transition-all duration-[400ms] ease-[cubic-bezier(0.25,1,0.5,1)] border",
|
|
2142
|
+
visibility === "PRIVATE" ? "bg-primary/15 border-primary/30 text-primary-light" : "bg-theme-subtle border-theme-subtle text-theme-muted hover:bg-theme-subtle"
|
|
2143
|
+
),
|
|
2144
|
+
children: [
|
|
2145
|
+
/* @__PURE__ */ jsx(OrgLock, { className: "w-3.5 h-3.5" }),
|
|
2146
|
+
"Privado"
|
|
2147
|
+
]
|
|
2148
|
+
}
|
|
2149
|
+
)
|
|
2150
|
+
] }),
|
|
2151
|
+
!showAdvanced ? /* @__PURE__ */ jsx(
|
|
2152
|
+
"button",
|
|
2153
|
+
{
|
|
2154
|
+
type: "button",
|
|
2155
|
+
onClick: () => setShowAdvanced(true),
|
|
2156
|
+
className: "text-xs text-primary-light/70 hover:text-primary-light transition-colors",
|
|
2157
|
+
children: "+ Adicionar descri\xE7\xE3o e membros"
|
|
2158
|
+
}
|
|
2159
|
+
) : /* @__PURE__ */ jsxs("div", { className: "space-y-4 animate-in fade-in-0 slide-in-from-top-2 duration-[400ms]", children: [
|
|
2160
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
2161
|
+
/* @__PURE__ */ jsx(Label, { className: "text-theme-secondary text-xs", children: "Descri\xE7\xE3o (opcional)" }),
|
|
2162
|
+
/* @__PURE__ */ jsx(
|
|
2163
|
+
Textarea,
|
|
2164
|
+
{
|
|
2165
|
+
placeholder: "Sobre o que \xE9 este canal...",
|
|
2166
|
+
value: channelDesc,
|
|
2167
|
+
onChange: (e) => setChannelDesc(e.target.value),
|
|
2168
|
+
className: "min-h-[60px] text-sm bg-theme-subtle border-theme-subtle text-theme"
|
|
2169
|
+
}
|
|
2170
|
+
)
|
|
2171
|
+
] }),
|
|
2172
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
2173
|
+
/* @__PURE__ */ jsxs(Label, { className: "text-theme-secondary text-xs", children: [
|
|
2174
|
+
"Adicionar membros (",
|
|
2175
|
+
selectedMembers.length,
|
|
2176
|
+
" selecionados)"
|
|
2177
|
+
] }),
|
|
2178
|
+
/* @__PURE__ */ jsx(ScrollArea, { className: "max-h-[150px] rounded-sm border border-theme-subtle bg-theme-subtle", children: /* @__PURE__ */ jsx("div", { className: "p-2 space-y-0.5", children: loadingMembers ? Array.from({ length: 3 }).map((_, i) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 p-2", children: [
|
|
2179
|
+
/* @__PURE__ */ jsx(Skeleton, { variant: "circular", className: "h-7 w-7" }),
|
|
2180
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-3 w-24" })
|
|
2181
|
+
] }, i)) : workspaceMembers.length === 0 ? /* @__PURE__ */ jsx("p", { className: "text-xs text-theme-muted p-2 text-center", children: "Sem membros dispon\xEDveis" }) : workspaceMembers.map((member) => /* @__PURE__ */ jsxs(
|
|
2182
|
+
"button",
|
|
2183
|
+
{
|
|
2184
|
+
type: "button",
|
|
2185
|
+
onClick: () => toggleMember(member.id),
|
|
2186
|
+
className: cn(
|
|
2187
|
+
"flex items-center gap-2 w-full p-2 rounded-sm text-left text-xs transition-colors",
|
|
2188
|
+
selectedMembers.includes(member.id) ? "bg-primary/15 text-primary-light" : "text-theme-secondary hover:bg-theme-subtle"
|
|
2189
|
+
),
|
|
2190
|
+
children: [
|
|
2191
|
+
/* @__PURE__ */ jsx(Avatar, { shape: "circle", size: "sm", className: "h-7 w-7", children: /* @__PURE__ */ jsx(AvatarFallback, { className: "bg-primary/20 text-primary-light text-[10px]", children: getInitials3(member.displayName) }) }),
|
|
2192
|
+
/* @__PURE__ */ jsx("span", { className: "flex-1 truncate", children: member.displayName }),
|
|
2193
|
+
selectedMembers.includes(member.id) && /* @__PURE__ */ jsx(Badge, { variant: "primary", className: "text-[9px] px-1 py-0 h-4", children: "\u2713" })
|
|
2194
|
+
]
|
|
2195
|
+
},
|
|
2196
|
+
member.id
|
|
2197
|
+
)) }) })
|
|
2198
|
+
] })
|
|
2199
|
+
] }),
|
|
2200
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-end gap-2 mt-6", children: [
|
|
2201
|
+
/* @__PURE__ */ jsx(
|
|
2202
|
+
Button,
|
|
2203
|
+
{
|
|
2204
|
+
variant: "ghost",
|
|
2205
|
+
size: "sm",
|
|
2206
|
+
onClick: () => onOpenChange(false),
|
|
2207
|
+
className: "text-xs text-theme-muted",
|
|
2208
|
+
children: "Cancelar"
|
|
2209
|
+
}
|
|
2210
|
+
),
|
|
2211
|
+
/* @__PURE__ */ jsx(
|
|
2212
|
+
Button,
|
|
2213
|
+
{
|
|
2214
|
+
variant: "default",
|
|
2215
|
+
size: "sm",
|
|
2216
|
+
onClick: handleCreateChannel,
|
|
2217
|
+
disabled: !channelName.trim() || creating,
|
|
2218
|
+
className: "text-xs",
|
|
2219
|
+
children: creating ? "Criando..." : "Criar Canal"
|
|
2220
|
+
}
|
|
2221
|
+
)
|
|
2222
|
+
] })
|
|
2223
|
+
] }),
|
|
2224
|
+
/* @__PURE__ */ jsxs(TabsContent, { value: "dm", className: "space-y-3 mt-4", children: [
|
|
2225
|
+
/* @__PURE__ */ jsxs("div", { className: "relative", children: [
|
|
2226
|
+
/* @__PURE__ */ jsx(OrgSearch, { className: "absolute left-3 top-1/2 -translate-y-1/2 w-3.5 h-3.5 text-theme-muted" }),
|
|
2227
|
+
/* @__PURE__ */ jsx(
|
|
2228
|
+
Input,
|
|
2229
|
+
{
|
|
2230
|
+
variant: "flat",
|
|
2231
|
+
placeholder: "Pesquisar membro...",
|
|
2232
|
+
value: dmSearch,
|
|
2233
|
+
onChange: (e) => setDmSearch(e.target.value),
|
|
2234
|
+
className: "h-9 text-sm bg-theme-subtle border-theme-subtle pl-9",
|
|
2235
|
+
autoFocus: true
|
|
2236
|
+
}
|
|
2237
|
+
)
|
|
2238
|
+
] }),
|
|
2239
|
+
/* @__PURE__ */ jsx(ScrollArea, { className: "max-h-[280px]", children: /* @__PURE__ */ jsx("div", { className: "space-y-0.5", children: loadingMembers ? Array.from({ length: 5 }).map((_, i) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 p-2.5", children: [
|
|
2240
|
+
/* @__PURE__ */ jsx(Skeleton, { variant: "circular", className: "h-8 w-8" }),
|
|
2241
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-1 space-y-1.5", children: [
|
|
2242
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-3 w-28" }),
|
|
2243
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-2 w-20" })
|
|
2244
|
+
] })
|
|
2245
|
+
] }, i)) : filteredMembers.length === 0 ? /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center py-8 text-theme-muted", children: [
|
|
2246
|
+
/* @__PURE__ */ jsx(OrgSearch, { className: "w-8 h-8 mb-2 opacity-50" }),
|
|
2247
|
+
/* @__PURE__ */ jsx("p", { className: "text-xs", children: dmSearch ? "Nenhum membro encontrado" : "Sem membros no workspace" })
|
|
2248
|
+
] }) : filteredMembers.map((member) => /* @__PURE__ */ jsxs(
|
|
2249
|
+
"button",
|
|
2250
|
+
{
|
|
2251
|
+
onClick: () => handleCreateDM(member.id),
|
|
2252
|
+
disabled: creating,
|
|
2253
|
+
className: "flex items-center gap-3 w-full p-2.5 rounded-sm text-left transition-all duration-[400ms] ease-[cubic-bezier(0.25,1,0.5,1)] hover:bg-theme-subtle active:scale-[0.98] disabled:opacity-50",
|
|
2254
|
+
children: [
|
|
2255
|
+
/* @__PURE__ */ jsx(Avatar, { shape: "circle", size: "sm", className: "h-8 w-8", children: /* @__PURE__ */ jsx(AvatarFallback, { className: "bg-primary/20 text-primary-light text-xs", children: getInitials3(member.displayName) }) }),
|
|
2256
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
|
|
2257
|
+
/* @__PURE__ */ jsx("p", { className: "text-[13px] font-medium text-theme truncate", children: member.displayName }),
|
|
2258
|
+
member.email && /* @__PURE__ */ jsx("p", { className: "text-[11px] text-theme-muted truncate", children: member.email })
|
|
2259
|
+
] }),
|
|
2260
|
+
member.status && /* @__PURE__ */ jsx(
|
|
2261
|
+
"span",
|
|
2262
|
+
{
|
|
2263
|
+
className: cn(
|
|
2264
|
+
"w-2 h-2 rounded-full flex-shrink-0",
|
|
2265
|
+
member.status === "ONLINE" && "bg-emerald-400 animate-pulse",
|
|
2266
|
+
member.status === "AWAY" && "bg-amber-400",
|
|
2267
|
+
member.status === "BUSY" && "bg-rose-400",
|
|
2268
|
+
member.status === "OFFLINE" && "bg-theme-subtle-20"
|
|
2269
|
+
)
|
|
2270
|
+
}
|
|
2271
|
+
)
|
|
2272
|
+
]
|
|
2273
|
+
},
|
|
2274
|
+
member.id
|
|
2275
|
+
)) }) })
|
|
2276
|
+
] })
|
|
2277
|
+
] })
|
|
2278
|
+
}
|
|
2279
|
+
);
|
|
2280
|
+
}
|
|
2281
|
+
function Separator({
|
|
2282
|
+
className,
|
|
2283
|
+
orientation = "horizontal",
|
|
2284
|
+
decorative = true,
|
|
2285
|
+
...props
|
|
2286
|
+
}) {
|
|
2287
|
+
return /* @__PURE__ */ jsx(
|
|
2288
|
+
SeparatorPrimitive.Root,
|
|
2289
|
+
{
|
|
2290
|
+
"data-slot": "separator",
|
|
2291
|
+
decorative,
|
|
2292
|
+
orientation,
|
|
2293
|
+
className: cn(
|
|
2294
|
+
"bg-border shrink-0 data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-px",
|
|
2295
|
+
className
|
|
2296
|
+
),
|
|
2297
|
+
...props
|
|
2298
|
+
}
|
|
2299
|
+
);
|
|
2300
|
+
}
|
|
2301
|
+
var Select = SelectPrimitive.Root;
|
|
2302
|
+
var SelectGroup = SelectPrimitive.Group;
|
|
2303
|
+
var SelectValue = SelectPrimitive.Value;
|
|
2304
|
+
var SelectTrigger = React7.forwardRef(({ className, children, label, ...props }, ref) => /* @__PURE__ */ jsxs("div", { className: "relative group", children: [
|
|
2305
|
+
label && /* @__PURE__ */ jsx("span", { className: "absolute -top-3 right-0 z-10 mr-2 bg-theme-surface px-2 text-label uppercase tracking-widest text-theme-muted", children: label }),
|
|
2306
|
+
/* @__PURE__ */ jsxs(
|
|
2307
|
+
SelectPrimitive.Trigger,
|
|
2308
|
+
{
|
|
2309
|
+
ref,
|
|
2310
|
+
className: cn(
|
|
2311
|
+
"flex w-full items-center justify-between bg-theme-subtle backdrop-blur-md border border-theme-subtle px-4 py-3 text-sm font-light text-theme-secondary rounded-sm transition-all duration-[400ms] ease-[cubic-bezier(0.25,1,0.5,1)] focus:outline-none focus:shadow-[0_0_0_3px_rgba(79,57,246,0.10),0_0_20px_rgba(79,57,246,0.08)] focus:border-primary-light/50 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",
|
|
2312
|
+
className
|
|
2313
|
+
),
|
|
2314
|
+
...props,
|
|
2315
|
+
children: [
|
|
2316
|
+
children,
|
|
2317
|
+
/* @__PURE__ */ jsx(SelectPrimitive.Icon, { asChild: true, children: /* @__PURE__ */ jsx(ChevronDown, { className: "h-4 w-4 text-primary-light opacity-60" }) })
|
|
2318
|
+
]
|
|
2319
|
+
}
|
|
2320
|
+
)
|
|
2321
|
+
] }));
|
|
2322
|
+
SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
|
|
2323
|
+
var SelectScrollUpButton = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
2324
|
+
SelectPrimitive.ScrollUpButton,
|
|
2325
|
+
{
|
|
2326
|
+
ref,
|
|
2327
|
+
className: cn("flex cursor-default items-center justify-center py-1", className),
|
|
2328
|
+
...props,
|
|
2329
|
+
children: /* @__PURE__ */ jsx(ChevronUp, { className: "h-4 w-4 text-theme-muted" })
|
|
2330
|
+
}
|
|
2331
|
+
));
|
|
2332
|
+
SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName;
|
|
2333
|
+
var SelectScrollDownButton = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
2334
|
+
SelectPrimitive.ScrollDownButton,
|
|
2335
|
+
{
|
|
2336
|
+
ref,
|
|
2337
|
+
className: cn("flex cursor-default items-center justify-center py-1", className),
|
|
2338
|
+
...props,
|
|
2339
|
+
children: /* @__PURE__ */ jsx(ChevronDown, { className: "h-4 w-4 text-theme-muted" })
|
|
2340
|
+
}
|
|
2341
|
+
));
|
|
2342
|
+
SelectScrollDownButton.displayName = SelectPrimitive.ScrollDownButton.displayName;
|
|
2343
|
+
var SelectContent = React7.forwardRef(({ className, children, position = "popper", ...props }, ref) => /* @__PURE__ */ jsx(SelectPrimitive.Portal, { children: /* @__PURE__ */ jsxs(
|
|
2344
|
+
SelectPrimitive.Content,
|
|
2345
|
+
{
|
|
2346
|
+
ref,
|
|
2347
|
+
className: cn(
|
|
2348
|
+
"relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-sm border border-glass-border bg-theme-glass-heavy backdrop-blur-[40px] saturate-[180%] text-theme shadow-glass data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
|
2349
|
+
position === "popper" && "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
|
|
2350
|
+
className
|
|
2351
|
+
),
|
|
2352
|
+
position,
|
|
2353
|
+
...props,
|
|
2354
|
+
children: [
|
|
2355
|
+
/* @__PURE__ */ jsx(SelectScrollUpButton, {}),
|
|
2356
|
+
/* @__PURE__ */ jsx(
|
|
2357
|
+
SelectPrimitive.Viewport,
|
|
2358
|
+
{
|
|
2359
|
+
className: cn(
|
|
2360
|
+
"p-1",
|
|
2361
|
+
position === "popper" && "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]"
|
|
2362
|
+
),
|
|
2363
|
+
children
|
|
2364
|
+
}
|
|
2365
|
+
),
|
|
2366
|
+
/* @__PURE__ */ jsx(SelectScrollDownButton, {})
|
|
2367
|
+
]
|
|
2368
|
+
}
|
|
2369
|
+
) }));
|
|
2370
|
+
SelectContent.displayName = SelectPrimitive.Content.displayName;
|
|
2371
|
+
var SelectItem = React7.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(
|
|
2372
|
+
SelectPrimitive.Item,
|
|
2373
|
+
{
|
|
2374
|
+
ref,
|
|
2375
|
+
className: cn(
|
|
2376
|
+
"relative flex w-full cursor-default select-none items-center rounded-sm py-2 pl-8 pr-2 text-sm font-light text-theme-secondary outline-none transition-colors duration-[400ms] ease-[cubic-bezier(0.25,1,0.5,1)] focus:bg-theme-subtle-10 focus:text-theme data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
2377
|
+
className
|
|
2378
|
+
),
|
|
2379
|
+
...props,
|
|
2380
|
+
children: [
|
|
2381
|
+
/* @__PURE__ */ jsx("span", { className: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center", children: /* @__PURE__ */ jsx(SelectPrimitive.ItemIndicator, { children: /* @__PURE__ */ jsx(Check, { className: "h-4 w-4 text-primary-light" }) }) }),
|
|
2382
|
+
/* @__PURE__ */ jsx(SelectPrimitive.ItemText, { children })
|
|
2383
|
+
]
|
|
2384
|
+
}
|
|
2385
|
+
));
|
|
2386
|
+
SelectItem.displayName = SelectPrimitive.Item.displayName;
|
|
2387
|
+
var SelectSeparator = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
2388
|
+
SelectPrimitive.Separator,
|
|
2389
|
+
{
|
|
2390
|
+
ref,
|
|
2391
|
+
className: cn("-mx-1 my-1 h-px bg-theme-subtle-10", className),
|
|
2392
|
+
...props
|
|
2393
|
+
}
|
|
2394
|
+
));
|
|
2395
|
+
SelectSeparator.displayName = SelectPrimitive.Separator.displayName;
|
|
2396
|
+
var SelectLabel = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
2397
|
+
SelectPrimitive.Label,
|
|
2398
|
+
{
|
|
2399
|
+
ref,
|
|
2400
|
+
className: cn("py-1.5 pl-8 pr-2 text-label uppercase tracking-widest text-theme-muted", className),
|
|
2401
|
+
...props
|
|
2402
|
+
}
|
|
2403
|
+
));
|
|
2404
|
+
SelectLabel.displayName = SelectPrimitive.Label.displayName;
|
|
2405
|
+
function getInitials4(name) {
|
|
2406
|
+
return name.split(" ").map((w) => w[0]).slice(0, 2).join("").toUpperCase();
|
|
2407
|
+
}
|
|
2408
|
+
var roleBadgeColors = {
|
|
2409
|
+
OWNER: "bg-amber-500/15 text-amber-300 border-amber-500/30",
|
|
2410
|
+
ADMIN: "bg-blue-500/15 text-blue-300 border-blue-500/30",
|
|
2411
|
+
MEMBER: "bg-white/5 text-white/40 border-white/10"
|
|
2412
|
+
};
|
|
2413
|
+
var roleLabels = {
|
|
2414
|
+
OWNER: "Dono",
|
|
2415
|
+
ADMIN: "Admin",
|
|
2416
|
+
MEMBER: "Membro"
|
|
2417
|
+
};
|
|
2418
|
+
function RoomManagementPanel({
|
|
2419
|
+
room,
|
|
2420
|
+
members,
|
|
2421
|
+
permissions,
|
|
2422
|
+
currentUserId,
|
|
2423
|
+
loading,
|
|
2424
|
+
onClose,
|
|
2425
|
+
onUpdateRoom,
|
|
2426
|
+
onArchiveRoom,
|
|
2427
|
+
onLeaveRoom,
|
|
2428
|
+
onRemoveMember,
|
|
2429
|
+
onUpdateMemberRole,
|
|
2430
|
+
className
|
|
2431
|
+
}) {
|
|
2432
|
+
const [view, setView] = React7.useState("info");
|
|
2433
|
+
const [editName, setEditName] = React7.useState(room.name || "");
|
|
2434
|
+
const [editDesc, setEditDesc] = React7.useState(room.description || "");
|
|
2435
|
+
const [editVisibility, setEditVisibility] = React7.useState(room.visibility);
|
|
2436
|
+
const [saving, setSaving] = React7.useState(false);
|
|
2437
|
+
const [confirmArchive, setConfirmArchive] = React7.useState(false);
|
|
2438
|
+
React7.useEffect(() => {
|
|
2439
|
+
setEditName(room.name || "");
|
|
2440
|
+
setEditDesc(room.description || "");
|
|
2441
|
+
setEditVisibility(room.visibility);
|
|
2442
|
+
}, [room]);
|
|
2443
|
+
const handleSave = async () => {
|
|
2444
|
+
setSaving(true);
|
|
2445
|
+
try {
|
|
2446
|
+
await onUpdateRoom(room.id, {
|
|
2447
|
+
name: editName.trim(),
|
|
2448
|
+
description: editDesc.trim() || void 0,
|
|
2449
|
+
visibility: editVisibility
|
|
2450
|
+
});
|
|
2451
|
+
setView("info");
|
|
2452
|
+
} finally {
|
|
2453
|
+
setSaving(false);
|
|
2454
|
+
}
|
|
2455
|
+
};
|
|
2456
|
+
const renderHeader = (title, showBack) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 px-4 py-3 border-b border-theme-subtle", children: [
|
|
2457
|
+
showBack && /* @__PURE__ */ jsx(
|
|
2458
|
+
"button",
|
|
2459
|
+
{
|
|
2460
|
+
onClick: () => setView("info"),
|
|
2461
|
+
className: "p-1 rounded-sm hover:bg-theme-subtle text-theme-muted hover:text-theme transition-colors",
|
|
2462
|
+
children: /* @__PURE__ */ jsx(OrgChevronLeft, { className: "w-4 h-4" })
|
|
2463
|
+
}
|
|
2464
|
+
),
|
|
2465
|
+
/* @__PURE__ */ jsx("h3", { className: "flex-1 text-sm font-semibold text-theme", children: title }),
|
|
2466
|
+
/* @__PURE__ */ jsx(
|
|
2467
|
+
"button",
|
|
2468
|
+
{
|
|
2469
|
+
onClick: onClose,
|
|
2470
|
+
className: "p-1 rounded-sm hover:bg-theme-subtle text-theme-muted hover:text-theme transition-colors",
|
|
2471
|
+
children: /* @__PURE__ */ jsx(OrgClose, { className: "w-4 h-4" })
|
|
2472
|
+
}
|
|
2473
|
+
)
|
|
2474
|
+
] });
|
|
2475
|
+
const renderInfo = () => /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2476
|
+
renderHeader(room.name || "Detalhes"),
|
|
2477
|
+
/* @__PURE__ */ jsxs("div", { className: "p-4 space-y-4", children: [
|
|
2478
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
|
|
2479
|
+
/* @__PURE__ */ jsx(
|
|
2480
|
+
"div",
|
|
2481
|
+
{
|
|
2482
|
+
className: cn(
|
|
2483
|
+
"flex items-center justify-center h-12 w-12 rounded-sm text-lg",
|
|
2484
|
+
"bg-primary/15 text-primary-light"
|
|
2485
|
+
),
|
|
2486
|
+
children: room.type === "DIRECT" ? "\u{1F4AC}" : "#"
|
|
2487
|
+
}
|
|
2488
|
+
),
|
|
2489
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
2490
|
+
/* @__PURE__ */ jsx("h4", { className: "text-[15px] font-semibold text-theme", children: room.name || "Sem nome" }),
|
|
2491
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 mt-0.5", children: [
|
|
2492
|
+
/* @__PURE__ */ jsx(
|
|
2493
|
+
Badge,
|
|
2494
|
+
{
|
|
2495
|
+
variant: "default",
|
|
2496
|
+
className: "text-[10px] px-1.5 py-0 h-4 border-theme-subtle-10 text-theme-muted",
|
|
2497
|
+
children: room.type === "DIRECT" ? "DM" : room.visibility === "PRIVATE" ? "\u{1F512} Privado" : "\u{1F310} P\xFAblico"
|
|
2498
|
+
}
|
|
2499
|
+
),
|
|
2500
|
+
/* @__PURE__ */ jsxs("span", { className: "text-[11px] text-theme-muted", children: [
|
|
2501
|
+
room.memberCount,
|
|
2502
|
+
" membros"
|
|
2503
|
+
] })
|
|
2504
|
+
] })
|
|
2505
|
+
] })
|
|
2506
|
+
] }),
|
|
2507
|
+
room.description && /* @__PURE__ */ jsx("p", { className: "text-xs text-theme-muted leading-relaxed", children: room.description }),
|
|
2508
|
+
/* @__PURE__ */ jsx(Separator, { className: "bg-theme-subtle" }),
|
|
2509
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
|
|
2510
|
+
/* @__PURE__ */ jsxs(
|
|
2511
|
+
"button",
|
|
2512
|
+
{
|
|
2513
|
+
onClick: () => setView("members"),
|
|
2514
|
+
className: "flex items-center gap-3 w-full px-3 py-2.5 rounded-sm text-sm text-theme-secondary hover:bg-theme-subtle hover:text-theme transition-colors",
|
|
2515
|
+
children: [
|
|
2516
|
+
/* @__PURE__ */ jsx(OrgTeam, { className: "w-4 h-4 text-theme-muted" }),
|
|
2517
|
+
/* @__PURE__ */ jsx("span", { className: "flex-1 text-left", children: "Membros" }),
|
|
2518
|
+
/* @__PURE__ */ jsx(Badge, { variant: "default", className: "text-[10px] px-1.5 py-0 h-4 border-theme-subtle-10 text-theme-muted", children: members.length })
|
|
2519
|
+
]
|
|
2520
|
+
}
|
|
2521
|
+
),
|
|
2522
|
+
permissions.canEditRoomSettings && room.type !== "DIRECT" && /* @__PURE__ */ jsxs(
|
|
2523
|
+
"button",
|
|
2524
|
+
{
|
|
2525
|
+
onClick: () => setView("edit"),
|
|
2526
|
+
className: "flex items-center gap-3 w-full px-3 py-2.5 rounded-sm text-sm text-theme-secondary hover:bg-theme-subtle hover:text-theme transition-colors",
|
|
2527
|
+
children: [
|
|
2528
|
+
/* @__PURE__ */ jsx(OrgEdit, { className: "w-4 h-4 text-theme-muted" }),
|
|
2529
|
+
/* @__PURE__ */ jsx("span", { className: "flex-1 text-left", children: "Editar canal" })
|
|
2530
|
+
]
|
|
2531
|
+
}
|
|
2532
|
+
),
|
|
2533
|
+
/* @__PURE__ */ jsx(Separator, { className: "bg-theme-subtle my-2" }),
|
|
2534
|
+
/* @__PURE__ */ jsxs(
|
|
2535
|
+
"button",
|
|
2536
|
+
{
|
|
2537
|
+
onClick: () => onLeaveRoom(room.id),
|
|
2538
|
+
className: "flex items-center gap-3 w-full px-3 py-2.5 rounded-sm text-sm text-theme-muted hover:bg-rose-500/10 hover:text-rose-400 transition-colors",
|
|
2539
|
+
children: [
|
|
2540
|
+
/* @__PURE__ */ jsx(OrgDoor, { className: "w-4 h-4" }),
|
|
2541
|
+
/* @__PURE__ */ jsx("span", { className: "flex-1 text-left", children: "Sair do canal" })
|
|
2542
|
+
]
|
|
2543
|
+
}
|
|
2544
|
+
),
|
|
2545
|
+
permissions.canArchiveRoom && /* @__PURE__ */ jsx(Fragment, { children: !confirmArchive ? /* @__PURE__ */ jsxs(
|
|
2546
|
+
"button",
|
|
2547
|
+
{
|
|
2548
|
+
onClick: () => setConfirmArchive(true),
|
|
2549
|
+
className: "flex items-center gap-3 w-full px-3 py-2.5 rounded-sm text-sm text-theme-muted hover:bg-rose-500/10 hover:text-rose-400 transition-colors",
|
|
2550
|
+
children: [
|
|
2551
|
+
/* @__PURE__ */ jsx(OrgTrash, { className: "w-4 h-4" }),
|
|
2552
|
+
/* @__PURE__ */ jsx("span", { className: "flex-1 text-left", children: "Arquivar canal" })
|
|
2553
|
+
]
|
|
2554
|
+
}
|
|
2555
|
+
) : /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 px-3 py-2 rounded-sm bg-rose-500/10 border border-rose-500/20 animate-in fade-in-0 duration-[400ms]", children: [
|
|
2556
|
+
/* @__PURE__ */ jsx("p", { className: "flex-1 text-xs text-rose-300", children: "Tem certeza?" }),
|
|
2557
|
+
/* @__PURE__ */ jsx(
|
|
2558
|
+
Button,
|
|
2559
|
+
{
|
|
2560
|
+
variant: "ghost",
|
|
2561
|
+
size: "sm",
|
|
2562
|
+
onClick: () => setConfirmArchive(false),
|
|
2563
|
+
className: "h-6 px-2 text-[10px] text-theme-muted",
|
|
2564
|
+
children: "N\xE3o"
|
|
2565
|
+
}
|
|
2566
|
+
),
|
|
2567
|
+
/* @__PURE__ */ jsx(
|
|
2568
|
+
Button,
|
|
2569
|
+
{
|
|
2570
|
+
variant: "destructive",
|
|
2571
|
+
size: "sm",
|
|
2572
|
+
onClick: () => onArchiveRoom(room.id),
|
|
2573
|
+
className: "h-6 px-2 text-[10px]",
|
|
2574
|
+
children: "Sim, arquivar"
|
|
2575
|
+
}
|
|
2576
|
+
)
|
|
2577
|
+
] }) })
|
|
2578
|
+
] })
|
|
2579
|
+
] })
|
|
2580
|
+
] });
|
|
2581
|
+
const renderMembers = () => /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2582
|
+
renderHeader("Membros", true),
|
|
2583
|
+
/* @__PURE__ */ jsx(ScrollArea, { className: "flex-1", children: /* @__PURE__ */ jsx("div", { className: "p-3 space-y-0.5", children: loading ? Array.from({ length: 4 }).map((_, i) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 p-2", children: [
|
|
2584
|
+
/* @__PURE__ */ jsx(Skeleton, { variant: "circular", className: "h-8 w-8" }),
|
|
2585
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-1 space-y-1", children: [
|
|
2586
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-3 w-24" }),
|
|
2587
|
+
/* @__PURE__ */ jsx(Skeleton, { className: "h-2 w-16" })
|
|
2588
|
+
] })
|
|
2589
|
+
] }, i)) : members.length === 0 ? /* @__PURE__ */ jsx("p", { className: "text-xs text-theme-muted text-center py-8", children: "Sem membros" }) : members.map((member) => /* @__PURE__ */ jsxs(
|
|
2590
|
+
"div",
|
|
2591
|
+
{
|
|
2592
|
+
className: "flex items-center gap-3 p-2 rounded-sm hover:bg-theme-subtle group transition-colors",
|
|
2593
|
+
children: [
|
|
2594
|
+
/* @__PURE__ */ jsx(Avatar, { shape: "circle", size: "sm", className: "h-8 w-8", children: /* @__PURE__ */ jsx(AvatarFallback, { className: "bg-primary/20 text-primary-light text-xs", children: getInitials4(member.displayName || member.userId.substring(0, 8)) }) }),
|
|
2595
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
|
|
2596
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
2597
|
+
/* @__PURE__ */ jsx("span", { className: "text-[13px] font-medium text-theme truncate", children: member.displayName || member.userId.substring(0, 8) }),
|
|
2598
|
+
member.userId === currentUserId && /* @__PURE__ */ jsx("span", { className: "text-[10px] text-theme-muted", children: "(voc\xEA)" })
|
|
2599
|
+
] }),
|
|
2600
|
+
/* @__PURE__ */ jsx(
|
|
2601
|
+
Badge,
|
|
2602
|
+
{
|
|
2603
|
+
variant: "default",
|
|
2604
|
+
className: cn(
|
|
2605
|
+
"text-[9px] px-1 py-0 h-3.5 border mt-0.5",
|
|
2606
|
+
roleBadgeColors[member.role]
|
|
2607
|
+
),
|
|
2608
|
+
children: roleLabels[member.role]
|
|
2609
|
+
}
|
|
2610
|
+
)
|
|
2611
|
+
] }),
|
|
2612
|
+
permissions.canManageMembers && member.userId !== currentUserId && member.role !== "OWNER" && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1 opacity-0 group-hover:opacity-100 transition-opacity", children: [
|
|
2613
|
+
/* @__PURE__ */ jsxs(
|
|
2614
|
+
Select,
|
|
2615
|
+
{
|
|
2616
|
+
value: member.role,
|
|
2617
|
+
onValueChange: (val) => onUpdateMemberRole(room.id, member.userId, val),
|
|
2618
|
+
children: [
|
|
2619
|
+
/* @__PURE__ */ jsx(SelectTrigger, { className: "h-6 w-20 text-[10px] bg-theme-subtle border-theme-subtle-10", children: /* @__PURE__ */ jsx(SelectValue, {}) }),
|
|
2620
|
+
/* @__PURE__ */ jsxs(SelectContent, { children: [
|
|
2621
|
+
/* @__PURE__ */ jsx(SelectItem, { value: "ADMIN", children: "Admin" }),
|
|
2622
|
+
/* @__PURE__ */ jsx(SelectItem, { value: "MEMBER", children: "Membro" })
|
|
2623
|
+
] })
|
|
2624
|
+
]
|
|
2625
|
+
}
|
|
2626
|
+
),
|
|
2627
|
+
/* @__PURE__ */ jsx(
|
|
2628
|
+
Button,
|
|
2629
|
+
{
|
|
2630
|
+
variant: "ghost",
|
|
2631
|
+
size: "sm",
|
|
2632
|
+
onClick: () => onRemoveMember(room.id, member.userId),
|
|
2633
|
+
className: "h-6 w-6 p-0 text-theme-muted hover:text-rose-400 hover:bg-rose-500/10",
|
|
2634
|
+
children: /* @__PURE__ */ jsx(OrgClose, { className: "w-3 h-3" })
|
|
2635
|
+
}
|
|
2636
|
+
)
|
|
2637
|
+
] })
|
|
2638
|
+
]
|
|
2639
|
+
},
|
|
2640
|
+
member.id
|
|
2641
|
+
)) }) })
|
|
2642
|
+
] });
|
|
2643
|
+
const renderEdit = () => /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
2644
|
+
renderHeader("Editar canal", true),
|
|
2645
|
+
/* @__PURE__ */ jsxs("div", { className: "p-4 space-y-4", children: [
|
|
2646
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
2647
|
+
/* @__PURE__ */ jsx(Label, { className: "text-theme-secondary text-xs", children: "Nome" }),
|
|
2648
|
+
/* @__PURE__ */ jsx(
|
|
2649
|
+
Input,
|
|
2650
|
+
{
|
|
2651
|
+
variant: "flat",
|
|
2652
|
+
value: editName,
|
|
2653
|
+
onChange: (e) => setEditName(e.target.value),
|
|
2654
|
+
className: "h-9 text-sm bg-theme-subtle border-theme-subtle"
|
|
2655
|
+
}
|
|
2656
|
+
)
|
|
2657
|
+
] }),
|
|
2658
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
2659
|
+
/* @__PURE__ */ jsx(Label, { className: "text-theme-secondary text-xs", children: "Descri\xE7\xE3o" }),
|
|
2660
|
+
/* @__PURE__ */ jsx(
|
|
2661
|
+
Textarea,
|
|
2662
|
+
{
|
|
2663
|
+
value: editDesc,
|
|
2664
|
+
onChange: (e) => setEditDesc(e.target.value),
|
|
2665
|
+
className: "min-h-[60px] text-sm bg-theme-subtle border-theme-subtle text-theme",
|
|
2666
|
+
placeholder: "Descri\xE7\xE3o do canal..."
|
|
2667
|
+
}
|
|
2668
|
+
)
|
|
2669
|
+
] }),
|
|
2670
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
|
|
2671
|
+
/* @__PURE__ */ jsx(Label, { className: "text-theme-secondary text-xs", children: "Visibilidade" }),
|
|
2672
|
+
/* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
|
|
2673
|
+
/* @__PURE__ */ jsxs(
|
|
2674
|
+
"button",
|
|
2675
|
+
{
|
|
2676
|
+
type: "button",
|
|
2677
|
+
onClick: () => setEditVisibility("WORKSPACE"),
|
|
2678
|
+
className: cn(
|
|
2679
|
+
"flex-1 flex items-center gap-2 px-3 py-2 rounded-sm text-xs transition-all duration-[400ms] border",
|
|
2680
|
+
editVisibility === "WORKSPACE" ? "bg-primary/15 border-primary/30 text-primary-light" : "bg-theme-subtle border-theme-subtle text-theme-muted hover:bg-theme-subtle"
|
|
2681
|
+
),
|
|
2682
|
+
children: [
|
|
2683
|
+
/* @__PURE__ */ jsx(OrgGlobe, { className: "w-3.5 h-3.5" }),
|
|
2684
|
+
"P\xFAblico"
|
|
2685
|
+
]
|
|
2686
|
+
}
|
|
2687
|
+
),
|
|
2688
|
+
/* @__PURE__ */ jsxs(
|
|
2689
|
+
"button",
|
|
2690
|
+
{
|
|
2691
|
+
type: "button",
|
|
2692
|
+
onClick: () => setEditVisibility("PRIVATE"),
|
|
2693
|
+
className: cn(
|
|
2694
|
+
"flex-1 flex items-center gap-2 px-3 py-2 rounded-sm text-xs transition-all duration-[400ms] border",
|
|
2695
|
+
editVisibility === "PRIVATE" ? "bg-primary/15 border-primary/30 text-primary-light" : "bg-theme-subtle border-theme-subtle text-theme-muted hover:bg-theme-subtle"
|
|
2696
|
+
),
|
|
2697
|
+
children: [
|
|
2698
|
+
/* @__PURE__ */ jsx(OrgLock, { className: "w-3.5 h-3.5" }),
|
|
2699
|
+
"Privado"
|
|
2700
|
+
]
|
|
2701
|
+
}
|
|
2702
|
+
)
|
|
2703
|
+
] })
|
|
2704
|
+
] }),
|
|
2705
|
+
/* @__PURE__ */ jsxs("div", { className: "flex justify-end gap-2 pt-2", children: [
|
|
2706
|
+
/* @__PURE__ */ jsx(
|
|
2707
|
+
Button,
|
|
2708
|
+
{
|
|
2709
|
+
variant: "ghost",
|
|
2710
|
+
size: "sm",
|
|
2711
|
+
onClick: () => setView("info"),
|
|
2712
|
+
className: "text-xs text-theme-muted",
|
|
2713
|
+
children: "Cancelar"
|
|
2714
|
+
}
|
|
2715
|
+
),
|
|
2716
|
+
/* @__PURE__ */ jsx(
|
|
2717
|
+
Button,
|
|
2718
|
+
{
|
|
2719
|
+
variant: "default",
|
|
2720
|
+
size: "sm",
|
|
2721
|
+
onClick: handleSave,
|
|
2722
|
+
disabled: !editName.trim() || saving,
|
|
2723
|
+
className: "text-xs",
|
|
2724
|
+
children: saving ? "Salvando..." : "Salvar"
|
|
2725
|
+
}
|
|
2726
|
+
)
|
|
2727
|
+
] })
|
|
2728
|
+
] })
|
|
2729
|
+
] });
|
|
2730
|
+
return /* @__PURE__ */ jsxs(
|
|
2731
|
+
"div",
|
|
2732
|
+
{
|
|
2733
|
+
className: cn(
|
|
2734
|
+
"flex flex-col h-full border-l border-theme-subtle bg-theme-surface w-[280px] shrink-0",
|
|
2735
|
+
"animate-in slide-in-from-right-5 duration-[400ms]",
|
|
2736
|
+
className
|
|
2737
|
+
),
|
|
2738
|
+
children: [
|
|
2739
|
+
view === "info" && renderInfo(),
|
|
2740
|
+
view === "members" && renderMembers(),
|
|
2741
|
+
view === "edit" && renderEdit()
|
|
2742
|
+
]
|
|
2743
|
+
}
|
|
2744
|
+
);
|
|
2745
|
+
}
|
|
2746
|
+
|
|
2747
|
+
// src/components/chat/types.ts
|
|
2748
|
+
function getRoomPermissions(role, isWorkspaceAdmin) {
|
|
2749
|
+
const isOwnerOrAdmin = role === "OWNER" || role === "ADMIN" || isWorkspaceAdmin;
|
|
2750
|
+
return {
|
|
2751
|
+
canCreateChannel: true,
|
|
2752
|
+
canCreateDM: true,
|
|
2753
|
+
canDeleteRoom: role === "OWNER" || !!isWorkspaceAdmin,
|
|
2754
|
+
canArchiveRoom: isOwnerOrAdmin ?? false,
|
|
2755
|
+
canManageMembers: isOwnerOrAdmin ?? false,
|
|
2756
|
+
canEditRoomSettings: isOwnerOrAdmin ?? false
|
|
2757
|
+
};
|
|
2758
|
+
}
|
|
2759
|
+
|
|
2760
|
+
// src/components/chat/use-chat.ts
|
|
2761
|
+
function useChat(options = {}) {
|
|
2762
|
+
const {
|
|
2763
|
+
workspaceRole,
|
|
2764
|
+
initialRooms,
|
|
2765
|
+
initialMessages
|
|
2766
|
+
} = options;
|
|
2767
|
+
const api = useOrganifyApi();
|
|
2768
|
+
const { gatewayUrl, authToken } = api;
|
|
2769
|
+
const user = useOrganifyUser();
|
|
2770
|
+
const { workspace } = useOrganifyWorkspace();
|
|
2771
|
+
const userId = user?.id ?? "";
|
|
2772
|
+
const workspaceId = workspace?.id ?? "";
|
|
2773
|
+
const [rooms, setRooms] = useState(initialRooms ?? []);
|
|
2774
|
+
const [messages, setMessages] = useState(initialMessages ?? []);
|
|
2775
|
+
const [activeRoomId, setActiveRoomId] = useState(null);
|
|
2776
|
+
const [loadingRooms, setLoadingRooms] = useState(false);
|
|
2777
|
+
const [loadingMessages, setLoadingMessages] = useState(false);
|
|
2778
|
+
const [hasMoreMessages, setHasMoreMessages] = useState(false);
|
|
2779
|
+
const [messageCursor, setMessageCursor] = useState(null);
|
|
2780
|
+
const [loadingMoreMessages, setLoadingMoreMessages] = useState(false);
|
|
2781
|
+
const [roomMembers, setRoomMembers] = useState([]);
|
|
2782
|
+
const [myRoomRole, setMyRoomRole] = useState(null);
|
|
2783
|
+
const [typingUsers, setTypingUsers] = useState([]);
|
|
2784
|
+
const [error, setError] = useState(null);
|
|
2785
|
+
const isWorkspaceAdmin = workspaceRole === "OWNER" || workspaceRole === "ADMIN";
|
|
2786
|
+
const isDemoMode = !!(initialRooms && initialRooms.length > 0);
|
|
2787
|
+
const permissions = getRoomPermissions(myRoomRole, isWorkspaceAdmin);
|
|
2788
|
+
const centralGql = useOrganifyGql();
|
|
2789
|
+
const gql = useCallback(
|
|
2790
|
+
async (query, variables) => {
|
|
2791
|
+
return centralGql("chat", query, variables);
|
|
2792
|
+
},
|
|
2793
|
+
[centralGql]
|
|
2794
|
+
);
|
|
2795
|
+
const [userCache, setUserCache] = useState(/* @__PURE__ */ new Map());
|
|
2796
|
+
const enrichedMessages = React7__default.useMemo(() => {
|
|
2797
|
+
return messages.map((msg) => {
|
|
2798
|
+
const cached = userCache.get(msg.authorId);
|
|
2799
|
+
if (cached && !msg.authorName) {
|
|
2800
|
+
return { ...msg, authorName: cached.displayName, authorAvatar: cached.avatarUrl };
|
|
2801
|
+
}
|
|
2802
|
+
return msg;
|
|
2803
|
+
});
|
|
2804
|
+
}, [messages, userCache]);
|
|
2805
|
+
useEffect(() => {
|
|
2806
|
+
setUserCache((prev) => {
|
|
2807
|
+
const next = new Map(prev);
|
|
2808
|
+
if (userId && user?.name) {
|
|
2809
|
+
next.set(userId, { displayName: user.name, avatarUrl: user.avatarUrl ?? void 0 });
|
|
2810
|
+
}
|
|
2811
|
+
if (options.workspaceMembers) {
|
|
2812
|
+
for (const member of options.workspaceMembers) {
|
|
2813
|
+
if (member.id && !next.has(member.id)) {
|
|
2814
|
+
next.set(member.id, {
|
|
2815
|
+
displayName: member.displayName || member.email || `User`,
|
|
2816
|
+
avatarUrl: member.avatarUrl ?? void 0
|
|
2817
|
+
});
|
|
2818
|
+
}
|
|
2819
|
+
}
|
|
2820
|
+
}
|
|
2821
|
+
return next;
|
|
2822
|
+
});
|
|
2823
|
+
}, [userId, user, options.workspaceMembers]);
|
|
2824
|
+
const fetchRooms = useCallback(async () => {
|
|
2825
|
+
if (initialRooms && initialRooms.length > 0) return;
|
|
2826
|
+
if (!workspaceId || workspaceId.startsWith("temp-")) return;
|
|
2827
|
+
if (!userId) return;
|
|
2828
|
+
setLoadingRooms(true);
|
|
2829
|
+
setError(null);
|
|
2830
|
+
try {
|
|
2831
|
+
const data = await gql(
|
|
2832
|
+
`query($workspaceId: String!) {
|
|
2833
|
+
rooms(workspaceId: $workspaceId) {
|
|
2834
|
+
items {
|
|
2835
|
+
id name slug type visibility description
|
|
2836
|
+
memberCount unreadCount avatarUrl createdBy
|
|
2837
|
+
archived lastMessageAt
|
|
2838
|
+
}
|
|
2839
|
+
total hasMore
|
|
2840
|
+
}
|
|
2841
|
+
}`,
|
|
2842
|
+
{ workspaceId }
|
|
2843
|
+
);
|
|
2844
|
+
setRooms(data.rooms?.items ?? []);
|
|
2845
|
+
} catch (err) {
|
|
2846
|
+
console.error("[organify-chat] fetchRooms:", err);
|
|
2847
|
+
setError("N\xE3o foi poss\xEDvel carregar as conversas. Tente novamente.");
|
|
2848
|
+
} finally {
|
|
2849
|
+
setLoadingRooms(false);
|
|
2850
|
+
}
|
|
2851
|
+
}, [workspaceId, userId, gql, initialRooms]);
|
|
2852
|
+
const MESSAGE_FIELDS = `
|
|
2853
|
+
id roomId authorId parentId
|
|
2854
|
+
content edited editedAt createdAt
|
|
2855
|
+
mentions { type targetId display }
|
|
2856
|
+
reactions { emoji userId }
|
|
2857
|
+
`;
|
|
2858
|
+
const fetchMessages = useCallback(
|
|
2859
|
+
async (roomId) => {
|
|
2860
|
+
if (initialMessages && initialMessages.length > 0) {
|
|
2861
|
+
const roomMessages = initialMessages.filter((m) => m.roomId === roomId);
|
|
2862
|
+
setMessages(roomMessages);
|
|
2863
|
+
setHasMoreMessages(false);
|
|
2864
|
+
setMessageCursor(null);
|
|
2865
|
+
return;
|
|
2866
|
+
}
|
|
2867
|
+
if (!userId) return;
|
|
2868
|
+
setLoadingMessages(true);
|
|
2869
|
+
setError(null);
|
|
2870
|
+
try {
|
|
2871
|
+
const data = await gql(
|
|
2872
|
+
`query($roomId: ID!, $filter: MessageFilterInput) {
|
|
2873
|
+
messages(roomId: $roomId, filter: $filter) {
|
|
2874
|
+
items { ${MESSAGE_FIELDS} }
|
|
2875
|
+
total hasMore nextCursor
|
|
2876
|
+
}
|
|
2877
|
+
}`,
|
|
2878
|
+
{ roomId, filter: { limit: 50 } }
|
|
2879
|
+
);
|
|
2880
|
+
setMessages(data.messages?.items ?? []);
|
|
2881
|
+
setHasMoreMessages(data.messages?.hasMore ?? false);
|
|
2882
|
+
setMessageCursor(data.messages?.nextCursor ?? null);
|
|
2883
|
+
} catch (err) {
|
|
2884
|
+
console.error("[organify-chat] fetchMessages:", err);
|
|
2885
|
+
setError("Erro ao carregar mensagens.");
|
|
2886
|
+
} finally {
|
|
2887
|
+
setLoadingMessages(false);
|
|
2888
|
+
}
|
|
2889
|
+
},
|
|
2890
|
+
[gql, initialMessages, userId]
|
|
2891
|
+
);
|
|
2892
|
+
const loadMoreMessages = useCallback(
|
|
2893
|
+
async () => {
|
|
2894
|
+
if (!activeRoomId || !hasMoreMessages || !messageCursor || loadingMoreMessages || !userId) return;
|
|
2895
|
+
setLoadingMoreMessages(true);
|
|
2896
|
+
try {
|
|
2897
|
+
const data = await gql(
|
|
2898
|
+
`query($roomId: ID!, $filter: MessageFilterInput) {
|
|
2899
|
+
messages(roomId: $roomId, filter: $filter) {
|
|
2900
|
+
items { ${MESSAGE_FIELDS} }
|
|
2901
|
+
total hasMore nextCursor
|
|
2902
|
+
}
|
|
2903
|
+
}`,
|
|
2904
|
+
{ roomId: activeRoomId, filter: { limit: 50, cursor: messageCursor } }
|
|
2905
|
+
);
|
|
2906
|
+
const olderMessages = data.messages?.items ?? [];
|
|
2907
|
+
setMessages((prev) => [...olderMessages, ...prev]);
|
|
2908
|
+
setHasMoreMessages(data.messages?.hasMore ?? false);
|
|
2909
|
+
setMessageCursor(data.messages?.nextCursor ?? null);
|
|
2910
|
+
} catch (err) {
|
|
2911
|
+
console.error("[organify-chat] loadMore:", err);
|
|
2912
|
+
} finally {
|
|
2913
|
+
setLoadingMoreMessages(false);
|
|
2914
|
+
}
|
|
2915
|
+
},
|
|
2916
|
+
[gql, activeRoomId, hasMoreMessages, messageCursor, loadingMoreMessages, userId]
|
|
2917
|
+
);
|
|
2918
|
+
const fetchRoomMembers = useCallback(
|
|
2919
|
+
async (roomId) => {
|
|
2920
|
+
if (isDemoMode) return;
|
|
2921
|
+
try {
|
|
2922
|
+
const data = await gql(
|
|
2923
|
+
`query($roomId: ID!) {
|
|
2924
|
+
room(id: $roomId) {
|
|
2925
|
+
members {
|
|
2926
|
+
id userId role muted pinned joinedAt
|
|
2927
|
+
}
|
|
2928
|
+
}
|
|
2929
|
+
}`,
|
|
2930
|
+
{ roomId }
|
|
2931
|
+
);
|
|
2932
|
+
const members = data?.room?.members ?? [];
|
|
2933
|
+
setRoomMembers(members);
|
|
2934
|
+
const myMembership = members.find((m) => m.userId === userId);
|
|
2935
|
+
setMyRoomRole(myMembership?.role ?? null);
|
|
2936
|
+
} catch (err) {
|
|
2937
|
+
console.error("[organify-chat] fetchRoomMembers:", err);
|
|
2938
|
+
}
|
|
2939
|
+
},
|
|
2940
|
+
[gql, userId, isDemoMode]
|
|
2941
|
+
);
|
|
2942
|
+
const selectRoom = useCallback(
|
|
2943
|
+
(roomId) => {
|
|
2944
|
+
if (activeRoomId === roomId) return;
|
|
2945
|
+
if (!workspaceId || workspaceId.startsWith("temp-")) {
|
|
2946
|
+
setActiveRoomId(roomId);
|
|
2947
|
+
setMessages([]);
|
|
2948
|
+
setRoomMembers([]);
|
|
2949
|
+
setMyRoomRole(null);
|
|
2950
|
+
setTypingUsers([]);
|
|
2951
|
+
setHasMoreMessages(false);
|
|
2952
|
+
setMessageCursor(null);
|
|
2953
|
+
return;
|
|
2954
|
+
}
|
|
2955
|
+
setActiveRoomId(roomId);
|
|
2956
|
+
setMessages([]);
|
|
2957
|
+
setRoomMembers([]);
|
|
2958
|
+
setMyRoomRole(null);
|
|
2959
|
+
setTypingUsers([]);
|
|
2960
|
+
setHasMoreMessages(false);
|
|
2961
|
+
setMessageCursor(null);
|
|
2962
|
+
fetchMessages(roomId);
|
|
2963
|
+
fetchRoomMembers(roomId);
|
|
2964
|
+
},
|
|
2965
|
+
[activeRoomId, workspaceId, fetchMessages, fetchRoomMembers]
|
|
2966
|
+
);
|
|
2967
|
+
const sendMessage = useCallback(
|
|
2968
|
+
async (content, replyToId) => {
|
|
2969
|
+
if (!activeRoomId || !content.trim()) return;
|
|
2970
|
+
const tempId = `temp_${Date.now()}_${Math.random().toString(36).slice(2)}`;
|
|
2971
|
+
const optimisticMsg = {
|
|
2972
|
+
id: tempId,
|
|
2973
|
+
roomId: activeRoomId,
|
|
2974
|
+
authorId: userId,
|
|
2975
|
+
content: content.trim(),
|
|
2976
|
+
edited: false,
|
|
2977
|
+
editedAt: null,
|
|
2978
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
2979
|
+
mentions: [],
|
|
2980
|
+
reactions: [],
|
|
2981
|
+
replyToId: replyToId ?? null,
|
|
2982
|
+
_status: "pending"
|
|
2983
|
+
};
|
|
2984
|
+
setMessages((prev) => [...prev, optimisticMsg]);
|
|
2985
|
+
if (isDemoMode) {
|
|
2986
|
+
setMessages(
|
|
2987
|
+
(prev) => prev.map((m) => m.id === tempId ? { ...m, id: `demo_${Date.now()}`, _status: "sent" } : m)
|
|
2988
|
+
);
|
|
2989
|
+
return;
|
|
2990
|
+
}
|
|
2991
|
+
try {
|
|
2992
|
+
const input = { roomId: activeRoomId, content: content.trim() };
|
|
2993
|
+
if (replyToId) input.parentId = replyToId;
|
|
2994
|
+
const data = await gql(
|
|
2995
|
+
`mutation($input: SendMessageInput!) {
|
|
2996
|
+
sendMessage(input: $input) {
|
|
2997
|
+
id roomId authorId parentId
|
|
2998
|
+
content edited editedAt createdAt
|
|
2999
|
+
mentions { type targetId display }
|
|
3000
|
+
reactions { emoji userId }
|
|
3001
|
+
}
|
|
3002
|
+
}`,
|
|
3003
|
+
{ input }
|
|
3004
|
+
);
|
|
3005
|
+
if (data?.sendMessage) {
|
|
3006
|
+
setMessages(
|
|
3007
|
+
(prev) => prev.map((m) => m.id === tempId ? { ...data.sendMessage, _status: "sent" } : m)
|
|
3008
|
+
);
|
|
3009
|
+
} else {
|
|
3010
|
+
setMessages(
|
|
3011
|
+
(prev) => prev.map((m) => m.id === tempId ? { ...m, _status: "error" } : m)
|
|
3012
|
+
);
|
|
3013
|
+
}
|
|
3014
|
+
} catch (err) {
|
|
3015
|
+
console.error("[organify-chat] sendMessage:", err);
|
|
3016
|
+
setMessages(
|
|
3017
|
+
(prev) => prev.map((m) => m.id === tempId ? { ...m, _status: "error" } : m)
|
|
3018
|
+
);
|
|
3019
|
+
}
|
|
3020
|
+
},
|
|
3021
|
+
[activeRoomId, gql, userId, isDemoMode]
|
|
3022
|
+
);
|
|
3023
|
+
const createRoom = useCallback(
|
|
3024
|
+
async (input) => {
|
|
3025
|
+
setError(null);
|
|
3026
|
+
if (isDemoMode) {
|
|
3027
|
+
const newRoom = {
|
|
3028
|
+
id: `demo_room_${Date.now()}`,
|
|
3029
|
+
name: input.name,
|
|
3030
|
+
slug: input.name.toLowerCase().replace(/\s+/g, "-"),
|
|
3031
|
+
type: input.type,
|
|
3032
|
+
visibility: input.visibility,
|
|
3033
|
+
description: input.description ?? null,
|
|
3034
|
+
memberCount: 1,
|
|
3035
|
+
unreadCount: 0,
|
|
3036
|
+
avatarUrl: null,
|
|
3037
|
+
createdBy: userId,
|
|
3038
|
+
archived: false,
|
|
3039
|
+
lastMessageAt: null
|
|
3040
|
+
};
|
|
3041
|
+
setRooms((prev) => [newRoom, ...prev]);
|
|
3042
|
+
return newRoom;
|
|
3043
|
+
}
|
|
3044
|
+
try {
|
|
3045
|
+
const data = await gql(
|
|
3046
|
+
`mutation($input: CreateRoomInput!) {
|
|
3047
|
+
createRoom(input: $input) {
|
|
3048
|
+
id name slug type visibility description
|
|
3049
|
+
memberCount unreadCount avatarUrl createdBy
|
|
3050
|
+
archived lastMessageAt
|
|
3051
|
+
}
|
|
3052
|
+
}`,
|
|
3053
|
+
{
|
|
3054
|
+
input: {
|
|
3055
|
+
...input,
|
|
3056
|
+
workspaceId
|
|
3057
|
+
}
|
|
3058
|
+
}
|
|
3059
|
+
);
|
|
3060
|
+
const newRoom = data?.createRoom;
|
|
3061
|
+
if (!newRoom) throw new Error("Failed to create room");
|
|
3062
|
+
setRooms((prev) => [newRoom, ...prev]);
|
|
3063
|
+
return newRoom;
|
|
3064
|
+
} catch (err) {
|
|
3065
|
+
console.error("[organify-chat] createRoom:", err);
|
|
3066
|
+
setError(err.message || "N\xE3o foi poss\xEDvel criar a sala.");
|
|
3067
|
+
return null;
|
|
3068
|
+
}
|
|
3069
|
+
},
|
|
3070
|
+
[gql, workspaceId, isDemoMode, userId]
|
|
3071
|
+
);
|
|
3072
|
+
const createDirectMessage = useCallback(
|
|
3073
|
+
async (targetUserId) => {
|
|
3074
|
+
setError(null);
|
|
3075
|
+
if (isDemoMode) {
|
|
3076
|
+
const dm = {
|
|
3077
|
+
id: `demo_dm_${Date.now()}`,
|
|
3078
|
+
name: targetUserId,
|
|
3079
|
+
slug: null,
|
|
3080
|
+
type: "DIRECT",
|
|
3081
|
+
visibility: "PRIVATE",
|
|
3082
|
+
description: null,
|
|
3083
|
+
memberCount: 2,
|
|
3084
|
+
unreadCount: 0,
|
|
3085
|
+
avatarUrl: null,
|
|
3086
|
+
createdBy: userId,
|
|
3087
|
+
archived: false,
|
|
3088
|
+
lastMessageAt: null
|
|
3089
|
+
};
|
|
3090
|
+
setRooms((prev) => [dm, ...prev]);
|
|
3091
|
+
return dm;
|
|
3092
|
+
}
|
|
3093
|
+
try {
|
|
3094
|
+
const data = await gql(
|
|
3095
|
+
`mutation($input: CreateRoomInput!) {
|
|
3096
|
+
createRoom(input: $input) {
|
|
3097
|
+
id name slug type visibility description
|
|
3098
|
+
memberCount unreadCount avatarUrl createdBy
|
|
3099
|
+
archived lastMessageAt
|
|
3100
|
+
members { userId role }
|
|
3101
|
+
}
|
|
3102
|
+
}`,
|
|
3103
|
+
{
|
|
3104
|
+
input: {
|
|
3105
|
+
workspaceId,
|
|
3106
|
+
name: "DM",
|
|
3107
|
+
type: "DIRECT",
|
|
3108
|
+
visibility: "PRIVATE",
|
|
3109
|
+
memberIds: [targetUserId]
|
|
3110
|
+
}
|
|
3111
|
+
}
|
|
3112
|
+
);
|
|
3113
|
+
const dm = data?.createRoom;
|
|
3114
|
+
if (!dm) throw new Error("Failed to create DM");
|
|
3115
|
+
setRooms((prev) => {
|
|
3116
|
+
if (prev.find((r) => r.id === dm.id)) return prev;
|
|
3117
|
+
return [dm, ...prev];
|
|
3118
|
+
});
|
|
3119
|
+
return dm;
|
|
3120
|
+
} catch (err) {
|
|
3121
|
+
console.error("[organify-chat] createDM:", err);
|
|
3122
|
+
setError(err.message || "N\xE3o foi poss\xEDvel iniciar a conversa.");
|
|
3123
|
+
return null;
|
|
3124
|
+
}
|
|
3125
|
+
},
|
|
3126
|
+
[gql, workspaceId, isDemoMode, userId]
|
|
3127
|
+
);
|
|
3128
|
+
const updateRoom = useCallback(
|
|
3129
|
+
async (roomId, updates) => {
|
|
3130
|
+
if (!permissions.canEditRoomSettings) return;
|
|
3131
|
+
if (isDemoMode) {
|
|
3132
|
+
setRooms((prev) => prev.map((r) => r.id === roomId ? { ...r, ...updates } : r));
|
|
3133
|
+
return;
|
|
3134
|
+
}
|
|
3135
|
+
try {
|
|
3136
|
+
const data = await gql(
|
|
3137
|
+
`mutation($id: ID!, $input: UpdateRoomInput!) {
|
|
3138
|
+
updateRoom(id: $id, input: $input) {
|
|
3139
|
+
id name slug type visibility description
|
|
3140
|
+
memberCount unreadCount avatarUrl createdBy
|
|
3141
|
+
archived lastMessageAt
|
|
3142
|
+
}
|
|
3143
|
+
}`,
|
|
3144
|
+
{ id: roomId, input: updates }
|
|
3145
|
+
);
|
|
3146
|
+
if (data?.updateRoom) {
|
|
3147
|
+
setRooms((prev) => prev.map((r) => r.id === roomId ? data.updateRoom : r));
|
|
3148
|
+
}
|
|
3149
|
+
} catch (err) {
|
|
3150
|
+
setError(err.message || "Erro ao atualizar sala.");
|
|
3151
|
+
}
|
|
3152
|
+
},
|
|
3153
|
+
[gql, permissions.canEditRoomSettings, isDemoMode]
|
|
3154
|
+
);
|
|
3155
|
+
const archiveRoom = useCallback(
|
|
3156
|
+
async (roomId) => {
|
|
3157
|
+
if (!permissions.canArchiveRoom) return;
|
|
3158
|
+
if (isDemoMode) {
|
|
3159
|
+
setRooms((prev) => prev.filter((r) => r.id !== roomId));
|
|
3160
|
+
if (activeRoomId === roomId) {
|
|
3161
|
+
setActiveRoomId(null);
|
|
3162
|
+
setMessages([]);
|
|
3163
|
+
}
|
|
3164
|
+
return;
|
|
3165
|
+
}
|
|
3166
|
+
try {
|
|
3167
|
+
await gql(
|
|
3168
|
+
`mutation($id: ID!, $input: UpdateRoomInput!) {
|
|
3169
|
+
updateRoom(id: $id, input: $input) { id archived }
|
|
3170
|
+
}`,
|
|
3171
|
+
{ id: roomId, input: { archived: true } }
|
|
3172
|
+
);
|
|
3173
|
+
setRooms((prev) => prev.filter((r) => r.id !== roomId));
|
|
3174
|
+
if (activeRoomId === roomId) {
|
|
3175
|
+
setActiveRoomId(null);
|
|
3176
|
+
setMessages([]);
|
|
3177
|
+
}
|
|
3178
|
+
} catch (err) {
|
|
3179
|
+
setError(err.message || "Erro ao arquivar sala.");
|
|
3180
|
+
}
|
|
3181
|
+
},
|
|
3182
|
+
[gql, permissions.canArchiveRoom, activeRoomId, isDemoMode]
|
|
3183
|
+
);
|
|
3184
|
+
const leaveRoom = useCallback(
|
|
3185
|
+
async (roomId) => {
|
|
3186
|
+
if (isDemoMode) {
|
|
3187
|
+
setRooms((prev) => prev.filter((r) => r.id !== roomId));
|
|
3188
|
+
if (activeRoomId === roomId) {
|
|
3189
|
+
setActiveRoomId(null);
|
|
3190
|
+
setMessages([]);
|
|
3191
|
+
}
|
|
3192
|
+
return;
|
|
3193
|
+
}
|
|
3194
|
+
try {
|
|
3195
|
+
await gql(
|
|
3196
|
+
`mutation($roomId: ID!) { leaveRoom(roomId: $roomId) }`,
|
|
3197
|
+
{ roomId }
|
|
3198
|
+
);
|
|
3199
|
+
setRooms((prev) => prev.filter((r) => r.id !== roomId));
|
|
3200
|
+
if (activeRoomId === roomId) {
|
|
3201
|
+
setActiveRoomId(null);
|
|
3202
|
+
setMessages([]);
|
|
3203
|
+
}
|
|
3204
|
+
} catch (err) {
|
|
3205
|
+
setError(err.message || "Erro ao sair da sala.");
|
|
3206
|
+
}
|
|
3207
|
+
},
|
|
3208
|
+
[gql, activeRoomId, isDemoMode]
|
|
3209
|
+
);
|
|
3210
|
+
const addMember = useCallback(
|
|
3211
|
+
async (roomId, targetUserId, _role = "MEMBER") => {
|
|
3212
|
+
if (!permissions.canManageMembers) return;
|
|
3213
|
+
if (isDemoMode) return;
|
|
3214
|
+
try {
|
|
3215
|
+
await gql(
|
|
3216
|
+
`mutation($roomId: ID!, $userId: String!) {
|
|
3217
|
+
inviteToRoom(roomId: $roomId, userId: $userId) { id userId role }
|
|
3218
|
+
}`,
|
|
3219
|
+
{ roomId, userId: targetUserId }
|
|
3220
|
+
);
|
|
3221
|
+
fetchRoomMembers(roomId);
|
|
3222
|
+
} catch (err) {
|
|
3223
|
+
setError(err.message || "Erro ao adicionar membro.");
|
|
3224
|
+
}
|
|
3225
|
+
},
|
|
3226
|
+
[gql, permissions.canManageMembers, fetchRoomMembers, isDemoMode]
|
|
3227
|
+
);
|
|
3228
|
+
const removeMember = useCallback(
|
|
3229
|
+
async (roomId, targetUserId) => {
|
|
3230
|
+
if (!permissions.canManageMembers) return;
|
|
3231
|
+
if (isDemoMode) return;
|
|
3232
|
+
try {
|
|
3233
|
+
await gql(
|
|
3234
|
+
`mutation($roomId: ID!, $targetUserId: String!) {
|
|
3235
|
+
removeMember(roomId: $roomId, targetUserId: $targetUserId)
|
|
3236
|
+
}`,
|
|
3237
|
+
{ roomId, targetUserId }
|
|
3238
|
+
);
|
|
3239
|
+
setRoomMembers((prev) => prev.filter((m) => m.userId !== targetUserId));
|
|
3240
|
+
} catch (err) {
|
|
3241
|
+
setError(err.message || "Erro ao remover membro.");
|
|
3242
|
+
}
|
|
3243
|
+
},
|
|
3244
|
+
[gql, permissions.canManageMembers, isDemoMode]
|
|
3245
|
+
);
|
|
3246
|
+
const updateMemberRole = useCallback(
|
|
3247
|
+
async (roomId, targetUserId, role) => {
|
|
3248
|
+
if (!permissions.canManageMembers) return;
|
|
3249
|
+
if (isDemoMode) return;
|
|
3250
|
+
try {
|
|
3251
|
+
await gql(
|
|
3252
|
+
`mutation($roomId: ID!, $targetUserId: String!, $role: MemberRole!) {
|
|
3253
|
+
updateMemberRole(roomId: $roomId, targetUserId: $targetUserId, role: $role) { id userId role }
|
|
3254
|
+
}`,
|
|
3255
|
+
{ roomId, targetUserId, role }
|
|
3256
|
+
);
|
|
3257
|
+
setRoomMembers(
|
|
3258
|
+
(prev) => prev.map((m) => m.userId === targetUserId ? { ...m, role } : m)
|
|
3259
|
+
);
|
|
3260
|
+
} catch (err) {
|
|
3261
|
+
setError(err.message || "Erro ao atualizar cargo.");
|
|
3262
|
+
}
|
|
3263
|
+
},
|
|
3264
|
+
[gql, permissions.canManageMembers, isDemoMode]
|
|
3265
|
+
);
|
|
3266
|
+
const editMessage = useCallback(
|
|
3267
|
+
async (messageId, content) => {
|
|
3268
|
+
if (!content.trim()) return;
|
|
3269
|
+
if (isDemoMode) {
|
|
3270
|
+
setMessages(
|
|
3271
|
+
(prev) => prev.map(
|
|
3272
|
+
(m) => m.id === messageId ? { ...m, content: content.trim(), edited: true, editedAt: (/* @__PURE__ */ new Date()).toISOString() } : m
|
|
3273
|
+
)
|
|
3274
|
+
);
|
|
3275
|
+
return;
|
|
3276
|
+
}
|
|
3277
|
+
try {
|
|
3278
|
+
const data = await gql(
|
|
3279
|
+
`mutation($id: ID!, $input: EditMessageInput!) {
|
|
3280
|
+
editMessage(id: $id, input: $input) {
|
|
3281
|
+
id roomId authorId parentId
|
|
3282
|
+
content edited editedAt createdAt
|
|
3283
|
+
mentions { type targetId display }
|
|
3284
|
+
reactions { emoji userId }
|
|
3285
|
+
}
|
|
3286
|
+
}`,
|
|
3287
|
+
{ id: messageId, input: { content: content.trim() } }
|
|
3288
|
+
);
|
|
3289
|
+
if (data?.editMessage) {
|
|
3290
|
+
setMessages(
|
|
3291
|
+
(prev) => prev.map((m) => m.id === messageId ? { ...data.editMessage, _status: "sent" } : m)
|
|
3292
|
+
);
|
|
3293
|
+
}
|
|
3294
|
+
} catch (err) {
|
|
3295
|
+
console.error("[organify-chat] editMessage:", err);
|
|
3296
|
+
setError(err.message || "Erro ao editar mensagem.");
|
|
3297
|
+
}
|
|
3298
|
+
},
|
|
3299
|
+
[gql, isDemoMode]
|
|
3300
|
+
);
|
|
3301
|
+
const deleteMessage = useCallback(
|
|
3302
|
+
async (messageId) => {
|
|
3303
|
+
if (isDemoMode) {
|
|
3304
|
+
setMessages((prev) => prev.filter((m) => m.id !== messageId));
|
|
3305
|
+
return;
|
|
3306
|
+
}
|
|
3307
|
+
const msg = messages.find((m) => m.id === messageId);
|
|
3308
|
+
if (!msg) return;
|
|
3309
|
+
try {
|
|
3310
|
+
await gql(
|
|
3311
|
+
`mutation($id: ID!, $roomId: ID!) {
|
|
3312
|
+
deleteMessage(id: $id, roomId: $roomId)
|
|
3313
|
+
}`,
|
|
3314
|
+
{ id: messageId, roomId: msg.roomId }
|
|
3315
|
+
);
|
|
3316
|
+
setMessages((prev) => prev.filter((m) => m.id !== messageId));
|
|
3317
|
+
} catch (err) {
|
|
3318
|
+
console.error("[organify-chat] deleteMessage:", err);
|
|
3319
|
+
setError(err.message || "Erro ao eliminar mensagem.");
|
|
3320
|
+
}
|
|
3321
|
+
},
|
|
3322
|
+
[gql, isDemoMode, messages]
|
|
3323
|
+
);
|
|
3324
|
+
const addReaction = useCallback(
|
|
3325
|
+
async (messageId, emoji) => {
|
|
3326
|
+
if (isDemoMode) {
|
|
3327
|
+
setMessages(
|
|
3328
|
+
(prev) => prev.map((m) => {
|
|
3329
|
+
if (m.id !== messageId) return m;
|
|
3330
|
+
const hasReaction = m.reactions.some((r) => r.emoji === emoji && r.userId === userId);
|
|
3331
|
+
if (hasReaction) return m;
|
|
3332
|
+
return { ...m, reactions: [...m.reactions, { emoji, userId }] };
|
|
3333
|
+
})
|
|
3334
|
+
);
|
|
3335
|
+
return;
|
|
3336
|
+
}
|
|
3337
|
+
try {
|
|
3338
|
+
await gql(
|
|
3339
|
+
`mutation($messageId: ID!, $emoji: String!) {
|
|
3340
|
+
addReaction(messageId: $messageId, emoji: $emoji) { emoji userId createdAt }
|
|
3341
|
+
}`,
|
|
3342
|
+
{ messageId, emoji }
|
|
3343
|
+
);
|
|
3344
|
+
setMessages(
|
|
3345
|
+
(prev) => prev.map((m) => {
|
|
3346
|
+
if (m.id !== messageId) return m;
|
|
3347
|
+
return { ...m, reactions: [...m.reactions, { emoji, userId }] };
|
|
3348
|
+
})
|
|
3349
|
+
);
|
|
3350
|
+
} catch (err) {
|
|
3351
|
+
console.error("[organify-chat] addReaction:", err);
|
|
3352
|
+
}
|
|
3353
|
+
},
|
|
3354
|
+
[gql, userId, isDemoMode]
|
|
3355
|
+
);
|
|
3356
|
+
const removeReaction = useCallback(
|
|
3357
|
+
async (messageId, emoji) => {
|
|
3358
|
+
if (isDemoMode) {
|
|
3359
|
+
setMessages(
|
|
3360
|
+
(prev) => prev.map((m) => {
|
|
3361
|
+
if (m.id !== messageId) return m;
|
|
3362
|
+
return { ...m, reactions: m.reactions.filter((r) => !(r.emoji === emoji && r.userId === userId)) };
|
|
3363
|
+
})
|
|
3364
|
+
);
|
|
3365
|
+
return;
|
|
3366
|
+
}
|
|
3367
|
+
try {
|
|
3368
|
+
await gql(
|
|
3369
|
+
`mutation($messageId: ID!, $emoji: String!) {
|
|
3370
|
+
removeReaction(messageId: $messageId, emoji: $emoji)
|
|
3371
|
+
}`,
|
|
3372
|
+
{ messageId, emoji }
|
|
3373
|
+
);
|
|
3374
|
+
setMessages(
|
|
3375
|
+
(prev) => prev.map((m) => {
|
|
3376
|
+
if (m.id !== messageId) return m;
|
|
3377
|
+
return { ...m, reactions: m.reactions.filter((r) => !(r.emoji === emoji && r.userId === userId)) };
|
|
3378
|
+
})
|
|
3379
|
+
);
|
|
3380
|
+
} catch (err) {
|
|
3381
|
+
console.error("[organify-chat] removeReaction:", err);
|
|
3382
|
+
}
|
|
3383
|
+
},
|
|
3384
|
+
[gql, userId, isDemoMode]
|
|
3385
|
+
);
|
|
3386
|
+
const reactToMessage = useCallback(
|
|
3387
|
+
async (messageId, emoji) => {
|
|
3388
|
+
const msg = messages.find((m) => m.id === messageId);
|
|
3389
|
+
if (!msg) return;
|
|
3390
|
+
const hasReaction = msg.reactions.some((r) => r.emoji === emoji && r.userId === userId);
|
|
3391
|
+
if (hasReaction) {
|
|
3392
|
+
await removeReaction(messageId, emoji);
|
|
3393
|
+
} else {
|
|
3394
|
+
await addReaction(messageId, emoji);
|
|
3395
|
+
}
|
|
3396
|
+
},
|
|
3397
|
+
[messages, userId, addReaction, removeReaction]
|
|
3398
|
+
);
|
|
3399
|
+
useEffect(() => {
|
|
3400
|
+
if (!userId || !activeRoomId) return;
|
|
3401
|
+
if (isDemoMode) return;
|
|
3402
|
+
const chatServiceUrl = api?.services?.chat || gatewayUrl;
|
|
3403
|
+
const wsUrl = chatServiceUrl.replace(/^http/, "ws") + "/graphql/chat";
|
|
3404
|
+
let ws = null;
|
|
3405
|
+
let pingInterval;
|
|
3406
|
+
let reconnectTimer;
|
|
3407
|
+
let reconnectAttempts = 0;
|
|
3408
|
+
const maxReconnectAttempts = 8;
|
|
3409
|
+
let disposed = false;
|
|
3410
|
+
function connect() {
|
|
3411
|
+
if (disposed || reconnectAttempts >= maxReconnectAttempts) return;
|
|
3412
|
+
try {
|
|
3413
|
+
ws = new WebSocket(wsUrl, "graphql-transport-ws");
|
|
3414
|
+
ws.onopen = () => {
|
|
3415
|
+
reconnectAttempts = 0;
|
|
3416
|
+
ws?.send(JSON.stringify({
|
|
3417
|
+
type: "connection_init",
|
|
3418
|
+
payload: { userId, workspaceId }
|
|
3419
|
+
}));
|
|
3420
|
+
};
|
|
3421
|
+
ws.onmessage = (event) => {
|
|
3422
|
+
try {
|
|
3423
|
+
const msg = JSON.parse(event.data);
|
|
3424
|
+
switch (msg.type) {
|
|
3425
|
+
case "connection_ack":
|
|
3426
|
+
ws?.send(JSON.stringify({
|
|
3427
|
+
id: "sub_msg_created",
|
|
3428
|
+
type: "subscribe",
|
|
3429
|
+
payload: {
|
|
3430
|
+
query: `subscription($roomId: ID!) {
|
|
3431
|
+
messageCreated(roomId: $roomId) {
|
|
3432
|
+
${MESSAGE_FIELDS}
|
|
3433
|
+
}
|
|
3434
|
+
}`,
|
|
3435
|
+
variables: { roomId: activeRoomId }
|
|
3436
|
+
}
|
|
3437
|
+
}));
|
|
3438
|
+
ws?.send(JSON.stringify({
|
|
3439
|
+
id: "sub_msg_updated",
|
|
3440
|
+
type: "subscribe",
|
|
3441
|
+
payload: {
|
|
3442
|
+
query: `subscription($roomId: ID!) {
|
|
3443
|
+
messageUpdated(roomId: $roomId) {
|
|
3444
|
+
${MESSAGE_FIELDS}
|
|
3445
|
+
}
|
|
3446
|
+
}`,
|
|
3447
|
+
variables: { roomId: activeRoomId }
|
|
3448
|
+
}
|
|
3449
|
+
}));
|
|
3450
|
+
ws?.send(JSON.stringify({
|
|
3451
|
+
id: "sub_msg_deleted",
|
|
3452
|
+
type: "subscribe",
|
|
3453
|
+
payload: {
|
|
3454
|
+
query: `subscription($roomId: ID!) {
|
|
3455
|
+
messageDeleted(roomId: $roomId)
|
|
3456
|
+
}`,
|
|
3457
|
+
variables: { roomId: activeRoomId }
|
|
3458
|
+
}
|
|
3459
|
+
}));
|
|
3460
|
+
ws?.send(JSON.stringify({
|
|
3461
|
+
id: "sub_typing",
|
|
3462
|
+
type: "subscribe",
|
|
3463
|
+
payload: {
|
|
3464
|
+
query: `subscription($roomId: ID!) {
|
|
3465
|
+
typingIndicator(roomId: $roomId) {
|
|
3466
|
+
roomId userId isTyping
|
|
3467
|
+
}
|
|
3468
|
+
}`,
|
|
3469
|
+
variables: { roomId: activeRoomId }
|
|
3470
|
+
}
|
|
3471
|
+
}));
|
|
3472
|
+
pingInterval = setInterval(() => {
|
|
3473
|
+
if (ws?.readyState === WebSocket.OPEN) {
|
|
3474
|
+
ws.send(JSON.stringify({ type: "ping" }));
|
|
3475
|
+
}
|
|
3476
|
+
}, 25e3);
|
|
3477
|
+
break;
|
|
3478
|
+
case "next":
|
|
3479
|
+
if (msg.id === "sub_msg_created" && msg.payload?.data?.messageCreated) {
|
|
3480
|
+
const newMsg = msg.payload.data.messageCreated;
|
|
3481
|
+
if (newMsg.authorId === userId) {
|
|
3482
|
+
setMessages(
|
|
3483
|
+
(prev) => prev.map(
|
|
3484
|
+
(m) => m._status === "pending" && m.content === newMsg.content && m.authorId === newMsg.authorId ? { ...newMsg, _status: "sent" } : m
|
|
3485
|
+
)
|
|
3486
|
+
);
|
|
3487
|
+
} else {
|
|
3488
|
+
setMessages((prev) => {
|
|
3489
|
+
if (prev.find((m) => m.id === newMsg.id)) return prev;
|
|
3490
|
+
return [...prev, newMsg];
|
|
3491
|
+
});
|
|
3492
|
+
}
|
|
3493
|
+
}
|
|
3494
|
+
if (msg.id === "sub_msg_updated" && msg.payload?.data?.messageUpdated) {
|
|
3495
|
+
const updated = msg.payload.data.messageUpdated;
|
|
3496
|
+
setMessages(
|
|
3497
|
+
(prev) => prev.map((m) => m.id === updated.id ? { ...updated, _status: "sent" } : m)
|
|
3498
|
+
);
|
|
3499
|
+
}
|
|
3500
|
+
if (msg.id === "sub_msg_deleted" && msg.payload?.data?.messageDeleted) {
|
|
3501
|
+
const deletedId = msg.payload.data.messageDeleted;
|
|
3502
|
+
setMessages((prev) => prev.filter((m) => m.id !== deletedId));
|
|
3503
|
+
}
|
|
3504
|
+
if (msg.id === "sub_typing" && msg.payload?.data?.typingIndicator) {
|
|
3505
|
+
const { userId: typerId, isTyping } = msg.payload.data.typingIndicator;
|
|
3506
|
+
if (typerId !== userId) {
|
|
3507
|
+
if (isTyping) {
|
|
3508
|
+
setTypingUsers(
|
|
3509
|
+
(prev) => prev.includes(typerId) ? prev : [...prev, typerId]
|
|
3510
|
+
);
|
|
3511
|
+
setTimeout(() => {
|
|
3512
|
+
setTypingUsers((prev) => prev.filter((id) => id !== typerId));
|
|
3513
|
+
}, 4e3);
|
|
3514
|
+
} else {
|
|
3515
|
+
setTypingUsers((prev) => prev.filter((id) => id !== typerId));
|
|
3516
|
+
}
|
|
3517
|
+
}
|
|
3518
|
+
}
|
|
3519
|
+
break;
|
|
3520
|
+
case "pong":
|
|
3521
|
+
break;
|
|
3522
|
+
case "error":
|
|
3523
|
+
console.warn("[organify-chat] Subscription error:", msg.payload);
|
|
3524
|
+
break;
|
|
3525
|
+
case "complete":
|
|
3526
|
+
break;
|
|
3527
|
+
}
|
|
3528
|
+
} catch {
|
|
3529
|
+
}
|
|
3530
|
+
};
|
|
3531
|
+
ws.onerror = () => {
|
|
3532
|
+
};
|
|
3533
|
+
ws.onclose = () => {
|
|
3534
|
+
clearInterval(pingInterval);
|
|
3535
|
+
if (!disposed && reconnectAttempts < maxReconnectAttempts) {
|
|
3536
|
+
const delay = Math.min(1e3 * Math.pow(2, reconnectAttempts) + Math.random() * 500, 3e4);
|
|
3537
|
+
reconnectAttempts++;
|
|
3538
|
+
reconnectTimer = setTimeout(connect, delay);
|
|
3539
|
+
}
|
|
3540
|
+
};
|
|
3541
|
+
} catch {
|
|
3542
|
+
if (!disposed && reconnectAttempts < maxReconnectAttempts) {
|
|
3543
|
+
reconnectAttempts++;
|
|
3544
|
+
reconnectTimer = setTimeout(connect, 3e3);
|
|
3545
|
+
}
|
|
3546
|
+
}
|
|
3547
|
+
}
|
|
3548
|
+
connect();
|
|
3549
|
+
return () => {
|
|
3550
|
+
disposed = true;
|
|
3551
|
+
clearInterval(pingInterval);
|
|
3552
|
+
clearTimeout(reconnectTimer);
|
|
3553
|
+
if (ws?.readyState === WebSocket.OPEN) {
|
|
3554
|
+
ws.send(JSON.stringify({ id: "sub_msg_created", type: "complete" }));
|
|
3555
|
+
ws.send(JSON.stringify({ id: "sub_msg_updated", type: "complete" }));
|
|
3556
|
+
ws.send(JSON.stringify({ id: "sub_msg_deleted", type: "complete" }));
|
|
3557
|
+
ws.send(JSON.stringify({ id: "sub_typing", type: "complete" }));
|
|
3558
|
+
ws.close(1e3, "unmount");
|
|
3559
|
+
} else {
|
|
3560
|
+
ws?.close();
|
|
3561
|
+
}
|
|
3562
|
+
};
|
|
3563
|
+
}, [api?.services?.chat, gatewayUrl, userId, workspaceId, activeRoomId, isDemoMode]);
|
|
3564
|
+
const sendTyping = useCallback(() => {
|
|
3565
|
+
}, []);
|
|
3566
|
+
useEffect(() => {
|
|
3567
|
+
if (workspaceId && !workspaceId.startsWith("temp-")) {
|
|
3568
|
+
fetchRooms();
|
|
3569
|
+
}
|
|
3570
|
+
}, [fetchRooms]);
|
|
3571
|
+
useEffect(() => {
|
|
3572
|
+
if (error) {
|
|
3573
|
+
const t = setTimeout(() => setError(null), 5e3);
|
|
3574
|
+
return () => clearTimeout(t);
|
|
3575
|
+
}
|
|
3576
|
+
}, [error]);
|
|
3577
|
+
return {
|
|
3578
|
+
// State
|
|
3579
|
+
rooms,
|
|
3580
|
+
messages: enrichedMessages,
|
|
3581
|
+
activeRoomId,
|
|
3582
|
+
loadingRooms,
|
|
3583
|
+
loadingMessages,
|
|
3584
|
+
hasMoreMessages,
|
|
3585
|
+
loadingMoreMessages,
|
|
3586
|
+
roomMembers,
|
|
3587
|
+
myRoomRole,
|
|
3588
|
+
permissions,
|
|
3589
|
+
typingUsers,
|
|
3590
|
+
error,
|
|
3591
|
+
// Room selection
|
|
3592
|
+
selectRoom,
|
|
3593
|
+
// Messaging
|
|
3594
|
+
sendMessage,
|
|
3595
|
+
editMessage,
|
|
3596
|
+
deleteMessage,
|
|
3597
|
+
reactToMessage,
|
|
3598
|
+
sendTyping,
|
|
3599
|
+
loadMoreMessages,
|
|
3600
|
+
// Room CRUD
|
|
3601
|
+
createRoom,
|
|
3602
|
+
createDirectMessage,
|
|
3603
|
+
updateRoom,
|
|
3604
|
+
archiveRoom,
|
|
3605
|
+
leaveRoom,
|
|
3606
|
+
// Member management
|
|
3607
|
+
addMember,
|
|
3608
|
+
removeMember,
|
|
3609
|
+
updateMemberRole,
|
|
3610
|
+
// Refresh
|
|
3611
|
+
fetchRooms,
|
|
3612
|
+
fetchRoomMembers,
|
|
3613
|
+
userCache
|
|
3614
|
+
};
|
|
3615
|
+
}
|
|
3616
|
+
var alertVariants = cva(
|
|
3617
|
+
"group relative overflow-visible border-l-4 border border-glass-border bg-glass-highlight/90 backdrop-blur-xl py-5 pl-14 pr-6 shadow-glass rounded-sm",
|
|
3618
|
+
{
|
|
3619
|
+
variants: {
|
|
3620
|
+
variant: {
|
|
3621
|
+
success: "border-l-emerald-500 bg-emerald-500/[0.06]",
|
|
3622
|
+
error: "border-l-rose-500 bg-rose-500/[0.06]",
|
|
3623
|
+
warning: "border-l-amber-400 bg-amber-400/[0.06]",
|
|
3624
|
+
info: "border-l-blue-500 bg-blue-500/[0.06]"
|
|
3625
|
+
}
|
|
3626
|
+
},
|
|
3627
|
+
defaultVariants: {
|
|
3628
|
+
variant: "info"
|
|
3629
|
+
}
|
|
3630
|
+
}
|
|
3631
|
+
);
|
|
3632
|
+
var alertIconVariants = cva(
|
|
3633
|
+
"absolute -left-4 top-1/2 z-10 flex h-10 w-10 -translate-y-1/2 items-center justify-center border border-glass-border-strong shadow-lg transition-transform duration-[400ms] ease-[cubic-bezier(0.25,1,0.5,1)] group-hover:rotate-0 rounded-sm",
|
|
3634
|
+
{
|
|
3635
|
+
variants: {
|
|
3636
|
+
variant: {
|
|
3637
|
+
success: "bg-emerald-500 text-black rotate-[-6deg]",
|
|
3638
|
+
error: "bg-rose-500 text-white rotate-[6deg]",
|
|
3639
|
+
warning: "bg-amber-400 text-black rotate-[-3deg]",
|
|
3640
|
+
info: "bg-blue-500 text-white rotate-[3deg]"
|
|
3641
|
+
}
|
|
3642
|
+
},
|
|
3643
|
+
defaultVariants: {
|
|
3644
|
+
variant: "info"
|
|
3645
|
+
}
|
|
3646
|
+
}
|
|
3647
|
+
);
|
|
3648
|
+
function Alert({
|
|
3649
|
+
className,
|
|
3650
|
+
variant,
|
|
3651
|
+
title,
|
|
3652
|
+
description,
|
|
3653
|
+
icon,
|
|
3654
|
+
onClose,
|
|
3655
|
+
action,
|
|
3656
|
+
...props
|
|
3657
|
+
}) {
|
|
3658
|
+
return /* @__PURE__ */ jsxs("div", { className: cn(alertVariants({ variant }), className), role: "alert", ...props, children: [
|
|
3659
|
+
/* @__PURE__ */ jsx("div", { className: cn(alertIconVariants({ variant })), children: icon || /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
3660
|
+
variant === "success" && /* @__PURE__ */ jsx(OrgCheckCircle, { className: "w-5 h-5" }),
|
|
3661
|
+
variant === "error" && /* @__PURE__ */ jsx(OrgError, { className: "w-5 h-5" }),
|
|
3662
|
+
variant === "warning" && /* @__PURE__ */ jsx(OrgWarning, { className: "w-5 h-5" }),
|
|
3663
|
+
variant === "info" && /* @__PURE__ */ jsx(OrgInfo, { className: "w-5 h-5" })
|
|
3664
|
+
] }) }),
|
|
3665
|
+
/* @__PURE__ */ jsx("div", { className: "pointer-events-none absolute inset-0 rounded-sm shadow-inner-light" }),
|
|
3666
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-4", children: [
|
|
3667
|
+
/* @__PURE__ */ jsxs("div", { className: "flex-1", children: [
|
|
3668
|
+
/* @__PURE__ */ jsx("h4", { className: "text-base font-semibold text-org-text", children: title }),
|
|
3669
|
+
description && /* @__PURE__ */ jsx("p", { className: "mt-1 text-sm font-light text-org-text-secondary", children: description })
|
|
3670
|
+
] }),
|
|
3671
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
|
|
3672
|
+
action && /* @__PURE__ */ jsx(
|
|
3673
|
+
"button",
|
|
3674
|
+
{
|
|
3675
|
+
onClick: action.onClick,
|
|
3676
|
+
className: cn(
|
|
3677
|
+
"whitespace-nowrap px-3 py-1.5 text-xs font-semibold tracking-wide transition-all border rounded-sm",
|
|
3678
|
+
variant === "error" && "border-rose-500/30 bg-rose-500/20 text-rose-300 hover:bg-rose-500/30",
|
|
3679
|
+
variant === "success" && "border-emerald-500/30 bg-emerald-500/20 text-emerald-300 hover:bg-emerald-500/30",
|
|
3680
|
+
variant === "warning" && "border-amber-400/30 bg-amber-400/20 text-amber-300 hover:bg-amber-400/30",
|
|
3681
|
+
variant === "info" && "border-blue-500/30 bg-blue-500/20 text-blue-300 hover:bg-blue-500/30"
|
|
3682
|
+
),
|
|
3683
|
+
children: action.label
|
|
3684
|
+
}
|
|
3685
|
+
),
|
|
3686
|
+
onClose && /* @__PURE__ */ jsx(
|
|
3687
|
+
"button",
|
|
3688
|
+
{
|
|
3689
|
+
onClick: onClose,
|
|
3690
|
+
className: "p-1 text-org-text-muted transition-colors hover:text-org-text",
|
|
3691
|
+
"aria-label": "Close alert",
|
|
3692
|
+
children: /* @__PURE__ */ jsx(OrgClose, { className: "h-4 w-4" })
|
|
3693
|
+
}
|
|
3694
|
+
)
|
|
3695
|
+
] })
|
|
3696
|
+
] })
|
|
3697
|
+
] });
|
|
3698
|
+
}
|
|
3699
|
+
|
|
3700
|
+
// src/components/chat/chat-mock-data.ts
|
|
3701
|
+
var MOCK_USERS = [
|
|
3702
|
+
{
|
|
3703
|
+
id: "user-1",
|
|
3704
|
+
displayName: "Ana Silva",
|
|
3705
|
+
email: "ana.silva@organify.dev",
|
|
3706
|
+
avatarUrl: null,
|
|
3707
|
+
status: "ONLINE"
|
|
3708
|
+
},
|
|
3709
|
+
{
|
|
3710
|
+
id: "user-2",
|
|
3711
|
+
displayName: "Bruno Costa",
|
|
3712
|
+
email: "bruno.costa@organify.dev",
|
|
3713
|
+
avatarUrl: null,
|
|
3714
|
+
status: "ONLINE"
|
|
3715
|
+
},
|
|
3716
|
+
{
|
|
3717
|
+
id: "user-3",
|
|
3718
|
+
displayName: "Carla Santos",
|
|
3719
|
+
email: "carla.santos@organify.dev",
|
|
3720
|
+
avatarUrl: null,
|
|
3721
|
+
status: "AWAY"
|
|
3722
|
+
},
|
|
3723
|
+
{
|
|
3724
|
+
id: "user-4",
|
|
3725
|
+
displayName: "Diego Ferreira",
|
|
3726
|
+
email: "diego.ferreira@organify.dev",
|
|
3727
|
+
avatarUrl: null,
|
|
3728
|
+
status: "ONLINE"
|
|
3729
|
+
},
|
|
3730
|
+
{
|
|
3731
|
+
id: "user-5",
|
|
3732
|
+
displayName: "Elena Rodrigues",
|
|
3733
|
+
email: "elena.rodrigues@organify.dev",
|
|
3734
|
+
avatarUrl: null,
|
|
3735
|
+
status: "BUSY"
|
|
3736
|
+
},
|
|
3737
|
+
{
|
|
3738
|
+
id: "user-6",
|
|
3739
|
+
displayName: "Fernando Lima",
|
|
3740
|
+
email: "fernando.lima@organify.dev",
|
|
3741
|
+
avatarUrl: null,
|
|
3742
|
+
status: "ONLINE"
|
|
3743
|
+
},
|
|
3744
|
+
{
|
|
3745
|
+
id: "user-7",
|
|
3746
|
+
displayName: "Gabriela Alves",
|
|
3747
|
+
email: "gabriela.alves@organify.dev",
|
|
3748
|
+
avatarUrl: null,
|
|
3749
|
+
status: "OFFLINE"
|
|
3750
|
+
},
|
|
3751
|
+
{
|
|
3752
|
+
id: "user-8",
|
|
3753
|
+
displayName: "Hugo Martins",
|
|
3754
|
+
email: "hugo.martins@organify.dev",
|
|
3755
|
+
avatarUrl: null,
|
|
3756
|
+
status: "ONLINE"
|
|
3757
|
+
}
|
|
3758
|
+
];
|
|
3759
|
+
var MOCK_PROJECTS = [
|
|
3760
|
+
{ id: "proj-1", name: "Organify Platform", key: "ORG" },
|
|
3761
|
+
{ id: "proj-2", name: "Mobile App", key: "MOB" },
|
|
3762
|
+
{ id: "proj-3", name: "Design System", key: "DS" },
|
|
3763
|
+
{ id: "proj-4", name: "Marketing Website", key: "WEB" },
|
|
3764
|
+
{ id: "proj-5", name: "API Gateway", key: "API" }
|
|
3765
|
+
];
|
|
3766
|
+
function getMockMentionOptions(query) {
|
|
3767
|
+
const q = query.toLowerCase();
|
|
3768
|
+
const options = [];
|
|
3769
|
+
MOCK_USERS.filter(
|
|
3770
|
+
(u) => u.displayName.toLowerCase().includes(q) || u.email?.toLowerCase().includes(q)
|
|
3771
|
+
).slice(0, 5).forEach((u) => {
|
|
3772
|
+
options.push({
|
|
3773
|
+
id: u.id,
|
|
3774
|
+
type: "user",
|
|
3775
|
+
display: u.displayName,
|
|
3776
|
+
avatarUrl: u.avatarUrl,
|
|
3777
|
+
subtitle: u.email
|
|
3778
|
+
});
|
|
3779
|
+
});
|
|
3780
|
+
MOCK_PROJECTS.filter(
|
|
3781
|
+
(p) => p.name.toLowerCase().includes(q) || p.key.toLowerCase().includes(q)
|
|
3782
|
+
).slice(0, 3).forEach((p) => {
|
|
3783
|
+
options.push({
|
|
3784
|
+
id: p.id,
|
|
3785
|
+
type: "project",
|
|
3786
|
+
display: p.name,
|
|
3787
|
+
subtitle: p.key
|
|
3788
|
+
});
|
|
3789
|
+
});
|
|
3790
|
+
return options;
|
|
3791
|
+
}
|
|
3792
|
+
var REPLY_TEMPLATES = [
|
|
3793
|
+
"Excelente ideia! \u{1F44D}",
|
|
3794
|
+
"Concordo totalmente.",
|
|
3795
|
+
"Vou verificar isso agora.",
|
|
3796
|
+
"Obrigado pela atualiza\xE7\xE3o! \u{1F389}",
|
|
3797
|
+
"Isso faz sentido.",
|
|
3798
|
+
"Pode deixar comigo!",
|
|
3799
|
+
"Bom ponto! N\xE3o tinha pensado nisso.",
|
|
3800
|
+
"J\xE1 estou trabalhando nisso.",
|
|
3801
|
+
"Perfeito, vamos em frente!",
|
|
3802
|
+
"\xD3timo trabalho! \u{1F680}",
|
|
3803
|
+
"Interessante... vou investigar.",
|
|
3804
|
+
"Combinado!",
|
|
3805
|
+
"Aceito o desafio! \u{1F4AA}",
|
|
3806
|
+
"Bom demais!",
|
|
3807
|
+
"Isso vai ajudar muito."
|
|
3808
|
+
];
|
|
3809
|
+
var RANDOM_COMMENTS = [
|
|
3810
|
+
"Algu\xE9m viu o novo design? Est\xE1 incr\xEDvel!",
|
|
3811
|
+
"Precisamos revisar a API de notifica\xE7\xF5es.",
|
|
3812
|
+
"O deploy de hoje foi um sucesso! \u{1F38A}",
|
|
3813
|
+
"Quem pode me ajudar com TypeScript?",
|
|
3814
|
+
"Reuni\xE3o marcada para amanh\xE3 \xE0s 10h.",
|
|
3815
|
+
"A feature de chat ficou muito boa!",
|
|
3816
|
+
"Encontrei um bug no formul\xE1rio de login.",
|
|
3817
|
+
"Code review pendente no PR #234",
|
|
3818
|
+
"Algu\xE9m tem tempo para pair programming?",
|
|
3819
|
+
"Nova sprint come\xE7a na segunda-feira!",
|
|
3820
|
+
"Preciso de feedback no mockup.",
|
|
3821
|
+
"Performance melhorou 40% com as \xFAltimas mudan\xE7as!",
|
|
3822
|
+
"Documenta\xE7\xE3o atualizada no Notion.",
|
|
3823
|
+
"Quem quer caf\xE9? \u2615",
|
|
3824
|
+
"Cliente adorou a demo!"
|
|
3825
|
+
];
|
|
3826
|
+
function getRandomUser() {
|
|
3827
|
+
return MOCK_USERS[Math.floor(Math.random() * MOCK_USERS.length)];
|
|
3828
|
+
}
|
|
3829
|
+
function getRandomTemplate() {
|
|
3830
|
+
return REPLY_TEMPLATES[Math.floor(Math.random() * REPLY_TEMPLATES.length)];
|
|
3831
|
+
}
|
|
3832
|
+
function getRandomComment() {
|
|
3833
|
+
return RANDOM_COMMENTS[Math.floor(Math.random() * RANDOM_COMMENTS.length)];
|
|
3834
|
+
}
|
|
3835
|
+
function generateAutoReplies(userMessage, roomId, count = 5) {
|
|
3836
|
+
const replies = [];
|
|
3837
|
+
const now = Date.now();
|
|
3838
|
+
for (let i = 0; i < count; i++) {
|
|
3839
|
+
const user = getRandomUser();
|
|
3840
|
+
const isReply = Math.random() > 0.6;
|
|
3841
|
+
const isComment = Math.random() > 0.7;
|
|
3842
|
+
replies.push({
|
|
3843
|
+
id: `mock-${Date.now()}-${i}`,
|
|
3844
|
+
roomId,
|
|
3845
|
+
authorId: user.id,
|
|
3846
|
+
authorName: user.displayName,
|
|
3847
|
+
content: isComment ? getRandomComment() : getRandomTemplate(),
|
|
3848
|
+
createdAt: new Date(now + (i + 1) * 2e3).toISOString(),
|
|
3849
|
+
// 2s interval
|
|
3850
|
+
replyToId: isReply ? userMessage.id : null,
|
|
3851
|
+
edited: false,
|
|
3852
|
+
editedAt: null,
|
|
3853
|
+
mentions: [],
|
|
3854
|
+
reactions: []
|
|
3855
|
+
});
|
|
3856
|
+
}
|
|
3857
|
+
return replies;
|
|
3858
|
+
}
|
|
3859
|
+
var TypingIndicatorMock = class {
|
|
3860
|
+
typingUsers = /* @__PURE__ */ new Set();
|
|
3861
|
+
timeouts = /* @__PURE__ */ new Map();
|
|
3862
|
+
listeners = [];
|
|
3863
|
+
constructor() {
|
|
3864
|
+
}
|
|
3865
|
+
/** Simula alguém começando a digitar */
|
|
3866
|
+
startTyping(userId) {
|
|
3867
|
+
const user = userId ? MOCK_USERS.find((u) => u.id === userId) : getRandomUser();
|
|
3868
|
+
if (!user) return;
|
|
3869
|
+
const existingTimeout = this.timeouts.get(user.id);
|
|
3870
|
+
if (existingTimeout) {
|
|
3871
|
+
clearTimeout(existingTimeout);
|
|
3872
|
+
}
|
|
3873
|
+
this.typingUsers.add(user.displayName);
|
|
3874
|
+
this.notify();
|
|
3875
|
+
const duration = 3e3 + Math.random() * 2e3;
|
|
3876
|
+
const timeout = setTimeout(() => {
|
|
3877
|
+
this.stopTyping(user.id);
|
|
3878
|
+
}, duration);
|
|
3879
|
+
this.timeouts.set(user.id, timeout);
|
|
3880
|
+
}
|
|
3881
|
+
/** Para de digitar */
|
|
3882
|
+
stopTyping(userId) {
|
|
3883
|
+
const user = MOCK_USERS.find((u) => u.id === userId);
|
|
3884
|
+
if (!user) return;
|
|
3885
|
+
this.typingUsers.delete(user.displayName);
|
|
3886
|
+
const timeout = this.timeouts.get(userId);
|
|
3887
|
+
if (timeout) {
|
|
3888
|
+
clearTimeout(timeout);
|
|
3889
|
+
this.timeouts.delete(userId);
|
|
3890
|
+
}
|
|
3891
|
+
this.notify();
|
|
3892
|
+
}
|
|
3893
|
+
/** Subscribe to typing changes */
|
|
3894
|
+
subscribe(callback) {
|
|
3895
|
+
this.listeners.push(callback);
|
|
3896
|
+
return () => {
|
|
3897
|
+
this.listeners = this.listeners.filter((l) => l !== callback);
|
|
3898
|
+
};
|
|
3899
|
+
}
|
|
3900
|
+
notify() {
|
|
3901
|
+
const users = Array.from(this.typingUsers);
|
|
3902
|
+
this.listeners.forEach((cb) => cb(users));
|
|
3903
|
+
}
|
|
3904
|
+
/** Simula typing indicator aleatório */
|
|
3905
|
+
simulateRandomTyping() {
|
|
3906
|
+
const delay = 2e3 + Math.random() * 6e3;
|
|
3907
|
+
setTimeout(() => {
|
|
3908
|
+
if (Math.random() > 0.5) {
|
|
3909
|
+
this.startTyping();
|
|
3910
|
+
}
|
|
3911
|
+
this.simulateRandomTyping();
|
|
3912
|
+
}, delay);
|
|
3913
|
+
}
|
|
3914
|
+
clear() {
|
|
3915
|
+
this.typingUsers.clear();
|
|
3916
|
+
this.timeouts.forEach((timeout) => clearTimeout(timeout));
|
|
3917
|
+
this.timeouts.clear();
|
|
3918
|
+
this.notify();
|
|
3919
|
+
}
|
|
3920
|
+
};
|
|
3921
|
+
var typingIndicator = new TypingIndicatorMock();
|
|
3922
|
+
if (typeof window !== "undefined") {
|
|
3923
|
+
typingIndicator.simulateRandomTyping();
|
|
3924
|
+
}
|
|
3925
|
+
function useMediaQuery2(query) {
|
|
3926
|
+
const [matches, setMatches] = React7.useState(false);
|
|
3927
|
+
React7.useEffect(() => {
|
|
3928
|
+
const mql = window.matchMedia(query);
|
|
3929
|
+
setMatches(mql.matches);
|
|
3930
|
+
const handler = (e) => setMatches(e.matches);
|
|
3931
|
+
mql.addEventListener("change", handler);
|
|
3932
|
+
return () => mql.removeEventListener("change", handler);
|
|
3933
|
+
}, [query]);
|
|
3934
|
+
return matches;
|
|
3935
|
+
}
|
|
3936
|
+
function OrganifyChat({
|
|
3937
|
+
workspaceRole,
|
|
3938
|
+
workspaceMembers = [],
|
|
3939
|
+
initialRooms,
|
|
3940
|
+
initialMessages,
|
|
3941
|
+
className
|
|
3942
|
+
}) {
|
|
3943
|
+
const user = useOrganifyUser();
|
|
3944
|
+
const userId = user?.id ?? "";
|
|
3945
|
+
const chat = useChat({
|
|
3946
|
+
workspaceRole,
|
|
3947
|
+
initialRooms,
|
|
3948
|
+
initialMessages,
|
|
3949
|
+
workspaceMembers
|
|
3950
|
+
});
|
|
3951
|
+
const isMobile = !useMediaQuery2("(min-width: 768px)");
|
|
3952
|
+
const [createDialogOpen, setCreateDialogOpen] = React7.useState(false);
|
|
3953
|
+
const [managementOpen, setManagementOpen] = React7.useState(false);
|
|
3954
|
+
const [mobileView, setMobileView] = React7.useState("sidebar");
|
|
3955
|
+
const activeRoom = React7.useMemo(
|
|
3956
|
+
() => chat.rooms.find((r) => r.id === chat.activeRoomId),
|
|
3957
|
+
[chat.rooms, chat.activeRoomId]
|
|
3958
|
+
);
|
|
3959
|
+
const handleRoomSelect = (roomId) => {
|
|
3960
|
+
chat.selectRoom(roomId);
|
|
3961
|
+
if (isMobile) {
|
|
3962
|
+
setMobileView("room");
|
|
3963
|
+
}
|
|
3964
|
+
};
|
|
3965
|
+
const handleBackToSidebar = () => {
|
|
3966
|
+
setMobileView("sidebar");
|
|
3967
|
+
};
|
|
3968
|
+
const handleOpenManagement = () => {
|
|
3969
|
+
if (isMobile) {
|
|
3970
|
+
setMobileView("management");
|
|
3971
|
+
} else {
|
|
3972
|
+
setManagementOpen(true);
|
|
3973
|
+
}
|
|
3974
|
+
};
|
|
3975
|
+
const handleCloseManagement = () => {
|
|
3976
|
+
if (isMobile) {
|
|
3977
|
+
setMobileView("room");
|
|
3978
|
+
} else {
|
|
3979
|
+
setManagementOpen(false);
|
|
3980
|
+
}
|
|
3981
|
+
};
|
|
3982
|
+
return /* @__PURE__ */ jsxs(
|
|
3983
|
+
"div",
|
|
3984
|
+
{
|
|
3985
|
+
className: cn(
|
|
3986
|
+
"relative flex h-full min-h-[500px]",
|
|
3987
|
+
"bg-theme-surface border border-theme-subtle rounded-sm overflow-hidden",
|
|
3988
|
+
className
|
|
3989
|
+
),
|
|
3990
|
+
children: [
|
|
3991
|
+
chat.error && /* @__PURE__ */ jsx("div", { className: "absolute top-0 left-0 right-0 z-50 px-4 pt-2 animate-in slide-in-from-top-2 duration-[400ms]", children: /* @__PURE__ */ jsx(
|
|
3992
|
+
Alert,
|
|
3993
|
+
{
|
|
3994
|
+
variant: "error",
|
|
3995
|
+
title: "Erro",
|
|
3996
|
+
description: chat.error,
|
|
3997
|
+
className: "py-3 text-xs"
|
|
3998
|
+
}
|
|
3999
|
+
) }),
|
|
4000
|
+
!isMobile && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
4001
|
+
/* @__PURE__ */ jsx("div", { className: "w-[260px] shrink-0", children: /* @__PURE__ */ jsx(
|
|
4002
|
+
ChatSidebar,
|
|
4003
|
+
{
|
|
4004
|
+
rooms: chat.rooms,
|
|
4005
|
+
activeRoomId: chat.activeRoomId,
|
|
4006
|
+
loading: chat.loadingRooms,
|
|
4007
|
+
onSelectRoom: chat.selectRoom,
|
|
4008
|
+
onCreateRoom: () => setCreateDialogOpen(true)
|
|
4009
|
+
}
|
|
4010
|
+
) }),
|
|
4011
|
+
/* @__PURE__ */ jsx("div", { className: "flex-1", children: /* @__PURE__ */ jsx(
|
|
4012
|
+
ChatMessages,
|
|
4013
|
+
{
|
|
4014
|
+
room: activeRoom,
|
|
4015
|
+
messages: chat.messages,
|
|
4016
|
+
loading: chat.loadingMessages,
|
|
4017
|
+
typingUsers: chat.typingUsers,
|
|
4018
|
+
currentUserId: userId,
|
|
4019
|
+
permissions: chat.permissions,
|
|
4020
|
+
onSendMessage: chat.sendMessage,
|
|
4021
|
+
onEditMessage: chat.editMessage,
|
|
4022
|
+
onDeleteMessage: chat.deleteMessage,
|
|
4023
|
+
onReactToMessage: chat.reactToMessage,
|
|
4024
|
+
onTyping: chat.sendTyping,
|
|
4025
|
+
onStopTyping: chat.sendTyping,
|
|
4026
|
+
onOpenManagement: () => setManagementOpen(true),
|
|
4027
|
+
isMobile: false,
|
|
4028
|
+
mentionUsers: workspaceMembers,
|
|
4029
|
+
mentionProjects: MOCK_PROJECTS,
|
|
4030
|
+
hasMore: chat.hasMoreMessages,
|
|
4031
|
+
onLoadMore: chat.loadMoreMessages,
|
|
4032
|
+
loadingMore: chat.loadingMoreMessages
|
|
4033
|
+
}
|
|
4034
|
+
) }),
|
|
4035
|
+
managementOpen && activeRoom && /* @__PURE__ */ jsx("div", { className: "w-[300px] shrink-0 border-l border-theme-subtle bg-theme-glass", children: /* @__PURE__ */ jsx(
|
|
4036
|
+
RoomManagementPanel,
|
|
4037
|
+
{
|
|
4038
|
+
room: activeRoom,
|
|
4039
|
+
members: chat.roomMembers,
|
|
4040
|
+
currentUserId: userId,
|
|
4041
|
+
permissions: chat.permissions,
|
|
4042
|
+
onClose: () => setManagementOpen(false),
|
|
4043
|
+
onUpdateRoom: chat.updateRoom,
|
|
4044
|
+
onArchiveRoom: chat.archiveRoom,
|
|
4045
|
+
onLeaveRoom: chat.leaveRoom,
|
|
4046
|
+
onRemoveMember: chat.removeMember,
|
|
4047
|
+
onUpdateMemberRole: chat.updateMemberRole
|
|
4048
|
+
}
|
|
4049
|
+
) })
|
|
4050
|
+
] }),
|
|
4051
|
+
isMobile && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
4052
|
+
mobileView === "sidebar" && /* @__PURE__ */ jsx("div", { className: "w-full animate-in fade-in-0 slide-in-from-left-5 duration-[400ms]", children: /* @__PURE__ */ jsx(
|
|
4053
|
+
ChatSidebar,
|
|
4054
|
+
{
|
|
4055
|
+
rooms: chat.rooms,
|
|
4056
|
+
activeRoomId: chat.activeRoomId,
|
|
4057
|
+
loading: chat.loadingRooms,
|
|
4058
|
+
onSelectRoom: handleRoomSelect,
|
|
4059
|
+
onCreateRoom: () => setCreateDialogOpen(true)
|
|
4060
|
+
}
|
|
4061
|
+
) }),
|
|
4062
|
+
mobileView === "room" && activeRoom && /* @__PURE__ */ jsx("div", { className: "w-full animate-in fade-in-0 slide-in-from-right-5 duration-[400ms]", children: /* @__PURE__ */ jsx(
|
|
4063
|
+
ChatMessages,
|
|
4064
|
+
{
|
|
4065
|
+
room: activeRoom,
|
|
4066
|
+
messages: chat.messages,
|
|
4067
|
+
loading: chat.loadingMessages,
|
|
4068
|
+
typingUsers: chat.typingUsers,
|
|
4069
|
+
currentUserId: userId,
|
|
4070
|
+
permissions: chat.permissions,
|
|
4071
|
+
onSendMessage: chat.sendMessage,
|
|
4072
|
+
onEditMessage: chat.editMessage,
|
|
4073
|
+
onDeleteMessage: chat.deleteMessage,
|
|
4074
|
+
onReactToMessage: chat.reactToMessage,
|
|
4075
|
+
onTyping: chat.sendTyping,
|
|
4076
|
+
onStopTyping: chat.sendTyping,
|
|
4077
|
+
onOpenManagement: handleOpenManagement,
|
|
4078
|
+
onBack: handleBackToSidebar,
|
|
4079
|
+
isMobile: true,
|
|
4080
|
+
mentionUsers: workspaceMembers,
|
|
4081
|
+
mentionProjects: MOCK_PROJECTS,
|
|
4082
|
+
hasMore: chat.hasMoreMessages,
|
|
4083
|
+
onLoadMore: chat.loadMoreMessages,
|
|
4084
|
+
loadingMore: chat.loadingMoreMessages
|
|
4085
|
+
}
|
|
4086
|
+
) }),
|
|
4087
|
+
mobileView === "management" && activeRoom && /* @__PURE__ */ jsxs("div", { className: "absolute inset-0 z-50 flex", children: [
|
|
4088
|
+
/* @__PURE__ */ jsx(
|
|
4089
|
+
"div",
|
|
4090
|
+
{
|
|
4091
|
+
className: "flex-1 bg-black/50 backdrop-blur-sm",
|
|
4092
|
+
onClick: handleCloseManagement
|
|
4093
|
+
}
|
|
4094
|
+
),
|
|
4095
|
+
/* @__PURE__ */ jsx("div", { className: "w-[85%] max-w-sm h-full bg-theme-glass-heavy backdrop-blur-2xl border-l border-theme-subtle animate-in slide-in-from-right-5 duration-[400ms]", children: /* @__PURE__ */ jsx(
|
|
4096
|
+
RoomManagementPanel,
|
|
4097
|
+
{
|
|
4098
|
+
room: activeRoom,
|
|
4099
|
+
members: chat.roomMembers,
|
|
4100
|
+
currentUserId: userId,
|
|
4101
|
+
permissions: chat.permissions,
|
|
4102
|
+
onClose: handleCloseManagement,
|
|
4103
|
+
onUpdateRoom: chat.updateRoom,
|
|
4104
|
+
onArchiveRoom: chat.archiveRoom,
|
|
4105
|
+
onLeaveRoom: chat.leaveRoom,
|
|
4106
|
+
onRemoveMember: chat.removeMember,
|
|
4107
|
+
onUpdateMemberRole: chat.updateMemberRole
|
|
4108
|
+
}
|
|
4109
|
+
) })
|
|
4110
|
+
] })
|
|
4111
|
+
] }),
|
|
4112
|
+
/* @__PURE__ */ jsx(
|
|
4113
|
+
CreateRoomDialog,
|
|
4114
|
+
{
|
|
4115
|
+
open: createDialogOpen,
|
|
4116
|
+
onOpenChange: setCreateDialogOpen,
|
|
4117
|
+
onCreateChannel: async (data) => {
|
|
4118
|
+
const room = await chat.createRoom({
|
|
4119
|
+
name: data.name,
|
|
4120
|
+
type: "CHANNEL",
|
|
4121
|
+
visibility: data.visibility,
|
|
4122
|
+
description: data.description,
|
|
4123
|
+
memberIds: data.memberIds
|
|
4124
|
+
});
|
|
4125
|
+
if (room) chat.selectRoom(room.id);
|
|
4126
|
+
return room;
|
|
4127
|
+
},
|
|
4128
|
+
onCreateDM: async (targetUserId) => {
|
|
4129
|
+
const room = await chat.createDirectMessage(targetUserId);
|
|
4130
|
+
if (room) chat.selectRoom(room.id);
|
|
4131
|
+
return room;
|
|
4132
|
+
},
|
|
4133
|
+
workspaceMembers
|
|
4134
|
+
}
|
|
4135
|
+
)
|
|
4136
|
+
]
|
|
4137
|
+
}
|
|
4138
|
+
);
|
|
4139
|
+
}
|
|
4140
|
+
|
|
4141
|
+
export { Alert, Button, ChatMessages, ChatSidebar, CreateRoomDialog, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, Drawer, DrawerClose, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerOverlay, DrawerPortal, DrawerTitle, DrawerTrigger, Input, Label, MOCK_PROJECTS, MOCK_USERS, MentionPopover, MessageBubble, MessageInput, OrgLoader, OrgLoaderInline, OrganifyChat, ResponsiveDialog, RoomManagementPanel, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator, Tabs, TabsContent, TabsList, TabsTrigger, Textarea, TypingIndicatorMock, alertVariants, buttonVariants, generateAutoReplies, getMockMentionOptions, getRoomPermissions, inputVariants, orgLoaderVariants, typingIndicator, useChat };
|
|
4142
|
+
//# sourceMappingURL=chunk-2Y3W6JSG.js.map
|
|
4143
|
+
//# sourceMappingURL=chunk-2Y3W6JSG.js.map
|