organify-ui 0.2.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,2359 @@
1
+ import { cn, TooltipProvider, Tooltip, TooltipTrigger, TooltipContent, ScrollArea, Skeleton, Avatar, AvatarFallback, Badge, useOrganifyApi, useOrganifyUser, useOrganifyWorkspace } from './chunk-WQKUJRYK.js';
2
+ import { OrgPlus, OrgSearch, OrgComment, OrgTeam, OrgGlobe, OrgLock, OrgCheckCircle, OrgError, OrgWarning, OrgInfo, OrgClose, OrgChevronDown, OrgChevronRight, OrgEdit, OrgDoor, OrgTrash, OrgChevronLeft } from './chunk-KGSUBEDA.js';
3
+ import * as React5 from 'react';
4
+ import { useState, useRef, 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 * as DialogPrimitive from '@radix-ui/react-dialog';
9
+ import { ChevronDown, ChevronUp, Check, XIcon } from 'lucide-react';
10
+ import * as TabsPrimitive from '@radix-ui/react-tabs';
11
+ import * as SeparatorPrimitive from '@radix-ui/react-separator';
12
+ import * as SelectPrimitive from '@radix-ui/react-select';
13
+
14
+ var orgLoaderVariants = cva("relative inline-flex items-center justify-center", {
15
+ variants: {
16
+ size: {
17
+ xs: "h-4 w-4",
18
+ sm: "h-6 w-6",
19
+ default: "h-10 w-10",
20
+ lg: "h-16 w-16",
21
+ xl: "h-24 w-24",
22
+ full: "h-32 w-32"
23
+ }
24
+ },
25
+ defaultVariants: {
26
+ size: "default"
27
+ }
28
+ });
29
+ var diamondSizes = {
30
+ xs: 3,
31
+ sm: 4,
32
+ default: 6,
33
+ lg: 8,
34
+ xl: 10,
35
+ full: 14
36
+ };
37
+ var strokeWidths = {
38
+ xs: 1.5,
39
+ sm: 1.5,
40
+ default: 2,
41
+ lg: 2,
42
+ xl: 2.5,
43
+ full: 2.5
44
+ };
45
+ function OrgLoader({
46
+ size = "default",
47
+ caption,
48
+ overlay = false,
49
+ className,
50
+ ...props
51
+ }) {
52
+ const s = size ?? "default";
53
+ const d = diamondSizes[s];
54
+ const sw = strokeWidths[s];
55
+ const loader = /* @__PURE__ */ jsxs(
56
+ "div",
57
+ {
58
+ className: cn(
59
+ "flex flex-col items-center gap-3",
60
+ overlay && "pointer-events-none",
61
+ className
62
+ ),
63
+ role: "status",
64
+ "aria-label": "Loading",
65
+ ...props,
66
+ children: [
67
+ /* @__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: [
68
+ /* @__PURE__ */ jsx(
69
+ "circle",
70
+ {
71
+ cx: "24",
72
+ cy: "24",
73
+ r: "18",
74
+ className: "stroke-current opacity-10 dark:opacity-[0.08]",
75
+ strokeWidth: sw
76
+ }
77
+ ),
78
+ /* @__PURE__ */ jsx(
79
+ "circle",
80
+ {
81
+ cx: "24",
82
+ cy: "24",
83
+ r: "18",
84
+ stroke: "url(#org-loader-gradient)",
85
+ strokeWidth: sw,
86
+ strokeLinecap: "round",
87
+ strokeDasharray: "85 28.27",
88
+ className: "origin-center",
89
+ style: { animation: "org-loader-spin 1.4s cubic-bezier(0.4, 0, 0.2, 1) infinite" }
90
+ }
91
+ ),
92
+ /* @__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(
93
+ "rect",
94
+ {
95
+ x: 24 - d / 2,
96
+ y: 6 - d / 2,
97
+ width: d,
98
+ height: d,
99
+ rx: d * 0.15,
100
+ transform: `rotate(45 24 6)`,
101
+ fill: "url(#org-loader-diamond)",
102
+ className: "drop-shadow-[0_0_6px_rgba(79,57,246,0.5)]"
103
+ }
104
+ ) }),
105
+ /* @__PURE__ */ jsx(
106
+ "circle",
107
+ {
108
+ cx: "24",
109
+ cy: "24",
110
+ r: "2",
111
+ className: "fill-current opacity-20",
112
+ style: { animation: "org-loader-pulse 1.4s ease-in-out infinite" }
113
+ }
114
+ ),
115
+ /* @__PURE__ */ jsxs("defs", { children: [
116
+ /* @__PURE__ */ jsxs("linearGradient", { id: "org-loader-gradient", x1: "6", y1: "6", x2: "42", y2: "42", gradientUnits: "userSpaceOnUse", children: [
117
+ /* @__PURE__ */ jsx("stop", { stopColor: "#4F39F6" }),
118
+ /* @__PURE__ */ jsx("stop", { offset: "0.5", stopColor: "#5b42f2" }),
119
+ /* @__PURE__ */ jsx("stop", { offset: "1", stopColor: "#241979" })
120
+ ] }),
121
+ /* @__PURE__ */ jsxs("linearGradient", { id: "org-loader-diamond", x1: "0", y1: "0", x2: "1", y2: "1", children: [
122
+ /* @__PURE__ */ jsx("stop", { stopColor: "#4F39F6" }),
123
+ /* @__PURE__ */ jsx("stop", { offset: "1", stopColor: "#241979" })
124
+ ] })
125
+ ] })
126
+ ] }) }),
127
+ caption && /* @__PURE__ */ jsx("span", { className: cn(
128
+ "font-mono uppercase tracking-widest text-current opacity-40",
129
+ s === "xs" || s === "sm" ? "text-[8px]" : "text-[10px]"
130
+ ), children: caption })
131
+ ]
132
+ }
133
+ );
134
+ if (overlay) {
135
+ 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 });
136
+ }
137
+ return loader;
138
+ }
139
+ function OrgLoaderInline({ className, ...props }) {
140
+ return /* @__PURE__ */ jsxs(
141
+ "svg",
142
+ {
143
+ className: cn("h-4 w-4", className),
144
+ viewBox: "0 0 48 48",
145
+ fill: "none",
146
+ xmlns: "http://www.w3.org/2000/svg",
147
+ role: "status",
148
+ "aria-label": "Loading",
149
+ ...props,
150
+ children: [
151
+ /* @__PURE__ */ jsx("circle", { cx: "24", cy: "24", r: "18", className: "stroke-current opacity-10", strokeWidth: "2" }),
152
+ /* @__PURE__ */ jsx(
153
+ "circle",
154
+ {
155
+ cx: "24",
156
+ cy: "24",
157
+ r: "18",
158
+ stroke: "currentColor",
159
+ strokeWidth: "2",
160
+ strokeLinecap: "round",
161
+ strokeDasharray: "85 28.27",
162
+ className: "origin-center",
163
+ style: { animation: "org-loader-spin 1.4s cubic-bezier(0.4, 0, 0.2, 1) infinite" }
164
+ }
165
+ ),
166
+ /* @__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" }) })
167
+ ]
168
+ }
169
+ );
170
+ }
171
+ var buttonVariants = cva(
172
+ "inline-flex items-center justify-center gap-2 whitespace-nowrap text-sm font-medium tracking-wide uppercase transition-all duration-300 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",
173
+ {
174
+ variants: {
175
+ variant: {
176
+ default: "bg-primary text-white hover:bg-primary-light shadow-glow-primary clip-geo",
177
+ secondary: "bg-transparent border border-white/20 text-white hover:text-white hover:border-primary-light hover:bg-white/5 clip-geo-inv",
178
+ ghost: "bg-transparent text-white/70 hover:bg-white/5 hover:text-white",
179
+ destructive: "bg-error text-white hover:bg-error/90 shadow-glow-error clip-geo",
180
+ outline: "bg-transparent border border-white/20 text-white hover:bg-white/5 hover:border-white/40",
181
+ link: "text-primary-light underline-offset-4 hover:underline",
182
+ // ─── Cream Light theme buttons ─────────────
183
+ cream: "bg-primary text-white hover:shadow-cream-glow hover:translate-y-[-1px] rounded-lg",
184
+ "cream-secondary": "bg-transparent border border-primary text-primary hover:bg-primary/[0.03] rounded-lg"
185
+ },
186
+ size: {
187
+ default: "px-8 py-3",
188
+ sm: "px-4 py-2 text-xs",
189
+ lg: "px-10 py-4 text-base",
190
+ icon: "h-10 w-10",
191
+ "icon-sm": "h-8 w-8",
192
+ "icon-lg": "h-12 w-12"
193
+ }
194
+ },
195
+ defaultVariants: {
196
+ variant: "default",
197
+ size: "default"
198
+ }
199
+ }
200
+ );
201
+ var Button = React5.forwardRef(
202
+ ({ className, variant, size, asChild = false, loading = false, children, disabled, ...props }, ref) => {
203
+ const Comp = asChild ? Slot : "button";
204
+ return /* @__PURE__ */ jsx(
205
+ Comp,
206
+ {
207
+ className: cn(buttonVariants({ variant, size, className })),
208
+ ref,
209
+ disabled: disabled || loading,
210
+ ...props,
211
+ children: loading ? /* @__PURE__ */ jsxs(Fragment, { children: [
212
+ /* @__PURE__ */ jsx(OrgLoaderInline, { className: "shrink-0" }),
213
+ children
214
+ ] }) : children
215
+ }
216
+ );
217
+ }
218
+ );
219
+ Button.displayName = "Button";
220
+ var inputVariants = cva(
221
+ "flex w-full bg-white/5 backdrop-blur-md border border-white/10 px-4 py-3 text-sm font-light text-white transition-all duration-200 file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-white/20 focus-visible:outline-none disabled:cursor-not-allowed disabled:opacity-50",
222
+ {
223
+ variants: {
224
+ variant: {
225
+ default: "clip-geo-br focus:ring-1 focus:ring-primary-light focus:border-primary-light/50",
226
+ flat: "rounded-none focus:ring-1 focus:ring-primary-light focus:border-primary-light/50",
227
+ rounded: "rounded-lg focus:ring-1 focus:ring-primary-light focus:border-primary-light/50",
228
+ error: "border-error/30 bg-error/5 text-red-100 placeholder:text-red-500/30 focus:border-error focus:ring-1 focus:ring-error/20 clip-geo-br",
229
+ // ─── Cream Light theme ─────────────────────
230
+ cream: "bg-white/40 border-black/5 text-neutral-900 placeholder:text-neutral-400 rounded-lg focus:border-primary focus:bg-white/80 focus:shadow-[0_0_0_1px_rgba(36,25,121,0.05)] backdrop-blur-sm"
231
+ }
232
+ },
233
+ defaultVariants: {
234
+ variant: "default"
235
+ }
236
+ }
237
+ );
238
+ var Input = React5.forwardRef(
239
+ ({ className, variant, type, label, error, labelPosition = "left", ...props }, ref) => {
240
+ const id = React5.useId();
241
+ const effectiveVariant = error ? "error" : variant;
242
+ return /* @__PURE__ */ jsxs("div", { className: "relative group", children: [
243
+ label && /* @__PURE__ */ jsx(
244
+ "label",
245
+ {
246
+ htmlFor: id,
247
+ className: cn(
248
+ "absolute -top-3 z-10 px-2 text-label uppercase tracking-widest",
249
+ effectiveVariant === "cream" ? "bg-cream-base" : "bg-surface",
250
+ labelPosition === "left" ? "left-0 ml-2" : "right-0 mr-2",
251
+ error ? "text-error" : "text-primary-light"
252
+ ),
253
+ children: label
254
+ }
255
+ ),
256
+ /* @__PURE__ */ jsx(
257
+ "input",
258
+ {
259
+ id,
260
+ type,
261
+ className: cn(inputVariants({ variant: effectiveVariant, className })),
262
+ ref,
263
+ "aria-invalid": !!error,
264
+ "aria-describedby": error ? `${id}-error` : void 0,
265
+ ...props
266
+ }
267
+ ),
268
+ effectiveVariant !== "cream" && /* @__PURE__ */ jsx("div", { className: "pointer-events-none absolute bottom-0 right-0 h-4 w-4 border-b border-r border-white/20" }),
269
+ 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 }) })
270
+ ] });
271
+ }
272
+ );
273
+ Input.displayName = "Input";
274
+ function getRoomIcon(room) {
275
+ if (room.type === "DIRECT") return /* @__PURE__ */ jsx(OrgComment, { className: "w-4 h-4" });
276
+ if (room.visibility === "PRIVATE") return /* @__PURE__ */ jsx(OrgLock, { className: "w-3.5 h-3.5" });
277
+ return /* @__PURE__ */ jsx("span", { className: "text-sm font-medium", children: "#" });
278
+ }
279
+ function ChatSidebar({
280
+ rooms,
281
+ activeRoomId,
282
+ loading,
283
+ onSelectRoom,
284
+ onCreateRoom,
285
+ onMobileClose
286
+ }) {
287
+ const [search, setSearch] = React5.useState("");
288
+ const [channelsOpen, setChannelsOpen] = React5.useState(true);
289
+ const [dmsOpen, setDmsOpen] = React5.useState(true);
290
+ const filtered = React5.useMemo(
291
+ () => search ? rooms.filter(
292
+ (r) => r.name?.toLowerCase().includes(search.toLowerCase()) || r.slug?.toLowerCase().includes(search.toLowerCase())
293
+ ) : rooms,
294
+ [rooms, search]
295
+ );
296
+ const channels = filtered.filter((r) => r.type === "CHANNEL");
297
+ const dms = filtered.filter((r) => r.type === "DIRECT");
298
+ const handleSelect = (roomId) => {
299
+ onSelectRoom(roomId);
300
+ onMobileClose?.();
301
+ };
302
+ const renderRoom = (room) => /* @__PURE__ */ jsxs(
303
+ "button",
304
+ {
305
+ onClick: () => handleSelect(room.id),
306
+ className: cn(
307
+ "flex items-center gap-2.5 w-full px-2.5 py-2 rounded-md text-left text-sm transition-all duration-150",
308
+ "active:scale-[0.98]",
309
+ room.id === activeRoomId ? "bg-primary/12 text-primary-light" : "text-white/70 hover:bg-white/4 hover:text-white"
310
+ ),
311
+ children: [
312
+ /* @__PURE__ */ jsx(
313
+ "span",
314
+ {
315
+ className: cn(
316
+ "flex items-center justify-center h-7 w-7 rounded-md text-sm shrink-0",
317
+ room.id === activeRoomId ? "bg-primary/20 text-primary-light" : "bg-white/8 text-white/50"
318
+ ),
319
+ children: getRoomIcon(room)
320
+ }
321
+ ),
322
+ /* @__PURE__ */ jsx("div", { className: "flex-1 min-w-0", children: /* @__PURE__ */ jsx("div", { className: "font-medium text-[13px] truncate", children: room.name || "Sem nome" }) }),
323
+ room.unreadCount > 0 && /* @__PURE__ */ jsx(
324
+ Badge,
325
+ {
326
+ variant: "primary",
327
+ 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-200",
328
+ children: room.unreadCount > 99 ? "99+" : room.unreadCount
329
+ }
330
+ )
331
+ ]
332
+ },
333
+ room.id
334
+ );
335
+ const renderSection = (title, items, isOpen, toggle) => /* @__PURE__ */ jsxs("div", { className: "mb-1", children: [
336
+ /* @__PURE__ */ jsxs(
337
+ "button",
338
+ {
339
+ onClick: toggle,
340
+ className: "flex items-center gap-1 w-full px-2.5 py-1.5 text-[11px] font-semibold uppercase tracking-wider text-white/30 hover:text-white/50 transition-colors",
341
+ children: [
342
+ isOpen ? /* @__PURE__ */ jsx(OrgChevronDown, { className: "w-3 h-3" }) : /* @__PURE__ */ jsx(OrgChevronRight, { className: "w-3 h-3" }),
343
+ /* @__PURE__ */ jsx("span", { children: title }),
344
+ /* @__PURE__ */ jsx("span", { className: "ml-auto text-[10px] font-normal", children: items.length })
345
+ ]
346
+ }
347
+ ),
348
+ isOpen && /* @__PURE__ */ jsx("div", { className: "space-y-0.5 animate-in fade-in-0 slide-in-from-top-1 duration-200", children: items.map(renderRoom) })
349
+ ] });
350
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col h-full border-r border-white/8 bg-surface", children: [
351
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between px-4 py-3 border-b border-white/8", children: [
352
+ /* @__PURE__ */ jsx("h3", { className: "text-sm font-semibold text-white", children: "Chat" }),
353
+ /* @__PURE__ */ jsx(TooltipProvider, { children: /* @__PURE__ */ jsxs(Tooltip, { children: [
354
+ /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
355
+ Button,
356
+ {
357
+ variant: "ghost",
358
+ size: "sm",
359
+ onClick: onCreateRoom,
360
+ className: "h-7 w-7 p-0 text-white/50 hover:text-primary-light hover:bg-primary/10 transition-colors",
361
+ children: /* @__PURE__ */ jsx(OrgPlus, { className: "w-4 h-4" })
362
+ }
363
+ ) }),
364
+ /* @__PURE__ */ jsx(TooltipContent, { children: "Nova conversa" })
365
+ ] }) })
366
+ ] }),
367
+ /* @__PURE__ */ jsx("div", { className: "px-3 py-2", children: /* @__PURE__ */ jsxs("div", { className: "relative", children: [
368
+ /* @__PURE__ */ jsx(OrgSearch, { className: "absolute left-2.5 top-1/2 -translate-y-1/2 w-3.5 h-3.5 text-white/25" }),
369
+ /* @__PURE__ */ jsx(
370
+ Input,
371
+ {
372
+ variant: "flat",
373
+ placeholder: "Pesquisar...",
374
+ value: search,
375
+ onChange: (e) => setSearch(e.target.value),
376
+ className: "h-8 text-xs bg-white/5 border-white/8 rounded-md pl-8"
377
+ }
378
+ )
379
+ ] }) }),
380
+ /* @__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: [
381
+ /* @__PURE__ */ jsx(Skeleton, { variant: "rounded", className: "h-7 w-7 shrink-0" }),
382
+ /* @__PURE__ */ jsx("div", { className: "flex-1 space-y-1.5", children: /* @__PURE__ */ jsx(Skeleton, { className: "h-3 w-24" }) })
383
+ ] }, i)) }) : filtered.length === 0 ? /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center py-8 text-white/30", children: [
384
+ /* @__PURE__ */ jsx(OrgSearch, { className: "w-6 h-6 mb-2 opacity-40" }),
385
+ /* @__PURE__ */ jsx("p", { className: "text-xs", children: search ? "Sem resultados" : "Sem conversas" }),
386
+ !search && /* @__PURE__ */ jsx(
387
+ "button",
388
+ {
389
+ onClick: onCreateRoom,
390
+ className: "mt-3 text-xs text-primary-light/70 hover:text-primary-light transition-colors",
391
+ children: "Criar primeira conversa \u2192"
392
+ }
393
+ )
394
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
395
+ channels.length > 0 && renderSection("Canais", channels, channelsOpen, () => setChannelsOpen(!channelsOpen)),
396
+ dms.length > 0 && renderSection("Mensagens Diretas", dms, dmsOpen, () => setDmsOpen(!dmsOpen))
397
+ ] }) }) })
398
+ ] });
399
+ }
400
+ function formatTime(iso) {
401
+ return new Date(iso).toLocaleTimeString("pt-BR", {
402
+ hour: "2-digit",
403
+ minute: "2-digit"
404
+ });
405
+ }
406
+ function formatDateSeparator(iso) {
407
+ const date = new Date(iso);
408
+ const today = /* @__PURE__ */ new Date();
409
+ const yesterday = new Date(today);
410
+ yesterday.setDate(yesterday.getDate() - 1);
411
+ if (date.toDateString() === today.toDateString()) return "Hoje";
412
+ if (date.toDateString() === yesterday.toDateString()) return "Ontem";
413
+ return date.toLocaleDateString("pt-BR", { day: "numeric", month: "long" });
414
+ }
415
+ function getInitials(name) {
416
+ if (!name) return "??";
417
+ return name.split(" ").map((w) => w[0]).slice(0, 2).join("").toUpperCase();
418
+ }
419
+ function groupReactions(reactions) {
420
+ const map = /* @__PURE__ */ new Map();
421
+ reactions.forEach((r) => map.set(r.emoji, (map.get(r.emoji) || 0) + 1));
422
+ return Array.from(map.entries());
423
+ }
424
+ function shouldShowDateSeparator(messages, index) {
425
+ if (index === 0) return true;
426
+ const curr = new Date(messages[index].createdAt).toDateString();
427
+ const prev = new Date(messages[index - 1].createdAt).toDateString();
428
+ return curr !== prev;
429
+ }
430
+ function MessageItem({ msg }) {
431
+ const isPending = msg._status === "pending";
432
+ const isError = msg._status === "error";
433
+ return /* @__PURE__ */ jsxs(
434
+ "div",
435
+ {
436
+ className: cn(
437
+ "group flex gap-3 px-2 py-1.5 rounded-md transition-colors hover:bg-white/[0.03]",
438
+ isPending && "opacity-60",
439
+ isError && "border-l-2 border-rose-500/50"
440
+ ),
441
+ children: [
442
+ /* @__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(msg.authorName || msg.authorId) }) }),
443
+ /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
444
+ /* @__PURE__ */ jsxs("div", { className: "flex items-baseline gap-2 mb-0.5", children: [
445
+ /* @__PURE__ */ jsx("span", { className: "text-[13px] font-semibold text-white", children: msg.authorName || msg.authorId.substring(0, 8) }),
446
+ /* @__PURE__ */ jsx("span", { className: "text-[11px] text-white/30", children: formatTime(msg.createdAt) }),
447
+ msg.edited && /* @__PURE__ */ jsx("span", { className: "text-[10px] text-white/25 italic", children: "(editado)" }),
448
+ isPending && /* @__PURE__ */ jsx("span", { className: "text-[10px] text-white/25 italic", children: "enviando..." }),
449
+ isError && /* @__PURE__ */ jsx("span", { className: "text-[10px] text-rose-400", children: "erro ao enviar \u2014 clique para reenviar" })
450
+ ] }),
451
+ /* @__PURE__ */ jsx("p", { className: "text-[13px] text-white/80 leading-relaxed break-words whitespace-pre-wrap", children: msg.content }),
452
+ msg.reactions.length > 0 && /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1 mt-1", children: groupReactions(msg.reactions).map(([emoji, count]) => /* @__PURE__ */ jsx(TooltipProvider, { children: /* @__PURE__ */ jsxs(Tooltip, { children: [
453
+ /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxs("button", { className: "inline-flex items-center gap-1 px-1.5 py-0.5 rounded-full text-xs bg-white/5 border border-white/8 hover:bg-primary/10 transition-colors", children: [
454
+ /* @__PURE__ */ jsx("span", { children: emoji }),
455
+ /* @__PURE__ */ jsx("span", { className: "text-white/50", children: count })
456
+ ] }) }),
457
+ /* @__PURE__ */ jsxs(TooltipContent, { children: [
458
+ emoji,
459
+ " \u2014 ",
460
+ count,
461
+ " rea\xE7\xE3o",
462
+ count > 1 ? "\xF5es" : ""
463
+ ] })
464
+ ] }) }, emoji)) })
465
+ ] })
466
+ ]
467
+ }
468
+ );
469
+ }
470
+ function TypingIndicator({ users }) {
471
+ if (users.length === 0) return null;
472
+ 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`;
473
+ return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 px-5 py-1 animate-in fade-in-0 duration-200", children: [
474
+ /* @__PURE__ */ jsx("div", { className: "flex gap-0.5", children: [0, 1, 2].map((i) => /* @__PURE__ */ jsx(
475
+ "span",
476
+ {
477
+ className: "w-1.5 h-1.5 rounded-full bg-primary-light/50 animate-bounce",
478
+ style: { animationDelay: `${i * 150}ms` }
479
+ },
480
+ i
481
+ )) }),
482
+ /* @__PURE__ */ jsx("span", { className: "text-[11px] text-white/30", children: text })
483
+ ] });
484
+ }
485
+ function ChatMessages({
486
+ room,
487
+ messages,
488
+ loading,
489
+ typingUsers = [],
490
+ currentUserId,
491
+ permissions,
492
+ onSendMessage,
493
+ onTyping,
494
+ onStopTyping,
495
+ onOpenManagement,
496
+ onBack,
497
+ isMobile
498
+ }) {
499
+ const [inputValue, setInputValue] = React5.useState("");
500
+ const scrollRef = React5.useRef(null);
501
+ React5.useEffect(() => {
502
+ requestAnimationFrame(() => {
503
+ const el = scrollRef.current;
504
+ if (el) el.scrollTop = el.scrollHeight;
505
+ });
506
+ }, [messages]);
507
+ const handleSend = () => {
508
+ if (!inputValue.trim()) return;
509
+ onSendMessage(inputValue);
510
+ setInputValue("");
511
+ };
512
+ const handleKeyDown = (e) => {
513
+ if (e.key === "Enter" && !e.shiftKey) {
514
+ e.preventDefault();
515
+ handleSend();
516
+ }
517
+ };
518
+ const handleInputChange = (e) => {
519
+ setInputValue(e.target.value);
520
+ onTyping?.();
521
+ };
522
+ if (!room) {
523
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-1 flex-col items-center justify-center text-center px-6", children: [
524
+ /* @__PURE__ */ jsx("div", { className: "w-16 h-16 rounded-2xl bg-primary/10 flex items-center justify-center mb-4", children: /* @__PURE__ */ jsx(OrgComment, { className: "w-8 h-8 text-primary-light/60" }) }),
525
+ /* @__PURE__ */ jsx("h3", { className: "text-lg font-medium text-white mb-2", children: "Selecione uma conversa" }),
526
+ /* @__PURE__ */ jsx("p", { className: "text-sm text-white/40 max-w-[240px]", children: "Escolha um canal ou mensagem direta para come\xE7ar a conversar." })
527
+ ] });
528
+ }
529
+ return /* @__PURE__ */ jsxs("div", { className: "flex flex-col h-full overflow-hidden relative", children: [
530
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 px-4 md:px-5 py-3 border-b border-white/8", children: [
531
+ isMobile && onBack && /* @__PURE__ */ jsx(
532
+ "button",
533
+ {
534
+ onClick: onBack,
535
+ className: "p-1.5 rounded-md text-white/50 hover:text-white hover:bg-white/5 transition-colors",
536
+ 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" }) })
537
+ }
538
+ ),
539
+ /* @__PURE__ */ jsx(
540
+ "span",
541
+ {
542
+ className: cn(
543
+ "flex items-center justify-center h-8 w-8 rounded-md text-sm",
544
+ "bg-primary/15 text-primary-light"
545
+ ),
546
+ children: room.type === "DIRECT" ? /* @__PURE__ */ jsx(OrgComment, { className: "w-4 h-4" }) : "#"
547
+ }
548
+ ),
549
+ /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
550
+ /* @__PURE__ */ jsx("h3", { className: "text-[15px] font-semibold text-white truncate", children: room.name || "Sem nome" }),
551
+ /* @__PURE__ */ jsxs("p", { className: "text-[11px] text-white/30 truncate", children: [
552
+ room.memberCount,
553
+ " membro",
554
+ room.memberCount !== 1 ? "s" : ""
555
+ ] })
556
+ ] }),
557
+ onOpenManagement && /* @__PURE__ */ jsx("div", { className: "flex items-center gap-1", children: /* @__PURE__ */ jsx(TooltipProvider, { children: /* @__PURE__ */ jsxs(Tooltip, { children: [
558
+ /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
559
+ Button,
560
+ {
561
+ variant: "ghost",
562
+ size: "sm",
563
+ onClick: onOpenManagement,
564
+ className: "h-8 w-8 p-0 text-white/40 hover:text-white",
565
+ children: /* @__PURE__ */ jsx(OrgTeam, { className: "w-4 h-4" })
566
+ }
567
+ ) }),
568
+ /* @__PURE__ */ jsx(TooltipContent, { children: "Detalhes do canal" })
569
+ ] }) }) })
570
+ ] }),
571
+ /* @__PURE__ */ jsx(
572
+ "div",
573
+ {
574
+ ref: scrollRef,
575
+ className: "flex-1 overflow-y-auto px-3 md:px-5 py-4 space-y-0.5",
576
+ children: 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: [
577
+ /* @__PURE__ */ jsx(Skeleton, { variant: "circular", className: "h-8 w-8 shrink-0" }),
578
+ /* @__PURE__ */ jsxs("div", { className: "flex-1 space-y-1.5", children: [
579
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-3 w-32" }),
580
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-3 w-full" }),
581
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-3 w-3/4" })
582
+ ] })
583
+ ] }, i)) }) : messages.length === 0 ? /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center justify-center h-full", children: [
584
+ /* @__PURE__ */ jsx("div", { className: "w-12 h-12 rounded-xl bg-primary/10 flex items-center justify-center mb-3", children: /* @__PURE__ */ jsx(OrgComment, { className: "w-6 h-6 text-primary-light/50" }) }),
585
+ /* @__PURE__ */ jsx("p", { className: "text-sm text-white/40", children: "Ainda sem mensagens." }),
586
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-white/25 mt-1", children: "Comece a conversa!" })
587
+ ] }) : messages.map((msg, idx) => /* @__PURE__ */ jsxs(React5.Fragment, { children: [
588
+ shouldShowDateSeparator(messages, idx) && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3 py-3", children: [
589
+ /* @__PURE__ */ jsx("div", { className: "flex-1 h-px bg-white/8" }),
590
+ /* @__PURE__ */ jsx("span", { className: "text-[11px] text-white/25 font-medium", children: formatDateSeparator(msg.createdAt) }),
591
+ /* @__PURE__ */ jsx("div", { className: "flex-1 h-px bg-white/8" })
592
+ ] }),
593
+ /* @__PURE__ */ jsx(MessageItem, { msg })
594
+ ] }, msg.id))
595
+ }
596
+ ),
597
+ /* @__PURE__ */ jsx(TypingIndicator, { users: typingUsers }),
598
+ /* @__PURE__ */ jsx("div", { className: "px-3 md:px-5 py-3 border-t border-white/8", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 bg-white/5 border border-white/8 rounded-lg px-3 py-2 focus-within:border-primary/50 transition-colors", children: [
599
+ /* @__PURE__ */ jsx(
600
+ "input",
601
+ {
602
+ type: "text",
603
+ value: inputValue,
604
+ onChange: handleInputChange,
605
+ onKeyDown: handleKeyDown,
606
+ placeholder: "Escreva uma mensagem...",
607
+ autoComplete: "off",
608
+ className: "flex-1 bg-transparent border-none outline-none text-[13px] text-white placeholder:text-white/25"
609
+ }
610
+ ),
611
+ /* @__PURE__ */ jsx(
612
+ Button,
613
+ {
614
+ variant: "default",
615
+ size: "sm",
616
+ onClick: handleSend,
617
+ disabled: !inputValue.trim(),
618
+ className: "h-7 px-3 text-xs",
619
+ children: "Enviar"
620
+ }
621
+ )
622
+ ] }) })
623
+ ] });
624
+ }
625
+ function Label({ className, ...props }) {
626
+ return /* @__PURE__ */ jsx(
627
+ "label",
628
+ {
629
+ "data-slot": "label",
630
+ className: cn(
631
+ "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70",
632
+ className
633
+ ),
634
+ ...props
635
+ }
636
+ );
637
+ }
638
+ function Textarea({ className, ...props }) {
639
+ return /* @__PURE__ */ jsx(
640
+ "textarea",
641
+ {
642
+ "data-slot": "textarea",
643
+ className: cn(
644
+ "file:text-foreground placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 border-input min-h-[80px] w-full min-w-0 rounded-md border bg-transparent px-3 py-2 text-base shadow-xs transition-[color,box-shadow] outline-none file:inline-flex file:h-7 file:border-0 file:bg-transparent file:text-sm file:font-medium disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50 md:text-sm resize-none text-foreground dark:text-white",
645
+ "focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px]",
646
+ "aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
647
+ className
648
+ ),
649
+ ...props
650
+ }
651
+ );
652
+ }
653
+ function Dialog({ ...props }) {
654
+ return /* @__PURE__ */ jsx(DialogPrimitive.Root, { "data-slot": "dialog", ...props });
655
+ }
656
+ function DialogTrigger({ ...props }) {
657
+ return /* @__PURE__ */ jsx(DialogPrimitive.Trigger, { "data-slot": "dialog-trigger", ...props });
658
+ }
659
+ function DialogPortal({ ...props }) {
660
+ return /* @__PURE__ */ jsx(DialogPrimitive.Portal, { "data-slot": "dialog-portal", ...props });
661
+ }
662
+ function DialogClose({ ...props }) {
663
+ return /* @__PURE__ */ jsx(DialogPrimitive.Close, { "data-slot": "dialog-close", ...props });
664
+ }
665
+ function DialogOverlay({
666
+ className,
667
+ ...props
668
+ }) {
669
+ return /* @__PURE__ */ jsx(
670
+ DialogPrimitive.Overlay,
671
+ {
672
+ "data-slot": "dialog-overlay",
673
+ className: cn(
674
+ "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",
675
+ className
676
+ ),
677
+ ...props
678
+ }
679
+ );
680
+ }
681
+ function DialogContent({
682
+ className,
683
+ children,
684
+ showCloseButton = true,
685
+ ...props
686
+ }) {
687
+ return /* @__PURE__ */ jsxs(DialogPortal, { "data-slot": "dialog-portal", children: [
688
+ /* @__PURE__ */ jsx(DialogOverlay, {}),
689
+ /* @__PURE__ */ jsxs(
690
+ DialogPrimitive.Content,
691
+ {
692
+ "data-slot": "dialog-content",
693
+ className: cn(
694
+ "bg-background 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-lg border p-6 shadow-lg duration-200 sm:max-w-lg",
695
+ className
696
+ ),
697
+ ...props,
698
+ children: [
699
+ children,
700
+ showCloseButton && /* @__PURE__ */ jsxs(
701
+ DialogPrimitive.Close,
702
+ {
703
+ "data-slot": "dialog-close",
704
+ 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",
705
+ children: [
706
+ /* @__PURE__ */ jsx(XIcon, {}),
707
+ /* @__PURE__ */ jsx("span", { className: "sr-only", children: "Close" })
708
+ ]
709
+ }
710
+ )
711
+ ]
712
+ }
713
+ )
714
+ ] });
715
+ }
716
+ function DialogHeader({ className, ...props }) {
717
+ return /* @__PURE__ */ jsx(
718
+ "div",
719
+ {
720
+ "data-slot": "dialog-header",
721
+ className: cn("flex flex-col gap-2 text-center sm:text-left", className),
722
+ ...props
723
+ }
724
+ );
725
+ }
726
+ function DialogFooter({ className, ...props }) {
727
+ return /* @__PURE__ */ jsx(
728
+ "div",
729
+ {
730
+ "data-slot": "dialog-footer",
731
+ className: cn("flex flex-col-reverse gap-2 sm:flex-row sm:justify-end", className),
732
+ ...props
733
+ }
734
+ );
735
+ }
736
+ function DialogTitle({
737
+ className,
738
+ ...props
739
+ }) {
740
+ return /* @__PURE__ */ jsx(
741
+ DialogPrimitive.Title,
742
+ {
743
+ "data-slot": "dialog-title",
744
+ className: cn("text-lg leading-none font-semibold", className),
745
+ ...props
746
+ }
747
+ );
748
+ }
749
+ function DialogDescription({
750
+ className,
751
+ ...props
752
+ }) {
753
+ return /* @__PURE__ */ jsx(
754
+ DialogPrimitive.Description,
755
+ {
756
+ "data-slot": "dialog-description",
757
+ className: cn("text-muted-foreground text-sm", className),
758
+ ...props
759
+ }
760
+ );
761
+ }
762
+ var Tabs = TabsPrimitive.Root;
763
+ function TabsList({
764
+ className,
765
+ ...props
766
+ }) {
767
+ return /* @__PURE__ */ jsx(
768
+ TabsPrimitive.List,
769
+ {
770
+ "data-slot": "tabs-list",
771
+ className: cn(
772
+ "inline-flex h-9 items-center justify-center rounded-lg bg-muted p-1 text-muted-foreground",
773
+ className
774
+ ),
775
+ ...props
776
+ }
777
+ );
778
+ }
779
+ function TabsTrigger({
780
+ className,
781
+ ...props
782
+ }) {
783
+ return /* @__PURE__ */ jsx(
784
+ TabsPrimitive.Trigger,
785
+ {
786
+ "data-slot": "tabs-trigger",
787
+ className: cn(
788
+ "inline-flex items-center justify-center whitespace-nowrap rounded-md px-3 py-1 text-sm font-medium ring-offset-background transition-all 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-background data-[state=active]:text-foreground data-[state=active]:shadow",
789
+ className
790
+ ),
791
+ ...props
792
+ }
793
+ );
794
+ }
795
+ function TabsContent({
796
+ className,
797
+ ...props
798
+ }) {
799
+ return /* @__PURE__ */ jsx(
800
+ TabsPrimitive.Content,
801
+ {
802
+ "data-slot": "tabs-content",
803
+ className: cn(
804
+ "mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
805
+ className
806
+ ),
807
+ ...props
808
+ }
809
+ );
810
+ }
811
+ function getInitials2(name) {
812
+ return name.split(" ").map((w) => w[0]).slice(0, 2).join("").toUpperCase();
813
+ }
814
+ function CreateRoomDialog({
815
+ open,
816
+ onOpenChange,
817
+ onCreateChannel,
818
+ onCreateDM,
819
+ workspaceMembers,
820
+ loadingMembers
821
+ }) {
822
+ const [tab, setTab] = React5.useState("channel");
823
+ const [creating, setCreating] = React5.useState(false);
824
+ const [channelName, setChannelName] = React5.useState("");
825
+ const [channelDesc, setChannelDesc] = React5.useState("");
826
+ const [visibility, setVisibility] = React5.useState("WORKSPACE");
827
+ const [selectedMembers, setSelectedMembers] = React5.useState([]);
828
+ const [showAdvanced, setShowAdvanced] = React5.useState(false);
829
+ const [dmSearch, setDmSearch] = React5.useState("");
830
+ const [nameError, setNameError] = React5.useState("");
831
+ const validateName = (value) => {
832
+ if (!value.trim()) {
833
+ setNameError("Nome do canal \xE9 obrigat\xF3rio");
834
+ return false;
835
+ }
836
+ if (value.length < 2) {
837
+ setNameError("Nome deve ter pelo menos 2 caracteres");
838
+ return false;
839
+ }
840
+ if (!/^[a-zA-Z0-9À-ÿ\s\-_]+$/.test(value)) {
841
+ setNameError("Apenas letras, n\xFAmeros, espa\xE7os e h\xEDfens");
842
+ return false;
843
+ }
844
+ setNameError("");
845
+ return true;
846
+ };
847
+ const filteredMembers = React5.useMemo(() => {
848
+ if (!dmSearch) return workspaceMembers;
849
+ const q = dmSearch.toLowerCase();
850
+ return workspaceMembers.filter(
851
+ (m) => m.displayName.toLowerCase().includes(q) || m.email?.toLowerCase().includes(q)
852
+ );
853
+ }, [workspaceMembers, dmSearch]);
854
+ const handleCreateChannel = async () => {
855
+ if (!validateName(channelName)) return;
856
+ setCreating(true);
857
+ try {
858
+ const result = await onCreateChannel({
859
+ name: channelName.trim(),
860
+ description: channelDesc.trim() || void 0,
861
+ visibility,
862
+ memberIds: selectedMembers
863
+ });
864
+ if (result) {
865
+ resetForm();
866
+ onOpenChange(false);
867
+ }
868
+ } finally {
869
+ setCreating(false);
870
+ }
871
+ };
872
+ const handleCreateDM = async (userId) => {
873
+ setCreating(true);
874
+ try {
875
+ const result = await onCreateDM(userId);
876
+ if (result) {
877
+ resetForm();
878
+ onOpenChange(false);
879
+ }
880
+ } finally {
881
+ setCreating(false);
882
+ }
883
+ };
884
+ const toggleMember = (userId) => {
885
+ setSelectedMembers(
886
+ (prev) => prev.includes(userId) ? prev.filter((id) => id !== userId) : [...prev, userId]
887
+ );
888
+ };
889
+ const resetForm = () => {
890
+ setChannelName("");
891
+ setChannelDesc("");
892
+ setVisibility("WORKSPACE");
893
+ setSelectedMembers([]);
894
+ setShowAdvanced(false);
895
+ setDmSearch("");
896
+ setNameError("");
897
+ setTab("channel");
898
+ };
899
+ return /* @__PURE__ */ jsx(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxs(DialogContent, { className: "sm:max-w-md bg-surface border-white/10", children: [
900
+ /* @__PURE__ */ jsxs(DialogHeader, { children: [
901
+ /* @__PURE__ */ jsx(DialogTitle, { className: "text-white", children: "Nova conversa" }),
902
+ /* @__PURE__ */ jsx(DialogDescription, { className: "text-white/50", children: "Crie um canal para a equipa ou inicie uma conversa direta." })
903
+ ] }),
904
+ /* @__PURE__ */ jsxs(Tabs, { value: tab, onValueChange: (v) => setTab(v), children: [
905
+ /* @__PURE__ */ jsxs(TabsList, { className: "grid w-full grid-cols-2 bg-white/5", children: [
906
+ /* @__PURE__ */ jsxs(
907
+ TabsTrigger,
908
+ {
909
+ value: "channel",
910
+ className: "text-xs data-[state=active]:bg-primary/20 data-[state=active]:text-primary-light",
911
+ children: [
912
+ /* @__PURE__ */ jsx(OrgTeam, { className: "w-3.5 h-3.5 mr-1.5" }),
913
+ "Canal"
914
+ ]
915
+ }
916
+ ),
917
+ /* @__PURE__ */ jsxs(
918
+ TabsTrigger,
919
+ {
920
+ value: "dm",
921
+ className: "text-xs data-[state=active]:bg-primary/20 data-[state=active]:text-primary-light",
922
+ children: [
923
+ /* @__PURE__ */ jsx(OrgComment, { className: "w-3.5 h-3.5 mr-1.5" }),
924
+ "Mensagem Direta"
925
+ ]
926
+ }
927
+ )
928
+ ] }),
929
+ /* @__PURE__ */ jsxs(TabsContent, { value: "channel", className: "space-y-4 mt-4", children: [
930
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
931
+ /* @__PURE__ */ jsx(Label, { className: "text-white/70 text-xs", children: "Nome do canal" }),
932
+ /* @__PURE__ */ jsx(
933
+ Input,
934
+ {
935
+ variant: "flat",
936
+ placeholder: "ex: equipa-design",
937
+ value: channelName,
938
+ onChange: (e) => {
939
+ setChannelName(e.target.value);
940
+ if (nameError) validateName(e.target.value);
941
+ },
942
+ onBlur: () => channelName && validateName(channelName),
943
+ className: cn(
944
+ "h-9 text-sm bg-white/5 border-white/8",
945
+ nameError && "border-rose-500/50 focus:border-rose-500"
946
+ ),
947
+ autoFocus: true
948
+ }
949
+ ),
950
+ nameError && /* @__PURE__ */ jsx("p", { className: "text-[11px] text-rose-400 animate-in fade-in-0 slide-in-from-top-1 duration-200", children: nameError })
951
+ ] }),
952
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
953
+ /* @__PURE__ */ jsxs(
954
+ "button",
955
+ {
956
+ type: "button",
957
+ onClick: () => setVisibility("WORKSPACE"),
958
+ className: cn(
959
+ "flex-1 flex items-center gap-2 px-3 py-2 rounded-md text-xs transition-all duration-200 border",
960
+ visibility === "WORKSPACE" ? "bg-primary/15 border-primary/30 text-primary-light" : "bg-white/3 border-white/8 text-white/50 hover:bg-white/5"
961
+ ),
962
+ children: [
963
+ /* @__PURE__ */ jsx(OrgGlobe, { className: "w-3.5 h-3.5" }),
964
+ "P\xFAblico"
965
+ ]
966
+ }
967
+ ),
968
+ /* @__PURE__ */ jsxs(
969
+ "button",
970
+ {
971
+ type: "button",
972
+ onClick: () => setVisibility("PRIVATE"),
973
+ className: cn(
974
+ "flex-1 flex items-center gap-2 px-3 py-2 rounded-md text-xs transition-all duration-200 border",
975
+ visibility === "PRIVATE" ? "bg-primary/15 border-primary/30 text-primary-light" : "bg-white/3 border-white/8 text-white/50 hover:bg-white/5"
976
+ ),
977
+ children: [
978
+ /* @__PURE__ */ jsx(OrgLock, { className: "w-3.5 h-3.5" }),
979
+ "Privado"
980
+ ]
981
+ }
982
+ )
983
+ ] }),
984
+ !showAdvanced ? /* @__PURE__ */ jsx(
985
+ "button",
986
+ {
987
+ type: "button",
988
+ onClick: () => setShowAdvanced(true),
989
+ className: "text-xs text-primary-light/70 hover:text-primary-light transition-colors",
990
+ children: "+ Adicionar descri\xE7\xE3o e membros"
991
+ }
992
+ ) : /* @__PURE__ */ jsxs("div", { className: "space-y-4 animate-in fade-in-0 slide-in-from-top-2 duration-300", children: [
993
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
994
+ /* @__PURE__ */ jsx(Label, { className: "text-white/70 text-xs", children: "Descri\xE7\xE3o (opcional)" }),
995
+ /* @__PURE__ */ jsx(
996
+ Textarea,
997
+ {
998
+ placeholder: "Sobre o que \xE9 este canal...",
999
+ value: channelDesc,
1000
+ onChange: (e) => setChannelDesc(e.target.value),
1001
+ className: "min-h-[60px] text-sm bg-white/5 border-white/8 text-white"
1002
+ }
1003
+ )
1004
+ ] }),
1005
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
1006
+ /* @__PURE__ */ jsxs(Label, { className: "text-white/70 text-xs", children: [
1007
+ "Adicionar membros (",
1008
+ selectedMembers.length,
1009
+ " selecionados)"
1010
+ ] }),
1011
+ /* @__PURE__ */ jsx(ScrollArea, { className: "max-h-[150px] rounded-md border border-white/8 bg-white/3", 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: [
1012
+ /* @__PURE__ */ jsx(Skeleton, { variant: "circular", className: "h-7 w-7" }),
1013
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-3 w-24" })
1014
+ ] }, i)) : workspaceMembers.length === 0 ? /* @__PURE__ */ jsx("p", { className: "text-xs text-white/30 p-2 text-center", children: "Sem membros dispon\xEDveis" }) : workspaceMembers.map((member) => /* @__PURE__ */ jsxs(
1015
+ "button",
1016
+ {
1017
+ type: "button",
1018
+ onClick: () => toggleMember(member.id),
1019
+ className: cn(
1020
+ "flex items-center gap-2 w-full p-2 rounded-md text-left text-xs transition-colors",
1021
+ selectedMembers.includes(member.id) ? "bg-primary/15 text-primary-light" : "text-white/70 hover:bg-white/5"
1022
+ ),
1023
+ children: [
1024
+ /* @__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: getInitials2(member.displayName) }) }),
1025
+ /* @__PURE__ */ jsx("span", { className: "flex-1 truncate", children: member.displayName }),
1026
+ selectedMembers.includes(member.id) && /* @__PURE__ */ jsx(Badge, { variant: "primary", className: "text-[9px] px-1 py-0 h-4", children: "\u2713" })
1027
+ ]
1028
+ },
1029
+ member.id
1030
+ )) }) })
1031
+ ] })
1032
+ ] }),
1033
+ /* @__PURE__ */ jsxs(DialogFooter, { children: [
1034
+ /* @__PURE__ */ jsx(
1035
+ Button,
1036
+ {
1037
+ variant: "ghost",
1038
+ size: "sm",
1039
+ onClick: () => onOpenChange(false),
1040
+ className: "text-xs text-white/50",
1041
+ children: "Cancelar"
1042
+ }
1043
+ ),
1044
+ /* @__PURE__ */ jsx(
1045
+ Button,
1046
+ {
1047
+ variant: "default",
1048
+ size: "sm",
1049
+ onClick: handleCreateChannel,
1050
+ disabled: !channelName.trim() || creating,
1051
+ className: "text-xs",
1052
+ children: creating ? "Criando..." : "Criar Canal"
1053
+ }
1054
+ )
1055
+ ] })
1056
+ ] }),
1057
+ /* @__PURE__ */ jsxs(TabsContent, { value: "dm", className: "space-y-3 mt-4", children: [
1058
+ /* @__PURE__ */ jsxs("div", { className: "relative", children: [
1059
+ /* @__PURE__ */ jsx(OrgSearch, { className: "absolute left-3 top-1/2 -translate-y-1/2 w-3.5 h-3.5 text-white/30" }),
1060
+ /* @__PURE__ */ jsx(
1061
+ Input,
1062
+ {
1063
+ variant: "flat",
1064
+ placeholder: "Pesquisar membro...",
1065
+ value: dmSearch,
1066
+ onChange: (e) => setDmSearch(e.target.value),
1067
+ className: "h-9 text-sm bg-white/5 border-white/8 pl-9",
1068
+ autoFocus: true
1069
+ }
1070
+ )
1071
+ ] }),
1072
+ /* @__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: [
1073
+ /* @__PURE__ */ jsx(Skeleton, { variant: "circular", className: "h-8 w-8" }),
1074
+ /* @__PURE__ */ jsxs("div", { className: "flex-1 space-y-1.5", children: [
1075
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-3 w-28" }),
1076
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-2 w-20" })
1077
+ ] })
1078
+ ] }, i)) : filteredMembers.length === 0 ? /* @__PURE__ */ jsxs("div", { className: "flex flex-col items-center py-8 text-white/30", children: [
1079
+ /* @__PURE__ */ jsx(OrgSearch, { className: "w-8 h-8 mb-2 opacity-50" }),
1080
+ /* @__PURE__ */ jsx("p", { className: "text-xs", children: dmSearch ? "Nenhum membro encontrado" : "Sem membros no workspace" })
1081
+ ] }) : filteredMembers.map((member) => /* @__PURE__ */ jsxs(
1082
+ "button",
1083
+ {
1084
+ onClick: () => handleCreateDM(member.id),
1085
+ disabled: creating,
1086
+ className: "flex items-center gap-3 w-full p-2.5 rounded-md text-left transition-all duration-200 hover:bg-white/5 active:scale-[0.98] disabled:opacity-50",
1087
+ children: [
1088
+ /* @__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: getInitials2(member.displayName) }) }),
1089
+ /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
1090
+ /* @__PURE__ */ jsx("p", { className: "text-[13px] font-medium text-white truncate", children: member.displayName }),
1091
+ member.email && /* @__PURE__ */ jsx("p", { className: "text-[11px] text-white/30 truncate", children: member.email })
1092
+ ] }),
1093
+ member.status && /* @__PURE__ */ jsx(
1094
+ "span",
1095
+ {
1096
+ className: cn(
1097
+ "w-2 h-2 rounded-full flex-shrink-0",
1098
+ member.status === "ONLINE" && "bg-emerald-400 animate-pulse",
1099
+ member.status === "AWAY" && "bg-amber-400",
1100
+ member.status === "BUSY" && "bg-rose-400",
1101
+ member.status === "OFFLINE" && "bg-white/20"
1102
+ )
1103
+ }
1104
+ )
1105
+ ]
1106
+ },
1107
+ member.id
1108
+ )) }) })
1109
+ ] })
1110
+ ] })
1111
+ ] }) });
1112
+ }
1113
+ function Separator({
1114
+ className,
1115
+ orientation = "horizontal",
1116
+ decorative = true,
1117
+ ...props
1118
+ }) {
1119
+ return /* @__PURE__ */ jsx(
1120
+ SeparatorPrimitive.Root,
1121
+ {
1122
+ "data-slot": "separator",
1123
+ decorative,
1124
+ orientation,
1125
+ className: cn(
1126
+ "bg-border shrink-0 data-[orientation=horizontal]:h-px data-[orientation=horizontal]:w-full data-[orientation=vertical]:h-full data-[orientation=vertical]:w-px",
1127
+ className
1128
+ ),
1129
+ ...props
1130
+ }
1131
+ );
1132
+ }
1133
+ var Select = SelectPrimitive.Root;
1134
+ var SelectGroup = SelectPrimitive.Group;
1135
+ var SelectValue = SelectPrimitive.Value;
1136
+ var SelectTrigger = React5.forwardRef(({ className, children, label, ...props }, ref) => /* @__PURE__ */ jsxs("div", { className: "relative group", children: [
1137
+ label && /* @__PURE__ */ jsx("span", { className: "absolute -top-3 right-0 z-10 mr-2 bg-card-surface px-2 text-label uppercase tracking-widest text-white/40", children: label }),
1138
+ /* @__PURE__ */ jsxs(
1139
+ SelectPrimitive.Trigger,
1140
+ {
1141
+ ref,
1142
+ className: cn(
1143
+ "flex w-full items-center justify-between bg-white/5 backdrop-blur-md border border-white/10 px-4 py-3 text-sm font-light text-white/80 transition-all clip-geo-br focus:outline-none focus:ring-1 focus:ring-primary-light/50 focus:border-primary-light/50 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",
1144
+ className
1145
+ ),
1146
+ ...props,
1147
+ children: [
1148
+ children,
1149
+ /* @__PURE__ */ jsx(SelectPrimitive.Icon, { asChild: true, children: /* @__PURE__ */ jsx(ChevronDown, { className: "h-4 w-4 text-primary-light opacity-60" }) })
1150
+ ]
1151
+ }
1152
+ )
1153
+ ] }));
1154
+ SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
1155
+ var SelectScrollUpButton = React5.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
1156
+ SelectPrimitive.ScrollUpButton,
1157
+ {
1158
+ ref,
1159
+ className: cn("flex cursor-default items-center justify-center py-1", className),
1160
+ ...props,
1161
+ children: /* @__PURE__ */ jsx(ChevronUp, { className: "h-4 w-4 text-white/40" })
1162
+ }
1163
+ ));
1164
+ SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName;
1165
+ var SelectScrollDownButton = React5.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
1166
+ SelectPrimitive.ScrollDownButton,
1167
+ {
1168
+ ref,
1169
+ className: cn("flex cursor-default items-center justify-center py-1", className),
1170
+ ...props,
1171
+ children: /* @__PURE__ */ jsx(ChevronDown, { className: "h-4 w-4 text-white/40" })
1172
+ }
1173
+ ));
1174
+ SelectScrollDownButton.displayName = SelectPrimitive.ScrollDownButton.displayName;
1175
+ var SelectContent = React5.forwardRef(({ className, children, position = "popper", ...props }, ref) => /* @__PURE__ */ jsx(SelectPrimitive.Portal, { children: /* @__PURE__ */ jsxs(
1176
+ SelectPrimitive.Content,
1177
+ {
1178
+ ref,
1179
+ className: cn(
1180
+ "glass-panel relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-none border border-white/10 bg-card-surface/95 backdrop-blur-2xl text-white shadow-glass-xl 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",
1181
+ 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",
1182
+ className
1183
+ ),
1184
+ position,
1185
+ ...props,
1186
+ children: [
1187
+ /* @__PURE__ */ jsx(SelectScrollUpButton, {}),
1188
+ /* @__PURE__ */ jsx(
1189
+ SelectPrimitive.Viewport,
1190
+ {
1191
+ className: cn(
1192
+ "p-1",
1193
+ position === "popper" && "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]"
1194
+ ),
1195
+ children
1196
+ }
1197
+ ),
1198
+ /* @__PURE__ */ jsx(SelectScrollDownButton, {})
1199
+ ]
1200
+ }
1201
+ ) }));
1202
+ SelectContent.displayName = SelectPrimitive.Content.displayName;
1203
+ var SelectItem = React5.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(
1204
+ SelectPrimitive.Item,
1205
+ {
1206
+ ref,
1207
+ className: cn(
1208
+ "relative flex w-full cursor-default select-none items-center py-2 pl-8 pr-2 text-sm font-light text-white/80 outline-none transition-colors focus:bg-white/5 focus:text-white data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
1209
+ className
1210
+ ),
1211
+ ...props,
1212
+ children: [
1213
+ /* @__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" }) }) }),
1214
+ /* @__PURE__ */ jsx(SelectPrimitive.ItemText, { children })
1215
+ ]
1216
+ }
1217
+ ));
1218
+ SelectItem.displayName = SelectPrimitive.Item.displayName;
1219
+ var SelectSeparator = React5.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
1220
+ SelectPrimitive.Separator,
1221
+ {
1222
+ ref,
1223
+ className: cn("-mx-1 my-1 h-px bg-white/5", className),
1224
+ ...props
1225
+ }
1226
+ ));
1227
+ SelectSeparator.displayName = SelectPrimitive.Separator.displayName;
1228
+ var SelectLabel = React5.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
1229
+ SelectPrimitive.Label,
1230
+ {
1231
+ ref,
1232
+ className: cn("py-1.5 pl-8 pr-2 text-label uppercase tracking-widest text-white/30", className),
1233
+ ...props
1234
+ }
1235
+ ));
1236
+ SelectLabel.displayName = SelectPrimitive.Label.displayName;
1237
+ function getInitials3(name) {
1238
+ return name.split(" ").map((w) => w[0]).slice(0, 2).join("").toUpperCase();
1239
+ }
1240
+ var roleBadgeColors = {
1241
+ OWNER: "bg-amber-500/15 text-amber-300 border-amber-500/30",
1242
+ ADMIN: "bg-blue-500/15 text-blue-300 border-blue-500/30",
1243
+ MEMBER: "bg-white/5 text-white/40 border-white/10"
1244
+ };
1245
+ var roleLabels = {
1246
+ OWNER: "Dono",
1247
+ ADMIN: "Admin",
1248
+ MEMBER: "Membro"
1249
+ };
1250
+ function RoomManagementPanel({
1251
+ room,
1252
+ members,
1253
+ permissions,
1254
+ currentUserId,
1255
+ loading,
1256
+ onClose,
1257
+ onUpdateRoom,
1258
+ onArchiveRoom,
1259
+ onLeaveRoom,
1260
+ onRemoveMember,
1261
+ onUpdateMemberRole,
1262
+ className
1263
+ }) {
1264
+ const [view, setView] = React5.useState("info");
1265
+ const [editName, setEditName] = React5.useState(room.name || "");
1266
+ const [editDesc, setEditDesc] = React5.useState(room.description || "");
1267
+ const [editVisibility, setEditVisibility] = React5.useState(room.visibility);
1268
+ const [saving, setSaving] = React5.useState(false);
1269
+ const [confirmArchive, setConfirmArchive] = React5.useState(false);
1270
+ React5.useEffect(() => {
1271
+ setEditName(room.name || "");
1272
+ setEditDesc(room.description || "");
1273
+ setEditVisibility(room.visibility);
1274
+ }, [room]);
1275
+ const handleSave = async () => {
1276
+ setSaving(true);
1277
+ try {
1278
+ await onUpdateRoom(room.id, {
1279
+ name: editName.trim(),
1280
+ description: editDesc.trim() || void 0,
1281
+ visibility: editVisibility
1282
+ });
1283
+ setView("info");
1284
+ } finally {
1285
+ setSaving(false);
1286
+ }
1287
+ };
1288
+ const renderHeader = (title, showBack) => /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 px-4 py-3 border-b border-white/8", children: [
1289
+ showBack && /* @__PURE__ */ jsx(
1290
+ "button",
1291
+ {
1292
+ onClick: () => setView("info"),
1293
+ className: "p-1 rounded-md hover:bg-white/5 text-white/50 hover:text-white transition-colors",
1294
+ children: /* @__PURE__ */ jsx(OrgChevronLeft, { className: "w-4 h-4" })
1295
+ }
1296
+ ),
1297
+ /* @__PURE__ */ jsx("h3", { className: "flex-1 text-sm font-semibold text-white", children: title }),
1298
+ /* @__PURE__ */ jsx(
1299
+ "button",
1300
+ {
1301
+ onClick: onClose,
1302
+ className: "p-1 rounded-md hover:bg-white/5 text-white/40 hover:text-white transition-colors",
1303
+ children: /* @__PURE__ */ jsx(OrgClose, { className: "w-4 h-4" })
1304
+ }
1305
+ )
1306
+ ] });
1307
+ const renderInfo = () => /* @__PURE__ */ jsxs(Fragment, { children: [
1308
+ renderHeader(room.name || "Detalhes"),
1309
+ /* @__PURE__ */ jsxs("div", { className: "p-4 space-y-4", children: [
1310
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
1311
+ /* @__PURE__ */ jsx(
1312
+ "div",
1313
+ {
1314
+ className: cn(
1315
+ "flex items-center justify-center h-12 w-12 rounded-lg text-lg",
1316
+ "bg-primary/15 text-primary-light"
1317
+ ),
1318
+ children: room.type === "DIRECT" ? "\u{1F4AC}" : "#"
1319
+ }
1320
+ ),
1321
+ /* @__PURE__ */ jsxs("div", { children: [
1322
+ /* @__PURE__ */ jsx("h4", { className: "text-[15px] font-semibold text-white", children: room.name || "Sem nome" }),
1323
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 mt-0.5", children: [
1324
+ /* @__PURE__ */ jsx(
1325
+ Badge,
1326
+ {
1327
+ variant: "default",
1328
+ className: "text-[10px] px-1.5 py-0 h-4 border-white/10 text-white/40",
1329
+ children: room.type === "DIRECT" ? "DM" : room.visibility === "PRIVATE" ? "\u{1F512} Privado" : "\u{1F310} P\xFAblico"
1330
+ }
1331
+ ),
1332
+ /* @__PURE__ */ jsxs("span", { className: "text-[11px] text-white/30", children: [
1333
+ room.memberCount,
1334
+ " membros"
1335
+ ] })
1336
+ ] })
1337
+ ] })
1338
+ ] }),
1339
+ room.description && /* @__PURE__ */ jsx("p", { className: "text-xs text-white/50 leading-relaxed", children: room.description }),
1340
+ /* @__PURE__ */ jsx(Separator, { className: "bg-white/8" }),
1341
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
1342
+ /* @__PURE__ */ jsxs(
1343
+ "button",
1344
+ {
1345
+ onClick: () => setView("members"),
1346
+ className: "flex items-center gap-3 w-full px-3 py-2.5 rounded-md text-sm text-white/70 hover:bg-white/5 hover:text-white transition-colors",
1347
+ children: [
1348
+ /* @__PURE__ */ jsx(OrgTeam, { className: "w-4 h-4 text-white/40" }),
1349
+ /* @__PURE__ */ jsx("span", { className: "flex-1 text-left", children: "Membros" }),
1350
+ /* @__PURE__ */ jsx(Badge, { variant: "default", className: "text-[10px] px-1.5 py-0 h-4 border-white/10 text-white/40", children: members.length })
1351
+ ]
1352
+ }
1353
+ ),
1354
+ permissions.canEditRoomSettings && room.type !== "DIRECT" && /* @__PURE__ */ jsxs(
1355
+ "button",
1356
+ {
1357
+ onClick: () => setView("edit"),
1358
+ className: "flex items-center gap-3 w-full px-3 py-2.5 rounded-md text-sm text-white/70 hover:bg-white/5 hover:text-white transition-colors",
1359
+ children: [
1360
+ /* @__PURE__ */ jsx(OrgEdit, { className: "w-4 h-4 text-white/40" }),
1361
+ /* @__PURE__ */ jsx("span", { className: "flex-1 text-left", children: "Editar canal" })
1362
+ ]
1363
+ }
1364
+ ),
1365
+ /* @__PURE__ */ jsx(Separator, { className: "bg-white/8 my-2" }),
1366
+ /* @__PURE__ */ jsxs(
1367
+ "button",
1368
+ {
1369
+ onClick: () => onLeaveRoom(room.id),
1370
+ className: "flex items-center gap-3 w-full px-3 py-2.5 rounded-md text-sm text-white/50 hover:bg-rose-500/10 hover:text-rose-400 transition-colors",
1371
+ children: [
1372
+ /* @__PURE__ */ jsx(OrgDoor, { className: "w-4 h-4" }),
1373
+ /* @__PURE__ */ jsx("span", { className: "flex-1 text-left", children: "Sair do canal" })
1374
+ ]
1375
+ }
1376
+ ),
1377
+ permissions.canArchiveRoom && /* @__PURE__ */ jsx(Fragment, { children: !confirmArchive ? /* @__PURE__ */ jsxs(
1378
+ "button",
1379
+ {
1380
+ onClick: () => setConfirmArchive(true),
1381
+ className: "flex items-center gap-3 w-full px-3 py-2.5 rounded-md text-sm text-white/50 hover:bg-rose-500/10 hover:text-rose-400 transition-colors",
1382
+ children: [
1383
+ /* @__PURE__ */ jsx(OrgTrash, { className: "w-4 h-4" }),
1384
+ /* @__PURE__ */ jsx("span", { className: "flex-1 text-left", children: "Arquivar canal" })
1385
+ ]
1386
+ }
1387
+ ) : /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 px-3 py-2 rounded-md bg-rose-500/10 border border-rose-500/20 animate-in fade-in-0 duration-200", children: [
1388
+ /* @__PURE__ */ jsx("p", { className: "flex-1 text-xs text-rose-300", children: "Tem certeza?" }),
1389
+ /* @__PURE__ */ jsx(
1390
+ Button,
1391
+ {
1392
+ variant: "ghost",
1393
+ size: "sm",
1394
+ onClick: () => setConfirmArchive(false),
1395
+ className: "h-6 px-2 text-[10px] text-white/50",
1396
+ children: "N\xE3o"
1397
+ }
1398
+ ),
1399
+ /* @__PURE__ */ jsx(
1400
+ Button,
1401
+ {
1402
+ variant: "destructive",
1403
+ size: "sm",
1404
+ onClick: () => onArchiveRoom(room.id),
1405
+ className: "h-6 px-2 text-[10px]",
1406
+ children: "Sim, arquivar"
1407
+ }
1408
+ )
1409
+ ] }) })
1410
+ ] })
1411
+ ] })
1412
+ ] });
1413
+ const renderMembers = () => /* @__PURE__ */ jsxs(Fragment, { children: [
1414
+ renderHeader("Membros", true),
1415
+ /* @__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: [
1416
+ /* @__PURE__ */ jsx(Skeleton, { variant: "circular", className: "h-8 w-8" }),
1417
+ /* @__PURE__ */ jsxs("div", { className: "flex-1 space-y-1", children: [
1418
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-3 w-24" }),
1419
+ /* @__PURE__ */ jsx(Skeleton, { className: "h-2 w-16" })
1420
+ ] })
1421
+ ] }, i)) : members.length === 0 ? /* @__PURE__ */ jsx("p", { className: "text-xs text-white/30 text-center py-8", children: "Sem membros" }) : members.map((member) => /* @__PURE__ */ jsxs(
1422
+ "div",
1423
+ {
1424
+ className: "flex items-center gap-3 p-2 rounded-md hover:bg-white/3 group transition-colors",
1425
+ children: [
1426
+ /* @__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) }) }),
1427
+ /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0", children: [
1428
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
1429
+ /* @__PURE__ */ jsx("span", { className: "text-[13px] font-medium text-white truncate", children: member.displayName }),
1430
+ member.userId === currentUserId && /* @__PURE__ */ jsx("span", { className: "text-[10px] text-white/30", children: "(voc\xEA)" })
1431
+ ] }),
1432
+ /* @__PURE__ */ jsx(
1433
+ Badge,
1434
+ {
1435
+ variant: "default",
1436
+ className: cn(
1437
+ "text-[9px] px-1 py-0 h-3.5 border mt-0.5",
1438
+ roleBadgeColors[member.role]
1439
+ ),
1440
+ children: roleLabels[member.role]
1441
+ }
1442
+ )
1443
+ ] }),
1444
+ 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: [
1445
+ /* @__PURE__ */ jsxs(
1446
+ Select,
1447
+ {
1448
+ value: member.role,
1449
+ onValueChange: (val) => onUpdateMemberRole(room.id, member.userId, val),
1450
+ children: [
1451
+ /* @__PURE__ */ jsx(SelectTrigger, { className: "h-6 w-20 text-[10px] bg-white/5 border-white/10", children: /* @__PURE__ */ jsx(SelectValue, {}) }),
1452
+ /* @__PURE__ */ jsxs(SelectContent, { children: [
1453
+ /* @__PURE__ */ jsx(SelectItem, { value: "ADMIN", children: "Admin" }),
1454
+ /* @__PURE__ */ jsx(SelectItem, { value: "MEMBER", children: "Membro" })
1455
+ ] })
1456
+ ]
1457
+ }
1458
+ ),
1459
+ /* @__PURE__ */ jsx(
1460
+ Button,
1461
+ {
1462
+ variant: "ghost",
1463
+ size: "sm",
1464
+ onClick: () => onRemoveMember(room.id, member.userId),
1465
+ className: "h-6 w-6 p-0 text-white/30 hover:text-rose-400 hover:bg-rose-500/10",
1466
+ children: /* @__PURE__ */ jsx(OrgClose, { className: "w-3 h-3" })
1467
+ }
1468
+ )
1469
+ ] })
1470
+ ]
1471
+ },
1472
+ member.id
1473
+ )) }) })
1474
+ ] });
1475
+ const renderEdit = () => /* @__PURE__ */ jsxs(Fragment, { children: [
1476
+ renderHeader("Editar canal", true),
1477
+ /* @__PURE__ */ jsxs("div", { className: "p-4 space-y-4", children: [
1478
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
1479
+ /* @__PURE__ */ jsx(Label, { className: "text-white/70 text-xs", children: "Nome" }),
1480
+ /* @__PURE__ */ jsx(
1481
+ Input,
1482
+ {
1483
+ variant: "flat",
1484
+ value: editName,
1485
+ onChange: (e) => setEditName(e.target.value),
1486
+ className: "h-9 text-sm bg-white/5 border-white/8"
1487
+ }
1488
+ )
1489
+ ] }),
1490
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
1491
+ /* @__PURE__ */ jsx(Label, { className: "text-white/70 text-xs", children: "Descri\xE7\xE3o" }),
1492
+ /* @__PURE__ */ jsx(
1493
+ Textarea,
1494
+ {
1495
+ value: editDesc,
1496
+ onChange: (e) => setEditDesc(e.target.value),
1497
+ className: "min-h-[60px] text-sm bg-white/5 border-white/8 text-white",
1498
+ placeholder: "Descri\xE7\xE3o do canal..."
1499
+ }
1500
+ )
1501
+ ] }),
1502
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
1503
+ /* @__PURE__ */ jsx(Label, { className: "text-white/70 text-xs", children: "Visibilidade" }),
1504
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
1505
+ /* @__PURE__ */ jsxs(
1506
+ "button",
1507
+ {
1508
+ type: "button",
1509
+ onClick: () => setEditVisibility("WORKSPACE"),
1510
+ className: cn(
1511
+ "flex-1 flex items-center gap-2 px-3 py-2 rounded-md text-xs transition-all duration-200 border",
1512
+ editVisibility === "WORKSPACE" ? "bg-primary/15 border-primary/30 text-primary-light" : "bg-white/3 border-white/8 text-white/50 hover:bg-white/5"
1513
+ ),
1514
+ children: [
1515
+ /* @__PURE__ */ jsx(OrgGlobe, { className: "w-3.5 h-3.5" }),
1516
+ "P\xFAblico"
1517
+ ]
1518
+ }
1519
+ ),
1520
+ /* @__PURE__ */ jsxs(
1521
+ "button",
1522
+ {
1523
+ type: "button",
1524
+ onClick: () => setEditVisibility("PRIVATE"),
1525
+ className: cn(
1526
+ "flex-1 flex items-center gap-2 px-3 py-2 rounded-md text-xs transition-all duration-200 border",
1527
+ editVisibility === "PRIVATE" ? "bg-primary/15 border-primary/30 text-primary-light" : "bg-white/3 border-white/8 text-white/50 hover:bg-white/5"
1528
+ ),
1529
+ children: [
1530
+ /* @__PURE__ */ jsx(OrgLock, { className: "w-3.5 h-3.5" }),
1531
+ "Privado"
1532
+ ]
1533
+ }
1534
+ )
1535
+ ] })
1536
+ ] }),
1537
+ /* @__PURE__ */ jsxs("div", { className: "flex justify-end gap-2 pt-2", children: [
1538
+ /* @__PURE__ */ jsx(
1539
+ Button,
1540
+ {
1541
+ variant: "ghost",
1542
+ size: "sm",
1543
+ onClick: () => setView("info"),
1544
+ className: "text-xs text-white/50",
1545
+ children: "Cancelar"
1546
+ }
1547
+ ),
1548
+ /* @__PURE__ */ jsx(
1549
+ Button,
1550
+ {
1551
+ variant: "default",
1552
+ size: "sm",
1553
+ onClick: handleSave,
1554
+ disabled: !editName.trim() || saving,
1555
+ className: "text-xs",
1556
+ children: saving ? "Salvando..." : "Salvar"
1557
+ }
1558
+ )
1559
+ ] })
1560
+ ] })
1561
+ ] });
1562
+ return /* @__PURE__ */ jsxs(
1563
+ "div",
1564
+ {
1565
+ className: cn(
1566
+ "flex flex-col h-full border-l border-white/8 bg-surface w-[280px] shrink-0",
1567
+ "animate-in slide-in-from-right-5 duration-300",
1568
+ className
1569
+ ),
1570
+ children: [
1571
+ view === "info" && renderInfo(),
1572
+ view === "members" && renderMembers(),
1573
+ view === "edit" && renderEdit()
1574
+ ]
1575
+ }
1576
+ );
1577
+ }
1578
+
1579
+ // src/components/chat/types.ts
1580
+ function getRoomPermissions(role, isWorkspaceAdmin) {
1581
+ const isOwnerOrAdmin = role === "OWNER" || role === "ADMIN" || isWorkspaceAdmin;
1582
+ return {
1583
+ canCreateChannel: true,
1584
+ canCreateDM: true,
1585
+ canDeleteRoom: role === "OWNER" || !!isWorkspaceAdmin,
1586
+ canArchiveRoom: isOwnerOrAdmin ?? false,
1587
+ canManageMembers: isOwnerOrAdmin ?? false,
1588
+ canEditRoomSettings: isOwnerOrAdmin ?? false
1589
+ };
1590
+ }
1591
+
1592
+ // src/components/chat/use-chat.ts
1593
+ function useChat(options = {}) {
1594
+ const {
1595
+ workspaceRole,
1596
+ initialRooms,
1597
+ initialMessages
1598
+ } = options;
1599
+ const { gatewayUrl, authToken } = useOrganifyApi();
1600
+ const user = useOrganifyUser();
1601
+ const { workspace } = useOrganifyWorkspace();
1602
+ const userId = user?.id ?? "";
1603
+ const workspaceId = workspace?.id ?? "";
1604
+ const [rooms, setRooms] = useState(initialRooms ?? []);
1605
+ const [messages, setMessages] = useState(initialMessages ?? []);
1606
+ const [activeRoomId, setActiveRoomId] = useState(null);
1607
+ const [loadingRooms, setLoadingRooms] = useState(false);
1608
+ const [loadingMessages, setLoadingMessages] = useState(false);
1609
+ const [roomMembers, setRoomMembers] = useState([]);
1610
+ const [myRoomRole, setMyRoomRole] = useState(null);
1611
+ const [typingUsers, setTypingUsers] = useState([]);
1612
+ const [error, setError] = useState(null);
1613
+ const wsRef = useRef(null);
1614
+ const typingTimerRef = useRef(void 0);
1615
+ const isWorkspaceAdmin = workspaceRole === "OWNER" || workspaceRole === "ADMIN";
1616
+ const permissions = getRoomPermissions(myRoomRole, isWorkspaceAdmin);
1617
+ const gql = useCallback(
1618
+ async (query, variables) => {
1619
+ const res = await fetch(`${gatewayUrl}/graphql/chat`, {
1620
+ method: "POST",
1621
+ headers: {
1622
+ "Content-Type": "application/json",
1623
+ ...authToken && { Authorization: `Bearer ${authToken}` },
1624
+ "x-user-id": userId
1625
+ },
1626
+ credentials: "include",
1627
+ body: JSON.stringify({ query, variables })
1628
+ });
1629
+ const json = await res.json();
1630
+ if (json.errors?.length) throw new Error(json.errors[0].message);
1631
+ return json.data;
1632
+ },
1633
+ [gatewayUrl, userId, authToken]
1634
+ );
1635
+ const fetchRooms = useCallback(async () => {
1636
+ if (initialRooms && initialRooms.length > 0) return;
1637
+ if (!workspaceId) return;
1638
+ setLoadingRooms(true);
1639
+ setError(null);
1640
+ try {
1641
+ const data = await gql(
1642
+ `query($workspaceId: String!) {
1643
+ rooms(workspaceId: $workspaceId) {
1644
+ items {
1645
+ id name slug type visibility description
1646
+ memberCount unreadCount avatarUrl createdBy
1647
+ archived lastMessageAt
1648
+ }
1649
+ total hasMore
1650
+ }
1651
+ }`,
1652
+ { workspaceId }
1653
+ );
1654
+ setRooms(data.rooms.items);
1655
+ } catch (err) {
1656
+ console.error("[organify-chat] fetchRooms:", err);
1657
+ setError("N\xE3o foi poss\xEDvel carregar as conversas. Tente novamente.");
1658
+ } finally {
1659
+ setLoadingRooms(false);
1660
+ }
1661
+ }, [workspaceId, gql, initialRooms]);
1662
+ const fetchMessages = useCallback(
1663
+ async (roomId) => {
1664
+ if (initialMessages && initialMessages.length > 0) {
1665
+ const roomMessages = initialMessages.filter((m) => m.roomId === roomId);
1666
+ setMessages(roomMessages);
1667
+ return;
1668
+ }
1669
+ setLoadingMessages(true);
1670
+ setError(null);
1671
+ try {
1672
+ const data = await gql(
1673
+ `query($roomId: ID!) {
1674
+ messages(roomId: $roomId) {
1675
+ items {
1676
+ id roomId authorId authorName authorAvatarUrl
1677
+ content edited editedAt createdAt
1678
+ mentions { type targetId display }
1679
+ reactions { emoji userId }
1680
+ }
1681
+ total hasMore nextCursor
1682
+ }
1683
+ }`,
1684
+ { roomId }
1685
+ );
1686
+ setMessages(data.messages.items);
1687
+ } catch (err) {
1688
+ console.error("[organify-chat] fetchMessages:", err);
1689
+ setError("Erro ao carregar mensagens.");
1690
+ } finally {
1691
+ setLoadingMessages(false);
1692
+ }
1693
+ },
1694
+ [gql, initialMessages]
1695
+ );
1696
+ const fetchRoomMembers = useCallback(
1697
+ async (roomId) => {
1698
+ try {
1699
+ const data = await gql(
1700
+ `query($roomId: ID!) {
1701
+ roomMembers(roomId: $roomId) {
1702
+ id userId displayName avatarUrl role joinedAt
1703
+ }
1704
+ }`,
1705
+ { roomId }
1706
+ );
1707
+ setRoomMembers(data.roomMembers);
1708
+ const myMembership = data.roomMembers.find((m) => m.userId === userId);
1709
+ setMyRoomRole(myMembership?.role ?? null);
1710
+ } catch (err) {
1711
+ console.error("[organify-chat] fetchRoomMembers:", err);
1712
+ }
1713
+ },
1714
+ [gql, userId]
1715
+ );
1716
+ const selectRoom = useCallback(
1717
+ (roomId) => {
1718
+ if (activeRoomId === roomId) return;
1719
+ if (activeRoomId && wsRef.current?.readyState === WebSocket.OPEN) {
1720
+ wsRef.current.send(JSON.stringify({ event: "leave_room", data: { roomId: activeRoomId } }));
1721
+ }
1722
+ setActiveRoomId(roomId);
1723
+ setMessages([]);
1724
+ setRoomMembers([]);
1725
+ setMyRoomRole(null);
1726
+ setTypingUsers([]);
1727
+ fetchMessages(roomId);
1728
+ fetchRoomMembers(roomId);
1729
+ if (wsRef.current?.readyState === WebSocket.OPEN) {
1730
+ wsRef.current.send(JSON.stringify({ event: "join_room", data: { roomId } }));
1731
+ }
1732
+ },
1733
+ [activeRoomId, fetchMessages, fetchRoomMembers]
1734
+ );
1735
+ const sendMessage = useCallback(
1736
+ async (content) => {
1737
+ if (!activeRoomId || !content.trim()) return;
1738
+ const tempId = `temp_${Date.now()}_${Math.random().toString(36).slice(2)}`;
1739
+ const optimisticMsg = {
1740
+ id: tempId,
1741
+ roomId: activeRoomId,
1742
+ authorId: userId,
1743
+ content: content.trim(),
1744
+ edited: false,
1745
+ editedAt: null,
1746
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
1747
+ mentions: [],
1748
+ reactions: [],
1749
+ _status: "pending"
1750
+ };
1751
+ setMessages((prev) => [...prev, optimisticMsg]);
1752
+ try {
1753
+ const data = await gql(
1754
+ `mutation($input: SendMessageInput!) {
1755
+ sendMessage(input: $input) {
1756
+ id roomId authorId authorName authorAvatarUrl
1757
+ content edited editedAt createdAt
1758
+ mentions { type targetId display }
1759
+ reactions { emoji userId }
1760
+ }
1761
+ }`,
1762
+ { input: { roomId: activeRoomId, content: content.trim() } }
1763
+ );
1764
+ setMessages(
1765
+ (prev) => prev.map((m) => m.id === tempId ? { ...data.sendMessage, _status: "sent" } : m)
1766
+ );
1767
+ } catch (err) {
1768
+ console.error("[organify-chat] sendMessage:", err);
1769
+ setMessages(
1770
+ (prev) => prev.map((m) => m.id === tempId ? { ...m, _status: "error" } : m)
1771
+ );
1772
+ }
1773
+ },
1774
+ [activeRoomId, gql, userId]
1775
+ );
1776
+ const createRoom = useCallback(
1777
+ async (input) => {
1778
+ setError(null);
1779
+ try {
1780
+ const data = await gql(
1781
+ `mutation($input: CreateRoomInput!) {
1782
+ createRoom(input: $input) {
1783
+ id name slug type visibility description
1784
+ memberCount unreadCount avatarUrl createdBy
1785
+ archived lastMessageAt
1786
+ }
1787
+ }`,
1788
+ {
1789
+ input: {
1790
+ ...input,
1791
+ workspaceId
1792
+ }
1793
+ }
1794
+ );
1795
+ const newRoom = data.createRoom;
1796
+ setRooms((prev) => [newRoom, ...prev]);
1797
+ return newRoom;
1798
+ } catch (err) {
1799
+ console.error("[organify-chat] createRoom:", err);
1800
+ setError(err.message || "N\xE3o foi poss\xEDvel criar a sala.");
1801
+ return null;
1802
+ }
1803
+ },
1804
+ [gql, workspaceId]
1805
+ );
1806
+ const createDirectMessage = useCallback(
1807
+ async (targetUserId) => {
1808
+ setError(null);
1809
+ try {
1810
+ const data = await gql(
1811
+ `mutation($input: CreateDMInput!) {
1812
+ createDirectMessage(input: $input) {
1813
+ id name slug type visibility description
1814
+ memberCount unreadCount avatarUrl createdBy
1815
+ archived lastMessageAt
1816
+ }
1817
+ }`,
1818
+ {
1819
+ input: {
1820
+ workspaceId,
1821
+ targetUserId
1822
+ }
1823
+ }
1824
+ );
1825
+ const dm = data.createDirectMessage;
1826
+ setRooms((prev) => {
1827
+ if (prev.find((r) => r.id === dm.id)) return prev;
1828
+ return [dm, ...prev];
1829
+ });
1830
+ return dm;
1831
+ } catch (err) {
1832
+ console.error("[organify-chat] createDM:", err);
1833
+ setError(err.message || "N\xE3o foi poss\xEDvel iniciar a conversa.");
1834
+ return null;
1835
+ }
1836
+ },
1837
+ [gql, workspaceId]
1838
+ );
1839
+ const updateRoom = useCallback(
1840
+ async (roomId, updates) => {
1841
+ if (!permissions.canEditRoomSettings) return;
1842
+ try {
1843
+ const data = await gql(
1844
+ `mutation($roomId: ID!, $input: UpdateRoomInput!) {
1845
+ updateRoom(roomId: $roomId, input: $input) {
1846
+ id name slug type visibility description
1847
+ memberCount unreadCount avatarUrl createdBy
1848
+ archived lastMessageAt
1849
+ }
1850
+ }`,
1851
+ { roomId, input: updates }
1852
+ );
1853
+ setRooms((prev) => prev.map((r) => r.id === roomId ? data.updateRoom : r));
1854
+ } catch (err) {
1855
+ setError(err.message || "Erro ao atualizar sala.");
1856
+ }
1857
+ },
1858
+ [gql, permissions.canEditRoomSettings]
1859
+ );
1860
+ const archiveRoom = useCallback(
1861
+ async (roomId) => {
1862
+ if (!permissions.canArchiveRoom) return;
1863
+ try {
1864
+ await gql(
1865
+ `mutation($roomId: ID!) { archiveRoom(roomId: $roomId) }`,
1866
+ { roomId }
1867
+ );
1868
+ setRooms((prev) => prev.filter((r) => r.id !== roomId));
1869
+ if (activeRoomId === roomId) {
1870
+ setActiveRoomId(null);
1871
+ setMessages([]);
1872
+ }
1873
+ } catch (err) {
1874
+ setError(err.message || "Erro ao arquivar sala.");
1875
+ }
1876
+ },
1877
+ [gql, permissions.canArchiveRoom, activeRoomId]
1878
+ );
1879
+ const leaveRoom = useCallback(
1880
+ async (roomId) => {
1881
+ try {
1882
+ await gql(
1883
+ `mutation($roomId: ID!) { leaveRoom(roomId: $roomId) }`,
1884
+ { roomId }
1885
+ );
1886
+ setRooms((prev) => prev.filter((r) => r.id !== roomId));
1887
+ if (activeRoomId === roomId) {
1888
+ setActiveRoomId(null);
1889
+ setMessages([]);
1890
+ }
1891
+ } catch (err) {
1892
+ setError(err.message || "Erro ao sair da sala.");
1893
+ }
1894
+ },
1895
+ [gql, activeRoomId]
1896
+ );
1897
+ const addMember = useCallback(
1898
+ async (roomId, targetUserId, role = "MEMBER") => {
1899
+ if (!permissions.canManageMembers) return;
1900
+ try {
1901
+ await gql(
1902
+ `mutation($roomId: ID!, $userId: String!, $role: MemberRole!) {
1903
+ addRoomMember(roomId: $roomId, userId: $userId, role: $role) { id userId role }
1904
+ }`,
1905
+ { roomId, userId: targetUserId, role }
1906
+ );
1907
+ fetchRoomMembers(roomId);
1908
+ } catch (err) {
1909
+ setError(err.message || "Erro ao adicionar membro.");
1910
+ }
1911
+ },
1912
+ [gql, permissions.canManageMembers, fetchRoomMembers]
1913
+ );
1914
+ const removeMember = useCallback(
1915
+ async (roomId, targetUserId) => {
1916
+ if (!permissions.canManageMembers) return;
1917
+ try {
1918
+ await gql(
1919
+ `mutation($roomId: ID!, $userId: String!) {
1920
+ removeRoomMember(roomId: $roomId, userId: $userId)
1921
+ }`,
1922
+ { roomId, userId: targetUserId }
1923
+ );
1924
+ setRoomMembers((prev) => prev.filter((m) => m.userId !== targetUserId));
1925
+ } catch (err) {
1926
+ setError(err.message || "Erro ao remover membro.");
1927
+ }
1928
+ },
1929
+ [gql, permissions.canManageMembers]
1930
+ );
1931
+ const updateMemberRole = useCallback(
1932
+ async (roomId, targetUserId, role) => {
1933
+ if (!permissions.canManageMembers) return;
1934
+ try {
1935
+ await gql(
1936
+ `mutation($roomId: ID!, $userId: String!, $role: MemberRole!) {
1937
+ updateMemberRole(roomId: $roomId, userId: $userId, role: $role) { id userId role }
1938
+ }`,
1939
+ { roomId, userId: targetUserId, role }
1940
+ );
1941
+ setRoomMembers(
1942
+ (prev) => prev.map((m) => m.userId === targetUserId ? { ...m, role } : m)
1943
+ );
1944
+ } catch (err) {
1945
+ setError(err.message || "Erro ao atualizar cargo.");
1946
+ }
1947
+ },
1948
+ [gql, permissions.canManageMembers]
1949
+ );
1950
+ const sendTyping = useCallback(() => {
1951
+ if (!activeRoomId || wsRef.current?.readyState !== WebSocket.OPEN) return;
1952
+ wsRef.current.send(JSON.stringify({ event: "typing", data: { roomId: activeRoomId, userId } }));
1953
+ clearTimeout(typingTimerRef.current);
1954
+ typingTimerRef.current = setTimeout(() => {
1955
+ if (wsRef.current?.readyState === WebSocket.OPEN) {
1956
+ wsRef.current.send(JSON.stringify({ event: "stop_typing", data: { roomId: activeRoomId, userId } }));
1957
+ }
1958
+ }, 3e3);
1959
+ }, [activeRoomId, userId]);
1960
+ useEffect(() => {
1961
+ const wsUrl = gatewayUrl.replace(/^http/, "ws");
1962
+ let reconnectTimer;
1963
+ let reconnectAttempts = 0;
1964
+ const maxReconnectDelay = 3e4;
1965
+ function connect() {
1966
+ try {
1967
+ const ws = new WebSocket(
1968
+ `${wsUrl}/chat?userId=${encodeURIComponent(userId)}&workspaceId=${encodeURIComponent(workspaceId)}`
1969
+ );
1970
+ wsRef.current = ws;
1971
+ ws.onopen = () => {
1972
+ reconnectAttempts = 0;
1973
+ if (activeRoomId) {
1974
+ ws.send(JSON.stringify({ event: "join_room", data: { roomId: activeRoomId } }));
1975
+ }
1976
+ };
1977
+ ws.onmessage = (event) => {
1978
+ try {
1979
+ const msg = JSON.parse(event.data);
1980
+ switch (msg.event) {
1981
+ case "new_message":
1982
+ setMessages((prev) => {
1983
+ if (prev.find((m) => m.id === msg.data.id)) return prev;
1984
+ return [...prev, msg.data];
1985
+ });
1986
+ if (msg.data.roomId !== activeRoomId) {
1987
+ setRooms(
1988
+ (prev) => prev.map(
1989
+ (r) => r.id === msg.data.roomId ? { ...r, unreadCount: r.unreadCount + 1 } : r
1990
+ )
1991
+ );
1992
+ }
1993
+ break;
1994
+ case "typing":
1995
+ if (msg.data.userId !== userId) {
1996
+ setTypingUsers(
1997
+ (prev) => prev.includes(msg.data.userId) ? prev : [...prev, msg.data.userId]
1998
+ );
1999
+ }
2000
+ break;
2001
+ case "stop_typing":
2002
+ setTypingUsers((prev) => prev.filter((id) => id !== msg.data.userId));
2003
+ break;
2004
+ case "room_updated":
2005
+ setRooms(
2006
+ (prev) => prev.map((r) => r.id === msg.data.id ? { ...r, ...msg.data } : r)
2007
+ );
2008
+ break;
2009
+ case "member_joined":
2010
+ case "member_left":
2011
+ if (msg.data.roomId === activeRoomId && activeRoomId) {
2012
+ fetchRoomMembers(activeRoomId);
2013
+ }
2014
+ break;
2015
+ }
2016
+ } catch {
2017
+ }
2018
+ };
2019
+ ws.onclose = () => {
2020
+ const delay = Math.min(1e3 * Math.pow(2, reconnectAttempts) + Math.random() * 1e3, maxReconnectDelay);
2021
+ reconnectAttempts++;
2022
+ reconnectTimer = setTimeout(connect, delay);
2023
+ };
2024
+ } catch {
2025
+ reconnectTimer = setTimeout(connect, 3e3);
2026
+ }
2027
+ }
2028
+ connect();
2029
+ return () => {
2030
+ clearTimeout(reconnectTimer);
2031
+ clearTimeout(typingTimerRef.current);
2032
+ wsRef.current?.close();
2033
+ };
2034
+ }, [gatewayUrl, userId, workspaceId]);
2035
+ useEffect(() => {
2036
+ fetchRooms();
2037
+ }, [fetchRooms]);
2038
+ useEffect(() => {
2039
+ if (error) {
2040
+ const t = setTimeout(() => setError(null), 5e3);
2041
+ return () => clearTimeout(t);
2042
+ }
2043
+ }, [error]);
2044
+ return {
2045
+ // State
2046
+ rooms,
2047
+ messages,
2048
+ activeRoomId,
2049
+ loadingRooms,
2050
+ loadingMessages,
2051
+ roomMembers,
2052
+ myRoomRole,
2053
+ permissions,
2054
+ typingUsers,
2055
+ error,
2056
+ // Room selection
2057
+ selectRoom,
2058
+ // Messaging
2059
+ sendMessage,
2060
+ sendTyping,
2061
+ // Room CRUD
2062
+ createRoom,
2063
+ createDirectMessage,
2064
+ updateRoom,
2065
+ archiveRoom,
2066
+ leaveRoom,
2067
+ // Member management
2068
+ addMember,
2069
+ removeMember,
2070
+ updateMemberRole,
2071
+ // Refresh
2072
+ fetchRooms,
2073
+ fetchRoomMembers
2074
+ };
2075
+ }
2076
+ var alertVariants = cva(
2077
+ "group relative overflow-visible rounded-r-2xl rounded-l-md border-l-4 border-y border-r border-white/10 bg-surface/80 py-5 pl-12 pr-6 shadow-lg backdrop-blur-xl",
2078
+ {
2079
+ variants: {
2080
+ variant: {
2081
+ success: "border-l-emerald-500",
2082
+ error: "border-l-rose-500",
2083
+ warning: "border-l-amber-400",
2084
+ info: "border-l-blue-500"
2085
+ }
2086
+ },
2087
+ defaultVariants: {
2088
+ variant: "info"
2089
+ }
2090
+ }
2091
+ );
2092
+ var alertIconVariants = cva(
2093
+ "absolute -left-4 top-1/2 z-10 flex h-10 w-10 -translate-y-1/2 items-center justify-center rounded-xl border border-white/20 shadow-lg transition-transform duration-300 group-hover:rotate-0",
2094
+ {
2095
+ variants: {
2096
+ variant: {
2097
+ success: "bg-emerald-500 text-black rotate-[-6deg]",
2098
+ error: "bg-rose-500 text-white rotate-[6deg]",
2099
+ warning: "bg-amber-400 text-black rotate-[-3deg]",
2100
+ info: "bg-blue-500 text-white rotate-[3deg]"
2101
+ }
2102
+ },
2103
+ defaultVariants: {
2104
+ variant: "info"
2105
+ }
2106
+ }
2107
+ );
2108
+ function Alert({
2109
+ className,
2110
+ variant,
2111
+ title,
2112
+ description,
2113
+ icon,
2114
+ onClose,
2115
+ action,
2116
+ ...props
2117
+ }) {
2118
+ return /* @__PURE__ */ jsxs("div", { className: cn(alertVariants({ variant }), className), role: "alert", ...props, children: [
2119
+ /* @__PURE__ */ jsx("div", { className: cn(alertIconVariants({ variant })), children: icon || /* @__PURE__ */ jsxs(Fragment, { children: [
2120
+ variant === "success" && /* @__PURE__ */ jsx(OrgCheckCircle, { className: "w-6 h-6" }),
2121
+ variant === "error" && /* @__PURE__ */ jsx(OrgError, { className: "w-6 h-6" }),
2122
+ variant === "warning" && /* @__PURE__ */ jsx(OrgWarning, { className: "w-6 h-6" }),
2123
+ variant === "info" && /* @__PURE__ */ jsx(OrgInfo, { className: "w-6 h-6" })
2124
+ ] }) }),
2125
+ /* @__PURE__ */ jsxs("div", { className: "flex items-start justify-between gap-4", children: [
2126
+ /* @__PURE__ */ jsxs("div", { className: "flex-1", children: [
2127
+ /* @__PURE__ */ jsx("h4", { className: "text-base font-semibold text-white", children: title }),
2128
+ description && /* @__PURE__ */ jsx("p", { className: "mt-1 text-sm font-light text-slate-300", children: description })
2129
+ ] }),
2130
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
2131
+ action && /* @__PURE__ */ jsx(
2132
+ "button",
2133
+ {
2134
+ onClick: action.onClick,
2135
+ className: cn(
2136
+ "whitespace-nowrap rounded-lg border px-3 py-1.5 text-xs font-semibold tracking-wide transition-all",
2137
+ variant === "error" && "border-rose-500/30 bg-rose-500/20 text-rose-300 hover:bg-rose-500/30",
2138
+ variant === "success" && "border-emerald-500/30 bg-emerald-500/20 text-emerald-300 hover:bg-emerald-500/30",
2139
+ variant === "warning" && "border-amber-400/30 bg-amber-400/20 text-amber-300 hover:bg-amber-400/30",
2140
+ variant === "info" && "border-blue-500/30 bg-blue-500/20 text-blue-300 hover:bg-blue-500/30"
2141
+ ),
2142
+ children: action.label
2143
+ }
2144
+ ),
2145
+ onClose && /* @__PURE__ */ jsx(
2146
+ "button",
2147
+ {
2148
+ onClick: onClose,
2149
+ className: "p-1 text-slate-400 transition-colors hover:text-white",
2150
+ "aria-label": "Close alert",
2151
+ children: /* @__PURE__ */ jsx(OrgClose, { className: "h-4 w-4" })
2152
+ }
2153
+ )
2154
+ ] })
2155
+ ] })
2156
+ ] });
2157
+ }
2158
+ function useMediaQuery(query) {
2159
+ const [matches, setMatches] = React5.useState(false);
2160
+ React5.useEffect(() => {
2161
+ const mql = window.matchMedia(query);
2162
+ setMatches(mql.matches);
2163
+ const handler = (e) => setMatches(e.matches);
2164
+ mql.addEventListener("change", handler);
2165
+ return () => mql.removeEventListener("change", handler);
2166
+ }, [query]);
2167
+ return matches;
2168
+ }
2169
+ function OrganifyChat({
2170
+ workspaceRole,
2171
+ workspaceMembers = [],
2172
+ initialRooms,
2173
+ initialMessages,
2174
+ className
2175
+ }) {
2176
+ const user = useOrganifyUser();
2177
+ const userId = user?.id ?? "";
2178
+ const chat = useChat({
2179
+ workspaceRole,
2180
+ initialRooms,
2181
+ initialMessages
2182
+ });
2183
+ const isMobile = !useMediaQuery("(min-width: 768px)");
2184
+ const [createDialogOpen, setCreateDialogOpen] = React5.useState(false);
2185
+ const [managementOpen, setManagementOpen] = React5.useState(false);
2186
+ const [mobileView, setMobileView] = React5.useState("sidebar");
2187
+ const activeRoom = React5.useMemo(
2188
+ () => chat.rooms.find((r) => r.id === chat.activeRoomId),
2189
+ [chat.rooms, chat.activeRoomId]
2190
+ );
2191
+ const handleRoomSelect = (roomId) => {
2192
+ chat.selectRoom(roomId);
2193
+ if (isMobile) {
2194
+ setMobileView("room");
2195
+ }
2196
+ };
2197
+ const handleBackToSidebar = () => {
2198
+ setMobileView("sidebar");
2199
+ };
2200
+ const handleOpenManagement = () => {
2201
+ if (isMobile) {
2202
+ setMobileView("management");
2203
+ } else {
2204
+ setManagementOpen(true);
2205
+ }
2206
+ };
2207
+ const handleCloseManagement = () => {
2208
+ if (isMobile) {
2209
+ setMobileView("room");
2210
+ } else {
2211
+ setManagementOpen(false);
2212
+ }
2213
+ };
2214
+ return /* @__PURE__ */ jsxs(
2215
+ "div",
2216
+ {
2217
+ className: cn(
2218
+ "relative flex h-full min-h-[500px]",
2219
+ "bg-background border border-white/8 rounded-lg overflow-hidden",
2220
+ className
2221
+ ),
2222
+ children: [
2223
+ 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-300", children: /* @__PURE__ */ jsx(
2224
+ Alert,
2225
+ {
2226
+ variant: "error",
2227
+ title: "Erro",
2228
+ description: chat.error,
2229
+ className: "py-3 text-xs"
2230
+ }
2231
+ ) }),
2232
+ !isMobile && /* @__PURE__ */ jsxs(Fragment, { children: [
2233
+ /* @__PURE__ */ jsx("div", { className: "w-[260px] shrink-0", children: /* @__PURE__ */ jsx(
2234
+ ChatSidebar,
2235
+ {
2236
+ rooms: chat.rooms,
2237
+ activeRoomId: chat.activeRoomId,
2238
+ loading: chat.loadingRooms,
2239
+ onSelectRoom: chat.selectRoom,
2240
+ onCreateRoom: () => setCreateDialogOpen(true)
2241
+ }
2242
+ ) }),
2243
+ /* @__PURE__ */ jsx("div", { className: "flex-1", children: /* @__PURE__ */ jsx(
2244
+ ChatMessages,
2245
+ {
2246
+ room: activeRoom,
2247
+ messages: chat.messages,
2248
+ loading: chat.loadingMessages,
2249
+ typingUsers: chat.typingUsers,
2250
+ currentUserId: userId,
2251
+ permissions: chat.permissions,
2252
+ onSendMessage: chat.sendMessage,
2253
+ onTyping: chat.sendTyping,
2254
+ onStopTyping: chat.sendTyping,
2255
+ onOpenManagement: () => setManagementOpen(true),
2256
+ isMobile: false
2257
+ }
2258
+ ) }),
2259
+ managementOpen && activeRoom && /* @__PURE__ */ jsx("div", { className: "w-[300px] shrink-0 border-l border-white/10 bg-card-surface/50", children: /* @__PURE__ */ jsx(
2260
+ RoomManagementPanel,
2261
+ {
2262
+ room: activeRoom,
2263
+ members: chat.roomMembers,
2264
+ currentUserId: userId,
2265
+ permissions: chat.permissions,
2266
+ onClose: () => setManagementOpen(false),
2267
+ onUpdateRoom: chat.updateRoom,
2268
+ onArchiveRoom: chat.archiveRoom,
2269
+ onLeaveRoom: chat.leaveRoom,
2270
+ onRemoveMember: chat.removeMember,
2271
+ onUpdateMemberRole: chat.updateMemberRole
2272
+ }
2273
+ ) })
2274
+ ] }),
2275
+ isMobile && /* @__PURE__ */ jsxs(Fragment, { children: [
2276
+ mobileView === "sidebar" && /* @__PURE__ */ jsx("div", { className: "w-full animate-in fade-in-0 slide-in-from-left-5 duration-300", children: /* @__PURE__ */ jsx(
2277
+ ChatSidebar,
2278
+ {
2279
+ rooms: chat.rooms,
2280
+ activeRoomId: chat.activeRoomId,
2281
+ loading: chat.loadingRooms,
2282
+ onSelectRoom: handleRoomSelect,
2283
+ onCreateRoom: () => setCreateDialogOpen(true)
2284
+ }
2285
+ ) }),
2286
+ mobileView === "room" && activeRoom && /* @__PURE__ */ jsx("div", { className: "w-full animate-in fade-in-0 slide-in-from-right-5 duration-300", children: /* @__PURE__ */ jsx(
2287
+ ChatMessages,
2288
+ {
2289
+ room: activeRoom,
2290
+ messages: chat.messages,
2291
+ loading: chat.loadingMessages,
2292
+ typingUsers: chat.typingUsers,
2293
+ currentUserId: userId,
2294
+ permissions: chat.permissions,
2295
+ onSendMessage: chat.sendMessage,
2296
+ onTyping: chat.sendTyping,
2297
+ onStopTyping: chat.sendTyping,
2298
+ onOpenManagement: handleOpenManagement,
2299
+ onBack: handleBackToSidebar,
2300
+ isMobile: true
2301
+ }
2302
+ ) }),
2303
+ mobileView === "management" && activeRoom && /* @__PURE__ */ jsxs("div", { className: "absolute inset-0 z-50 flex", children: [
2304
+ /* @__PURE__ */ jsx(
2305
+ "div",
2306
+ {
2307
+ className: "flex-1 bg-black/50 backdrop-blur-sm",
2308
+ onClick: handleCloseManagement
2309
+ }
2310
+ ),
2311
+ /* @__PURE__ */ jsx("div", { className: "w-[85%] max-w-sm h-full bg-card-surface/95 backdrop-blur-2xl border-l border-white/10 animate-in slide-in-from-right-5 duration-300", children: /* @__PURE__ */ jsx(
2312
+ RoomManagementPanel,
2313
+ {
2314
+ room: activeRoom,
2315
+ members: chat.roomMembers,
2316
+ currentUserId: userId,
2317
+ permissions: chat.permissions,
2318
+ onClose: handleCloseManagement,
2319
+ onUpdateRoom: chat.updateRoom,
2320
+ onArchiveRoom: chat.archiveRoom,
2321
+ onLeaveRoom: chat.leaveRoom,
2322
+ onRemoveMember: chat.removeMember,
2323
+ onUpdateMemberRole: chat.updateMemberRole
2324
+ }
2325
+ ) })
2326
+ ] })
2327
+ ] }),
2328
+ /* @__PURE__ */ jsx(
2329
+ CreateRoomDialog,
2330
+ {
2331
+ open: createDialogOpen,
2332
+ onOpenChange: setCreateDialogOpen,
2333
+ onCreateChannel: async (data) => {
2334
+ const room = await chat.createRoom({
2335
+ name: data.name,
2336
+ type: "CHANNEL",
2337
+ visibility: data.visibility,
2338
+ description: data.description,
2339
+ memberIds: data.memberIds
2340
+ });
2341
+ if (room) chat.selectRoom(room.id);
2342
+ return room;
2343
+ },
2344
+ onCreateDM: async (targetUserId) => {
2345
+ const room = await chat.createDirectMessage(targetUserId);
2346
+ if (room) chat.selectRoom(room.id);
2347
+ return room;
2348
+ },
2349
+ workspaceMembers
2350
+ }
2351
+ )
2352
+ ]
2353
+ }
2354
+ );
2355
+ }
2356
+
2357
+ export { Alert, Button, ChatMessages, ChatSidebar, CreateRoomDialog, Dialog, DialogClose, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger, Input, Label, OrgLoader, OrgLoaderInline, OrganifyChat, RoomManagementPanel, Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectScrollDownButton, SelectScrollUpButton, SelectSeparator, SelectTrigger, SelectValue, Separator, Tabs, TabsContent, TabsList, TabsTrigger, Textarea, alertVariants, buttonVariants, getRoomPermissions, inputVariants, orgLoaderVariants, useChat };
2358
+ //# sourceMappingURL=chunk-CK63AZ3Z.js.map
2359
+ //# sourceMappingURL=chunk-CK63AZ3Z.js.map