@snksergio/design-system 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/README.md +260 -0
  2. package/dist-lib/chunks/app-shell-BNTvOETJ.mjs +2681 -0
  3. package/dist-lib/chunks/app-shell-BNTvOETJ.mjs.map +1 -0
  4. package/dist-lib/chunks/app-shell-CDy42aNI.mjs +2663 -0
  5. package/dist-lib/chunks/app-shell-CDy42aNI.mjs.map +1 -0
  6. package/dist-lib/chunks/app-shell-mocks-kWIVSzLs.mjs +208 -0
  7. package/dist-lib/chunks/app-shell-mocks-kWIVSzLs.mjs.map +1 -0
  8. package/dist-lib/chunks/app-shell-mocks-oO_IV3Gw.mjs +207 -0
  9. package/dist-lib/chunks/app-shell-mocks-oO_IV3Gw.mjs.map +1 -0
  10. package/dist-lib/chunks/badge-B_2Zt52o.mjs +797 -0
  11. package/dist-lib/chunks/badge-B_2Zt52o.mjs.map +1 -0
  12. package/dist-lib/chunks/badge-DKpXURa1.mjs +813 -0
  13. package/dist-lib/chunks/badge-DKpXURa1.mjs.map +1 -0
  14. package/dist-lib/chunks/chat-v2-mocks-BV9Wha_Z.mjs +192 -0
  15. package/dist-lib/chunks/chat-v2-mocks-BV9Wha_Z.mjs.map +1 -0
  16. package/dist-lib/chunks/chat-v2-mocks-DvZRkhmz.mjs +193 -0
  17. package/dist-lib/chunks/chat-v2-mocks-DvZRkhmz.mjs.map +1 -0
  18. package/dist-lib/chunks/chip-group-CsKBBHW2.mjs +796 -0
  19. package/dist-lib/chunks/chip-group-CsKBBHW2.mjs.map +1 -0
  20. package/dist-lib/chunks/chip-group-D3D0i72q.mjs +780 -0
  21. package/dist-lib/chunks/chip-group-D3D0i72q.mjs.map +1 -0
  22. package/dist-lib/chunks/clientes-showcase-mocks-BMfbNNJh.mjs +185 -0
  23. package/dist-lib/chunks/clientes-showcase-mocks-BMfbNNJh.mjs.map +1 -0
  24. package/dist-lib/chunks/clientes-showcase-mocks-Cq9u3nGZ.mjs +169 -0
  25. package/dist-lib/chunks/clientes-showcase-mocks-Cq9u3nGZ.mjs.map +1 -0
  26. package/dist-lib/chunks/input-374MpdBL.mjs +72 -0
  27. package/dist-lib/chunks/input-374MpdBL.mjs.map +1 -0
  28. package/dist-lib/chunks/input-DF4XAWmr.mjs +56 -0
  29. package/dist-lib/chunks/input-DF4XAWmr.mjs.map +1 -0
  30. package/dist-lib/chunks/page-header-Dw-3wqAG.mjs +75 -0
  31. package/dist-lib/chunks/page-header-Dw-3wqAG.mjs.map +1 -0
  32. package/dist-lib/chunks/page-header-mC1gGtOI.mjs +76 -0
  33. package/dist-lib/chunks/page-header-mC1gGtOI.mjs.map +1 -0
  34. package/dist-lib/chunks/panel-Ds15whqp.mjs +9717 -0
  35. package/dist-lib/chunks/panel-Ds15whqp.mjs.map +1 -0
  36. package/dist-lib/chunks/panel-bzNmFlEZ.mjs +9737 -0
  37. package/dist-lib/chunks/panel-bzNmFlEZ.mjs.map +1 -0
  38. package/dist-lib/chunks/useTheme-BUPLZBMs.mjs +66 -0
  39. package/dist-lib/chunks/useTheme-BUPLZBMs.mjs.map +1 -0
  40. package/dist-lib/chunks/useTheme-Dee4mo-A.mjs +65 -0
  41. package/dist-lib/chunks/useTheme-Dee4mo-A.mjs.map +1 -0
  42. package/dist-lib/index.cjs +1207 -0
  43. package/dist-lib/index.cjs.map +1 -0
  44. package/dist-lib/index.d.ts +2 -0
  45. package/dist-lib/index.mjs +1209 -0
  46. package/dist-lib/index.mjs.map +1 -0
  47. package/dist-lib/preview/chat.cjs +1120 -0
  48. package/dist-lib/preview/chat.cjs.map +1 -0
  49. package/dist-lib/preview/chat.d.ts +6 -0
  50. package/dist-lib/preview/chat.mjs +1120 -0
  51. package/dist-lib/preview/chat.mjs.map +1 -0
  52. package/dist-lib/preview/clientes.cjs +897 -0
  53. package/dist-lib/preview/clientes.cjs.map +1 -0
  54. package/dist-lib/preview/clientes.d.ts +6 -0
  55. package/dist-lib/preview/clientes.mjs +897 -0
  56. package/dist-lib/preview/clientes.mjs.map +1 -0
  57. package/dist-lib/preview/dashboard.cjs +733 -0
  58. package/dist-lib/preview/dashboard.cjs.map +1 -0
  59. package/dist-lib/preview/dashboard.d.ts +4 -0
  60. package/dist-lib/preview/dashboard.mjs +733 -0
  61. package/dist-lib/preview/dashboard.mjs.map +1 -0
  62. package/dist-lib/preview/mocks.cjs +15 -0
  63. package/dist-lib/preview/mocks.cjs.map +1 -0
  64. package/dist-lib/preview/mocks.d.ts +2 -0
  65. package/dist-lib/preview/mocks.mjs +15 -0
  66. package/dist-lib/preview/mocks.mjs.map +1 -0
  67. package/dist-lib/theme.css +668 -0
  68. package/dist-lib/tokens.cjs +915 -0
  69. package/dist-lib/tokens.cjs.map +1 -0
  70. package/dist-lib/tokens.d.ts +2 -0
  71. package/dist-lib/tokens.mjs +915 -0
  72. package/dist-lib/tokens.mjs.map +1 -0
  73. package/package.json +142 -0
@@ -0,0 +1,1120 @@
1
+ import { jsxs, jsx, Fragment } from "react/jsx-runtime";
2
+ import { useState, useRef, useEffect, useCallback, useMemo } from "react";
3
+ import { t as tv, B as Button, C as Chip, c as cn, a as ChipGroup, b as ChipGroupItem } from "../chunks/chip-group-D3D0i72q.mjs";
4
+ import { u as useTheme } from "../chunks/useTheme-BUPLZBMs.mjs";
5
+ import { G as DropdownMenu, J as DropdownMenuTrigger, K as DropdownMenuContent, r as DropdownMenuItem, v as DropdownMenuSeparator, a as Avatar, b as AvatarFallback, A as AppShell } from "../chunks/app-shell-CDy42aNI.mjs";
6
+ import { MoreVertical, CheckCircle2, Phone, Video, Tag, UserPlus, Archive, CheckCheck, Check, ArrowLeft, PanelRightClose, PanelRightOpen, Paperclip, Smile, Send, Mic, ChevronDown, X, Plus, Menu, Search } from "lucide-react";
7
+ import { e as APP_SHELL_THEME_OPTIONS, c as APP_SHELL_MESSAGES, d as APP_SHELL_NOTIFICATIONS, A as APP_SHELL_COMMANDS, a as APP_SHELL_CONTEXTS } from "../chunks/app-shell-mocks-kWIVSzLs.mjs";
8
+ import { C as CHANNEL_LABEL, a as CHANNEL_HEX, S as SAMPLE_MESSAGES, b as STATUS_DOT, F as FILTER_GROUPS, d as CONVERSATIONS, e as CONVERSATION_DETAILS, D as DEFAULT_DETAIL } from "../chunks/chat-v2-mocks-DvZRkhmz.mjs";
9
+ import { I as Input } from "../chunks/input-DF4XAWmr.mjs";
10
+ const chatV2Styles = tv({
11
+ slots: {
12
+ /** Body do AppShell — flex row com gap entre colunas. `relative` pra mobile overlays. */
13
+ root: "flex flex-1 min-h-0 gap-gp-2xl relative",
14
+ /** Wrapper dos filtros (col 1) — escondido em mobile. */
15
+ filtersWrap: "hidden md:contents"
16
+ }
17
+ });
18
+ const mobileOverlay = {
19
+ active: "max-md:absolute max-md:inset-0 max-md:w-full max-md:rounded-radius-none max-md:border-0",
20
+ hidden: "max-md:hidden"
21
+ };
22
+ function useResizable({
23
+ initial,
24
+ min,
25
+ max,
26
+ storageKey
27
+ }) {
28
+ const [width, setWidth] = useState(initial);
29
+ const hydrated = useRef(false);
30
+ useEffect(() => {
31
+ if (typeof window === "undefined") return;
32
+ try {
33
+ const raw = window.localStorage.getItem(storageKey);
34
+ const n = raw ? Number(raw) : NaN;
35
+ if (!Number.isNaN(n) && n >= min && n <= max) setWidth(n);
36
+ } catch {
37
+ }
38
+ hydrated.current = true;
39
+ }, [min, max, storageKey]);
40
+ useEffect(() => {
41
+ if (!hydrated.current || typeof window === "undefined") return;
42
+ try {
43
+ window.localStorage.setItem(storageKey, String(width));
44
+ } catch {
45
+ }
46
+ }, [width, storageKey]);
47
+ const onResizeStart = useCallback(
48
+ (e) => {
49
+ e.preventDefault();
50
+ const startX = e.clientX;
51
+ const startWidth = width;
52
+ const onMove = (ev) => {
53
+ const delta = startX - ev.clientX;
54
+ const next = Math.max(min, Math.min(max, startWidth + delta));
55
+ setWidth(next);
56
+ };
57
+ const onUp = () => {
58
+ document.removeEventListener("mousemove", onMove);
59
+ document.removeEventListener("mouseup", onUp);
60
+ document.body.style.cursor = "";
61
+ document.body.style.userSelect = "";
62
+ };
63
+ document.addEventListener("mousemove", onMove);
64
+ document.addEventListener("mouseup", onUp);
65
+ document.body.style.cursor = "col-resize";
66
+ document.body.style.userSelect = "none";
67
+ },
68
+ [width, min, max]
69
+ );
70
+ return { width, onResizeStart };
71
+ }
72
+ function ChannelDot({ channel, dotOnly = false }) {
73
+ return /* @__PURE__ */ jsxs(
74
+ "span",
75
+ {
76
+ className: "inline-flex items-center gap-[4px] text-[11px] text-fg-muted",
77
+ title: CHANNEL_LABEL[channel],
78
+ children: [
79
+ /* @__PURE__ */ jsx(
80
+ "span",
81
+ {
82
+ className: "size-[8px] rounded-radius-full shrink-0",
83
+ style: { background: CHANNEL_HEX[channel] },
84
+ "aria-hidden": true
85
+ }
86
+ ),
87
+ !dotOnly && CHANNEL_LABEL[channel]
88
+ ]
89
+ }
90
+ );
91
+ }
92
+ function ConversationActionsMenu({
93
+ showResolveInMenu = false,
94
+ onResolve,
95
+ onCall,
96
+ onVideo,
97
+ onAddTag,
98
+ onAssign,
99
+ onArchive
100
+ }) {
101
+ return /* @__PURE__ */ jsxs(DropdownMenu, { children: [
102
+ /* @__PURE__ */ jsx(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsx(
103
+ Button,
104
+ {
105
+ color: "secondary",
106
+ variant: "outline",
107
+ size: "icon-sm",
108
+ "aria-label": "Mais opções",
109
+ children: /* @__PURE__ */ jsx(MoreVertical, {})
110
+ }
111
+ ) }),
112
+ /* @__PURE__ */ jsxs(DropdownMenuContent, { align: "end", sideOffset: 6, className: "min-w-[200px]", children: [
113
+ showResolveInMenu && /* @__PURE__ */ jsxs(Fragment, { children: [
114
+ /* @__PURE__ */ jsxs(DropdownMenuItem, { onSelect: onResolve, children: [
115
+ /* @__PURE__ */ jsx(CheckCircle2, {}),
116
+ " Resolver"
117
+ ] }),
118
+ /* @__PURE__ */ jsx(DropdownMenuSeparator, {})
119
+ ] }),
120
+ /* @__PURE__ */ jsxs(DropdownMenuItem, { onSelect: onCall, children: [
121
+ /* @__PURE__ */ jsx(Phone, {}),
122
+ " Ligar"
123
+ ] }),
124
+ /* @__PURE__ */ jsxs(DropdownMenuItem, { onSelect: onVideo, children: [
125
+ /* @__PURE__ */ jsx(Video, {}),
126
+ " Vídeo chamada"
127
+ ] }),
128
+ /* @__PURE__ */ jsxs(DropdownMenuItem, { onSelect: onAddTag, children: [
129
+ /* @__PURE__ */ jsx(Tag, {}),
130
+ " Adicionar tag"
131
+ ] }),
132
+ /* @__PURE__ */ jsxs(DropdownMenuItem, { onSelect: onAssign, children: [
133
+ /* @__PURE__ */ jsx(UserPlus, {}),
134
+ " Atribuir"
135
+ ] }),
136
+ /* @__PURE__ */ jsx(DropdownMenuSeparator, {}),
137
+ /* @__PURE__ */ jsxs(DropdownMenuItem, { variant: "destructive", onSelect: onArchive, children: [
138
+ /* @__PURE__ */ jsx(Archive, {}),
139
+ " Arquivar"
140
+ ] })
141
+ ] })
142
+ ] });
143
+ }
144
+ function DateSeparator({ label }) {
145
+ return /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center my-pad-md", children: /* @__PURE__ */ jsx("span", { className: "px-pad-lg py-pad-2xs rounded-radius-full bg-bg-muted text-[11px] text-fg-muted [font-variant-numeric:tabular-nums]", children: label }) });
146
+ }
147
+ const messageBubbleStyles = tv({
148
+ slots: {
149
+ /** Wrapper que controla o alinhamento horizontal (esquerda/direita). */
150
+ row: "flex",
151
+ /** Balão em si — variants definem cor + corner pointer. */
152
+ bubble: "max-w-[70%] px-pad-xl py-pad-md rounded-radius-xl shadow-sh-sm",
153
+ /** Texto da mensagem. */
154
+ text: "text-[13.5px] leading-[1.45] whitespace-pre-wrap break-words",
155
+ /** Footer com horário + status icon. */
156
+ meta: "flex items-center justify-end gap-[4px] mt-[2px] text-[10.5px]",
157
+ /** Span do horário (tabular-nums pra alinhar). */
158
+ time: "[font-variant-numeric:tabular-nums]"
159
+ },
160
+ variants: {
161
+ from: {
162
+ me: {
163
+ row: "justify-end",
164
+ bubble: "bg-bg-brand text-fg-on-brand rounded-br-[4px]",
165
+ meta: "text-fg-on-brand opacity-80"
166
+ },
167
+ them: {
168
+ row: "justify-start",
169
+ bubble: "bg-bg-surface text-fg-default border border-border-subtle rounded-bl-[4px]",
170
+ meta: "text-fg-muted"
171
+ }
172
+ }
173
+ },
174
+ defaultVariants: {
175
+ from: "them"
176
+ }
177
+ });
178
+ function MessageBubble({ msg }) {
179
+ const from = msg.from === "me" ? "me" : "them";
180
+ const { row, bubble, text, meta, time } = messageBubbleStyles({ from });
181
+ return /* @__PURE__ */ jsx("div", { className: row(), children: /* @__PURE__ */ jsxs("div", { className: bubble(), children: [
182
+ /* @__PURE__ */ jsx("div", { className: text(), children: msg.text }),
183
+ /* @__PURE__ */ jsxs("div", { className: meta(), children: [
184
+ /* @__PURE__ */ jsx("span", { className: time(), children: msg.time }),
185
+ from === "me" && (msg.status === "read" ? /* @__PURE__ */ jsx(CheckCheck, { size: 12, strokeWidth: 2 }) : /* @__PURE__ */ jsx(Check, { size: 12, strokeWidth: 2 }))
186
+ ] })
187
+ ] }) });
188
+ }
189
+ const SIZE_CLASSES = {
190
+ sm: "size-[28px] text-[11px]",
191
+ md: "size-[36px] text-[13px]",
192
+ lg: "size-[40px] text-[14px]"
193
+ };
194
+ function PersonAvatar({ initials, hex, size = "md" }) {
195
+ return /* @__PURE__ */ jsx(Avatar, { className: SIZE_CLASSES[size], style: { background: hex }, children: /* @__PURE__ */ jsx(AvatarFallback, { className: "bg-transparent text-white font-bold", children: initials }) });
196
+ }
197
+ const conversationColumnStyles = tv({
198
+ slots: {
199
+ root: "flex flex-col flex-1 min-w-0 bg-bg-surface border border-border-subtle rounded-radius-xl overflow-hidden",
200
+ header: "flex items-center gap-gp-lg px-pad-2xl py-pad-md border-b border-border-subtle shrink-0",
201
+ headerInfo: "flex-1 min-w-0 flex flex-col gap-[2px]",
202
+ headerTitleRow: "flex items-center gap-gp-sm",
203
+ headerName: "text-title-md text-fg-default whitespace-nowrap overflow-hidden text-ellipsis",
204
+ headerMetaRow: "flex flex-wrap items-center gap-x-gp-sm gap-y-[2px] text-[11.5px] text-fg-muted min-w-0",
205
+ headerActions: "flex items-center gap-gp-sm shrink-0",
206
+ statusInner: "inline-flex items-center gap-[4px]",
207
+ statusDot: "size-[6px] rounded-radius-full shrink-0",
208
+ thread: "flex-1 min-h-0 overflow-y-auto bg-bg-muted dark:bg-bg-canvas px-pad-3xl py-pad-2xl flex flex-col gap-gp-md",
209
+ composer: "flex items-center gap-gp-sm px-pad-2xl py-pad-md border-t border-border-subtle shrink-0 bg-bg-surface"
210
+ }
211
+ });
212
+ function ConversationColumn({
213
+ conversation,
214
+ detailsOpen,
215
+ onToggleDetails,
216
+ onBackToList,
217
+ className
218
+ }) {
219
+ const s = conversationColumnStyles();
220
+ const [text, setText] = useState("");
221
+ const [msgs, setMsgs] = useState(SAMPLE_MESSAGES);
222
+ const send = (e) => {
223
+ e?.preventDefault();
224
+ if (!text.trim()) return;
225
+ setMsgs((m) => [
226
+ ...m,
227
+ {
228
+ id: Date.now(),
229
+ from: "me",
230
+ text: text.trim(),
231
+ time: (/* @__PURE__ */ new Date()).toLocaleTimeString("pt-BR", {
232
+ hour: "2-digit",
233
+ minute: "2-digit"
234
+ }),
235
+ status: "sent"
236
+ }
237
+ ]);
238
+ setText("");
239
+ };
240
+ return /* @__PURE__ */ jsxs("section", { "aria-label": "Conversa", className: cn(s.root(), className), children: [
241
+ /* @__PURE__ */ jsxs("header", { className: s.header(), children: [
242
+ onBackToList && /* @__PURE__ */ jsx(
243
+ Button,
244
+ {
245
+ color: "secondary",
246
+ variant: "ghost",
247
+ size: "icon-sm",
248
+ "aria-label": "Voltar pra lista",
249
+ onClick: onBackToList,
250
+ className: "md:hidden",
251
+ children: /* @__PURE__ */ jsx(ArrowLeft, {})
252
+ }
253
+ ),
254
+ /* @__PURE__ */ jsx("div", { className: "max-md:hidden", children: /* @__PURE__ */ jsx(
255
+ PersonAvatar,
256
+ {
257
+ initials: conversation.initials,
258
+ hex: conversation.avatarHex,
259
+ size: "md"
260
+ }
261
+ ) }),
262
+ /* @__PURE__ */ jsxs("div", { className: s.headerInfo(), children: [
263
+ /* @__PURE__ */ jsxs("div", { className: s.headerTitleRow(), children: [
264
+ /* @__PURE__ */ jsx("span", { className: s.headerName(), children: conversation.name }),
265
+ /* @__PURE__ */ jsx(Chip, { color: conversation.tagKind, variant: "soft", size: "sm", shape: "pill", children: conversation.tag })
266
+ ] }),
267
+ /* @__PURE__ */ jsxs("div", { className: s.headerMetaRow(), children: [
268
+ /* @__PURE__ */ jsx("span", { className: "[font-variant-numeric:tabular-nums]", children: conversation.id }),
269
+ /* @__PURE__ */ jsx("span", { "aria-hidden": true, children: "·" }),
270
+ /* @__PURE__ */ jsx(ChannelDot, { channel: conversation.channel }),
271
+ /* @__PURE__ */ jsx("span", { "aria-hidden": true, children: "·" }),
272
+ /* @__PURE__ */ jsxs("span", { className: s.statusInner(), children: [
273
+ /* @__PURE__ */ jsx(
274
+ "span",
275
+ {
276
+ className: s.statusDot(),
277
+ style: { background: STATUS_DOT[conversation.status] },
278
+ "aria-hidden": true
279
+ }
280
+ ),
281
+ conversation.statusLabel
282
+ ] })
283
+ ] })
284
+ ] }),
285
+ /* @__PURE__ */ jsxs("div", { className: s.headerActions(), children: [
286
+ /* @__PURE__ */ jsx(
287
+ Button,
288
+ {
289
+ color: "primary",
290
+ variant: "filled",
291
+ size: "sm",
292
+ iconLeft: /* @__PURE__ */ jsx(CheckCircle2, {}),
293
+ className: "max-md:hidden",
294
+ children: "Resolver"
295
+ }
296
+ ),
297
+ /* @__PURE__ */ jsx(
298
+ ConversationActionsMenu,
299
+ {
300
+ showResolveInMenu: true,
301
+ onResolve: () => console.log("resolver"),
302
+ onCall: () => console.log("ligar"),
303
+ onVideo: () => console.log("video"),
304
+ onAddTag: () => console.log("tag"),
305
+ onAssign: () => console.log("atribuir"),
306
+ onArchive: () => console.log("arquivar")
307
+ }
308
+ ),
309
+ /* @__PURE__ */ jsx(
310
+ Button,
311
+ {
312
+ color: "secondary",
313
+ variant: "outline",
314
+ size: "icon-sm",
315
+ "aria-label": detailsOpen ? "Ocultar detalhes" : "Mostrar detalhes",
316
+ "aria-pressed": detailsOpen,
317
+ onClick: onToggleDetails,
318
+ children: detailsOpen ? /* @__PURE__ */ jsx(PanelRightClose, {}) : /* @__PURE__ */ jsx(PanelRightOpen, {})
319
+ }
320
+ )
321
+ ] })
322
+ ] }),
323
+ /* @__PURE__ */ jsxs("div", { className: s.thread(), "aria-live": "polite", children: [
324
+ /* @__PURE__ */ jsx(DateSeparator, { label: "Hoje" }),
325
+ msgs.map((m) => /* @__PURE__ */ jsx(MessageBubble, { msg: m }, m.id))
326
+ ] }),
327
+ /* @__PURE__ */ jsxs("form", { onSubmit: send, className: s.composer(), children: [
328
+ /* @__PURE__ */ jsx(
329
+ Button,
330
+ {
331
+ color: "secondary",
332
+ variant: "ghost",
333
+ size: "icon-sm",
334
+ "aria-label": "Anexar",
335
+ type: "button",
336
+ children: /* @__PURE__ */ jsx(Paperclip, {})
337
+ }
338
+ ),
339
+ /* @__PURE__ */ jsx(
340
+ Input,
341
+ {
342
+ type: "text",
343
+ value: text,
344
+ onChange: (e) => setText(e.target.value),
345
+ placeholder: "Digite uma mensagem...",
346
+ "aria-label": "Mensagem",
347
+ className: "flex-1"
348
+ }
349
+ ),
350
+ /* @__PURE__ */ jsx(
351
+ Button,
352
+ {
353
+ color: "secondary",
354
+ variant: "ghost",
355
+ size: "icon-sm",
356
+ "aria-label": "Emoji",
357
+ type: "button",
358
+ children: /* @__PURE__ */ jsx(Smile, {})
359
+ }
360
+ ),
361
+ text.trim() ? /* @__PURE__ */ jsx(
362
+ Button,
363
+ {
364
+ color: "primary",
365
+ variant: "filled",
366
+ size: "icon-sm",
367
+ "aria-label": "Enviar",
368
+ type: "submit",
369
+ children: /* @__PURE__ */ jsx(Send, { strokeWidth: 2.2 })
370
+ }
371
+ ) : /* @__PURE__ */ jsx(
372
+ Button,
373
+ {
374
+ color: "primary",
375
+ variant: "filled",
376
+ size: "icon-sm",
377
+ "aria-label": "Gravar áudio",
378
+ type: "button",
379
+ children: /* @__PURE__ */ jsx(Mic, { strokeWidth: 2.2 })
380
+ }
381
+ )
382
+ ] })
383
+ ] });
384
+ }
385
+ function DetailField({ label, value }) {
386
+ return /* @__PURE__ */ jsxs("div", { className: "flex items-baseline justify-between gap-gp-md py-pad-2xs", children: [
387
+ /* @__PURE__ */ jsx("span", { className: "text-[12px] text-fg-muted shrink-0", children: label }),
388
+ /* @__PURE__ */ jsx("span", { className: "text-[12.5px] text-fg-default text-right break-words min-w-0", children: value || "—" })
389
+ ] });
390
+ }
391
+ function DetailSection({
392
+ title,
393
+ open,
394
+ onToggle,
395
+ children
396
+ }) {
397
+ return /* @__PURE__ */ jsxs("section", { className: "flex flex-col border-b border-border-subtle pb-pad-xl last:border-b-0 last:pb-0", children: [
398
+ /* @__PURE__ */ jsxs(
399
+ "button",
400
+ {
401
+ type: "button",
402
+ onClick: onToggle,
403
+ "aria-expanded": open,
404
+ className: "flex items-center justify-between w-full px-pad-sm py-pad-sm -mx-pad-sm rounded-radius-md cursor-pointer text-left transition-colors duration-150 hover:bg-bg-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring-brand",
405
+ children: [
406
+ /* @__PURE__ */ jsx("span", { className: "text-[13px] font-semibold text-fg-default", children: title }),
407
+ /* @__PURE__ */ jsx(
408
+ ChevronDown,
409
+ {
410
+ size: 14,
411
+ className: [
412
+ "text-fg-muted transition-transform duration-150",
413
+ open ? "" : "-rotate-90"
414
+ ].join(" ")
415
+ }
416
+ )
417
+ ]
418
+ }
419
+ ),
420
+ open && /* @__PURE__ */ jsx("div", { className: "flex flex-col gap-gp-sm pt-pad-xs", children })
421
+ ] });
422
+ }
423
+ const detailsColumnStyles = tv({
424
+ slots: {
425
+ root: "relative flex flex-col shrink-0 bg-bg-surface border border-border-subtle rounded-radius-xl overflow-hidden",
426
+ /** Resize handle no left edge — 4px. */
427
+ handle: "absolute left-0 top-0 bottom-0 w-[4px] z-[1] bg-transparent cursor-col-resize hover:bg-bg-brand active:bg-bg-brand transition-colors duration-150 focus-visible:outline-none focus-visible:bg-bg-brand",
428
+ header: "flex items-center gap-gp-md px-pad-2xl py-pad-lg border-b border-border-subtle shrink-0",
429
+ title: "m-0 text-title-md text-fg-default flex-1",
430
+ body: "flex-1 min-h-0 overflow-y-auto px-pad-2xl py-pad-md flex flex-col gap-gp-2xl",
431
+ contactHead: "flex flex-col items-center gap-gp-md pb-pad-md",
432
+ contactName: "text-[14px] font-semibold text-fg-default text-center",
433
+ tagsRow: "flex flex-col gap-gp-sm pt-pad-xs",
434
+ tagsLabel: "text-[12px] text-fg-muted",
435
+ tagsChips: "flex items-center gap-gp-xs flex-wrap",
436
+ tagAddBtn: "inline-flex items-center gap-[3px] px-pad-md h-[22px] rounded-radius-full border border-dashed border-border-input text-[11px] font-medium text-fg-muted hover:bg-bg-muted hover:text-fg-default transition-colors",
437
+ historyEmpty: "text-[12px] text-fg-subtle text-center py-pad-md",
438
+ historyList: "m-0 p-0 list-none flex flex-col gap-gp-sm",
439
+ historyItem: "flex gap-gp-md p-pad-md rounded-radius-md bg-bg-muted border border-border-subtle",
440
+ historyId: "text-[11px] text-fg-muted shrink-0 [font-variant-numeric:tabular-nums] pt-[1px]",
441
+ historyBody: "flex-1 min-w-0 flex flex-col gap-[1px]",
442
+ historySubject: "text-[12.5px] font-medium text-fg-default",
443
+ historyMeta: "text-[11px] text-fg-muted"
444
+ }
445
+ });
446
+ function DetailsColumn({
447
+ conversation,
448
+ detail,
449
+ openSections,
450
+ onToggleSection,
451
+ onClose,
452
+ width,
453
+ onResizeStart,
454
+ onBackToChat,
455
+ className
456
+ }) {
457
+ const s = detailsColumnStyles();
458
+ return /* @__PURE__ */ jsxs(
459
+ "aside",
460
+ {
461
+ "aria-label": "Detalhes do contato",
462
+ style: { width },
463
+ className: cn(s.root(), className),
464
+ children: [
465
+ /* @__PURE__ */ jsx(
466
+ "button",
467
+ {
468
+ type: "button",
469
+ onMouseDown: onResizeStart,
470
+ "aria-label": "Redimensionar painel de detalhes",
471
+ className: s.handle()
472
+ }
473
+ ),
474
+ /* @__PURE__ */ jsxs("header", { className: s.header(), children: [
475
+ onBackToChat && /* @__PURE__ */ jsx(
476
+ Button,
477
+ {
478
+ color: "secondary",
479
+ variant: "ghost",
480
+ size: "icon-sm",
481
+ "aria-label": "Voltar pra conversa",
482
+ onClick: onBackToChat,
483
+ className: "md:hidden",
484
+ children: /* @__PURE__ */ jsx(ArrowLeft, {})
485
+ }
486
+ ),
487
+ /* @__PURE__ */ jsx("h3", { className: s.title(), children: "Detalhes" }),
488
+ /* @__PURE__ */ jsx(
489
+ Button,
490
+ {
491
+ color: "secondary",
492
+ variant: "ghost",
493
+ size: "icon-sm",
494
+ "aria-label": "Fechar painel",
495
+ onClick: onClose,
496
+ children: /* @__PURE__ */ jsx(X, {})
497
+ }
498
+ )
499
+ ] }),
500
+ /* @__PURE__ */ jsxs("div", { className: s.body(), children: [
501
+ /* @__PURE__ */ jsxs(
502
+ DetailSection,
503
+ {
504
+ title: "Contato",
505
+ open: openSections.contact,
506
+ onToggle: () => onToggleSection("contact"),
507
+ children: [
508
+ /* @__PURE__ */ jsxs("div", { className: s.contactHead(), children: [
509
+ /* @__PURE__ */ jsx(
510
+ PersonAvatar,
511
+ {
512
+ initials: conversation.initials,
513
+ hex: conversation.avatarHex,
514
+ size: "lg"
515
+ }
516
+ ),
517
+ /* @__PURE__ */ jsx("div", { className: s.contactName(), children: conversation.name })
518
+ ] }),
519
+ /* @__PURE__ */ jsx(DetailField, { label: "Telefone", value: detail.phone }),
520
+ /* @__PURE__ */ jsx(DetailField, { label: "Email", value: detail.email }),
521
+ /* @__PURE__ */ jsx(DetailField, { label: "Primeiro contato", value: detail.firstContact })
522
+ ]
523
+ }
524
+ ),
525
+ /* @__PURE__ */ jsxs(
526
+ DetailSection,
527
+ {
528
+ title: "Atendimento",
529
+ open: openSections.ticket,
530
+ onToggle: () => onToggleSection("ticket"),
531
+ children: [
532
+ /* @__PURE__ */ jsx(DetailField, { label: "ID", value: conversation.id }),
533
+ /* @__PURE__ */ jsx(DetailField, { label: "Status", value: conversation.statusLabel }),
534
+ /* @__PURE__ */ jsx(DetailField, { label: "Atribuído a", value: detail.assignedTo }),
535
+ /* @__PURE__ */ jsx(DetailField, { label: "Canal", value: CHANNEL_LABEL[conversation.channel] }),
536
+ /* @__PURE__ */ jsx(DetailField, { label: "Criado em", value: detail.createdAt }),
537
+ /* @__PURE__ */ jsxs("div", { className: s.tagsRow(), children: [
538
+ /* @__PURE__ */ jsx("span", { className: s.tagsLabel(), children: "Tags" }),
539
+ /* @__PURE__ */ jsxs("div", { className: s.tagsChips(), children: [
540
+ /* @__PURE__ */ jsx(Chip, { color: conversation.tagKind, variant: "soft", size: "sm", shape: "pill", children: conversation.tag }),
541
+ detail.extraTags.map((t) => /* @__PURE__ */ jsx(Chip, { color: "info", variant: "soft", size: "sm", shape: "pill", children: t }, t)),
542
+ /* @__PURE__ */ jsxs("button", { type: "button", className: s.tagAddBtn(), children: [
543
+ /* @__PURE__ */ jsx(Plus, { size: 11, strokeWidth: 2.2 }),
544
+ " tag"
545
+ ] })
546
+ ] })
547
+ ] })
548
+ ]
549
+ }
550
+ ),
551
+ /* @__PURE__ */ jsx(
552
+ DetailSection,
553
+ {
554
+ title: "Histórico",
555
+ open: openSections.history,
556
+ onToggle: () => onToggleSection("history"),
557
+ children: detail.history.length === 0 ? /* @__PURE__ */ jsx("div", { className: s.historyEmpty(), children: "Nenhum atendimento anterior." }) : /* @__PURE__ */ jsx("ul", { className: s.historyList(), children: detail.history.map((h) => /* @__PURE__ */ jsxs("li", { className: s.historyItem(), children: [
558
+ /* @__PURE__ */ jsx("span", { className: s.historyId(), children: h.id }),
559
+ /* @__PURE__ */ jsxs("div", { className: s.historyBody(), children: [
560
+ /* @__PURE__ */ jsx("span", { className: s.historySubject(), children: h.subject }),
561
+ /* @__PURE__ */ jsxs("span", { className: s.historyMeta(), children: [
562
+ h.date,
563
+ " · ",
564
+ h.status
565
+ ] })
566
+ ] })
567
+ ] }, h.id)) })
568
+ }
569
+ )
570
+ ] })
571
+ ]
572
+ }
573
+ );
574
+ }
575
+ const filterRowStyles = tv({
576
+ slots: {
577
+ button: [
578
+ "flex items-center gap-gp-md w-full",
579
+ "px-pad-md py-pad-sm rounded-radius-md text-left",
580
+ "transition-colors duration-150 cursor-pointer"
581
+ ].join(" "),
582
+ /** Leading slot (icon ou dot fallback). */
583
+ leading: "shrink-0 inline-flex items-center justify-center size-[18px]",
584
+ /** Label do filtro (ellipsis). */
585
+ label: "flex-1 min-w-0 text-[12.5px] font-medium whitespace-nowrap overflow-hidden text-ellipsis",
586
+ /** Dot brand 6px pra "tem novidades". */
587
+ unreadDot: "size-[6px] rounded-radius-full bg-bg-brand shrink-0",
588
+ /** Contador à direita. */
589
+ count: "text-[11px] text-fg-muted shrink-0 [font-variant-numeric:tabular-nums]",
590
+ /** Dot fallback (quando item não tem icon). */
591
+ fallbackDot: "size-[8px] rounded-radius-full"
592
+ },
593
+ variants: {
594
+ isActive: {
595
+ true: {
596
+ button: "bg-bg-muted dark:bg-bg-emphasis text-fg-default font-semibold"
597
+ },
598
+ false: {
599
+ button: "text-fg-muted hover:bg-bg-muted hover:text-fg-default"
600
+ }
601
+ }
602
+ },
603
+ defaultVariants: {
604
+ isActive: false
605
+ }
606
+ });
607
+ function FilterRow({ item, isActive, onClick }) {
608
+ const Icon = item.icon;
609
+ const s = filterRowStyles({ isActive });
610
+ return /* @__PURE__ */ jsxs(
611
+ "button",
612
+ {
613
+ type: "button",
614
+ onClick,
615
+ "aria-pressed": isActive,
616
+ className: s.button(),
617
+ children: [
618
+ /* @__PURE__ */ jsx("span", { className: s.leading(), children: Icon ? /* @__PURE__ */ jsx(
619
+ Icon,
620
+ {
621
+ size: 15,
622
+ strokeWidth: 1.8,
623
+ style: { color: item.color ?? "var(--color-fg-muted)" }
624
+ }
625
+ ) : /* @__PURE__ */ jsx(
626
+ "span",
627
+ {
628
+ className: s.fallbackDot(),
629
+ style: { background: item.color ?? "var(--color-fg-muted)" },
630
+ "aria-hidden": true
631
+ }
632
+ ) }),
633
+ /* @__PURE__ */ jsx("span", { className: s.label(), children: item.name }),
634
+ item.hasUnread && /* @__PURE__ */ jsx("span", { className: s.unreadDot(), "aria-label": "tem novidades" }),
635
+ /* @__PURE__ */ jsx("span", { className: s.count(), children: item.count })
636
+ ]
637
+ }
638
+ );
639
+ }
640
+ const filtersColumnStyles = tv({
641
+ slots: {
642
+ /** Aside raiz da versão expandida (280px). */
643
+ root: "flex flex-col w-[280px] shrink-0 bg-bg-surface border border-border-subtle rounded-radius-xl overflow-hidden",
644
+ /** Aside raiz da versão rail (64px). */
645
+ rail: "flex flex-col w-[64px] shrink-0 bg-bg-surface border border-border-subtle rounded-radius-xl overflow-hidden",
646
+ /** Container scrollável das seções (expandida). */
647
+ scroll: "flex-1 min-h-0 overflow-y-auto px-pad-md py-pad-md flex flex-col gap-gp-lg",
648
+ /** Container scrollável (rail). */
649
+ railScroll: "flex-1 min-h-0 overflow-y-auto py-pad-md flex flex-col items-center gap-gp-sm",
650
+ /** Section wrapper (expandida). */
651
+ section: "flex flex-col gap-gp-2xs",
652
+ /** Header de section (título all-caps). Usa `subheading-strong-sm` —
653
+ token DS criado especificamente pra este caso (10px/700/+0.06em). */
654
+ sectionTitle: "px-pad-md pt-pad-2xs pb-pad-2xs text-[10px] font-bold tracking-[0.06em] text-fg-subtle uppercase",
655
+ /** Group wrapper (rail) — divider entre grupos. */
656
+ railGroup: "w-full flex flex-col items-center gap-gp-xs px-pad-md",
657
+ /** Modifier de divisor pros grupos não-primeiros (rail). */
658
+ railGroupDivider: "pt-pad-sm border-t border-border-subtle",
659
+ /** Modifier de divisor pras sections não-primeiras (expandida). */
660
+ sectionDivider: "pt-pad-lg border-t border-border-subtle"
661
+ }
662
+ });
663
+ function FiltersColumn({
664
+ filters,
665
+ onToggleFilter,
666
+ className
667
+ }) {
668
+ const s = filtersColumnStyles();
669
+ return /* @__PURE__ */ jsx("aside", { "aria-label": "Filtros", className: cn(s.root(), className), children: /* @__PURE__ */ jsx("div", { className: s.scroll(), children: FILTER_GROUPS.map((sec, i) => /* @__PURE__ */ jsx(
670
+ FilterSection,
671
+ {
672
+ title: sec.title,
673
+ group: sec.group,
674
+ items: sec.items,
675
+ filters,
676
+ onToggle: onToggleFilter,
677
+ isFirst: i === 0
678
+ },
679
+ sec.group
680
+ )) }) });
681
+ }
682
+ function FilterSection({
683
+ title,
684
+ group,
685
+ items,
686
+ filters,
687
+ onToggle,
688
+ isFirst
689
+ }) {
690
+ const s = filtersColumnStyles();
691
+ return /* @__PURE__ */ jsxs("section", { className: cn(s.section(), !isFirst && s.sectionDivider()), children: [
692
+ /* @__PURE__ */ jsx("div", { className: s.sectionTitle(), children: title }),
693
+ items.map((item) => /* @__PURE__ */ jsx(
694
+ FilterRow,
695
+ {
696
+ item,
697
+ isActive: filters[group].has(item.id),
698
+ onClick: () => onToggle(group, item.id)
699
+ },
700
+ item.id
701
+ ))
702
+ ] });
703
+ }
704
+ const railItemStyles = tv({
705
+ slots: {
706
+ button: [
707
+ "relative grid place-items-center size-form-md rounded-radius-md cursor-pointer",
708
+ "transition-colors duration-150"
709
+ ].join(" "),
710
+ /** Badge dot 6px (top-right) pra "tem novidades". */
711
+ unreadDot: "absolute top-[2px] right-[2px] size-[6px] rounded-radius-full bg-bg-brand",
712
+ /** Fallback dot (quando item não tem icon). */
713
+ fallbackDot: "size-[10px] rounded-radius-full"
714
+ },
715
+ variants: {
716
+ isActive: {
717
+ true: {
718
+ button: "bg-bg-muted dark:bg-bg-emphasis text-fg-default"
719
+ },
720
+ false: {
721
+ button: "text-fg-muted hover:bg-bg-muted hover:text-fg-default"
722
+ }
723
+ }
724
+ },
725
+ defaultVariants: {
726
+ isActive: false
727
+ }
728
+ });
729
+ function RailItem({ item, isActive, onClick }) {
730
+ const Icon = item.icon;
731
+ const s = railItemStyles({ isActive });
732
+ return /* @__PURE__ */ jsxs(
733
+ "button",
734
+ {
735
+ type: "button",
736
+ onClick,
737
+ "aria-pressed": isActive,
738
+ title: `${item.name} (${item.count})`,
739
+ className: s.button(),
740
+ children: [
741
+ Icon ? /* @__PURE__ */ jsx(
742
+ Icon,
743
+ {
744
+ size: 16,
745
+ strokeWidth: 1.8,
746
+ style: { color: item.color ?? "currentColor" }
747
+ }
748
+ ) : /* @__PURE__ */ jsx(
749
+ "span",
750
+ {
751
+ className: s.fallbackDot(),
752
+ style: { background: item.color ?? "var(--color-fg-muted)" },
753
+ "aria-hidden": true
754
+ }
755
+ ),
756
+ item.hasUnread && /* @__PURE__ */ jsx("span", { className: s.unreadDot(), "aria-label": "tem novidades" })
757
+ ]
758
+ }
759
+ );
760
+ }
761
+ function FiltersRail({
762
+ filters,
763
+ onToggleFilter,
764
+ className
765
+ }) {
766
+ const s = filtersColumnStyles();
767
+ return /* @__PURE__ */ jsx("aside", { "aria-label": "Filtros (compactos)", className: cn(s.rail(), className), children: /* @__PURE__ */ jsx("div", { className: s.railScroll(), children: FILTER_GROUPS.map(({ group, items }, gi) => /* @__PURE__ */ jsx(
768
+ "div",
769
+ {
770
+ className: cn(s.railGroup(), gi > 0 && s.railGroupDivider()),
771
+ children: items.map((item) => /* @__PURE__ */ jsx(
772
+ RailItem,
773
+ {
774
+ item,
775
+ isActive: filters[group].has(item.id),
776
+ onClick: () => onToggleFilter(group, item.id)
777
+ },
778
+ item.id
779
+ ))
780
+ },
781
+ group
782
+ )) }) });
783
+ }
784
+ const conversationListItemStyles = tv({
785
+ slots: {
786
+ /** Card raiz (button) — flex horizontal com avatar + conteúdo. */
787
+ root: [
788
+ "group/conv text-left relative",
789
+ "flex gap-gp-md p-pad-md rounded-radius-md",
790
+ "cursor-pointer transition-colors duration-150"
791
+ ].join(" "),
792
+ /** Coluna direita (3 rows). */
793
+ content: "flex-1 min-w-0 flex flex-col gap-[3px]",
794
+ /** Row 1 — nome + horário. */
795
+ rowTop: "flex items-center gap-gp-sm",
796
+ /** Nome (ellipsis). */
797
+ name: "text-[13px] font-semibold text-fg-default whitespace-nowrap overflow-hidden text-ellipsis flex-1 min-w-0",
798
+ /** Horário. */
799
+ time: "text-[11px] text-fg-muted shrink-0 [font-variant-numeric:tabular-nums]",
800
+ /** Row 2 — preview + badge unread. */
801
+ rowMid: "flex items-center gap-gp-sm",
802
+ /** Preview da última mensagem (ellipsis). */
803
+ preview: "text-[12px] text-fg-muted whitespace-nowrap overflow-hidden text-ellipsis flex-1 min-w-0",
804
+ /** Badge contador de não lidas — usa `subheading-strong-md` (11px/700) como
805
+ base e força `font-bold` + `leading-none` inline pra preservar o weight
806
+ e a centralização vertical do número dentro do círculo (preset DS sozinho
807
+ usa lineHeight 0.875rem, que descentraliza o conteúdo). */
808
+ unread: "inline-flex items-center justify-center min-w-[18px] h-[18px] px-[5px] rounded-radius-full bg-bg-brand text-fg-on-brand text-[10.5px] font-bold leading-none shrink-0 [font-variant-numeric:tabular-nums]",
809
+ /** Row 3 — canal + tag + ID. */
810
+ rowBot: "flex items-center gap-gp-sm",
811
+ /** ID da conversa (canto inferior direito). */
812
+ id: "ml-auto text-[10.5px] text-fg-subtle shrink-0 [font-variant-numeric:tabular-nums]"
813
+ },
814
+ variants: {
815
+ isActive: {
816
+ true: {
817
+ root: [
818
+ "bg-bg-muted dark:bg-bg-emphasis",
819
+ "before:content-[''] before:absolute before:left-0 before:top-[10px] before:bottom-[10px]",
820
+ "before:w-[3px] before:bg-bg-brand before:rounded-r-[2px] before:pointer-events-none"
821
+ ].join(" ")
822
+ },
823
+ false: {
824
+ root: "hover:bg-bg-muted"
825
+ }
826
+ }
827
+ },
828
+ defaultVariants: {
829
+ isActive: false
830
+ }
831
+ });
832
+ function ConversationListItem({
833
+ conv,
834
+ isActive,
835
+ onSelect
836
+ }) {
837
+ const s = conversationListItemStyles({ isActive });
838
+ return /* @__PURE__ */ jsxs(
839
+ "button",
840
+ {
841
+ type: "button",
842
+ onClick: () => onSelect(conv.id),
843
+ className: s.root(),
844
+ "aria-pressed": isActive,
845
+ children: [
846
+ /* @__PURE__ */ jsx(PersonAvatar, { initials: conv.initials, hex: conv.avatarHex, size: "md" }),
847
+ /* @__PURE__ */ jsxs("div", { className: s.content(), children: [
848
+ /* @__PURE__ */ jsxs("div", { className: s.rowTop(), children: [
849
+ /* @__PURE__ */ jsx("span", { className: s.name(), children: conv.name }),
850
+ /* @__PURE__ */ jsx("span", { className: s.time(), children: conv.time })
851
+ ] }),
852
+ /* @__PURE__ */ jsxs("div", { className: s.rowMid(), children: [
853
+ /* @__PURE__ */ jsx("span", { className: s.preview(), children: conv.last }),
854
+ conv.unread > 0 && /* @__PURE__ */ jsx("span", { className: s.unread(), "aria-label": `${conv.unread} não lidas`, children: conv.unread })
855
+ ] }),
856
+ /* @__PURE__ */ jsxs("div", { className: s.rowBot(), children: [
857
+ /* @__PURE__ */ jsx(ChannelDot, { channel: conv.channel }),
858
+ /* @__PURE__ */ jsx(Chip, { color: conv.tagKind, variant: "soft", size: "sm", shape: "pill", children: conv.tag }),
859
+ /* @__PURE__ */ jsx("span", { className: s.id(), children: conv.id })
860
+ ] })
861
+ ] })
862
+ ]
863
+ }
864
+ );
865
+ }
866
+ const queueColumnStyles = tv({
867
+ slots: {
868
+ root: "flex flex-col w-[340px] shrink-0 bg-bg-surface border border-border-subtle rounded-radius-xl overflow-hidden",
869
+ header: "flex items-center gap-gp-sm px-pad-md pt-pad-md pb-pad-md",
870
+ title: "m-0 text-title-md text-fg-default flex-1 min-w-0",
871
+ searchWrap: "px-pad-md pb-pad-lg",
872
+ searchInner: "relative",
873
+ searchIcon: "absolute left-pad-lg top-1/2 -translate-y-1/2 text-fg-muted pointer-events-none",
874
+ searchInput: "pl-[34px] pr-[30px]",
875
+ searchClear: "absolute right-pad-md top-1/2 -translate-y-1/2 grid place-items-center size-[20px] rounded-radius-md text-fg-muted hover:bg-bg-muted hover:text-fg-default transition-colors",
876
+ chipsWrap: "px-pad-md pb-pad-2xl",
877
+ list: "flex-1 min-h-0 overflow-y-auto px-pad-md pb-pad-md flex flex-col gap-gp-xs",
878
+ empty: "py-pad-4xl px-pad-xl text-center text-[12px] text-fg-subtle"
879
+ }
880
+ });
881
+ function QueueColumn({
882
+ activeId,
883
+ onSelect,
884
+ searchQuery,
885
+ onSearchChange,
886
+ queueFilter,
887
+ onQueueFilterChange,
888
+ filtersCollapsed,
889
+ onToggleFilters,
890
+ className
891
+ }) {
892
+ const s = queueColumnStyles();
893
+ const filtered = useMemo(() => {
894
+ const q = searchQuery.trim().toLowerCase();
895
+ const bySearch = q ? CONVERSATIONS.filter(
896
+ (c) => c.name.toLowerCase().includes(q) || c.id.toLowerCase().includes(q) || c.last.toLowerCase().includes(q)
897
+ ) : CONVERSATIONS;
898
+ if (queueFilter === "unread") return bySearch.filter((c) => c.unread > 0);
899
+ if (queueFilter === "read") return bySearch.filter((c) => c.unread === 0);
900
+ return bySearch;
901
+ }, [searchQuery, queueFilter]);
902
+ const counts = useMemo(() => {
903
+ const q = searchQuery.trim().toLowerCase();
904
+ const bySearch = q ? CONVERSATIONS.filter(
905
+ (c) => c.name.toLowerCase().includes(q) || c.id.toLowerCase().includes(q) || c.last.toLowerCase().includes(q)
906
+ ) : CONVERSATIONS;
907
+ return {
908
+ all: bySearch.length,
909
+ unread: bySearch.filter((c) => c.unread > 0).length,
910
+ read: bySearch.filter((c) => c.unread === 0).length
911
+ };
912
+ }, [searchQuery]);
913
+ return /* @__PURE__ */ jsxs("aside", { "aria-label": "Fila de conversas", className: cn(s.root(), className), children: [
914
+ /* @__PURE__ */ jsxs("header", { className: s.header(), children: [
915
+ /* @__PURE__ */ jsx(
916
+ Button,
917
+ {
918
+ color: "secondary",
919
+ variant: "ghost",
920
+ size: "icon-sm",
921
+ onClick: onToggleFilters,
922
+ "aria-label": filtersCollapsed ? "Expandir filtros" : "Colapsar filtros",
923
+ "aria-pressed": !filtersCollapsed,
924
+ children: /* @__PURE__ */ jsx(Menu, {})
925
+ }
926
+ ),
927
+ /* @__PURE__ */ jsx("h2", { className: s.title(), children: "Conversas" }),
928
+ /* @__PURE__ */ jsx(Chip, { color: "primary", variant: "soft", size: "sm", shape: "rounded", children: filtered.length })
929
+ ] }),
930
+ /* @__PURE__ */ jsx("div", { className: s.searchWrap(), children: /* @__PURE__ */ jsxs("div", { className: s.searchInner(), children: [
931
+ /* @__PURE__ */ jsx(Search, { size: 14, strokeWidth: 1.8, className: s.searchIcon() }),
932
+ /* @__PURE__ */ jsx(
933
+ Input,
934
+ {
935
+ type: "text",
936
+ placeholder: "Buscar conversa, ID ou nome...",
937
+ value: searchQuery,
938
+ onChange: (e) => onSearchChange(e.target.value),
939
+ className: s.searchInput()
940
+ }
941
+ ),
942
+ searchQuery && /* @__PURE__ */ jsx(
943
+ "button",
944
+ {
945
+ type: "button",
946
+ onClick: () => onSearchChange(""),
947
+ className: s.searchClear(),
948
+ "aria-label": "Limpar busca",
949
+ children: /* @__PURE__ */ jsx(X, { size: 12 })
950
+ }
951
+ )
952
+ ] }) }),
953
+ /* @__PURE__ */ jsx("div", { className: s.chipsWrap(), children: /* @__PURE__ */ jsxs(
954
+ ChipGroup,
955
+ {
956
+ type: "single",
957
+ value: queueFilter,
958
+ onValueChange: (v) => onQueueFilterChange(v || "all"),
959
+ size: "md",
960
+ ariaLabel: "Filtrar conversas",
961
+ children: [
962
+ /* @__PURE__ */ jsxs(ChipGroupItem, { value: "all", children: [
963
+ "Todas",
964
+ counts.all > 0 && ` (${counts.all})`
965
+ ] }),
966
+ /* @__PURE__ */ jsxs(ChipGroupItem, { value: "unread", children: [
967
+ "Não lidas",
968
+ counts.unread > 0 && ` (${counts.unread})`
969
+ ] }),
970
+ /* @__PURE__ */ jsxs(ChipGroupItem, { value: "read", children: [
971
+ "Lidas",
972
+ counts.read > 0 && ` (${counts.read})`
973
+ ] })
974
+ ]
975
+ }
976
+ ) }),
977
+ /* @__PURE__ */ jsx("div", { className: s.list(), children: filtered.length === 0 ? /* @__PURE__ */ jsx("div", { className: s.empty(), children: "Nenhuma conversa encontrada." }) : filtered.map((c) => /* @__PURE__ */ jsx(
978
+ ConversationListItem,
979
+ {
980
+ conv: c,
981
+ isActive: c.id === activeId,
982
+ onSelect
983
+ },
984
+ c.id
985
+ )) })
986
+ ] });
987
+ }
988
+ function ChatV2() {
989
+ const s = chatV2Styles();
990
+ const [activeId, setActiveId] = useState(CONVERSATIONS[0].id);
991
+ const [searchQuery, setSearchQuery] = useState("");
992
+ const { theme, setTheme } = useTheme();
993
+ const [mobileView, setMobileView] = useState("list");
994
+ const handleSelectConversation = (id) => {
995
+ setActiveId(id);
996
+ setMobileView("chat");
997
+ };
998
+ const handleToggleDetails = () => {
999
+ const isMobile = typeof window !== "undefined" && window.matchMedia("(max-width: 767px)").matches;
1000
+ if (isMobile) {
1001
+ setDetailsOpen(true);
1002
+ setMobileView((v) => v === "details" ? "chat" : "details");
1003
+ } else {
1004
+ setDetailsOpen((o) => !o);
1005
+ }
1006
+ };
1007
+ const [filters, setFilters] = useState({
1008
+ status: /* @__PURE__ */ new Set(["open"]),
1009
+ channels: /* @__PURE__ */ new Set(),
1010
+ categories: /* @__PURE__ */ new Set(),
1011
+ innerStatus: /* @__PURE__ */ new Set()
1012
+ });
1013
+ const toggleFilter = (group, id) => {
1014
+ setFilters((prev) => {
1015
+ const next = new Set(prev[group]);
1016
+ if (next.has(id)) next.delete(id);
1017
+ else next.add(id);
1018
+ return { ...prev, [group]: next };
1019
+ });
1020
+ };
1021
+ const [filtersCollapsed, setFiltersCollapsed] = useState(false);
1022
+ const [queueFilter, setQueueFilter] = useState("all");
1023
+ const [detailsOpen, setDetailsOpen] = useState(true);
1024
+ const [openSections, setOpenSections] = useState({
1025
+ contact: true,
1026
+ ticket: true,
1027
+ history: true
1028
+ });
1029
+ const toggleSection = (id) => setOpenSections((m) => ({ ...m, [id]: !m[id] }));
1030
+ const detailsResize = useResizable({
1031
+ initial: 360,
1032
+ min: 280,
1033
+ max: 520,
1034
+ // Mantém storageKey diferente da ChatShowcase pra não conflitar valores entre as 2 páginas.
1035
+ storageKey: "chat-v2.detailsWidth"
1036
+ });
1037
+ const activeConversation = CONVERSATIONS.find((c) => c.id === activeId) ?? CONVERSATIONS[0];
1038
+ const activeDetail = CONVERSATION_DETAILS[activeConversation.id] ?? DEFAULT_DETAIL;
1039
+ return /* @__PURE__ */ jsx(
1040
+ AppShell,
1041
+ {
1042
+ contexts: APP_SHELL_CONTEXTS,
1043
+ defaultActiveContextId: "inbox",
1044
+ defaultActiveItemHref: "#chat-v2",
1045
+ breadcrumb: [{ label: "Atendimentos" }],
1046
+ commandGroups: APP_SHELL_COMMANDS,
1047
+ notifications: {
1048
+ items: APP_SHELL_NOTIFICATIONS,
1049
+ onMarkAllRead: () => alert("Marcar todas como lidas"),
1050
+ onMoreActions: () => alert("Mais ações"),
1051
+ onViewAll: () => alert("Ver todas")
1052
+ },
1053
+ messages: {
1054
+ items: APP_SHELL_MESSAGES,
1055
+ onNewMessage: () => alert("Nova mensagem"),
1056
+ onExpand: () => alert("Expandir"),
1057
+ onViewAll: () => alert("Ver todas")
1058
+ },
1059
+ theme,
1060
+ onThemeChange: (id) => setTheme(id),
1061
+ themeOptions: APP_SHELL_THEME_OPTIONS,
1062
+ mobileEdgeToEdge: true,
1063
+ children: /* @__PURE__ */ jsxs("div", { className: s.root(), children: [
1064
+ /* @__PURE__ */ jsx("div", { className: s.filtersWrap(), children: filtersCollapsed ? /* @__PURE__ */ jsx(FiltersRail, { filters, onToggleFilter: toggleFilter }) : /* @__PURE__ */ jsx(FiltersColumn, { filters, onToggleFilter: toggleFilter }) }),
1065
+ /* @__PURE__ */ jsx(
1066
+ QueueColumn,
1067
+ {
1068
+ activeId,
1069
+ onSelect: handleSelectConversation,
1070
+ searchQuery,
1071
+ onSearchChange: setSearchQuery,
1072
+ queueFilter,
1073
+ onQueueFilterChange: setQueueFilter,
1074
+ filtersCollapsed,
1075
+ onToggleFilters: () => setFiltersCollapsed((c) => !c),
1076
+ className: cn(
1077
+ mobileView === "list" ? `${mobileOverlay.active} max-md:z-[1]` : mobileOverlay.hidden
1078
+ )
1079
+ }
1080
+ ),
1081
+ /* @__PURE__ */ jsx(
1082
+ ConversationColumn,
1083
+ {
1084
+ conversation: activeConversation,
1085
+ detailsOpen,
1086
+ onToggleDetails: handleToggleDetails,
1087
+ onBackToList: () => setMobileView("list"),
1088
+ className: cn(
1089
+ mobileView === "chat" ? `${mobileOverlay.active} max-md:z-[2]` : mobileOverlay.hidden
1090
+ )
1091
+ }
1092
+ ),
1093
+ detailsOpen && /* @__PURE__ */ jsx(
1094
+ DetailsColumn,
1095
+ {
1096
+ conversation: activeConversation,
1097
+ detail: activeDetail,
1098
+ openSections,
1099
+ onToggleSection: toggleSection,
1100
+ onClose: () => {
1101
+ setDetailsOpen(false);
1102
+ setMobileView("chat");
1103
+ },
1104
+ onBackToChat: () => setMobileView("chat"),
1105
+ width: detailsResize.width,
1106
+ onResizeStart: detailsResize.onResizeStart,
1107
+ className: cn(
1108
+ mobileView === "details" ? `${mobileOverlay.active} max-md:!w-full max-md:z-[3]` : mobileOverlay.hidden
1109
+ )
1110
+ }
1111
+ )
1112
+ ] })
1113
+ }
1114
+ );
1115
+ }
1116
+ export {
1117
+ ChatV2,
1118
+ ChatV2 as default
1119
+ };
1120
+ //# sourceMappingURL=chat.mjs.map