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