@adatechnology/conversations-ui 0.1.0-rc.1 → 0.1.0-rc.11

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 (84) hide show
  1. package/dist/{chunk-ZDURDZTM.js → chunk-2AYDBWNE.js} +14 -0
  2. package/dist/chunk-3RKFU46R.js +1778 -0
  3. package/dist/flows/index.js +3 -3
  4. package/dist/index.d.ts +644 -136
  5. package/dist/index.js +1068 -1024
  6. package/dist/preview/index.d.ts +341 -0
  7. package/dist/preview/index.js +1296 -0
  8. package/dist/styles.css +198 -0
  9. package/dist/types-B5C1DLu1.d.ts +365 -0
  10. package/package.json +10 -3
  11. package/src/Avatar.tsx +30 -4
  12. package/src/ChannelIcon.tsx +87 -0
  13. package/src/ConversationContextPanel.tsx +106 -0
  14. package/src/ConversationDocumentsPanel.tsx +425 -0
  15. package/src/ConversationHeader.tsx +257 -0
  16. package/src/ConversationListItem.tsx +54 -7
  17. package/src/ConversationLocalesProvider.tsx +16 -0
  18. package/src/ConversationRow.tsx +137 -0
  19. package/src/DateDivider.tsx +16 -3
  20. package/src/DocumentsLibrary.tsx +322 -0
  21. package/src/EmojiPicker.tsx +69 -55
  22. package/src/FileIcon.test.ts +83 -0
  23. package/src/FileIcon.tsx +88 -11
  24. package/src/InteractiveMessage.test.tsx +41 -0
  25. package/src/InteractiveMessage.tsx +143 -0
  26. package/src/Lightbox.tsx +18 -3
  27. package/src/MediaRenderer.tsx +14 -11
  28. package/src/MessageBubble.tsx +54 -5
  29. package/src/MessageComposer.test.tsx +35 -0
  30. package/src/MessageComposer.tsx +146 -19
  31. package/src/Wallpaper.test.tsx +21 -0
  32. package/src/Wallpaper.tsx +54 -6
  33. package/src/WhatsAppMessageEditor.tsx +28 -4
  34. package/src/WindowExpiredNotice.tsx +57 -0
  35. package/src/conversationChannel.test.ts +53 -0
  36. package/src/conversationChannel.ts +146 -0
  37. package/src/conversationTranscript.test.ts +65 -0
  38. package/src/conversationTranscript.ts +64 -0
  39. package/src/conversationWindow.test.ts +90 -0
  40. package/src/conversationWindow.ts +78 -0
  41. package/src/emojiCatalog.test.ts +35 -0
  42. package/src/emojiCatalog.ts +189 -0
  43. package/src/flows/FlowMapCanvas.tsx +2 -2
  44. package/src/hooks/useConversationActions.ts +56 -0
  45. package/src/hooks/useConversationDocuments.ts +15 -9
  46. package/src/hooks/useConversationList.ts +15 -9
  47. package/src/hooks/useConversationMessages.ts +2 -2
  48. package/src/index.ts +114 -16
  49. package/src/lib/cn.test.ts +29 -0
  50. package/src/lib/cn.ts +15 -0
  51. package/src/lib/createMediaUrlResolver.ts +33 -0
  52. package/src/lib/paginated.test.ts +33 -0
  53. package/src/lib/paginated.ts +26 -0
  54. package/src/lib/phone.ts +34 -0
  55. package/src/preview/AudioRecorderButton.tsx +155 -0
  56. package/src/preview/ConversationPreview.tsx +322 -0
  57. package/src/preview/MediaTypesPreview.tsx +87 -0
  58. package/src/preview/audioRecorderFormat.test.ts +67 -0
  59. package/src/preview/conversationPreviewFailures.test.ts +64 -0
  60. package/src/preview/createMockConversationsApi.ts +271 -0
  61. package/src/preview/createMockSSEProvider.ts +40 -0
  62. package/src/preview/createPreviewWebhookClient.test.ts +105 -0
  63. package/src/preview/createPreviewWebhookClient.ts +126 -0
  64. package/src/preview/index.ts +54 -0
  65. package/src/preview/mediaTypeOf.test.ts +15 -0
  66. package/src/preview/mockDocumentsSearch.test.ts +57 -0
  67. package/src/preview/mockEventSource.ts +53 -0
  68. package/src/preview/preview.test.ts +177 -0
  69. package/src/preview/previewFileSamples.test.ts +151 -0
  70. package/src/preview/previewFileSamples.ts +74 -0
  71. package/src/preview/previewFixtures.ts +440 -0
  72. package/src/preview/previewMediaSource.test.ts +62 -0
  73. package/src/preview/previewMediaSource.ts +91 -0
  74. package/src/preview/previewStore.ts +193 -0
  75. package/src/preview/startPreviewScript.ts +60 -0
  76. package/src/providers/types.ts +163 -11
  77. package/src/quickReply.test.ts +58 -0
  78. package/src/settings/WhatsAppCreateTemplateForm.tsx +3 -1
  79. package/src/settings/WhatsAppTemplatesSettings.tsx +106 -0
  80. package/src/styles.css +136 -0
  81. package/src/types.ts +46 -1
  82. package/src/useDarkMode.ts +26 -0
  83. package/src/useIsNarrow.ts +29 -0
  84. package/src/useWaitingNotifications.ts +74 -29
package/dist/index.js CHANGED
@@ -1,652 +1,69 @@
1
+ import {
2
+ AudioPlayer,
3
+ ConversationDocumentsPanel,
4
+ ConversationLocalesProvider,
5
+ ConversationWallpaper,
6
+ ConversationsProvider,
7
+ DEFAULT_ACCEPTED_FILE_TYPES,
8
+ DEFAULT_CONVERSATION_DOCUMENTS_LABELS,
9
+ DEFAULT_DOCUMENTS_LIBRARY_LABELS,
10
+ DEFAULT_EMOJI_PICKER_LABELS,
11
+ DEFAULT_INTERACTIVE_MESSAGE_LABELS,
12
+ DEFAULT_LIGHTBOX_LABELS,
13
+ DEFAULT_MESSAGE_COMPOSER_LABELS,
14
+ DOCUMENT_SOURCE_FILTER,
15
+ DateDivider,
16
+ DocumentsLibrary,
17
+ EMOJI_CATEGORIES,
18
+ EmojiPicker,
19
+ FileIcon,
20
+ InteractiveMessage,
21
+ Lightbox,
22
+ MediaRenderer,
23
+ MessageBubble,
24
+ MessageComposer,
25
+ StatusTicks,
26
+ applyQuickReplyVariables,
27
+ cn,
28
+ conversationsOf,
29
+ createMediaUrlResolver,
30
+ formatDateTime,
31
+ formatFileSize,
32
+ formatPhone,
33
+ formatTimestamp,
34
+ isSameDay,
35
+ phoneCountryFlag,
36
+ phoneInitials,
37
+ resolveQuickReply,
38
+ searchEmojis,
39
+ totalOf,
40
+ useAsyncResource,
41
+ useConversationDocuments,
42
+ useConversationLocales,
43
+ useConversations
44
+ } from "./chunk-3RKFU46R.js";
1
45
  import {
2
46
  htmlToWA,
3
47
  parseWhatsAppFormatting,
4
48
  useDarkMode,
49
+ useIsDarkTheme,
5
50
  waToHTML,
6
51
  waToHTMLInline
7
- } from "./chunk-ZDURDZTM.js";
8
-
9
- // src/MessageBubble.tsx
10
- import { useState as useState3 } from "react";
11
- import { Check } from "lucide-react";
12
-
13
- // src/ConversationLocalesProvider.tsx
14
- import { createContext, useContext } from "react";
15
- import { jsx } from "react/jsx-runtime";
16
- var DEFAULT_LOCALES = {
17
- bubble: {
18
- customer: "Cliente",
19
- bot: "Bot",
20
- agent: "Atendente",
21
- document: "documento",
22
- media: "m\xEDdia",
23
- templateLabel: "Template",
24
- readAt: "Lida \xE0s ",
25
- windowExpired: "Janela de 24h expirada",
26
- viewImage: "Ver imagem",
27
- listenAudio: "Ouvir \xE1udio",
28
- viewVideo: "Ver v\xEDdeo"
29
- },
30
- selection: {
31
- select: "Selecionar"
32
- },
33
- dateDivider: {
34
- today: "Hoje",
35
- yesterday: "Ontem"
36
- }
37
- };
38
- var ConversationLocalesContext = createContext(DEFAULT_LOCALES);
39
- function ConversationLocalesProvider({ children, locales }) {
40
- const merged = {
41
- bubble: { ...DEFAULT_LOCALES.bubble, ...locales?.bubble },
42
- selection: { ...DEFAULT_LOCALES.selection, ...locales?.selection },
43
- dateDivider: { ...DEFAULT_LOCALES.dateDivider, ...locales?.dateDivider }
44
- };
45
- return /* @__PURE__ */ jsx(ConversationLocalesContext.Provider, { value: merged, children });
46
- }
47
- function useConversationLocales() {
48
- return useContext(ConversationLocalesContext);
49
- }
50
-
51
- // src/StatusTicks.tsx
52
- import { AlertTriangle } from "lucide-react";
53
- import { jsx as jsx2, jsxs } from "react/jsx-runtime";
54
- var STATUS_COLOR_CLASS = {
55
- sent: "text-black/40 dark:text-white/40",
56
- delivered: "text-black/40 dark:text-white/40",
57
- read: "text-sky-500",
58
- failed: "text-red-500"
59
- };
60
- function Ticks({ double }) {
61
- return /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 20 12", width: "15", height: "9", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [
62
- double && /* @__PURE__ */ jsx2("path", { d: "M1 6.5L4.5 10L11 2", stroke: "currentColor", strokeWidth: "1.6", strokeLinecap: "round", strokeLinejoin: "round" }),
63
- /* @__PURE__ */ jsx2(
64
- "path",
65
- {
66
- d: double ? "M6 6.5L9.5 10L19 1" : "M1 6.5L5 10.5L14.5 1",
67
- stroke: "currentColor",
68
- strokeWidth: "1.6",
69
- strokeLinecap: "round",
70
- strokeLinejoin: "round"
71
- }
72
- )
73
- ] });
74
- }
75
- function StatusTicks({ status, title }) {
76
- const colorClass = STATUS_COLOR_CLASS[status] ?? STATUS_COLOR_CLASS.sent;
77
- return /* @__PURE__ */ jsx2("span", { className: `cursor-help leading-none flex items-center ${colorClass}`, title, children: status === "failed" ? /* @__PURE__ */ jsx2(AlertTriangle, { size: 11 }) : /* @__PURE__ */ jsx2(Ticks, { double: status !== "sent" }) });
78
- }
79
-
80
- // src/MediaRenderer.tsx
81
- import { useState as useState2 } from "react";
82
-
83
- // src/AudioPlayer.tsx
84
- import { useEffect, useRef, useState } from "react";
85
- import { Pause, Play } from "lucide-react";
86
- import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
87
- var BARS = Array.from({ length: 30 }, (_, i) => {
88
- const heights = [3, 5, 8, 12, 7, 10, 14, 9, 6, 11, 15, 8, 4, 13, 7, 10, 6, 12, 9, 5, 14, 8, 11, 6, 13, 7, 10, 5, 9, 4];
89
- return heights[i % heights.length];
90
- });
91
- function fmt(sec) {
92
- if (!isFinite(sec)) return "0:00";
93
- const m = Math.floor(sec / 60);
94
- const s = Math.floor(sec % 60);
95
- return `${m}:${s.toString().padStart(2, "0")}`;
96
- }
97
- function AudioPlayer({ src, isMine = false }) {
98
- const audioRef = useRef(null);
99
- const [playing, setPlaying] = useState(false);
100
- const [progress, setProgress] = useState(0);
101
- const [duration, setDuration] = useState(0);
102
- const [current, setCurrent] = useState(0);
103
- useEffect(() => {
104
- const audio = audioRef.current;
105
- if (!audio) return;
106
- const onTime = () => {
107
- setCurrent(audio.currentTime);
108
- setProgress(audio.duration ? audio.currentTime / audio.duration : 0);
109
- };
110
- const onMeta = () => setDuration(audio.duration);
111
- const onEnd = () => {
112
- setPlaying(false);
113
- setProgress(0);
114
- setCurrent(0);
115
- };
116
- audio.addEventListener("timeupdate", onTime);
117
- audio.addEventListener("loadedmetadata", onMeta);
118
- audio.addEventListener("ended", onEnd);
119
- return () => {
120
- audio.removeEventListener("timeupdate", onTime);
121
- audio.removeEventListener("loadedmetadata", onMeta);
122
- audio.removeEventListener("ended", onEnd);
123
- };
124
- }, []);
125
- const toggle = () => {
126
- const audio = audioRef.current;
127
- if (!audio) return;
128
- if (playing) {
129
- audio.pause();
130
- setPlaying(false);
131
- } else {
132
- audio.play();
133
- setPlaying(true);
134
- }
135
- };
136
- const seek = (e) => {
137
- const audio = audioRef.current;
138
- if (!audio || !audio.duration) return;
139
- const rect = e.currentTarget.getBoundingClientRect();
140
- const ratio = (e.clientX - rect.left) / rect.width;
141
- audio.currentTime = ratio * audio.duration;
142
- };
143
- const activeBars = Math.round(progress * BARS.length);
144
- const playBtn = isMine ? "bg-green-600 hover:bg-green-700 text-white" : "bg-gray-600 hover:bg-gray-700 text-white";
145
- const activeBar = isMine ? "bg-green-700" : "bg-gray-700";
146
- const inactiveBar = isMine ? "bg-green-300" : "bg-gray-300";
147
- return /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-2 w-full", style: { minWidth: "200px", maxWidth: "260px" }, children: [
148
- /* @__PURE__ */ jsx3("audio", { ref: audioRef, src, preload: "metadata" }),
149
- /* @__PURE__ */ jsx3(
150
- "button",
151
- {
152
- onClick: toggle,
153
- className: `flex-shrink-0 w-9 h-9 rounded-full flex items-center justify-center transition-colors ${playBtn}`,
154
- children: playing ? /* @__PURE__ */ jsx3(Pause, { size: 16 }) : /* @__PURE__ */ jsx3(Play, { size: 16, className: "translate-x-0.5" })
155
- }
156
- ),
157
- /* @__PURE__ */ jsxs2("div", { className: "flex-1 flex flex-col gap-1", children: [
158
- /* @__PURE__ */ jsx3("div", { className: "flex items-end gap-0.5 h-8 cursor-pointer", onClick: seek, children: BARS.map((h, i) => /* @__PURE__ */ jsx3(
159
- "div",
160
- {
161
- className: `flex-1 rounded-full transition-colors ${i < activeBars ? activeBar : inactiveBar}`,
162
- style: { height: `${h * 2}px` }
163
- },
164
- i
165
- )) }),
166
- /* @__PURE__ */ jsx3("span", { className: "text-gray-400 tabular-nums text-xs", children: playing || current > 0 ? fmt(current) : fmt(duration) })
167
- ] })
168
- ] });
169
- }
170
-
171
- // src/FileIcon.tsx
172
- import { FileArchive, FileSpreadsheet, FileText, File as FileGeneric } from "lucide-react";
173
- import { jsx as jsx4 } from "react/jsx-runtime";
174
- var EXTENSION_STYLE = {
175
- pdf: { Icon: FileText, colorClass: "text-red-500" },
176
- doc: { Icon: FileText, colorClass: "text-blue-500" },
177
- docx: { Icon: FileText, colorClass: "text-blue-500" },
178
- xls: { Icon: FileSpreadsheet, colorClass: "text-green-600" },
179
- xlsx: { Icon: FileSpreadsheet, colorClass: "text-green-600" },
180
- zip: { Icon: FileArchive, colorClass: "text-orange-500" }
181
- };
182
- function getExtension(filename, mimeType) {
183
- const fromFilename = filename?.split(".").pop()?.toLowerCase();
184
- if (fromFilename && EXTENSION_STYLE[fromFilename]) return fromFilename;
185
- return mimeType?.split("/")[1]?.toLowerCase() ?? "";
186
- }
187
- function FileIcon({ filename, mimeType, size = 20, className = "" }) {
188
- const extension = getExtension(filename, mimeType);
189
- const style = EXTENSION_STYLE[extension] ?? { Icon: FileGeneric, colorClass: "text-gray-500" };
190
- const { Icon, colorClass } = style;
191
- return /* @__PURE__ */ jsx4(Icon, { size, className: `${colorClass} ${className}`.trim() });
192
- }
193
-
194
- // src/lib/format.ts
195
- function formatTimestamp(timestamp) {
196
- try {
197
- const date = new Date(timestamp);
198
- const hours = date.getHours().toString().padStart(2, "0");
199
- const minutes = date.getMinutes().toString().padStart(2, "0");
200
- return `${hours}:${minutes}`;
201
- } catch {
202
- return timestamp;
203
- }
204
- }
205
- function formatDateTime(iso) {
206
- const d = new Date(iso);
207
- return d.toLocaleString("pt-BR", { day: "2-digit", month: "2-digit", hour: "2-digit", minute: "2-digit" });
208
- }
209
- function isSameDay(a, b) {
210
- return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();
211
- }
212
- var FILE_SIZE_UNITS = ["B", "KB", "MB", "GB"];
213
- function formatFileSize(bytes) {
214
- if (!isFinite(bytes) || bytes < 0) return "";
215
- if (bytes < 1) return "0 B";
216
- const exponent = Math.min(Math.floor(Math.log(bytes) / Math.log(1024)), FILE_SIZE_UNITS.length - 1);
217
- const value = bytes / 1024 ** exponent;
218
- const formatted = exponent === 0 ? value.toString() : value.toFixed(value < 10 ? 1 : 0);
219
- return `${formatted} ${FILE_SIZE_UNITS[exponent]}`;
220
- }
221
-
222
- // src/MediaRenderer.tsx
223
- import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
224
- function resolveMediaSource(message) {
225
- if (message.mediaUrl) return message.mediaUrl;
226
- if (message.base64) {
227
- const prefix = message.mimeType ? `data:${message.mimeType};base64,` : "data:application/octet-stream;base64,";
228
- return prefix + message.base64;
229
- }
230
- return null;
231
- }
232
- function hasLazyRef(message) {
233
- return Boolean(message.uploadId || message.mediaId);
234
- }
235
- function useLazyMediaUrl(message, onResolveUrl) {
236
- const [url, setUrl] = useState2(null);
237
- const [loading, setLoading] = useState2(false);
238
- const [error, setError] = useState2(false);
239
- const load = async () => {
240
- if (url || loading || !onResolveUrl) return;
241
- setLoading(true);
242
- setError(false);
243
- try {
244
- const resolved = await onResolveUrl(message);
245
- if (resolved) setUrl(resolved);
246
- else setError(true);
247
- } catch {
248
- setError(true);
249
- } finally {
250
- setLoading(false);
251
- }
252
- };
253
- return { url, loading, error, load };
254
- }
255
- function MediaRenderer({ message, onLightbox, onResolveUrl }) {
256
- const { bubble } = useConversationLocales();
257
- const eagerSrc = resolveMediaSource(message);
258
- const lazy = useLazyMediaUrl(message, onResolveUrl);
259
- const src = eagerSrc ?? lazy.url;
260
- const canLazyLoad = !eagerSrc && hasLazyRef(message) && Boolean(onResolveUrl);
261
- const lazyButtonClass = "text-xs text-blue-600 underline flex items-center gap-1";
262
- switch (message.type) {
263
- case "image":
264
- case "sticker": {
265
- if (!src && canLazyLoad) {
266
- return /* @__PURE__ */ jsx5("button", { onClick: lazy.load, className: lazyButtonClass, children: lazy.loading ? "Carregando..." : lazy.error ? "Erro \u2014 tentar novamente" : bubble.viewImage });
267
- }
268
- return /* @__PURE__ */ jsx5("div", { className: "min-w-[200px]", children: src ? /* @__PURE__ */ jsx5("img", { src, alt: message.caption ?? "Image", className: "w-full max-h-80 object-cover cursor-pointer hover:opacity-90 transition-opacity", onClick: () => onLightbox(src), loading: "lazy" }) : /* @__PURE__ */ jsx5("div", { className: "w-full h-40 bg-gray-200 flex items-center justify-center text-gray-400", children: /* @__PURE__ */ jsxs3("svg", { width: "32", height: "32", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.5", children: [
269
- /* @__PURE__ */ jsx5("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2", ry: "2" }),
270
- /* @__PURE__ */ jsx5("circle", { cx: "8.5", cy: "8.5", r: "1.5" }),
271
- /* @__PURE__ */ jsx5("polyline", { points: "21 15 16 10 5 21" })
272
- ] }) }) });
273
- }
274
- case "video": {
275
- if (!src && canLazyLoad) {
276
- return /* @__PURE__ */ jsx5("button", { onClick: lazy.load, className: lazyButtonClass, children: lazy.loading ? "Carregando..." : lazy.error ? "Erro \u2014 tentar novamente" : bubble.viewVideo });
277
- }
278
- return /* @__PURE__ */ jsx5("div", { className: "min-w-[200px]", children: src ? /* @__PURE__ */ jsx5("video", { src, className: "w-full max-h-80 rounded-lg", controls: true, preload: "metadata", children: /* @__PURE__ */ jsx5("track", { kind: "captions" }) }) : /* @__PURE__ */ jsx5("div", { className: "w-full h-32 bg-gray-200 rounded-lg flex items-center justify-center text-gray-400", children: /* @__PURE__ */ jsxs3("svg", { width: "32", height: "32", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.5", children: [
279
- /* @__PURE__ */ jsx5("polygon", { points: "23 7 16 12 23 17 23 7" }),
280
- /* @__PURE__ */ jsx5("rect", { x: "1", y: "5", width: "15", height: "14", rx: "2", ry: "2" })
281
- ] }) }) });
282
- }
283
- case "audio": {
284
- if (!src && canLazyLoad) {
285
- return /* @__PURE__ */ jsx5("button", { onClick: lazy.load, className: lazyButtonClass, children: lazy.loading ? "Carregando..." : lazy.error ? "Erro \u2014 tentar novamente" : bubble.listenAudio });
286
- }
287
- return /* @__PURE__ */ jsx5("div", { className: "min-w-[200px]", children: src ? /* @__PURE__ */ jsx5(AudioPlayer, { src, isMine: message.direction === "outbound" }) : /* @__PURE__ */ jsx5("div", { className: "h-12 bg-gray-200 rounded-lg flex items-center justify-center text-gray-400 text-xs", children: "Audio unavailable" }) });
288
- }
289
- case "document": {
290
- const typeLabel = message.mimeType?.split("/")[1]?.toUpperCase() ?? "FILE";
291
- const sizeLabel = message.sizeBytes ? formatFileSize(message.sizeBytes) : null;
292
- return /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-3 min-w-[200px]", children: [
293
- /* @__PURE__ */ jsx5("div", { className: "w-10 h-10 bg-gray-200 rounded-lg flex items-center justify-center flex-shrink-0", children: /* @__PURE__ */ jsx5(FileIcon, { filename: message.filename, mimeType: message.mimeType }) }),
294
- /* @__PURE__ */ jsxs3("div", { className: "flex-1 min-w-0", children: [
295
- /* @__PURE__ */ jsx5("p", { className: "text-sm font-medium truncate", children: message.filename ?? "Document" }),
296
- /* @__PURE__ */ jsx5("p", { className: "text-xs text-gray-500", children: sizeLabel ? `${typeLabel} \xB7 ${sizeLabel}` : typeLabel })
297
- ] }),
298
- src ? /* @__PURE__ */ jsx5("a", { href: src, download: message.filename, className: "w-8 h-8 flex items-center justify-center rounded-full bg-gray-200 hover:bg-gray-300 flex-shrink-0 transition-colors", "aria-label": "Download", children: /* @__PURE__ */ jsxs3("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", className: "text-gray-600", children: [
299
- /* @__PURE__ */ jsx5("path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" }),
300
- /* @__PURE__ */ jsx5("polyline", { points: "7 10 12 15 17 10" }),
301
- /* @__PURE__ */ jsx5("line", { x1: "12", y1: "15", x2: "12", y2: "3" })
302
- ] }) }) : canLazyLoad ? /* @__PURE__ */ jsx5(
303
- "button",
304
- {
305
- onClick: lazy.load,
306
- disabled: lazy.loading,
307
- className: "w-8 h-8 flex items-center justify-center rounded-full bg-gray-200 hover:bg-gray-300 flex-shrink-0 transition-colors disabled:opacity-50",
308
- "aria-label": "Download",
309
- children: /* @__PURE__ */ jsxs3("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", className: "text-gray-600", children: [
310
- /* @__PURE__ */ jsx5("path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" }),
311
- /* @__PURE__ */ jsx5("polyline", { points: "7 10 12 15 17 10" }),
312
- /* @__PURE__ */ jsx5("line", { x1: "12", y1: "15", x2: "12", y2: "3" })
313
- ] })
314
- }
315
- ) : null,
316
- lazy.error && /* @__PURE__ */ jsx5("span", { className: "text-xs text-red-500 flex-shrink-0", children: "Erro" })
317
- ] });
318
- }
319
- default:
320
- return null;
321
- }
322
- }
323
-
324
- // src/Lightbox.tsx
325
- import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
326
- function Lightbox({ imageUrl, caption, onClose }) {
327
- return /* @__PURE__ */ jsx6("div", { className: "fixed inset-0 z-50 bg-black/85 flex items-center justify-center p-4", onClick: onClose, children: /* @__PURE__ */ jsxs4("div", { className: "max-w-[90vw] max-h-[90vh] flex flex-col items-center", onClick: (e) => e.stopPropagation(), children: [
328
- /* @__PURE__ */ jsx6("img", { src: imageUrl, alt: caption ?? "Image", className: "max-w-full max-h-[80vh] object-contain rounded-lg" }),
329
- caption && /* @__PURE__ */ jsx6("p", { className: "text-white text-sm mt-3 text-center", children: caption }),
330
- /* @__PURE__ */ jsx6("button", { onClick: onClose, className: "mt-4 px-4 py-2 bg-white/20 text-white rounded-lg hover:bg-white/30 transition-colors", children: "Fechar" })
331
- ] }) });
332
- }
333
-
334
- // src/MessageBubble.tsx
335
- import { Fragment, jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
336
- var BUBBLE_COLOR = {
337
- agent: "bg-[#d9fdd3] dark:bg-[#005c4b]",
338
- bot: "bg-[#d7f0ec] dark:bg-[#0a3d3a]",
339
- customer: "bg-white dark:bg-[#202c33]"
340
- };
341
- var MEDIA_TYPES = /* @__PURE__ */ new Set(["image", "audio", "video", "document", "sticker"]);
342
- function MessageBubble({
343
- message,
344
- isMine,
345
- senderName,
346
- isFirstInGroup = true,
347
- isSelecting = false,
348
- isSelected = false,
349
- onToggleSelect,
350
- onResolveMediaUrl
351
- }) {
352
- const { bubble, selection } = useConversationLocales();
353
- const [lightboxSrc, setLightboxSrc] = useState3(null);
354
- const bubbleColor = BUBBLE_COLOR[message.sender] ?? BUBBLE_COLOR.customer;
355
- const hasError = message.status === "failed";
356
- const isMedia = MEDIA_TYPES.has(message.type);
357
- const isTemplate = message.type === "template";
358
- const displayName = message.sender === "agent" && senderName ? senderName : bubble[message.sender] ?? message.sender;
359
- const tooltipText = message.status === "read" && message.readAt ? `${bubble.readAt}${formatDateTime(message.readAt)}` : message.status === "failed" ? bubble.windowExpired : void 0;
360
- const tailCornerClass = isFirstInGroup ? isMine ? "rounded-tr-md" : "rounded-tl-md" : "";
361
- const checkbox = /* @__PURE__ */ jsx7(
362
- "button",
363
- {
364
- onClick: (e) => {
365
- e.stopPropagation();
366
- onToggleSelect?.();
367
- },
368
- title: selection.select,
369
- className: `
370
- flex-shrink-0 self-end mb-1 w-5 h-5 rounded-full border-2 flex items-center justify-center transition-all
371
- ${isSelected ? "bg-teal-600 border-teal-600" : "bg-white/80 dark:bg-black/40 border-black/20 dark:border-white/30"}
372
- ${isSelecting ? "opacity-100" : "opacity-0 group-hover:opacity-100"}
373
- `,
374
- children: isSelected && /* @__PURE__ */ jsx7(Check, { size: 12, className: "text-white", strokeWidth: 3 })
375
- }
376
- );
377
- return /* @__PURE__ */ jsxs5("div", { className: `flex items-end gap-1.5 ${isMine ? "justify-end" : "justify-start"} group ${isFirstInGroup ? "mt-2" : "mt-0.5"}`, children: [
378
- isMine && checkbox,
379
- /* @__PURE__ */ jsxs5(
380
- "div",
381
- {
382
- onClick: isSelecting ? onToggleSelect : void 0,
383
- className: `
384
- max-w-[75%] sm:max-w-[65%] rounded-2xl ${tailCornerClass} px-2.5 py-1.5 shadow-sm
385
- ${bubbleColor}
386
- ${hasError ? "ring-1 ring-inset ring-red-400" : ""}
387
- ${isSelecting ? "cursor-pointer" : ""}
388
- ${isSelected ? "ring-2 ring-teal-500" : ""}
389
- relative
390
- `,
391
- children: [
392
- isMine && isFirstInGroup && (message.sender === "bot" || message.sender === "agent" && senderName) && /* @__PURE__ */ jsx7("div", { className: "text-xs font-semibold mb-0.5 text-teal-700 dark:text-teal-400", children: displayName }),
393
- isMedia ? /* @__PURE__ */ jsx7(MediaRenderer, { message, onLightbox: setLightboxSrc, onResolveUrl: onResolveMediaUrl }) : /* @__PURE__ */ jsxs5(Fragment, { children: [
394
- isTemplate && /* @__PURE__ */ jsxs5("p", { className: "text-xs text-gray-500 dark:text-gray-400 flex items-center gap-1 mb-0.5", children: [
395
- /* @__PURE__ */ jsx7("span", { children: "\u{1F4E8}" }),
396
- /* @__PURE__ */ jsxs5("span", { className: "font-medium", children: [
397
- bubble.templateLabel,
398
- message.templateName ? ` \u2014 ${message.templateName}` : ""
399
- ] })
400
- ] }),
401
- /* @__PURE__ */ jsx7("div", { className: "text-sm text-gray-900 dark:text-gray-100 whitespace-pre-wrap break-words leading-[19px]", children: parseWhatsAppFormatting(message.content ?? "") })
402
- ] }),
403
- /* @__PURE__ */ jsxs5("div", { className: "flex items-center justify-end gap-1 mt-0.5 select-none", children: [
404
- /* @__PURE__ */ jsx7("span", { className: "text-xs text-black/40 dark:text-white/40 font-medium", children: formatTimestamp(message.timestamp) }),
405
- isMine && message.status && /* @__PURE__ */ jsx7(StatusTicks, { status: message.status, title: tooltipText })
406
- ] })
407
- ]
408
- }
409
- ),
410
- !isMine && checkbox,
411
- lightboxSrc && /* @__PURE__ */ jsx7(Lightbox, { imageUrl: lightboxSrc, onClose: () => setLightboxSrc(null) })
412
- ] });
413
- }
414
-
415
- // src/Wallpaper.tsx
416
- import { jsx as jsx8 } from "react/jsx-runtime";
417
- function ConversationWallpaper({ children, className = "" }) {
418
- return /* @__PURE__ */ jsx8("div", { className: `cv-wallpaper ${className}`.trim(), children });
419
- }
420
-
421
- // src/EmojiPicker.tsx
422
- import { useState as useState4, useCallback } from "react";
423
- import { jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
424
- var EMOJI_CATEGORIES = [
425
- {
426
- name: "Smileys",
427
- emojis: ["\u{1F600}", "\u{1F603}", "\u{1F604}", "\u{1F601}", "\u{1F605}", "\u{1F602}", "\u{1F923}", "\u{1F60A}", "\u{1F607}", "\u{1F642}", "\u{1F609}", "\u{1F60C}", "\u{1F60D}", "\u{1F970}", "\u{1F618}", "\u{1F617}", "\u{1F60B}", "\u{1F61B}", "\u{1F61C}", "\u{1F92A}"]
428
- },
429
- {
430
- name: "Gestures",
431
- emojis: ["\u{1F44D}", "\u{1F44E}", "\u{1F44C}", "\u270C\uFE0F", "\u{1F91E}", "\u{1F91F}", "\u{1F918}", "\u{1F919}", "\u{1F44B}", "\u{1F91A}", "\u{1F590}\uFE0F", "\u270B", "\u{1F596}", "\u{1F44F}", "\u{1F64C}", "\u{1F91D}", "\u{1F64F}", "\u270D\uFE0F", "\u{1F485}", "\u{1F933}"]
432
- },
433
- {
434
- name: "Hearts",
435
- emojis: ["\u2764\uFE0F", "\u{1F9E1}", "\u{1F49B}", "\u{1F49A}", "\u{1F499}", "\u{1F49C}", "\u{1F5A4}", "\u{1F90D}", "\u{1F90E}", "\u{1F494}", "\u2763\uFE0F", "\u{1F495}", "\u{1F49E}", "\u{1F493}", "\u{1F497}", "\u{1F496}", "\u{1F498}", "\u{1F49D}", "\u{1F49F}", "\u2665\uFE0F"]
436
- },
437
- {
438
- name: "Food",
439
- emojis: ["\u{1F354}", "\u{1F35F}", "\u{1F355}", "\u{1F32D}", "\u{1F37F}", "\u{1F9C2}", "\u{1F953}", "\u{1F95A}", "\u{1F373}", "\u{1F9C7}", "\u{1F95E}", "\u{1F9C8}", "\u{1F35E}", "\u{1F950}", "\u{1F968}", "\u{1F96F}", "\u{1F956}", "\u{1F9C0}", "\u{1F957}", "\u{1F959}"]
440
- },
441
- {
442
- name: "Drinks",
443
- emojis: ["\u2615", "\u{1F375}", "\u{1F376}", "\u{1F37E}", "\u{1F377}", "\u{1F378}", "\u{1F379}", "\u{1F37A}", "\u{1F37B}", "\u{1F942}", "\u{1F943}", "\u{1F964}", "\u{1F9CB}", "\u{1F9C3}", "\u{1F9C9}", "\u{1F9CA}", "\u{1F962}", "\u{1F37D}\uFE0F", "\u{1F374}", "\u{1F944}"]
444
- },
445
- {
446
- name: "Objects",
447
- emojis: ["\u{1F381}", "\u{1F382}", "\u{1F388}", "\u{1F389}", "\u{1F38A}", "\u{1F380}", "\u{1F4F1}", "\u{1F4BB}", "\u231A", "\u{1F4F7}", "\u{1F511}", "\u{1F4B0}", "\u{1F4B3}", "\u{1F4DD}", "\u{1F4CC}", "\u{1F4CD}", "\u2702\uFE0F", "\u{1F50D}", "\u{1F4A1}", "\u{1F514}"]
448
- }
449
- ];
450
- var EmojiPicker = ({ onSelect, className = "" }) => {
451
- const [activeCategory, setActiveCategory] = useState4(0);
452
- const handleSelect = useCallback(
453
- (emoji) => {
454
- onSelect(emoji);
455
- },
456
- [onSelect]
457
- );
458
- return /* @__PURE__ */ jsxs6("div", { className: `bg-white border border-gray-200 rounded-lg shadow-lg overflow-hidden ${className}`, children: [
459
- /* @__PURE__ */ jsx9("div", { className: "flex border-b border-gray-200 overflow-x-auto", children: EMOJI_CATEGORIES.map((category, index) => /* @__PURE__ */ jsxs6(
460
- "button",
461
- {
462
- onClick: () => setActiveCategory(index),
463
- className: `px-3 py-2 text-xs font-medium whitespace-nowrap border-b-2 transition-colors ${activeCategory === index ? "border-blue-500 text-blue-600" : "border-transparent text-gray-500 hover:text-gray-700"}`,
464
- children: [
465
- category.emojis[0],
466
- " ",
467
- category.name
468
- ]
469
- },
470
- category.name
471
- )) }),
472
- /* @__PURE__ */ jsx9("div", { className: "grid grid-cols-10 gap-0.5 p-2 max-h-[240px] overflow-y-auto", children: EMOJI_CATEGORIES[activeCategory].emojis.map((emoji) => /* @__PURE__ */ jsx9(
473
- "button",
474
- {
475
- onClick: () => handleSelect(emoji),
476
- className: "w-8 h-8 flex items-center justify-center text-lg hover:bg-gray-100 rounded transition-colors cursor-pointer",
477
- "aria-label": emoji,
478
- children: emoji
479
- },
480
- emoji
481
- )) })
482
- ] });
483
- };
484
-
485
- // src/MessageComposer.tsx
486
- import { useState as useState5, useRef as useRef2, useCallback as useCallback2 } from "react";
487
- import { Fragment as Fragment2, jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
488
- var DEFAULT_ACCEPTED_FILE_TYPES = "image/*,video/*,audio/*,.pdf,.doc,.docx,.xls,.xlsx,.zip";
489
- var MessageComposer = ({
490
- onSend,
491
- onAttach,
492
- value: externalValue,
493
- onChange: externalOnChange,
494
- features,
495
- placeholder = "Digite uma mensagem...",
496
- maxLength,
497
- disabled = false,
498
- acceptedFileTypes = DEFAULT_ACCEPTED_FILE_TYPES
499
- }) => {
500
- const [internalText, setInternalText] = useState5("");
501
- const [showEmoji, setShowEmoji] = useState5(false);
502
- const [attachments, setAttachments] = useState5([]);
503
- const textareaRef = useRef2(null);
504
- const fileInputRef = useRef2(null);
505
- const isControlled = externalValue !== void 0;
506
- const text = isControlled ? externalValue : internalText;
507
- const setText = useCallback2((newText) => {
508
- if (isControlled) {
509
- externalOnChange?.(newText);
510
- } else {
511
- setInternalText(newText);
512
- }
513
- }, [isControlled, externalOnChange]);
514
- const showEmojiButton = features?.emoji !== false;
515
- const showAttachButton = features?.documents !== false;
516
- const sendMessage = useCallback2(() => {
517
- const trimmed = text.trim();
518
- if (!trimmed && attachments.length === 0) return;
519
- if (trimmed) onSend(trimmed);
520
- for (const a of attachments) {
521
- onAttach?.(a.file);
522
- URL.revokeObjectURL(a.previewUrl);
523
- }
524
- if (!isControlled) setInternalText("");
525
- setAttachments([]);
526
- setShowEmoji(false);
527
- if (textareaRef.current) textareaRef.current.style.height = "auto";
528
- }, [text, attachments, onSend, onAttach, isControlled]);
529
- const handleKeyDown = useCallback2((e) => {
530
- if (e.key === "Enter" && !e.shiftKey) {
531
- e.preventDefault();
532
- if (!disabled) sendMessage();
533
- }
534
- }, [sendMessage, disabled]);
535
- const handleInput = useCallback2(() => {
536
- const ta = textareaRef.current;
537
- if (!ta) return;
538
- ta.style.height = "auto";
539
- ta.style.height = `${Math.min(ta.scrollHeight, 100)}px`;
540
- }, []);
541
- const handleEmojiSelect = useCallback2((emoji) => {
542
- const ta = textareaRef.current;
543
- if (!ta) {
544
- setText(text + emoji);
545
- return;
546
- }
547
- const start = ta.selectionStart;
548
- const end = ta.selectionEnd;
549
- const newText = text.slice(0, start) + emoji + text.slice(end);
550
- setText(newText);
551
- requestAnimationFrame(() => {
552
- ta.focus();
553
- ta.setSelectionRange(start + emoji.length, start + emoji.length);
554
- handleInput();
555
- });
556
- }, [text, setText, handleInput]);
557
- const handleFileChange = useCallback2((e) => {
558
- const files = e.target.files;
559
- if (!files) return;
560
- const previews = [];
561
- for (let i = 0; i < files.length; i++) {
562
- const file = files[i];
563
- previews.push({ file, previewUrl: file.type.startsWith("image/") ? URL.createObjectURL(file) : "" });
564
- }
565
- setAttachments((prev) => [...prev, ...previews]);
566
- if (fileInputRef.current) fileInputRef.current.value = "";
567
- }, []);
568
- const removeAttachment = useCallback2((index) => {
569
- setAttachments((prev) => {
570
- const next = [...prev];
571
- if (next[index].previewUrl) URL.revokeObjectURL(next[index].previewUrl);
572
- next.splice(index, 1);
573
- return next;
574
- });
575
- }, []);
576
- const insertFormatting = useCallback2((marker) => {
577
- const ta = textareaRef.current;
578
- if (!ta) return;
579
- const start = ta.selectionStart;
580
- const end = ta.selectionEnd;
581
- const sel = text.slice(start, end);
582
- if (sel) {
583
- setText(text.slice(0, start) + marker + sel + marker + text.slice(end));
584
- requestAnimationFrame(() => {
585
- ta.focus();
586
- ta.setSelectionRange(start + marker.length + sel.length + marker.length, start + marker.length + sel.length + marker.length);
587
- });
588
- }
589
- }, [text, setText]);
590
- const canSend = text.trim().length > 0 || attachments.length > 0;
591
- const remaining = maxLength ? maxLength - text.length : null;
592
- return /* @__PURE__ */ jsxs7("div", { children: [
593
- attachments.length > 0 && /* @__PURE__ */ jsx10("div", { className: "flex gap-2 px-1 pb-2 overflow-x-auto", children: attachments.map((a, i) => /* @__PURE__ */ jsxs7("div", { className: "relative flex-shrink-0", children: [
594
- a.previewUrl ? /* @__PURE__ */ jsx10("img", { src: a.previewUrl, alt: "", className: "w-16 h-16 object-cover rounded-lg border border-gray-200" }) : /* @__PURE__ */ jsx10("div", { className: "w-16 h-16 bg-gray-100 rounded-lg border border-gray-200 flex items-center justify-center", children: /* @__PURE__ */ jsxs7("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "#9ca3af", strokeWidth: "1.5", children: [
595
- /* @__PURE__ */ jsx10("path", { d: "M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" }),
596
- /* @__PURE__ */ jsx10("polyline", { points: "14 2 14 8 20 8" })
597
- ] }) }),
598
- /* @__PURE__ */ jsx10("button", { onClick: () => removeAttachment(i), className: "absolute -top-2 -right-2 w-5 h-5 bg-gray-600 text-white rounded-full flex items-center justify-center hover:bg-gray-800 text-xs", children: "\u2715" })
599
- ] }, i)) }),
600
- /* @__PURE__ */ jsxs7("div", { className: "flex items-end gap-1.5 bg-[#f0f2f5] rounded-xl px-3 py-2", children: [
601
- showEmojiButton && /* @__PURE__ */ jsxs7("div", { className: "relative flex-shrink-0", children: [
602
- /* @__PURE__ */ jsx10("button", { onClick: () => setShowEmoji((v) => !v), className: "w-9 h-9 flex items-center justify-center rounded-full text-gray-500 hover:bg-gray-200 transition-colors", "aria-label": "Emoji", children: /* @__PURE__ */ jsxs7("svg", { width: "22", height: "22", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
603
- /* @__PURE__ */ jsx10("circle", { cx: "12", cy: "12", r: "10" }),
604
- /* @__PURE__ */ jsx10("path", { d: "M8 14s1.5 2 4 2 4-2 4-2" }),
605
- /* @__PURE__ */ jsx10("circle", { cx: "9", cy: "9", r: "0.5", fill: "currentColor" }),
606
- /* @__PURE__ */ jsx10("circle", { cx: "15", cy: "9", r: "0.5", fill: "currentColor" })
607
- ] }) }),
608
- showEmoji && /* @__PURE__ */ jsx10("div", { className: "absolute bottom-full left-0 mb-2 z-10", children: /* @__PURE__ */ jsx10(EmojiPicker, { onSelect: handleEmojiSelect }) })
609
- ] }),
610
- /* @__PURE__ */ jsx10(
611
- "textarea",
612
- {
613
- ref: textareaRef,
614
- value: text,
615
- onChange: (e) => {
616
- setText(e.target.value);
617
- handleInput();
618
- },
619
- onKeyDown: handleKeyDown,
620
- placeholder,
621
- rows: 1,
622
- disabled,
623
- className: "flex-1 resize-none bg-transparent text-[15px] text-[#3b4a54] placeholder-[#8696a0] outline-none py-1.5 max-h-[100px] leading-relaxed",
624
- style: { fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif" }
625
- }
626
- ),
627
- showAttachButton && /* @__PURE__ */ jsxs7(Fragment2, { children: [
628
- /* @__PURE__ */ jsx10("input", { ref: fileInputRef, type: "file", multiple: true, accept: acceptedFileTypes, onChange: handleFileChange, className: "hidden" }),
629
- /* @__PURE__ */ jsx10("button", { onClick: () => fileInputRef.current?.click(), className: "w-9 h-9 flex items-center justify-center rounded-full text-gray-500 hover:bg-gray-200 flex-shrink-0 transition-colors", "aria-label": "Anexar", children: /* @__PURE__ */ jsx10("svg", { width: "22", height: "22", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ jsx10("path", { d: "M21.44 11.05l-9.19 9.19a6 6 0 0 1-8.49-8.49l9.19-9.19a4 4 0 0 1 5.66 5.66l-9.2 9.19a2 2 0 0 1-2.83-2.83l8.49-8.48" }) }) })
630
- ] }),
631
- /* @__PURE__ */ jsx10(
632
- "button",
633
- {
634
- onClick: sendMessage,
635
- disabled: !canSend || disabled,
636
- className: `w-10 h-10 flex items-center justify-center rounded-full flex-shrink-0 transition-all ${canSend && !disabled ? "bg-[#00a884] text-white hover:bg-[#06cf9c] shadow-sm" : "bg-gray-200 text-gray-400 cursor-not-allowed"}`,
637
- "aria-label": "Enviar",
638
- children: /* @__PURE__ */ jsx10("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "currentColor", children: /* @__PURE__ */ jsx10("path", { d: "M2.01 21L23 12 2.01 3 2 10l15 2-15 2z" }) })
639
- }
640
- )
641
- ] }),
642
- remaining !== null && /* @__PURE__ */ jsx10("div", { className: "flex justify-end mt-1 pr-1", children: /* @__PURE__ */ jsx10("span", { className: `text-xs ${remaining < 20 ? "text-red-500" : "text-gray-400"}`, children: remaining }) })
643
- ] });
644
- };
52
+ } from "./chunk-2AYDBWNE.js";
645
53
 
646
54
  // src/WhatsAppMessageEditor.tsx
647
- import { useRef as useRef3 } from "react";
55
+ import { useRef } from "react";
648
56
  import { Bold, Italic, Strikethrough } from "lucide-react";
649
- import { Fragment as Fragment3, jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
57
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
58
+ var DEFAULT_WHATSAPP_MESSAGE_EDITOR_LABELS = {
59
+ bold: "Negrito",
60
+ boldHint: "Negrito (*texto*)",
61
+ italic: "It\xE1lico",
62
+ italicHint: "It\xE1lico (_texto_)",
63
+ strikethrough: "Tachado",
64
+ strikethroughHint: "Tachado (~texto~)",
65
+ insertPlaceholder: (token) => `Inserir ${token}`
66
+ };
650
67
  var INLINE_TOKEN = /(\*[^*\n]+\*|_[^_\n]+_|~[^~\n]+~|`[^`\n]+`|\{[a-zA-Z_]+\})/g;
651
68
  function renderLine(line, lineKey) {
652
69
  const nodes = [];
@@ -658,11 +75,11 @@ function renderLine(line, lineKey) {
658
75
  if (match.index > lastIndex) nodes.push(line.slice(lastIndex, match.index));
659
76
  const token = match[0];
660
77
  const key = `${lineKey}-${tokenIndex++}`;
661
- if (token.startsWith("*")) nodes.push(/* @__PURE__ */ jsx11("strong", { children: token.slice(1, -1) }, key));
662
- else if (token.startsWith("_")) nodes.push(/* @__PURE__ */ jsx11("em", { children: token.slice(1, -1) }, key));
663
- else if (token.startsWith("~")) nodes.push(/* @__PURE__ */ jsx11("s", { children: token.slice(1, -1) }, key));
664
- else if (token.startsWith("`")) nodes.push(/* @__PURE__ */ jsx11("code", { className: "font-mono text-[0.85em]", children: token.slice(1, -1) }, key));
665
- else nodes.push(/* @__PURE__ */ jsx11("span", { className: "rounded bg-blue-100 dark:bg-blue-900/50 text-blue-700 dark:text-blue-300 px-1", children: token }, key));
78
+ if (token.startsWith("*")) nodes.push(/* @__PURE__ */ jsx("strong", { children: token.slice(1, -1) }, key));
79
+ else if (token.startsWith("_")) nodes.push(/* @__PURE__ */ jsx("em", { children: token.slice(1, -1) }, key));
80
+ else if (token.startsWith("~")) nodes.push(/* @__PURE__ */ jsx("s", { children: token.slice(1, -1) }, key));
81
+ else if (token.startsWith("`")) nodes.push(/* @__PURE__ */ jsx("code", { className: "font-mono text-[0.85em]", children: token.slice(1, -1) }, key));
82
+ else nodes.push(/* @__PURE__ */ jsx("span", { className: "rounded bg-blue-100 dark:bg-blue-900/50 text-blue-700 dark:text-blue-300 px-1", children: token }, key));
666
83
  lastIndex = INLINE_TOKEN.lastIndex;
667
84
  }
668
85
  if (lastIndex < line.length) nodes.push(line.slice(lastIndex));
@@ -670,9 +87,9 @@ function renderLine(line, lineKey) {
670
87
  }
671
88
  function renderWhatsApp(text) {
672
89
  const lines = text.split("\n");
673
- return lines.map((line, index) => /* @__PURE__ */ jsxs8("span", { children: [
90
+ return lines.map((line, index) => /* @__PURE__ */ jsxs("span", { children: [
674
91
  renderLine(line, `ln-${index}`),
675
- index < lines.length - 1 ? /* @__PURE__ */ jsx11("br", {}) : null
92
+ index < lines.length - 1 ? /* @__PURE__ */ jsx("br", {}) : null
676
93
  ] }, `ln-${index}`));
677
94
  }
678
95
  function WhatsAppMessageEditor({
@@ -682,9 +99,11 @@ function WhatsAppMessageEditor({
682
99
  placeholders = [],
683
100
  rows = 4,
684
101
  previewLabel = "Pr\xE9via (como aparece no WhatsApp)",
685
- emptyPreviewText = "Sua mensagem aparecer\xE1 aqui\u2026"
102
+ emptyPreviewText = "Sua mensagem aparecer\xE1 aqui\u2026",
103
+ labels
686
104
  }) {
687
- const textareaRef = useRef3(null);
105
+ const editorLabels = { ...DEFAULT_WHATSAPP_MESSAGE_EDITOR_LABELS, ...labels };
106
+ const textareaRef = useRef(null);
688
107
  function wrapSelection(marker) {
689
108
  const textarea = textareaRef.current;
690
109
  if (!textarea) return;
@@ -712,27 +131,27 @@ function WhatsAppMessageEditor({
712
131
  });
713
132
  }
714
133
  const toolbarButtonClass = "inline-flex items-center justify-center h-8 w-8 rounded-lg border border-gray-200 dark:border-gray-600 text-gray-600 dark:text-gray-300 hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors";
715
- return /* @__PURE__ */ jsxs8("div", { children: [
716
- /* @__PURE__ */ jsxs8("div", { className: "flex flex-wrap items-center gap-1.5 mb-2", children: [
717
- /* @__PURE__ */ jsx11("button", { type: "button", onClick: () => wrapSelection("*"), className: toolbarButtonClass, title: "Negrito (*texto*)", "aria-label": "Negrito", children: /* @__PURE__ */ jsx11(Bold, { size: 15 }) }),
718
- /* @__PURE__ */ jsx11("button", { type: "button", onClick: () => wrapSelection("_"), className: toolbarButtonClass, title: "It\xE1lico (_texto_)", "aria-label": "It\xE1lico", children: /* @__PURE__ */ jsx11(Italic, { size: 15 }) }),
719
- /* @__PURE__ */ jsx11("button", { type: "button", onClick: () => wrapSelection("~"), className: toolbarButtonClass, title: "Tachado (~texto~)", "aria-label": "Tachado", children: /* @__PURE__ */ jsx11(Strikethrough, { size: 15 }) }),
720
- placeholders.length > 0 && /* @__PURE__ */ jsxs8(Fragment3, { children: [
721
- /* @__PURE__ */ jsx11("span", { className: "mx-1 h-5 w-px bg-gray-200 dark:bg-gray-600" }),
722
- placeholders.map((token) => /* @__PURE__ */ jsx11(
134
+ return /* @__PURE__ */ jsxs("div", { children: [
135
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-1.5 mb-2", children: [
136
+ /* @__PURE__ */ jsx("button", { type: "button", onClick: () => wrapSelection("*"), className: toolbarButtonClass, title: editorLabels.boldHint, "aria-label": editorLabels.bold, children: /* @__PURE__ */ jsx(Bold, { size: 15 }) }),
137
+ /* @__PURE__ */ jsx("button", { type: "button", onClick: () => wrapSelection("_"), className: toolbarButtonClass, title: editorLabels.italicHint, "aria-label": editorLabels.italic, children: /* @__PURE__ */ jsx(Italic, { size: 15 }) }),
138
+ /* @__PURE__ */ jsx("button", { type: "button", onClick: () => wrapSelection("~"), className: toolbarButtonClass, title: editorLabels.strikethroughHint, "aria-label": editorLabels.strikethrough, children: /* @__PURE__ */ jsx(Strikethrough, { size: 15 }) }),
139
+ placeholders.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
140
+ /* @__PURE__ */ jsx("span", { className: "mx-1 h-5 w-px bg-gray-200 dark:bg-gray-600" }),
141
+ placeholders.map((token) => /* @__PURE__ */ jsx(
723
142
  "button",
724
143
  {
725
144
  type: "button",
726
145
  onClick: () => insertAtCursor(token),
727
146
  className: "inline-flex items-center h-8 px-2 rounded-lg border border-gray-200 dark:border-gray-600 text-xs text-blue-600 dark:text-blue-300 hover:bg-blue-50 dark:hover:bg-blue-900/40 transition-colors",
728
- title: `Inserir ${token}`,
147
+ title: editorLabels.insertPlaceholder(token),
729
148
  children: token
730
149
  },
731
150
  token
732
151
  ))
733
152
  ] })
734
153
  ] }),
735
- /* @__PURE__ */ jsx11(
154
+ /* @__PURE__ */ jsx(
736
155
  "textarea",
737
156
  {
738
157
  ref: textareaRef,
@@ -743,17 +162,17 @@ function WhatsAppMessageEditor({
743
162
  className: "w-full border border-gray-200 dark:border-gray-600 dark:bg-gray-700 dark:text-gray-100 rounded-xl px-3 py-2.5 text-sm focus:outline-none focus:ring-2 focus:ring-blue-400 focus:border-transparent transition-all placeholder:text-gray-400 dark:placeholder:text-gray-500 resize-y"
744
163
  }
745
164
  ),
746
- /* @__PURE__ */ jsxs8("div", { className: "mt-2", children: [
747
- /* @__PURE__ */ jsx11("p", { className: "text-xs text-gray-500 dark:text-gray-400 mb-1", children: previewLabel }),
748
- /* @__PURE__ */ jsx11("div", { className: "rounded-xl bg-[#e5ded8] dark:bg-gray-900 p-3", children: /* @__PURE__ */ jsx11("div", { className: "inline-block max-w-full rounded-lg rounded-tl-none bg-white dark:bg-gray-800 shadow-sm px-3 py-2 text-sm text-gray-800 dark:text-gray-100 whitespace-pre-wrap break-words", children: value.trim() ? renderWhatsApp(value) : /* @__PURE__ */ jsx11("span", { className: "text-gray-400 dark:text-gray-500", children: emptyPreviewText }) }) })
165
+ /* @__PURE__ */ jsxs("div", { className: "mt-2", children: [
166
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-gray-500 dark:text-gray-400 mb-1", children: previewLabel }),
167
+ /* @__PURE__ */ jsx("div", { className: "rounded-xl bg-[#e5ded8] dark:bg-gray-900 p-3", children: /* @__PURE__ */ jsx("div", { className: "inline-block max-w-full rounded-lg rounded-tl-none bg-white dark:bg-gray-800 shadow-sm px-3 py-2 text-sm text-gray-800 dark:text-gray-100 whitespace-pre-wrap break-words", children: value.trim() ? renderWhatsApp(value) : /* @__PURE__ */ jsx("span", { className: "text-gray-400 dark:text-gray-500", children: emptyPreviewText }) }) })
749
168
  ] })
750
169
  ] });
751
170
  }
752
171
 
753
172
  // src/SimpleEmojiPicker.tsx
754
- import { useEffect as useEffect2, useRef as useRef4, useState as useState6 } from "react";
173
+ import { useEffect, useRef as useRef2, useState } from "react";
755
174
  import { Smile } from "lucide-react";
756
- import { jsx as jsx12, jsxs as jsxs9 } from "react/jsx-runtime";
175
+ import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
757
176
  var EMOJIS = [
758
177
  "\u{1F600}",
759
178
  "\u{1F602}",
@@ -837,9 +256,9 @@ var EMOJIS = [
837
256
  "\u{1F6E1}\uFE0F"
838
257
  ];
839
258
  function SimpleEmojiPicker({ onSelect, label = "Emojis", pickerWidth = "280px", pickerMaxHeight = "220px" }) {
840
- const [open, setOpen] = useState6(false);
841
- const containerRef = useRef4(null);
842
- useEffect2(() => {
259
+ const [open, setOpen] = useState(false);
260
+ const containerRef = useRef2(null);
261
+ useEffect(() => {
843
262
  if (!open) return;
844
263
  const handleClickOutside = (e) => {
845
264
  if (containerRef.current && !containerRef.current.contains(e.target)) {
@@ -849,23 +268,23 @@ function SimpleEmojiPicker({ onSelect, label = "Emojis", pickerWidth = "280px",
849
268
  document.addEventListener("mousedown", handleClickOutside);
850
269
  return () => document.removeEventListener("mousedown", handleClickOutside);
851
270
  }, [open]);
852
- return /* @__PURE__ */ jsxs9("div", { ref: containerRef, className: "relative flex-shrink-0", children: [
853
- /* @__PURE__ */ jsx12(
271
+ return /* @__PURE__ */ jsxs2("div", { ref: containerRef, className: "relative flex-shrink-0", children: [
272
+ /* @__PURE__ */ jsx2(
854
273
  "button",
855
274
  {
856
275
  type: "button",
857
276
  onClick: () => setOpen((v) => !v),
858
277
  className: "w-7 h-7 flex items-center justify-center rounded-lg text-gray-400 dark:text-gray-500 hover:text-teal-600 dark:hover:text-teal-400 hover:bg-teal-50 dark:hover:bg-teal-900/30 transition-colors",
859
278
  title: label,
860
- children: /* @__PURE__ */ jsx12(Smile, { size: 15 })
279
+ children: /* @__PURE__ */ jsx2(Smile, { size: 15 })
861
280
  }
862
281
  ),
863
- open && /* @__PURE__ */ jsx12("div", { className: "absolute bottom-full left-1/2 -translate-x-1/2 mb-2 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-600 rounded-xl shadow-xl p-2 z-50", children: /* @__PURE__ */ jsx12(
282
+ open && /* @__PURE__ */ jsx2("div", { className: "absolute bottom-full left-1/2 -translate-x-1/2 mb-2 bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-600 rounded-xl shadow-xl p-2 z-50", children: /* @__PURE__ */ jsx2(
864
283
  "div",
865
284
  {
866
285
  className: "grid grid-cols-10 gap-0.5 overflow-y-auto",
867
286
  style: { width: pickerWidth, maxHeight: pickerMaxHeight },
868
- children: EMOJIS.map((emoji) => /* @__PURE__ */ jsx12(
287
+ children: EMOJIS.map((emoji) => /* @__PURE__ */ jsx2(
869
288
  "button",
870
289
  {
871
290
  type: "button",
@@ -883,24 +302,11 @@ function SimpleEmojiPicker({ onSelect, label = "Emojis", pickerWidth = "280px",
883
302
  ] });
884
303
  }
885
304
 
886
- // src/DateDivider.tsx
887
- import { jsx as jsx13 } from "react/jsx-runtime";
888
- function DateDivider({ iso }) {
889
- const { dateDivider } = useConversationLocales();
890
- return /* @__PURE__ */ jsx13("div", { className: "flex justify-center sticky top-0 z-10 my-2 pointer-events-none", children: /* @__PURE__ */ jsx13("span", { className: "bg-white/95 dark:bg-gray-800/95 text-gray-600 dark:text-gray-300 text-xs font-medium px-3 py-1 rounded-lg shadow-sm", children: formatDividerLabel(iso, dateDivider) }) });
891
- }
892
- function formatDividerLabel(iso, dateDivider) {
893
- const date = new Date(iso);
894
- const today = /* @__PURE__ */ new Date();
895
- const yesterday = new Date(today);
896
- yesterday.setDate(today.getDate() - 1);
897
- if (isSameDay(date, today)) return dateDivider.today;
898
- if (isSameDay(date, yesterday)) return dateDivider.yesterday;
899
- return date.toLocaleDateString("pt-BR", { day: "2-digit", month: "long", year: "numeric" });
900
- }
901
-
902
305
  // src/Avatar.tsx
903
- import { jsx as jsx14 } from "react/jsx-runtime";
306
+ import { jsx as jsx3 } from "react/jsx-runtime";
307
+ var DEFAULT_AVATAR_LABELS = {
308
+ unnamedContact: "Contato sem nome"
309
+ };
904
310
  var sizeClasses = {
905
311
  sm: "w-6 h-6 text-xs",
906
312
  md: "w-8 h-8 text-sm",
@@ -926,10 +332,11 @@ function getBgColor(name) {
926
332
  const hash = name.charCodeAt(0) + (name.charCodeAt(name.length - 1) || 0);
927
333
  return bgColors[hash % bgColors.length];
928
334
  }
929
- function Avatar({ name, avatarUrl, size = "md", className = "" }) {
335
+ function Avatar({ name, avatarUrl, size = "md", className = "", labels }) {
930
336
  const sizeClass = sizeClasses[size];
337
+ const unnamedContactLabel = labels?.unnamedContact ?? DEFAULT_AVATAR_LABELS.unnamedContact;
931
338
  if (avatarUrl) {
932
- return /* @__PURE__ */ jsx14(
339
+ return /* @__PURE__ */ jsx3(
933
340
  "img",
934
341
  {
935
342
  src: avatarUrl,
@@ -938,14 +345,24 @@ function Avatar({ name, avatarUrl, size = "md", className = "" }) {
938
345
  }
939
346
  );
940
347
  }
941
- const initials = getInitials(name);
942
348
  const bgColor = getBgColor(name);
943
- return /* @__PURE__ */ jsx14(
349
+ if (!name) {
350
+ return /* @__PURE__ */ jsx3(
351
+ "div",
352
+ {
353
+ className: `${sizeClass} ${bgColor} rounded-full flex items-center justify-center text-white flex-shrink-0 ${className}`,
354
+ role: "img",
355
+ "aria-label": unnamedContactLabel,
356
+ children: /* @__PURE__ */ jsx3("svg", { viewBox: "0 0 24 24", fill: "currentColor", className: "h-[60%] w-[60%]", "aria-hidden": true, children: /* @__PURE__ */ jsx3("path", { d: "M12 12a5 5 0 1 0 0-10 5 5 0 0 0 0 10Zm0 2c-4.42 0-8 2.24-8 5v1h16v-1c0-2.76-3.58-5-8-5Z" }) })
357
+ }
358
+ );
359
+ }
360
+ return /* @__PURE__ */ jsx3(
944
361
  "div",
945
362
  {
946
363
  className: `${sizeClass} ${bgColor} rounded-full flex items-center justify-center text-white font-semibold flex-shrink-0 ${className}`,
947
- title: name || void 0,
948
- children: initials
364
+ title: name,
365
+ children: getInitials(name)
949
366
  }
950
367
  );
951
368
  }
@@ -953,30 +370,91 @@ function Avatar({ name, avatarUrl, size = "md", className = "" }) {
953
370
  // src/ConversationListItem.tsx
954
371
  import { useMemo } from "react";
955
372
 
956
- // src/lib/phone.ts
957
- function formatPhone(number) {
958
- const digits = number.replace(/\D/g, "");
959
- if (digits.length === 13) {
960
- return `+${digits.slice(0, 2)} (${digits.slice(2, 4)}) ${digits.slice(4, 9)}-${digits.slice(9, 13)}`;
961
- }
962
- if (digits.length === 12) {
963
- return `+${digits.slice(0, 2)} (${digits.slice(2, 4)}) ${digits.slice(4, 8)}-${digits.slice(8, 12)}`;
964
- }
965
- if (digits.length === 11) {
966
- return `(${digits.slice(0, 2)}) ${digits.slice(2, 7)}-${digits.slice(7, 11)}`;
967
- }
968
- if (digits.length === 10) {
969
- return `(${digits.slice(0, 2)}) ${digits.slice(2, 6)}-${digits.slice(6, 10)}`;
373
+ // src/conversationChannel.ts
374
+ var CONVERSATION_CHANNEL = {
375
+ WHATSAPP: "whatsapp",
376
+ MESSENGER: "messenger",
377
+ INSTAGRAM: "instagram",
378
+ WEBCHAT: "webchat"
379
+ };
380
+ var DEFAULT_CONVERSATION_CHANNEL = CONVERSATION_CHANNEL.WHATSAPP;
381
+ var REOPEN_MECHANISM = {
382
+ TEMPLATE: "template",
383
+ TAG: "tag",
384
+ NONE: "none"
385
+ };
386
+ var HANDLE_KIND = {
387
+ PHONE: "phone",
388
+ USERNAME: "username",
389
+ SESSION: "session"
390
+ };
391
+ var CHANNEL_CAPABILITIES = {
392
+ [CONVERSATION_CHANNEL.WHATSAPP]: {
393
+ label: "WhatsApp",
394
+ icon: "\u{1F4AC}",
395
+ hasSessionWindow: true,
396
+ windowHours: 24,
397
+ reopenMechanism: REOPEN_MECHANISM.TEMPLATE,
398
+ handleKind: HANDLE_KIND.PHONE
399
+ },
400
+ [CONVERSATION_CHANNEL.MESSENGER]: {
401
+ // Messenger também tem 24h, mas reabre com message tag — não com template aprovado.
402
+ label: "Messenger",
403
+ icon: "\u{1F4E8}",
404
+ hasSessionWindow: true,
405
+ windowHours: 24,
406
+ reopenMechanism: REOPEN_MECHANISM.TAG,
407
+ handleKind: HANDLE_KIND.USERNAME
408
+ },
409
+ [CONVERSATION_CHANNEL.INSTAGRAM]: {
410
+ label: "Instagram",
411
+ icon: "\u{1F4F7}",
412
+ hasSessionWindow: true,
413
+ windowHours: 24,
414
+ reopenMechanism: REOPEN_MECHANISM.TAG,
415
+ handleKind: HANDLE_KIND.USERNAME
416
+ },
417
+ [CONVERSATION_CHANNEL.WEBCHAT]: {
418
+ // Chat próprio: sem intermediário, sem janela. Bloquear o composer aqui seria inventar limite.
419
+ label: "Chat do site",
420
+ icon: "\u{1F310}",
421
+ hasSessionWindow: false,
422
+ windowHours: 0,
423
+ reopenMechanism: REOPEN_MECHANISM.NONE,
424
+ handleKind: HANDLE_KIND.SESSION
970
425
  }
971
- return number;
426
+ };
427
+ function capabilitiesOf(channel) {
428
+ return CHANNEL_CAPABILITIES[channel ?? DEFAULT_CONVERSATION_CHANNEL];
429
+ }
430
+ var CHANNEL_FILTER_ALL = "all";
431
+ function channelFiltersFor(conversations) {
432
+ const present = new Set(
433
+ conversations.map((conversation) => conversation.channel ?? DEFAULT_CONVERSATION_CHANNEL)
434
+ );
435
+ if (present.size < 2) return [];
436
+ const ordered = Object.keys(CHANNEL_CAPABILITIES).filter((channel) => present.has(channel));
437
+ return [
438
+ { value: CHANNEL_FILTER_ALL, label: "Todos" },
439
+ ...ordered.map((channel) => ({ value: channel, label: CHANNEL_CAPABILITIES[channel].label }))
440
+ ];
972
441
  }
973
- function phoneInitials(number) {
974
- const digits = number.replace(/\D/g, "");
975
- return digits.slice(-2);
442
+ function formatContactHandle(params) {
443
+ const { handleKind } = capabilitiesOf(params.channel);
444
+ if (handleKind === HANDLE_KIND.PHONE) return formatPhone(params.handle);
445
+ if (handleKind === HANDLE_KIND.USERNAME) return params.handle.startsWith("@") ? params.handle : `@${params.handle}`;
446
+ return `Visitante ${params.handle.slice(-6)}`;
447
+ }
448
+ function contactFlag(params) {
449
+ return capabilitiesOf(params.channel).handleKind === HANDLE_KIND.PHONE ? phoneCountryFlag(params.handle) : "";
976
450
  }
977
451
 
978
452
  // src/ConversationListItem.tsx
979
- import { jsx as jsx15, jsxs as jsxs10 } from "react/jsx-runtime";
453
+ import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
454
+ var DEFAULT_CONVERSATION_LIST_ITEM_LABELS = {
455
+ expiredWindow: "Janela expirada",
456
+ warningWindow: "Janela pr\xF3xima do fim"
457
+ };
980
458
  function formatRelativeTime(dateStr) {
981
459
  try {
982
460
  const date = new Date(dateStr);
@@ -1035,50 +513,63 @@ var ConversationListItem = ({
1035
513
  active = false,
1036
514
  selected = false,
1037
515
  onClick,
1038
- onSelect
516
+ onSelect,
517
+ showDivider = true,
518
+ highlightActive = true,
519
+ labels
1039
520
  }) => {
521
+ const expiredWindowLabel = labels?.expiredWindow ?? DEFAULT_CONVERSATION_LIST_ITEM_LABELS.expiredWindow;
522
+ const warningWindowLabel = labels?.warningWindow ?? DEFAULT_CONVERSATION_LIST_ITEM_LABELS.warningWindow;
1040
523
  const isActive = active || selected;
1041
524
  const windowStatus = useMemo(() => getWindowStatus(conversation.lastInboundAt), [conversation.lastInboundAt]);
1042
525
  const preview = useMemo(() => lastMessagePreview(conversation), [conversation.lastContent]);
1043
- const name = conversation.clientName || formatPhone(conversation.whatsappNumber) || conversation.whatsappNumber;
526
+ const handle = conversation.contactId ?? conversation.whatsappNumber;
527
+ const displayHandle = formatContactHandle({ handle, channel: conversation.channel });
528
+ const name = conversation.clientName || displayHandle || handle;
1044
529
  const timestamp = formatRelativeTime(conversation.lastAt);
530
+ const flag = contactFlag({ handle, channel: conversation.channel });
1045
531
  const isInbound = conversation.lastDirection === "inbound";
1046
532
  const handleClick = () => {
1047
533
  onClick?.();
1048
534
  onSelect?.(conversation.id);
1049
535
  };
1050
- return /* @__PURE__ */ jsxs10(
536
+ return /* @__PURE__ */ jsxs3(
1051
537
  "button",
1052
538
  {
1053
539
  onClick: handleClick,
1054
- className: "w-full flex items-center gap-3 px-3 py-2.5 text-left transition-colors hover:bg-[#f0f2f5] border-b border-[#e9edef]",
540
+ className: `w-full flex items-center gap-3 px-3 py-2.5 text-left transition-colors ${highlightActive ? "hover:bg-[#f0f2f5]" : ""} ${showDivider ? "border-b border-[#e9edef]" : ""}`,
1055
541
  style: {
1056
- backgroundColor: isActive ? "#f0f2f5" : "transparent"
542
+ backgroundColor: isActive && highlightActive ? "#f0f2f5" : "transparent"
1057
543
  },
1058
544
  children: [
1059
- /* @__PURE__ */ jsxs10("div", { className: "relative flex-shrink-0", children: [
1060
- /* @__PURE__ */ jsx15(Avatar, { name, size: "lg" }),
1061
- conversation.waitingHuman && /* @__PURE__ */ jsx15("div", { className: "absolute -bottom-0.5 -right-0.5 w-3.5 h-3.5 bg-amber-400 rounded-full border-2 border-white" })
545
+ /* @__PURE__ */ jsxs3("div", { className: "relative flex-shrink-0", children: [
546
+ /* @__PURE__ */ jsx4(Avatar, { name: conversation.clientName, size: "lg" }),
547
+ conversation.waitingHuman && /* @__PURE__ */ jsx4("div", { className: "absolute -bottom-0.5 -right-0.5 w-3.5 h-3.5 bg-amber-400 rounded-full border-2 border-white" })
1062
548
  ] }),
1063
- /* @__PURE__ */ jsxs10("div", { className: "flex-1 min-w-0", children: [
1064
- /* @__PURE__ */ jsxs10("div", { className: "flex items-center justify-between gap-2", children: [
1065
- /* @__PURE__ */ jsxs10("div", { className: "flex items-center gap-1.5 min-w-0", children: [
1066
- /* @__PURE__ */ jsx15("span", { className: "text-[16px] text-[#111b21] truncate", children: name }),
1067
- windowStatus && windowStatus.label === "expired" && /* @__PURE__ */ jsx15("span", { className: "w-2 h-2 rounded-full bg-red-500 flex-shrink-0", title: "Janela expirada" }),
1068
- windowStatus && windowStatus.label === "warning" && /* @__PURE__ */ jsx15("span", { className: "w-2 h-2 rounded-full bg-orange-500 flex-shrink-0", title: "Janela pr\xF3xima do fim" })
549
+ /* @__PURE__ */ jsxs3("div", { className: "flex-1 min-w-0", children: [
550
+ /* @__PURE__ */ jsxs3("div", { className: "flex items-center justify-between gap-2", children: [
551
+ /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-1.5 min-w-0", children: [
552
+ !conversation.clientName && flag ? /* @__PURE__ */ jsx4("span", { "aria-hidden": true, children: flag }) : null,
553
+ /* @__PURE__ */ jsx4("span", { className: "text-[16px] text-[#111b21] truncate", children: name }),
554
+ windowStatus && windowStatus.label === "expired" && /* @__PURE__ */ jsx4("span", { className: "w-2 h-2 rounded-full bg-red-500 flex-shrink-0", title: expiredWindowLabel }),
555
+ windowStatus && windowStatus.label === "warning" && /* @__PURE__ */ jsx4("span", { className: "w-2 h-2 rounded-full bg-orange-500 flex-shrink-0", title: warningWindowLabel })
1069
556
  ] }),
1070
- timestamp && /* @__PURE__ */ jsx15("span", { className: "text-xs text-[#667781] flex-shrink-0", children: timestamp })
557
+ timestamp && /* @__PURE__ */ jsx4("span", { className: "text-xs text-[#667781] flex-shrink-0", children: timestamp })
1071
558
  ] }),
1072
- /* @__PURE__ */ jsxs10("div", { className: "flex items-center justify-between mt-0.5", children: [
1073
- /* @__PURE__ */ jsxs10("span", { className: "text-sm text-[#667781] truncate max-w-[180px]", children: [
1074
- preview.icon && /* @__PURE__ */ jsx15("span", { className: "mr-1", children: preview.icon }),
559
+ conversation.clientName ? /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-1 text-xs text-[#667781]", children: [
560
+ flag ? /* @__PURE__ */ jsx4("span", { "aria-hidden": true, children: flag }) : null,
561
+ /* @__PURE__ */ jsx4("span", { className: "truncate", children: displayHandle })
562
+ ] }) : null,
563
+ /* @__PURE__ */ jsxs3("div", { className: "flex items-center justify-between mt-0.5", children: [
564
+ /* @__PURE__ */ jsxs3("span", { className: "text-sm text-[#667781] truncate max-w-[180px]", children: [
565
+ preview.icon && /* @__PURE__ */ jsx4("span", { className: "mr-1", children: preview.icon }),
1075
566
  preview.preview || "Sem mensagens"
1076
567
  ] }),
1077
- conversation.unread > 0 && /* @__PURE__ */ jsx15("span", { className: "bg-[#25d366] text-white text-xs font-semibold rounded-full min-w-[20px] h-5 px-1.5 flex items-center justify-center flex-shrink-0 ml-2", children: conversation.unread > 99 ? "99+" : conversation.unread })
568
+ conversation.unread > 0 && /* @__PURE__ */ jsx4("span", { className: "bg-[#25d366] text-white text-xs font-semibold rounded-full min-w-[20px] h-5 px-1.5 flex items-center justify-center flex-shrink-0 ml-2", children: conversation.unread > 99 ? "99+" : conversation.unread })
1078
569
  ] }),
1079
- /* @__PURE__ */ jsxs10("div", { className: "flex items-center gap-1.5 mt-1", children: [
1080
- conversation.mode === "human" && /* @__PURE__ */ jsx15("span", { className: "text-[10px] px-1.5 py-0.5 rounded font-medium bg-purple-100 text-purple-700", children: "Humano" }),
1081
- conversation.mode === "bot" && conversation.waitingHuman && /* @__PURE__ */ jsx15("span", { className: "text-[10px] px-1.5 py-0.5 rounded font-medium bg-amber-100 text-amber-700", children: "Aguardando" })
570
+ /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-1.5 mt-1", children: [
571
+ conversation.mode === "human" && /* @__PURE__ */ jsx4("span", { className: "text-[10px] px-1.5 py-0.5 rounded font-medium bg-purple-100 text-purple-700", children: "Humano" }),
572
+ conversation.mode === "bot" && conversation.waitingHuman && /* @__PURE__ */ jsx4("span", { className: "text-[10px] px-1.5 py-0.5 rounded font-medium bg-amber-100 text-amber-700", children: "Aguardando" })
1082
573
  ] })
1083
574
  ] })
1084
575
  ]
@@ -1087,9 +578,9 @@ var ConversationListItem = ({
1087
578
  };
1088
579
 
1089
580
  // src/Toast.tsx
1090
- import { createContext as createContext2, useContext as useContext2, useState as useState7, useCallback as useCallback3, useEffect as useEffect3 } from "react";
1091
- import { jsx as jsx16, jsxs as jsxs11 } from "react/jsx-runtime";
1092
- var ToastContext = createContext2(null);
581
+ import { createContext, useContext, useState as useState2, useCallback, useEffect as useEffect2 } from "react";
582
+ import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
583
+ var ToastContext = createContext(null);
1093
584
  var nextId = 0;
1094
585
  var singletonShow = null;
1095
586
  var toast = {
@@ -1105,34 +596,34 @@ var typeStyles = {
1105
596
  info: "bg-blue-600"
1106
597
  };
1107
598
  var typeIcons = {
1108
- success: /* @__PURE__ */ jsx16("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ jsx16("polyline", { points: "20 6 9 17 4 12" }) }),
1109
- error: /* @__PURE__ */ jsxs11("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
1110
- /* @__PURE__ */ jsx16("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
1111
- /* @__PURE__ */ jsx16("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
599
+ success: /* @__PURE__ */ jsx5("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ jsx5("polyline", { points: "20 6 9 17 4 12" }) }),
600
+ error: /* @__PURE__ */ jsxs4("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
601
+ /* @__PURE__ */ jsx5("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
602
+ /* @__PURE__ */ jsx5("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
1112
603
  ] }),
1113
- info: /* @__PURE__ */ jsxs11("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
1114
- /* @__PURE__ */ jsx16("circle", { cx: "12", cy: "12", r: "10" }),
1115
- /* @__PURE__ */ jsx16("line", { x1: "12", y1: "16", x2: "12", y2: "12" }),
1116
- /* @__PURE__ */ jsx16("line", { x1: "12", y1: "8", x2: "12.01", y2: "8" })
604
+ info: /* @__PURE__ */ jsxs4("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
605
+ /* @__PURE__ */ jsx5("circle", { cx: "12", cy: "12", r: "10" }),
606
+ /* @__PURE__ */ jsx5("line", { x1: "12", y1: "16", x2: "12", y2: "12" }),
607
+ /* @__PURE__ */ jsx5("line", { x1: "12", y1: "8", x2: "12.01", y2: "8" })
1117
608
  ] })
1118
609
  };
1119
610
  function ToastItemComponent({ item, onRemove }) {
1120
- const [exiting, setExiting] = useState7(false);
1121
- useEffect3(() => {
611
+ const [exiting, setExiting] = useState2(false);
612
+ useEffect2(() => {
1122
613
  const timer = setTimeout(() => {
1123
614
  setExiting(true);
1124
615
  setTimeout(() => onRemove(item.id), 300);
1125
616
  }, 5e3);
1126
617
  return () => clearTimeout(timer);
1127
618
  }, [item.id, onRemove]);
1128
- return /* @__PURE__ */ jsxs11(
619
+ return /* @__PURE__ */ jsxs4(
1129
620
  "div",
1130
621
  {
1131
622
  className: `flex items-center gap-2 px-4 py-3 rounded-lg shadow-lg text-white text-sm min-w-[280px] max-w-[400px] ${typeStyles[item.type]} ${exiting ? "animate-slide-out" : "animate-slide-in"}`,
1132
623
  children: [
1133
- /* @__PURE__ */ jsx16("span", { className: "flex-shrink-0", children: typeIcons[item.type] }),
1134
- /* @__PURE__ */ jsx16("span", { className: "flex-1", children: item.message }),
1135
- /* @__PURE__ */ jsx16(
624
+ /* @__PURE__ */ jsx5("span", { className: "flex-shrink-0", children: typeIcons[item.type] }),
625
+ /* @__PURE__ */ jsx5("span", { className: "flex-1", children: item.message }),
626
+ /* @__PURE__ */ jsx5(
1136
627
  "button",
1137
628
  {
1138
629
  onClick: () => {
@@ -1140,9 +631,9 @@ function ToastItemComponent({ item, onRemove }) {
1140
631
  setTimeout(() => onRemove(item.id), 300);
1141
632
  },
1142
633
  className: "flex-shrink-0 opacity-70 hover:opacity-100 transition-opacity",
1143
- children: /* @__PURE__ */ jsxs11("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
1144
- /* @__PURE__ */ jsx16("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
1145
- /* @__PURE__ */ jsx16("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
634
+ children: /* @__PURE__ */ jsxs4("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
635
+ /* @__PURE__ */ jsx5("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
636
+ /* @__PURE__ */ jsx5("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
1146
637
  ] })
1147
638
  }
1148
639
  )
@@ -1151,24 +642,24 @@ function ToastItemComponent({ item, onRemove }) {
1151
642
  );
1152
643
  }
1153
644
  function ToastProvider({ children }) {
1154
- const [items, setItems] = useState7([]);
1155
- const show = useCallback3((type, message) => {
645
+ const [items, setItems] = useState2([]);
646
+ const show = useCallback((type, message) => {
1156
647
  const id = ++nextId;
1157
648
  setItems((prev) => [...prev, { id, type, message }]);
1158
649
  }, []);
1159
- const remove = useCallback3((id) => {
650
+ const remove = useCallback((id) => {
1160
651
  setItems((prev) => prev.filter((item) => item.id !== id));
1161
652
  }, []);
1162
- useEffect3(() => {
653
+ useEffect2(() => {
1163
654
  singletonShow = show;
1164
655
  return () => {
1165
656
  singletonShow = null;
1166
657
  };
1167
658
  }, [show]);
1168
- return /* @__PURE__ */ jsxs11(ToastContext.Provider, { value: { show }, children: [
659
+ return /* @__PURE__ */ jsxs4(ToastContext.Provider, { value: { show }, children: [
1169
660
  children,
1170
- /* @__PURE__ */ jsx16("div", { className: "fixed top-4 right-4 z-[100] flex flex-col gap-2", children: items.map((item) => /* @__PURE__ */ jsx16(ToastItemComponent, { item, onRemove: remove }, item.id)) }),
1171
- /* @__PURE__ */ jsx16("style", { children: `
661
+ /* @__PURE__ */ jsx5("div", { className: "fixed top-4 right-4 z-[100] flex flex-col gap-2", children: items.map((item) => /* @__PURE__ */ jsx5(ToastItemComponent, { item, onRemove: remove }, item.id)) }),
662
+ /* @__PURE__ */ jsx5("style", { children: `
1172
663
  @keyframes slide-in {
1173
664
  from { transform: translateX(100%); opacity: 0; }
1174
665
  to { transform: translateX(0); opacity: 1; }
@@ -1183,7 +674,7 @@ function ToastProvider({ children }) {
1183
674
  ] });
1184
675
  }
1185
676
  function useToast() {
1186
- const context = useContext2(ToastContext);
677
+ const context = useContext(ToastContext);
1187
678
  if (!context) {
1188
679
  throw new Error("useToast must be used within a ToastProvider");
1189
680
  }
@@ -1191,11 +682,11 @@ function useToast() {
1191
682
  }
1192
683
 
1193
684
  // src/MessageText.tsx
1194
- import { useCallback as useCallback4, useState as useState8 } from "react";
1195
- import { jsx as jsx17, jsxs as jsxs12 } from "react/jsx-runtime";
685
+ import { useCallback as useCallback2, useState as useState3 } from "react";
686
+ import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
1196
687
  function MessageText({ message }) {
1197
- const [copied, setCopied] = useState8(false);
1198
- const handleCopy = useCallback4(async () => {
688
+ const [copied, setCopied] = useState3(false);
689
+ const handleCopy = useCallback2(async () => {
1199
690
  if (!message.content) return;
1200
691
  try {
1201
692
  await navigator.clipboard.writeText(message.content);
@@ -1205,102 +696,557 @@ function MessageText({ message }) {
1205
696
  }
1206
697
  }, [message.content]);
1207
698
  if (message.type === "template")
1208
- return /* @__PURE__ */ jsx17("p", { className: "text-sm italic text-[#667781]", children: message.content ?? "Template message" });
1209
- return /* @__PURE__ */ jsxs12(
699
+ return /* @__PURE__ */ jsx6("p", { className: "text-sm italic text-[#667781]", children: message.content ?? "Template message" });
700
+ return /* @__PURE__ */ jsxs5(
1210
701
  "div",
1211
702
  {
1212
703
  onClick: handleCopy,
1213
704
  className: "text-[14.2px] leading-[19px] whitespace-pre-wrap break-words select-all [&_strong]:font-bold [&_em]:italic [&_del]:line-through",
1214
705
  children: [
1215
- /* @__PURE__ */ jsx17("span", { children: parseWhatsAppFormatting(message.content ?? "") }),
1216
- copied && /* @__PURE__ */ jsx17("span", { className: "absolute top-0 right-0 -translate-y-full bg-[#3b4a54] text-white text-[11px] px-1.5 py-0.5 rounded shadow-lg", children: "Copiado!" })
706
+ /* @__PURE__ */ jsx6("span", { children: parseWhatsAppFormatting(message.content ?? "") }),
707
+ copied && /* @__PURE__ */ jsx6("span", { className: "absolute top-0 right-0 -translate-y-full bg-[#3b4a54] text-white text-[11px] px-1.5 py-0.5 rounded shadow-lg", children: "Copiado!" })
1217
708
  ]
1218
709
  }
1219
710
  );
1220
711
  }
1221
712
 
1222
713
  // src/MessageTimestamp.tsx
1223
- import { jsx as jsx18, jsxs as jsxs13 } from "react/jsx-runtime";
714
+ import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
1224
715
  function MessageTimestamp({ timestamp, status, isOutbound }) {
1225
- return /* @__PURE__ */ jsxs13("div", { className: "flex items-center justify-end gap-[3px]", children: [
1226
- /* @__PURE__ */ jsx18("span", { className: "text-[11px] text-[#667781] leading-[15px] select-none", children: timestamp }),
1227
- isOutbound && status && /* @__PURE__ */ jsx18("span", { className: "flex items-center -mr-[2px]", children: /* @__PURE__ */ jsx18(StatusTicks, { status }) })
716
+ return /* @__PURE__ */ jsxs6("div", { className: "flex items-center justify-end gap-[3px]", children: [
717
+ /* @__PURE__ */ jsx7("span", { className: "text-[11px] text-[#667781] leading-[15px] select-none", children: timestamp }),
718
+ isOutbound && status && /* @__PURE__ */ jsx7("span", { className: "flex items-center -mr-[2px]", children: /* @__PURE__ */ jsx7(StatusTicks, { status }) })
1228
719
  ] });
1229
720
  }
1230
721
 
1231
722
  // src/MessageTail.tsx
1232
- import { jsx as jsx19 } from "react/jsx-runtime";
723
+ import { jsx as jsx8 } from "react/jsx-runtime";
1233
724
  function MessageTail({ isOutbound }) {
1234
725
  if (isOutbound)
1235
- return /* @__PURE__ */ jsx19("svg", { className: "absolute top-0 -right-[5px] w-[5px] h-[11px]", viewBox: "0 0 5 11", fill: "#d9fdd3", children: /* @__PURE__ */ jsx19("path", { d: "M5 0H0v11c1.5-2 4.5-2 5-4V0z" }) });
1236
- return /* @__PURE__ */ jsx19("svg", { className: "absolute top-0 -left-[5px] w-[5px] h-[11px]", viewBox: "0 0 5 11", fill: "white", children: /* @__PURE__ */ jsx19("path", { d: "M0 0h5v11C3.5 9 .5 9 0 7V0z" }) });
726
+ return /* @__PURE__ */ jsx8("svg", { className: "absolute top-0 -right-[5px] w-[5px] h-[11px]", viewBox: "0 0 5 11", fill: "#d9fdd3", children: /* @__PURE__ */ jsx8("path", { d: "M5 0H0v11c1.5-2 4.5-2 5-4V0z" }) });
727
+ return /* @__PURE__ */ jsx8("svg", { className: "absolute top-0 -left-[5px] w-[5px] h-[11px]", viewBox: "0 0 5 11", fill: "white", children: /* @__PURE__ */ jsx8("path", { d: "M0 0h5v11C3.5 9 .5 9 0 7V0z" }) });
1237
728
  }
1238
729
 
1239
- // src/useWaitingNotifications.ts
1240
- import { useState as useState9, useEffect as useEffect4, useRef as useRef5, useCallback as useCallback5 } from "react";
730
+ // src/conversationWindow.ts
731
+ var MINUTE_MS = 6e4;
732
+ var HOUR_MS = 60 * MINUTE_MS;
733
+ var DAY_MS = 24 * HOUR_MS;
734
+ var CONVERSATION_WINDOW = {
735
+ ALL: "all",
736
+ FRESH: "fresh",
737
+ WARNING: "warning",
738
+ CRITICAL: "critical",
739
+ EXPIRED: "expired"
740
+ };
741
+ var WINDOW_FILTERS = [
742
+ { value: CONVERSATION_WINDOW.ALL, label: "Todas", dotClass: "" },
743
+ { value: CONVERSATION_WINDOW.FRESH, label: "<12h", dotClass: "bg-green-500" },
744
+ { value: CONVERSATION_WINDOW.WARNING, label: "12-21h", dotClass: "bg-yellow-500" },
745
+ { value: CONVERSATION_WINDOW.CRITICAL, label: "21-24h", dotClass: "bg-red-500" },
746
+ { value: CONVERSATION_WINDOW.EXPIRED, label: ">24h", dotClass: "bg-gray-400" }
747
+ ];
748
+ var WARNING_RATIO = 0.5;
749
+ var CRITICAL_RATIO = 0.875;
750
+ function windowOf(params) {
751
+ const capabilities = capabilitiesOf(params.channel);
752
+ if (!capabilities.hasSessionWindow) return CONVERSATION_WINDOW.FRESH;
753
+ if (!params.lastInboundAt) return CONVERSATION_WINDOW.EXPIRED;
754
+ const windowMs = capabilities.windowHours * HOUR_MS;
755
+ const elapsed = params.now - new Date(params.lastInboundAt).getTime();
756
+ if (elapsed >= windowMs) return CONVERSATION_WINDOW.EXPIRED;
757
+ if (elapsed >= windowMs * CRITICAL_RATIO) return CONVERSATION_WINDOW.CRITICAL;
758
+ if (elapsed >= windowMs * WARNING_RATIO) return CONVERSATION_WINDOW.WARNING;
759
+ return CONVERSATION_WINDOW.FRESH;
760
+ }
761
+ function formatStalledFor(lastAt, now) {
762
+ const elapsed = Math.max(0, now - new Date(lastAt).getTime());
763
+ const days = Math.floor(elapsed / DAY_MS);
764
+ const hours = Math.floor(elapsed % DAY_MS / HOUR_MS);
765
+ const minutes = Math.floor(elapsed % HOUR_MS / MINUTE_MS);
766
+ if (days > 0) return `${days}d ${hours}h:${String(minutes).padStart(2, "0")}m`;
767
+ if (hours > 0) return `${hours}h:${String(minutes).padStart(2, "0")}m`;
768
+ return `${minutes}m`;
769
+ }
770
+
771
+ // src/ChannelIcon.tsx
772
+ import { Fragment as Fragment2, jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
773
+ var CHANNEL_BRAND_COLOR = {
774
+ [CONVERSATION_CHANNEL.WHATSAPP]: "#25D366",
775
+ [CONVERSATION_CHANNEL.MESSENGER]: "#0084FF",
776
+ [CONVERSATION_CHANNEL.INSTAGRAM]: "#E4405F",
777
+ [CONVERSATION_CHANNEL.WEBCHAT]: "#64748B"
778
+ };
779
+ function Glyph({ channel }) {
780
+ const color = CHANNEL_BRAND_COLOR[channel];
781
+ if (channel === CONVERSATION_CHANNEL.WHATSAPP) {
782
+ return /* @__PURE__ */ jsxs7(Fragment2, { children: [
783
+ /* @__PURE__ */ jsx9("circle", { cx: "12", cy: "12", r: "11", fill: color }),
784
+ /* @__PURE__ */ jsx9(
785
+ "path",
786
+ {
787
+ d: "M8.6 7.2c.3-.1.7 0 .9.3l1 1.6c.2.3.1.7-.1.9l-.7.7c.6 1.3 1.6 2.3 2.9 2.9l.7-.7c.2-.2.6-.3.9-.1l1.6 1c.3.2.4.6.3.9-.4 1-1.4 1.6-2.4 1.4-3-.5-5.4-2.9-5.9-5.9-.2-1 .4-2 1.4-2.4Z",
788
+ fill: "#fff"
789
+ }
790
+ )
791
+ ] });
792
+ }
793
+ if (channel === CONVERSATION_CHANNEL.MESSENGER) {
794
+ return /* @__PURE__ */ jsxs7(Fragment2, { children: [
795
+ /* @__PURE__ */ jsx9("circle", { cx: "12", cy: "12", r: "11", fill: color }),
796
+ /* @__PURE__ */ jsx9("path", { d: "M6.5 15.5 11 10.8l2.4 2.4L17.5 9l-4.6 4.9-2.4-2.4-4 4Z", fill: "#fff", stroke: "#fff", strokeWidth: "1.4", strokeLinejoin: "round" })
797
+ ] });
798
+ }
799
+ if (channel === CONVERSATION_CHANNEL.INSTAGRAM) {
800
+ return /* @__PURE__ */ jsxs7(Fragment2, { children: [
801
+ /* @__PURE__ */ jsx9("rect", { x: "2", y: "2", width: "20", height: "20", rx: "6", fill: color }),
802
+ /* @__PURE__ */ jsx9("rect", { x: "6.5", y: "6.5", width: "11", height: "11", rx: "3.5", fill: "none", stroke: "#fff", strokeWidth: "1.6" }),
803
+ /* @__PURE__ */ jsx9("circle", { cx: "12", cy: "12", r: "2.6", fill: "none", stroke: "#fff", strokeWidth: "1.6" }),
804
+ /* @__PURE__ */ jsx9("circle", { cx: "16.4", cy: "7.6", r: "1", fill: "#fff" })
805
+ ] });
806
+ }
807
+ return /* @__PURE__ */ jsxs7(Fragment2, { children: [
808
+ /* @__PURE__ */ jsx9("circle", { cx: "12", cy: "12", r: "11", fill: color }),
809
+ /* @__PURE__ */ jsx9("path", { d: "M6.5 8.5h11v7h-5.5L8.5 18v-2.5h-2v-7Z", fill: "#fff" })
810
+ ] });
811
+ }
812
+ function ChannelIcon({ channel, size = 14, className = "" }) {
813
+ const resolved = channel ?? CONVERSATION_CHANNEL.WHATSAPP;
814
+ const { label } = capabilitiesOf(resolved);
815
+ return /* @__PURE__ */ jsx9(
816
+ "svg",
817
+ {
818
+ viewBox: "0 0 24 24",
819
+ width: size,
820
+ height: size,
821
+ className: `inline-block shrink-0 ${className}`.trim(),
822
+ role: "img",
823
+ "aria-label": label,
824
+ children: /* @__PURE__ */ jsx9(Glyph, { channel: resolved })
825
+ }
826
+ );
827
+ }
828
+
829
+ // src/ConversationRow.tsx
830
+ import { jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
831
+ var WINDOW_TITLE = {
832
+ [CONVERSATION_WINDOW.ALL]: "",
833
+ [CONVERSATION_WINDOW.FRESH]: "Janela de 24h aberta (menos de 12h)",
834
+ [CONVERSATION_WINDOW.WARNING]: "Janela de 24h fechando (12-21h)",
835
+ [CONVERSATION_WINDOW.CRITICAL]: "Janela de 24h quase expirada (21-24h)",
836
+ [CONVERSATION_WINDOW.EXPIRED]: "Janela de 24h expirada \u2014 s\xF3 template"
837
+ };
838
+ var STALLED_THRESHOLD_MS = 60 * 60 * 1e3;
839
+ var WINDOW_BAR_CLASS = {
840
+ [CONVERSATION_WINDOW.ALL]: "bg-transparent",
841
+ [CONVERSATION_WINDOW.FRESH]: "bg-green-500",
842
+ [CONVERSATION_WINDOW.WARNING]: "bg-yellow-500",
843
+ [CONVERSATION_WINDOW.CRITICAL]: "bg-red-500",
844
+ [CONVERSATION_WINDOW.EXPIRED]: "bg-gray-300"
845
+ };
846
+ function ConversationRow({
847
+ conversation,
848
+ active,
849
+ selected,
850
+ now,
851
+ busy,
852
+ onOpen,
853
+ onToggleSelected,
854
+ onTakeover,
855
+ className,
856
+ classNames
857
+ }) {
858
+ const capabilities = capabilitiesOf(conversation.channel);
859
+ const window2 = windowOf({ lastInboundAt: conversation.lastInboundAt, now, channel: conversation.channel });
860
+ const stalledMs = now - new Date(conversation.lastAt).getTime();
861
+ const isStalled = stalledMs > STALLED_THRESHOLD_MS;
862
+ const isWaiting = conversation.mode === "bot" && conversation.waitingHuman;
863
+ return /* @__PURE__ */ jsxs8("div", { className: cn("cv-row flex border-b", active && "cv-row--active", classNames?.root, className), children: [
864
+ /* @__PURE__ */ jsx10(
865
+ "span",
866
+ {
867
+ className: cn("w-1 shrink-0", WINDOW_BAR_CLASS[window2], classNames?.windowBar),
868
+ title: WINDOW_TITLE[window2],
869
+ "aria-label": WINDOW_TITLE[window2]
870
+ }
871
+ ),
872
+ /* @__PURE__ */ jsxs8("div", { className: "min-w-0 flex-1", children: [
873
+ /* @__PURE__ */ jsxs8("div", { className: "flex items-start gap-2 px-2 pt-2", children: [
874
+ /* @__PURE__ */ jsx10(
875
+ "input",
876
+ {
877
+ type: "checkbox",
878
+ checked: selected,
879
+ onChange: onToggleSelected,
880
+ "aria-label": `Selecionar conversa ${conversation.whatsappNumber}`,
881
+ className: "mt-3"
882
+ }
883
+ ),
884
+ /* @__PURE__ */ jsx10("div", { className: "min-w-0 flex-1", children: /* @__PURE__ */ jsx10(
885
+ ConversationListItem,
886
+ {
887
+ conversation,
888
+ active,
889
+ onClick: onOpen,
890
+ showDivider: false,
891
+ highlightActive: false
892
+ }
893
+ ) })
894
+ ] }),
895
+ /* @__PURE__ */ jsxs8("div", { className: "flex flex-wrap items-center gap-1.5 px-4 pb-2 pl-8", children: [
896
+ /* @__PURE__ */ jsxs8("span", { className: "cv-pill", children: [
897
+ /* @__PURE__ */ jsx10(ChannelIcon, { channel: conversation.channel }),
898
+ " ",
899
+ capabilities.label
900
+ ] }),
901
+ conversation.mode === "human" ? /* @__PURE__ */ jsx10("span", { className: "cv-pill cv-pill--success", children: "\u{1F9D1}\u200D\u{1F4BC} em atendimento" }) : null,
902
+ isWaiting ? /* @__PURE__ */ jsx10("span", { className: "cv-pill cv-pill--warning", children: "\u23F3 aguardando atendimento" }) : null,
903
+ !isWaiting && conversation.mode === "bot" ? /* @__PURE__ */ jsx10("span", { className: "cv-pill", children: "\u{1F916} bot ativo" }) : null,
904
+ isStalled ? /* @__PURE__ */ jsxs8("span", { className: "cv-pill cv-pill--danger", children: [
905
+ "\u23F1\uFE0F parada h\xE1 ",
906
+ formatStalledFor(conversation.lastAt, now)
907
+ ] }) : null,
908
+ isWaiting ? /* @__PURE__ */ jsx10("button", { type: "button", onClick: onTakeover, disabled: busy, className: "cv-header-action", children: "\u25B6\uFE0F Continuar Atendimento" }) : null
909
+ ] })
910
+ ] })
911
+ ] });
912
+ }
1241
913
 
1242
- // src/providers/ConversationsProvider.tsx
1243
- import { createContext as createContext3, useContext as useContext3 } from "react";
1244
- import { jsx as jsx20 } from "react/jsx-runtime";
1245
- var ConversationsContext = createContext3(null);
1246
- function ConversationsProvider({
1247
- api,
1248
- sse,
1249
- children
914
+ // src/ConversationHeader.tsx
915
+ import { useState as useState4 } from "react";
916
+ import { jsx as jsx11, jsxs as jsxs9 } from "react/jsx-runtime";
917
+ var DEFAULT_CONVERSATION_HEADER_LABELS = {
918
+ botMode: "\u{1F916} atendimento autom\xE1tico",
919
+ humanMode: "\u{1F9D1}\u200D\u{1F4BC} atendimento humano",
920
+ returnToBot: "Devolver ao bot",
921
+ finish: "Finalizar",
922
+ takeover: "Assumir atendimento",
923
+ download: "Baixar conversa",
924
+ documents: "Arquivos da conversa",
925
+ back: "Voltar para a lista",
926
+ moreActions: "Mais a\xE7\xF5es"
927
+ };
928
+ function ConversationHeader({
929
+ conversation,
930
+ busy = false,
931
+ onTakeover,
932
+ onReturnToBot,
933
+ onFinish,
934
+ onDownload,
935
+ onOpenDocuments,
936
+ documentsOpen = false,
937
+ extraUtilities,
938
+ onBack,
939
+ labels: labelsOverride,
940
+ className,
941
+ classNames
1250
942
  }) {
1251
- return /* @__PURE__ */ jsx20(ConversationsContext.Provider, { value: { api, sse }, children });
943
+ const labels = { ...DEFAULT_CONVERSATION_HEADER_LABELS, ...labelsOverride };
944
+ const isHuman = conversation.mode === "human";
945
+ const handle = conversation.contactId ?? conversation.whatsappNumber;
946
+ const displayHandle = formatContactHandle({ handle, channel: conversation.channel });
947
+ const flag = contactFlag({ handle, channel: conversation.channel });
948
+ const capabilities = capabilitiesOf(conversation.channel);
949
+ const [menuOpen, setMenuOpen] = useState4(false);
950
+ const utilities = [
951
+ onOpenDocuments ? { key: "documents", icon: "\u{1F4C4}", label: labels.documents, run: onOpenDocuments, active: documentsOpen } : void 0,
952
+ onDownload ? { key: "download", icon: "\u2B07\uFE0F", label: labels.download, run: onDownload, active: false } : void 0,
953
+ ...(extraUtilities ?? []).map((utility) => ({ ...utility, active: utility.active ?? false }))
954
+ ].filter(
955
+ (utility) => Boolean(utility)
956
+ );
957
+ const actions = [
958
+ !isHuman && onTakeover ? { key: "takeover", label: labels.takeover, run: onTakeover, className: "cv-header-action--primary" } : void 0,
959
+ isHuman && onReturnToBot ? { key: "release", label: labels.returnToBot, run: onReturnToBot, className: "" } : void 0,
960
+ isHuman && onFinish ? { key: "finish", label: `\u2715 ${labels.finish}`, run: onFinish, className: "cv-header-action--danger" } : void 0
961
+ ].filter((action) => Boolean(action));
962
+ const menuItems = [
963
+ ...utilities.map((utility) => ({
964
+ key: utility.key,
965
+ label: `${utility.icon} ${utility.label}`,
966
+ run: utility.run,
967
+ className: ""
968
+ })),
969
+ ...actions
970
+ ];
971
+ return /* @__PURE__ */ jsxs9(
972
+ "header",
973
+ {
974
+ className: cn(
975
+ "flex flex-nowrap items-center justify-between gap-3 border-b px-4 py-3",
976
+ classNames?.root,
977
+ className
978
+ ),
979
+ children: [
980
+ /* @__PURE__ */ jsxs9("div", { className: cn("flex min-w-0 flex-1 items-center gap-3", classNames?.identity), children: [
981
+ onBack ? /* @__PURE__ */ jsx11("button", { type: "button", onClick: onBack, className: "cv-back cv-touch", "aria-label": labels.back, title: labels.back, children: "\u2190" }) : null,
982
+ /* @__PURE__ */ jsx11(Avatar, { name: conversation.clientName, size: "md" }),
983
+ /* @__PURE__ */ jsxs9("div", { className: "min-w-0", children: [
984
+ /* @__PURE__ */ jsx11("p", { className: cn("truncate font-medium", classNames?.name), children: conversation.clientName ?? displayHandle }),
985
+ /* @__PURE__ */ jsxs9("p", { className: cn("truncate text-xs text-gray-500", classNames?.meta), children: [
986
+ flag ? `${flag} ` : "",
987
+ displayHandle,
988
+ /* @__PURE__ */ jsxs9("span", { className: "ml-2 inline-flex items-center gap-1", children: [
989
+ /* @__PURE__ */ jsx11(ChannelIcon, { channel: conversation.channel }),
990
+ /* @__PURE__ */ jsx11("span", { className: "cv-only-wide", children: capabilities.label })
991
+ ] }),
992
+ /* @__PURE__ */ jsx11("span", { className: "ml-2 cv-only-wide", children: isHuman ? labels.humanMode : labels.botMode }),
993
+ /* @__PURE__ */ jsx11("span", { className: "ml-1 cv-only-narrow", title: isHuman ? labels.humanMode : labels.botMode, children: isHuman ? "\u{1F9D1}\u200D\u{1F4BC}" : "\u{1F916}" })
994
+ ] })
995
+ ] })
996
+ ] }),
997
+ /* @__PURE__ */ jsxs9("div", { className: cn("flex shrink-0 items-center gap-2", classNames?.actions), children: [
998
+ /* @__PURE__ */ jsxs9("div", { className: cn("hidden items-center gap-2 lg:flex", classNames?.desktopActions), children: [
999
+ utilities.map((utility) => /* @__PURE__ */ jsx11(
1000
+ "button",
1001
+ {
1002
+ type: "button",
1003
+ onClick: utility.run,
1004
+ disabled: busy,
1005
+ "aria-pressed": utility.active,
1006
+ title: utility.label,
1007
+ "aria-label": utility.label,
1008
+ className: `cv-header-icon ${utility.active ? "cv-header-icon--active" : ""}`,
1009
+ children: utility.icon
1010
+ },
1011
+ utility.key
1012
+ )),
1013
+ actions.map((action) => /* @__PURE__ */ jsx11(
1014
+ "button",
1015
+ {
1016
+ type: "button",
1017
+ onClick: action.run,
1018
+ disabled: busy,
1019
+ className: `cv-header-action ${action.className}`,
1020
+ children: action.label
1021
+ },
1022
+ action.key
1023
+ ))
1024
+ ] }),
1025
+ menuItems.length > 0 ? /* @__PURE__ */ jsxs9("div", { className: cn("cv-menu lg:hidden", classNames?.mobileMenu), children: [
1026
+ /* @__PURE__ */ jsx11(
1027
+ "button",
1028
+ {
1029
+ type: "button",
1030
+ onClick: () => setMenuOpen(!menuOpen),
1031
+ "aria-expanded": menuOpen,
1032
+ "aria-label": labels.moreActions,
1033
+ title: labels.moreActions,
1034
+ className: "cv-header-icon cv-touch",
1035
+ children: "\u22EE"
1036
+ }
1037
+ ),
1038
+ menuOpen ? /* @__PURE__ */ jsx11("div", { className: "cv-menu-panel", role: "menu", children: menuItems.map((item) => /* @__PURE__ */ jsx11(
1039
+ "button",
1040
+ {
1041
+ type: "button",
1042
+ role: "menuitem",
1043
+ onClick: () => {
1044
+ setMenuOpen(false);
1045
+ item.run();
1046
+ },
1047
+ disabled: busy,
1048
+ className: `cv-header-action cv-touch ${item.className}`,
1049
+ children: item.label
1050
+ },
1051
+ item.key
1052
+ )) }) : null
1053
+ ] }) : null
1054
+ ] })
1055
+ ]
1056
+ }
1057
+ );
1252
1058
  }
1253
- function useConversations() {
1254
- return useContext3(ConversationsContext);
1059
+
1060
+ // src/ConversationContextPanel.tsx
1061
+ import { useState as useState6 } from "react";
1062
+
1063
+ // src/useIsNarrow.ts
1064
+ import { useEffect as useEffect3, useState as useState5 } from "react";
1065
+ var NARROW_MAX_WIDTH_PX = 1023;
1066
+ function useIsNarrow() {
1067
+ const [isNarrow, setIsNarrow] = useState5(
1068
+ () => typeof window !== "undefined" && window.matchMedia(`(max-width: ${NARROW_MAX_WIDTH_PX}px)`).matches
1069
+ );
1070
+ useEffect3(() => {
1071
+ const query = window.matchMedia(`(max-width: ${NARROW_MAX_WIDTH_PX}px)`);
1072
+ const handleChange = (event) => setIsNarrow(event.matches);
1073
+ query.addEventListener("change", handleChange);
1074
+ setIsNarrow(query.matches);
1075
+ return () => query.removeEventListener("change", handleChange);
1076
+ }, []);
1077
+ return isNarrow;
1078
+ }
1079
+
1080
+ // src/ConversationContextPanel.tsx
1081
+ import { jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
1082
+ var DEFAULT_CONVERSATION_CONTEXT_LABELS = {
1083
+ title: "\u{1F4CB} Suas Sele\xE7\xF5es",
1084
+ empty: "Nada coletado ainda nesta conversa."
1085
+ };
1086
+ function ConversationContextPanel({
1087
+ entries,
1088
+ labels: labelsOverride,
1089
+ className,
1090
+ classNames
1091
+ }) {
1092
+ const labels = { ...DEFAULT_CONVERSATION_CONTEXT_LABELS, ...labelsOverride };
1093
+ const filled = entries.filter((entry) => Boolean(entry.value));
1094
+ const isNarrow = useIsNarrow();
1095
+ const [manualOpen, setManualOpen] = useState6(void 0);
1096
+ const open = manualOpen ?? (!isNarrow && filled.length > 0);
1097
+ return /* @__PURE__ */ jsxs10("section", { className: cn("border-b", classNames?.root, className), children: [
1098
+ /* @__PURE__ */ jsxs10(
1099
+ "button",
1100
+ {
1101
+ type: "button",
1102
+ onClick: () => setManualOpen(!open),
1103
+ "aria-expanded": open,
1104
+ className: cn("flex w-full items-center gap-2 px-4 py-3 text-left text-sm font-medium", classNames?.toggle),
1105
+ children: [
1106
+ /* @__PURE__ */ jsx12("span", { "aria-hidden": true, className: "text-xs", children: open ? "\u25BE" : "\u25B8" }),
1107
+ /* @__PURE__ */ jsx12("span", { children: labels.title }),
1108
+ /* @__PURE__ */ jsxs10("span", { className: cn("rounded-full bg-gray-200 px-2 text-xs dark:bg-gray-700", classNames?.counter), children: [
1109
+ filled.length,
1110
+ "/",
1111
+ entries.length
1112
+ ] })
1113
+ ]
1114
+ }
1115
+ ),
1116
+ open ? /* @__PURE__ */ jsx12("div", { className: cn("px-4 pb-3", classNames?.body), children: entries.length === 0 ? /* @__PURE__ */ jsx12("p", { className: "text-xs text-gray-500", children: labels.empty }) : /* @__PURE__ */ jsx12("dl", { className: "grid grid-cols-2 gap-2 text-xs", children: entries.map((entry) => /* @__PURE__ */ jsxs10("div", { className: "min-w-0", children: [
1117
+ /* @__PURE__ */ jsxs10("dt", { className: "truncate text-gray-500", children: [
1118
+ entry.icon ? `${entry.icon} ` : "",
1119
+ entry.label
1120
+ ] }),
1121
+ /* @__PURE__ */ jsx12("dd", { className: "truncate font-medium", children: entry.value ? /* @__PURE__ */ jsxs10("span", { className: "text-green-700 dark:text-green-400", children: [
1122
+ "\u2713 ",
1123
+ entry.value
1124
+ ] }) : "\u2014" })
1125
+ ] }, entry.key)) }) }) : null
1126
+ ] });
1127
+ }
1128
+
1129
+ // src/WindowExpiredNotice.tsx
1130
+ import { jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
1131
+ var DEFAULT_WINDOW_EXPIRED_LABELS = {
1132
+ title: "\u23F0 Janela de 24h expirada \u2014 o WhatsApp n\xE3o aceita mensagens livres.",
1133
+ description: "Envie uma mensagem de template para reabrir a conversa com o cliente.",
1134
+ sendTemplate: "\u{1F4E8} Enviar Template (HSM)"
1135
+ };
1136
+ function WindowExpiredNotice({
1137
+ onSendTemplate,
1138
+ disabled = false,
1139
+ labels: labelsOverride,
1140
+ className
1141
+ }) {
1142
+ const labels = { ...DEFAULT_WINDOW_EXPIRED_LABELS, ...labelsOverride };
1143
+ return /* @__PURE__ */ jsxs11("div", { role: "status", className: cn("border-t bg-yellow-50 px-4 py-3 text-sm dark:bg-yellow-950", className), children: [
1144
+ /* @__PURE__ */ jsx13("p", { className: "font-medium text-yellow-900 dark:text-yellow-200", children: labels.title }),
1145
+ /* @__PURE__ */ jsx13("p", { className: "text-yellow-800 dark:text-yellow-300", children: labels.description }),
1146
+ onSendTemplate ? /* @__PURE__ */ jsx13("button", { type: "button", onClick: onSendTemplate, disabled, className: "cv-header-action mt-2", children: labels.sendTemplate }) : null
1147
+ ] });
1148
+ }
1149
+ function isWindowBlocking(window2) {
1150
+ return window2 === CONVERSATION_WINDOW.EXPIRED;
1151
+ }
1152
+
1153
+ // src/conversationTranscript.ts
1154
+ var SENDER_LABEL = {
1155
+ customer: "Cliente",
1156
+ bot: "Bot",
1157
+ agent: "Atendente"
1158
+ };
1159
+ function buildTranscriptText(params) {
1160
+ const header = [
1161
+ `Conversa: ${params.clientName ?? params.whatsappNumber}`,
1162
+ `N\xFAmero: ${params.whatsappNumber}`,
1163
+ `Mensagens: ${params.messages.length}`,
1164
+ ""
1165
+ ];
1166
+ const lines = params.messages.map((message) => {
1167
+ const stamp = new Date(message.timestamp).toLocaleString("pt-BR");
1168
+ const author = message.agentName ?? SENDER_LABEL[message.sender];
1169
+ const body = message.content ?? message.caption ?? `<${message.type}>`;
1170
+ return `[${stamp}] ${author}: ${body}`;
1171
+ });
1172
+ return [...header, ...lines].join("\n");
1173
+ }
1174
+ function buildTranscriptFilename(whatsappNumber, generatedAt) {
1175
+ const stamp = generatedAt.toISOString().slice(0, 10);
1176
+ return `conversa-${whatsappNumber}-${stamp}.txt`;
1177
+ }
1178
+ function downloadTextFile(filename, content) {
1179
+ const blob = new Blob([content], { type: "text/plain;charset=utf-8" });
1180
+ const url = URL.createObjectURL(blob);
1181
+ const anchor = document.createElement("a");
1182
+ anchor.href = url;
1183
+ anchor.download = filename;
1184
+ anchor.click();
1185
+ URL.revokeObjectURL(url);
1255
1186
  }
1256
1187
 
1257
1188
  // src/useWaitingNotifications.ts
1258
- function useWaitingNotifications() {
1259
- const conversationsCtx = useConversations();
1260
- const [conversations, setConversations] = useState9([]);
1261
- const previousUnreadMap = useRef5(/* @__PURE__ */ new Map());
1262
- const notifiedIds = useRef5(/* @__PURE__ */ new Set());
1263
- const pollConversations = useCallback5(async () => {
1264
- if (!conversationsCtx) return;
1189
+ import { useState as useState7, useEffect as useEffect4, useRef as useRef3, useCallback as useCallback3 } from "react";
1190
+ var DEFAULT_POLL_INTERVAL_MS = 1e4;
1191
+ var DEFAULT_LIMIT = 50;
1192
+ var DEFAULT_LABELS = {
1193
+ title: (conversation) => conversation.clientName ?? conversation.whatsappNumber,
1194
+ body: (conversation) => conversation.lastContent ?? "Nova mensagem"
1195
+ };
1196
+ function useWaitingNotifications(params) {
1197
+ const conversationsContext = useConversations();
1198
+ const [conversations, setConversations] = useState7([]);
1199
+ const previousUnreadMap = useRef3(/* @__PURE__ */ new Map());
1200
+ const notifiedIds = useRef3(/* @__PURE__ */ new Set());
1201
+ const isPollingRef = useRef3(false);
1202
+ const isEnabled = params?.enabled ?? true;
1203
+ const intervalMs = params?.intervalMs ?? DEFAULT_POLL_INTERVAL_MS;
1204
+ const icon = params?.icon;
1205
+ const listParams = params?.params;
1206
+ const labelTitle = params?.labels?.title ?? DEFAULT_LABELS.title;
1207
+ const labelBody = params?.labels?.body ?? DEFAULT_LABELS.body;
1208
+ const refresh = useCallback3(async () => {
1209
+ if (!conversationsContext || isPollingRef.current) return;
1210
+ isPollingRef.current = true;
1265
1211
  try {
1266
- const result = await conversationsCtx.api.fetchConversations({ limit: 50 });
1212
+ const result = conversationsOf(
1213
+ await conversationsContext.api.fetchConversations({ limit: DEFAULT_LIMIT, ...listParams })
1214
+ );
1267
1215
  setConversations(result);
1268
1216
  const currentMap = /* @__PURE__ */ new Map();
1269
- for (const conv of result) {
1270
- currentMap.set(conv.id, conv.unread);
1271
- }
1272
- for (const conv of result) {
1273
- if (conv.unread <= 0) continue;
1274
- const prev = previousUnreadMap.current.get(conv.id) ?? 0;
1275
- if (conv.unread > prev && !notifiedIds.current.has(conv.id)) {
1276
- notifiedIds.current.add(conv.id);
1277
- if (typeof window !== "undefined" && "Notification" in window && Notification.permission === "granted") {
1278
- const title = conv.clientName ?? conv.whatsappNumber;
1279
- const body = conv.lastContent ?? "Nova mensagem";
1280
- new Notification(title, { body, icon: "/favicon.ico" });
1281
- }
1217
+ for (const conversation of result) currentMap.set(conversation.id, conversation.unread);
1218
+ for (const conversation of result) {
1219
+ if (conversation.unread <= 0) continue;
1220
+ const previousUnread = previousUnreadMap.current.get(conversation.id) ?? 0;
1221
+ if (conversation.unread <= previousUnread || notifiedIds.current.has(conversation.id)) continue;
1222
+ notifiedIds.current.add(conversation.id);
1223
+ if (typeof window !== "undefined" && "Notification" in window && Notification.permission === "granted") {
1224
+ new Notification(labelTitle(conversation), { body: labelBody(conversation), ...icon ? { icon } : {} });
1282
1225
  }
1283
1226
  }
1284
1227
  previousUnreadMap.current = currentMap;
1285
1228
  } catch {
1229
+ } finally {
1230
+ isPollingRef.current = false;
1286
1231
  }
1287
- }, [conversationsCtx]);
1232
+ }, [conversationsContext, listParams, icon, labelTitle, labelBody]);
1288
1233
  useEffect4(() => {
1234
+ if (!isEnabled) return;
1289
1235
  if (typeof window !== "undefined" && "Notification" in window && Notification.permission === "default") {
1290
1236
  Notification.requestPermission();
1291
1237
  }
1292
- pollConversations();
1293
- const interval = setInterval(pollConversations, 1e4);
1238
+ void refresh();
1239
+ const interval = setInterval(() => void refresh(), intervalMs);
1294
1240
  return () => clearInterval(interval);
1295
- }, [pollConversations]);
1296
- const unreadCount = conversations.reduce((sum, conv) => sum + conv.unread, 0);
1297
- return { unreadCount, conversations };
1241
+ }, [refresh, isEnabled, intervalMs]);
1242
+ const unreadCount = conversations.reduce((sum, conversation) => sum + conversation.unread, 0);
1243
+ return { unreadCount, conversations, refresh };
1298
1244
  }
1299
1245
 
1300
1246
  // src/settings/WhatsAppTemplateSettingsForm.tsx
1301
1247
  import { Plus, RefreshCw, Save, Trash2 } from "lucide-react";
1302
- import { jsx as jsx21, jsxs as jsxs14 } from "react/jsx-runtime";
1303
- var DEFAULT_LABELS = {
1248
+ import { jsx as jsx14, jsxs as jsxs12 } from "react/jsx-runtime";
1249
+ var DEFAULT_LABELS2 = {
1304
1250
  sectionTitle: "WhatsApp",
1305
1251
  sectionDescription: "Template usado para reabrir a janela de 24h com o cliente.",
1306
1252
  templateLabel: "Template aprovado",
@@ -1334,7 +1280,7 @@ function WhatsAppTemplateSettingsForm({
1334
1280
  onSave,
1335
1281
  labels: labelsOverride
1336
1282
  }) {
1337
- const labels = { ...DEFAULT_LABELS, ...labelsOverride };
1283
+ const labels = { ...DEFAULT_LABELS2, ...labelsOverride };
1338
1284
  const selected = templates.find((template) => template.name === selectedTemplateName);
1339
1285
  function handleSelect(name) {
1340
1286
  const template = templates.find((t) => t.name === name);
@@ -1358,16 +1304,16 @@ function WhatsAppTemplateSettingsForm({
1358
1304
  }
1359
1305
  onVariablesChange([...variables, token]);
1360
1306
  }
1361
- return /* @__PURE__ */ jsxs14("section", { className: "bg-white dark:bg-gray-800 rounded-xl border border-gray-200 dark:border-gray-700 overflow-hidden", children: [
1362
- /* @__PURE__ */ jsxs14("div", { className: "px-6 py-4 border-b border-gray-100 dark:border-gray-700", children: [
1363
- /* @__PURE__ */ jsx21("h2", { className: "text-sm font-semibold text-gray-900 dark:text-gray-100", children: labels.sectionTitle }),
1364
- /* @__PURE__ */ jsx21("p", { className: "text-xs text-gray-500 dark:text-gray-400 mt-1", children: labels.sectionDescription })
1307
+ return /* @__PURE__ */ jsxs12("section", { className: "bg-white dark:bg-gray-800 rounded-xl border border-gray-200 dark:border-gray-700 overflow-hidden", children: [
1308
+ /* @__PURE__ */ jsxs12("div", { className: "px-6 py-4 border-b border-gray-100 dark:border-gray-700", children: [
1309
+ /* @__PURE__ */ jsx14("h2", { className: "text-sm font-semibold text-gray-900 dark:text-gray-100", children: labels.sectionTitle }),
1310
+ /* @__PURE__ */ jsx14("p", { className: "text-xs text-gray-500 dark:text-gray-400 mt-1", children: labels.sectionDescription })
1365
1311
  ] }),
1366
- /* @__PURE__ */ jsxs14("form", { onSubmit: onSave, className: "p-6 space-y-4", children: [
1367
- /* @__PURE__ */ jsxs14("div", { children: [
1368
- /* @__PURE__ */ jsxs14("div", { className: "flex items-center justify-between mb-1.5", children: [
1369
- /* @__PURE__ */ jsx21("label", { className: "text-sm font-medium text-gray-700 dark:text-gray-300", children: labels.templateLabel }),
1370
- /* @__PURE__ */ jsxs14(
1312
+ /* @__PURE__ */ jsxs12("form", { onSubmit: onSave, className: "p-6 space-y-4", children: [
1313
+ /* @__PURE__ */ jsxs12("div", { children: [
1314
+ /* @__PURE__ */ jsxs12("div", { className: "flex items-center justify-between mb-1.5", children: [
1315
+ /* @__PURE__ */ jsx14("label", { className: "text-sm font-medium text-gray-700 dark:text-gray-300", children: labels.templateLabel }),
1316
+ /* @__PURE__ */ jsxs12(
1371
1317
  "button",
1372
1318
  {
1373
1319
  type: "button",
@@ -1375,31 +1321,31 @@ function WhatsAppTemplateSettingsForm({
1375
1321
  disabled: loadingTemplates,
1376
1322
  className: "flex items-center gap-1 text-xs text-blue-600 dark:text-blue-400 hover:underline disabled:opacity-50",
1377
1323
  children: [
1378
- /* @__PURE__ */ jsx21(RefreshCw, { size: 11, className: loadingTemplates ? "animate-spin" : "" }),
1324
+ /* @__PURE__ */ jsx14(RefreshCw, { size: 11, className: loadingTemplates ? "animate-spin" : "" }),
1379
1325
  labels.refreshList
1380
1326
  ]
1381
1327
  }
1382
1328
  )
1383
1329
  ] }),
1384
- templatesError && /* @__PURE__ */ jsxs14("div", { className: "mb-2 rounded-lg bg-amber-50 dark:bg-amber-950/40 border border-amber-200 dark:border-amber-800 px-3 py-2 text-xs text-amber-700 dark:text-amber-400", children: [
1330
+ templatesError && /* @__PURE__ */ jsxs12("div", { className: "mb-2 rounded-lg bg-amber-50 dark:bg-amber-950/40 border border-amber-200 dark:border-amber-800 px-3 py-2 text-xs text-amber-700 dark:text-amber-400", children: [
1385
1331
  labels.errorLoading,
1386
- /* @__PURE__ */ jsx21("br", {}),
1387
- /* @__PURE__ */ jsxs14("span", { className: "text-gray-500 dark:text-gray-400 mt-0.5 block", children: [
1332
+ /* @__PURE__ */ jsx14("br", {}),
1333
+ /* @__PURE__ */ jsxs12("span", { className: "text-gray-500 dark:text-gray-400 mt-0.5 block", children: [
1388
1334
  labels.currentNameSaved,
1389
1335
  " ",
1390
- /* @__PURE__ */ jsx21("strong", { children: selectedTemplateName || "\u2014" })
1336
+ /* @__PURE__ */ jsx14("strong", { children: selectedTemplateName || "\u2014" })
1391
1337
  ] })
1392
1338
  ] }),
1393
- loadingTemplates && /* @__PURE__ */ jsx21("div", { className: "h-10 rounded-xl bg-gray-100 dark:bg-gray-700 animate-pulse" }),
1394
- !loadingTemplates && !templatesError && /* @__PURE__ */ jsxs14(
1339
+ loadingTemplates && /* @__PURE__ */ jsx14("div", { className: "h-10 rounded-xl bg-gray-100 dark:bg-gray-700 animate-pulse" }),
1340
+ !loadingTemplates && !templatesError && /* @__PURE__ */ jsxs12(
1395
1341
  "select",
1396
1342
  {
1397
1343
  value: selectedTemplateName,
1398
1344
  onChange: (e) => handleSelect(e.target.value),
1399
1345
  className: "w-full border border-gray-200 dark:border-gray-600 dark:bg-gray-700 dark:text-gray-100 rounded-xl px-3 py-2.5 text-sm focus:outline-none focus:ring-2 focus:ring-blue-400 focus:border-transparent transition-all",
1400
1346
  children: [
1401
- /* @__PURE__ */ jsx21("option", { value: "", children: labels.selectPlaceholder }),
1402
- templates.map((template) => /* @__PURE__ */ jsxs14("option", { value: template.name, disabled: template.status !== "APPROVED", children: [
1347
+ /* @__PURE__ */ jsx14("option", { value: "", children: labels.selectPlaceholder }),
1348
+ templates.map((template) => /* @__PURE__ */ jsxs12("option", { value: template.name, disabled: template.status !== "APPROVED", children: [
1403
1349
  template.displayName,
1404
1350
  " \xB7 ",
1405
1351
  template.shortId,
@@ -1411,27 +1357,27 @@ function WhatsAppTemplateSettingsForm({
1411
1357
  ]
1412
1358
  }
1413
1359
  ),
1414
- selected?.bodyText && /* @__PURE__ */ jsx21("div", { className: "mt-2 rounded-lg bg-gray-50 dark:bg-gray-700/50 border border-gray-200 dark:border-gray-600 px-3 py-2 text-xs text-gray-600 dark:text-gray-300 whitespace-pre-wrap", children: selected.bodyText })
1360
+ selected?.bodyText && /* @__PURE__ */ jsx14("div", { className: "mt-2 rounded-lg bg-gray-50 dark:bg-gray-700/50 border border-gray-200 dark:border-gray-600 px-3 py-2 text-xs text-gray-600 dark:text-gray-300 whitespace-pre-wrap", children: selected.bodyText })
1415
1361
  ] }),
1416
- /* @__PURE__ */ jsxs14("div", { children: [
1417
- /* @__PURE__ */ jsxs14("div", { className: "flex items-center justify-between mb-1.5", children: [
1418
- /* @__PURE__ */ jsx21("label", { className: "text-sm font-medium text-gray-700 dark:text-gray-300", children: labels.variablesLabel }),
1419
- /* @__PURE__ */ jsxs14(
1362
+ /* @__PURE__ */ jsxs12("div", { children: [
1363
+ /* @__PURE__ */ jsxs12("div", { className: "flex items-center justify-between mb-1.5", children: [
1364
+ /* @__PURE__ */ jsx14("label", { className: "text-sm font-medium text-gray-700 dark:text-gray-300", children: labels.variablesLabel }),
1365
+ /* @__PURE__ */ jsxs12(
1420
1366
  "button",
1421
1367
  {
1422
1368
  type: "button",
1423
1369
  onClick: () => onVariablesChange([...variables, ""]),
1424
1370
  className: "flex items-center gap-1 text-xs text-blue-600 dark:text-blue-400 hover:underline",
1425
1371
  children: [
1426
- /* @__PURE__ */ jsx21(Plus, { size: 12 }),
1372
+ /* @__PURE__ */ jsx14(Plus, { size: 12 }),
1427
1373
  labels.addVariable
1428
1374
  ]
1429
1375
  }
1430
1376
  )
1431
1377
  ] }),
1432
- availableVariables.length > 0 && /* @__PURE__ */ jsxs14("div", { className: "mb-3 rounded-lg border border-gray-200 dark:border-gray-600 bg-gray-50 dark:bg-gray-700/40 p-2.5", children: [
1433
- /* @__PURE__ */ jsx21("p", { className: "text-xs font-medium text-gray-600 dark:text-gray-300 mb-1.5", children: labels.variablesAvailableTitle }),
1434
- /* @__PURE__ */ jsx21("div", { className: "flex flex-wrap gap-1.5", children: availableVariables.map((available) => /* @__PURE__ */ jsxs14(
1378
+ availableVariables.length > 0 && /* @__PURE__ */ jsxs12("div", { className: "mb-3 rounded-lg border border-gray-200 dark:border-gray-600 bg-gray-50 dark:bg-gray-700/40 p-2.5", children: [
1379
+ /* @__PURE__ */ jsx14("p", { className: "text-xs font-medium text-gray-600 dark:text-gray-300 mb-1.5", children: labels.variablesAvailableTitle }),
1380
+ /* @__PURE__ */ jsx14("div", { className: "flex flex-wrap gap-1.5", children: availableVariables.map((available) => /* @__PURE__ */ jsxs12(
1435
1381
  "button",
1436
1382
  {
1437
1383
  type: "button",
@@ -1439,8 +1385,8 @@ function WhatsAppTemplateSettingsForm({
1439
1385
  title: `Inserir ${available.token}`,
1440
1386
  className: "inline-flex items-center gap-1 rounded-md border border-gray-200 dark:border-gray-600 bg-white dark:bg-gray-800 px-2 py-1 text-xs hover:border-blue-400 dark:hover:border-blue-500 transition-colors",
1441
1387
  children: [
1442
- /* @__PURE__ */ jsx21("code", { className: "text-blue-600 dark:text-blue-300", children: available.token }),
1443
- /* @__PURE__ */ jsxs14("span", { className: "text-gray-400 dark:text-gray-500", children: [
1388
+ /* @__PURE__ */ jsx14("code", { className: "text-blue-600 dark:text-blue-300", children: available.token }),
1389
+ /* @__PURE__ */ jsxs12("span", { className: "text-gray-400 dark:text-gray-500", children: [
1444
1390
  "\xB7 ",
1445
1391
  available.label
1446
1392
  ] })
@@ -1448,13 +1394,13 @@ function WhatsAppTemplateSettingsForm({
1448
1394
  },
1449
1395
  available.token
1450
1396
  )) }),
1451
- /* @__PURE__ */ jsx21("p", { className: "mt-1.5 text-[11px] text-gray-400 dark:text-gray-500", children: labels.variablesAvailableHint })
1397
+ /* @__PURE__ */ jsx14("p", { className: "mt-1.5 text-[11px] text-gray-400 dark:text-gray-500", children: labels.variablesAvailableHint })
1452
1398
  ] }),
1453
- /* @__PURE__ */ jsx21("p", { className: "text-xs text-gray-400 dark:text-gray-500 mb-2", children: labels.variablesMappingHint }),
1454
- variables.length === 0 && /* @__PURE__ */ jsx21("p", { className: "text-xs text-gray-400 dark:text-gray-500 italic", children: labels.noVariables }),
1455
- /* @__PURE__ */ jsx21("div", { className: "space-y-2", children: variables.map((variable, index) => /* @__PURE__ */ jsxs14("div", { className: "flex items-center gap-2", children: [
1456
- /* @__PURE__ */ jsx21("span", { className: "text-xs text-gray-400 dark:text-gray-500 w-6 text-right flex-shrink-0", children: `{{${index + 1}}}` }),
1457
- /* @__PURE__ */ jsx21(
1399
+ /* @__PURE__ */ jsx14("p", { className: "text-xs text-gray-400 dark:text-gray-500 mb-2", children: labels.variablesMappingHint }),
1400
+ variables.length === 0 && /* @__PURE__ */ jsx14("p", { className: "text-xs text-gray-400 dark:text-gray-500 italic", children: labels.noVariables }),
1401
+ /* @__PURE__ */ jsx14("div", { className: "space-y-2", children: variables.map((variable, index) => /* @__PURE__ */ jsxs12("div", { className: "flex items-center gap-2", children: [
1402
+ /* @__PURE__ */ jsx14("span", { className: "text-xs text-gray-400 dark:text-gray-500 w-6 text-right flex-shrink-0", children: `{{${index + 1}}}` }),
1403
+ /* @__PURE__ */ jsx14(
1458
1404
  "input",
1459
1405
  {
1460
1406
  type: "text",
@@ -1464,27 +1410,27 @@ function WhatsAppTemplateSettingsForm({
1464
1410
  className: "flex-1 border border-gray-200 dark:border-gray-600 dark:bg-gray-700 dark:text-gray-100 rounded-lg px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-blue-400 focus:border-transparent transition-all placeholder:text-gray-400 dark:placeholder:text-gray-500"
1465
1411
  }
1466
1412
  ),
1467
- /* @__PURE__ */ jsx21(
1413
+ /* @__PURE__ */ jsx14(
1468
1414
  "button",
1469
1415
  {
1470
1416
  type: "button",
1471
1417
  onClick: () => removeVariable(index),
1472
1418
  className: "text-gray-400 hover:text-red-500 dark:hover:text-red-400 transition-colors flex-shrink-0",
1473
1419
  title: labels.removeVariable,
1474
- children: /* @__PURE__ */ jsx21(Trash2, { size: 14 })
1420
+ children: /* @__PURE__ */ jsx14(Trash2, { size: 14 })
1475
1421
  }
1476
1422
  )
1477
1423
  ] }, index)) })
1478
1424
  ] }),
1479
- saveSuccess && /* @__PURE__ */ jsx21("div", { className: "bg-green-50 dark:bg-green-950/40 border border-green-200 dark:border-green-800 rounded-xl px-4 py-3 text-sm text-green-700 dark:text-green-400", children: labels.saveSuccess }),
1480
- /* @__PURE__ */ jsx21("div", { className: "flex justify-end", children: /* @__PURE__ */ jsxs14(
1425
+ saveSuccess && /* @__PURE__ */ jsx14("div", { className: "bg-green-50 dark:bg-green-950/40 border border-green-200 dark:border-green-800 rounded-xl px-4 py-3 text-sm text-green-700 dark:text-green-400", children: labels.saveSuccess }),
1426
+ /* @__PURE__ */ jsx14("div", { className: "flex justify-end", children: /* @__PURE__ */ jsxs12(
1481
1427
  "button",
1482
1428
  {
1483
1429
  type: "submit",
1484
1430
  disabled: saving || !selectedTemplateName.trim(),
1485
1431
  className: "flex items-center gap-2 bg-blue-600 hover:bg-blue-700 disabled:bg-blue-400 text-white font-semibold px-5 py-2.5 rounded-xl transition-colors text-sm",
1486
1432
  children: [
1487
- saving ? /* @__PURE__ */ jsx21("span", { className: "w-4 h-4 border-2 border-white/40 border-t-white rounded-full animate-spin" }) : /* @__PURE__ */ jsx21(Save, { size: 14 }),
1433
+ saving ? /* @__PURE__ */ jsx14("span", { className: "w-4 h-4 border-2 border-white/40 border-t-white rounded-full animate-spin" }) : /* @__PURE__ */ jsx14(Save, { size: 14 }),
1488
1434
  saving ? labels.saving : labels.save
1489
1435
  ]
1490
1436
  }
@@ -1495,12 +1441,13 @@ function WhatsAppTemplateSettingsForm({
1495
1441
 
1496
1442
  // src/settings/WhatsAppCreateTemplateForm.tsx
1497
1443
  import { SendHorizonal } from "lucide-react";
1498
- import { jsx as jsx22, jsxs as jsxs15 } from "react/jsx-runtime";
1499
- var DEFAULT_LABELS2 = {
1444
+ import { jsx as jsx15, jsxs as jsxs13 } from "react/jsx-runtime";
1445
+ var DEFAULT_LABELS3 = {
1500
1446
  sectionTitle: "Criar novo template",
1501
1447
  sectionDescription: "Envia um template para aprova\xE7\xE3o da Meta.",
1502
1448
  nameLabel: "Nome do template",
1503
1449
  nameHint: "Somente letras min\xFAsculas, n\xFAmeros e underscore.",
1450
+ namePlaceholder: "reengajamento_cliente",
1504
1451
  categoryLabel: "Categoria",
1505
1452
  languageLabel: "Idioma",
1506
1453
  headerLabel: "Cabe\xE7alho",
@@ -1546,82 +1493,82 @@ function WhatsAppCreateTemplateForm({
1546
1493
  labels: labelsOverride,
1547
1494
  variableExamples = DEFAULT_VARIABLE_EXAMPLES
1548
1495
  }) {
1549
- const labels = { ...DEFAULT_LABELS2, ...labelsOverride };
1496
+ const labels = { ...DEFAULT_LABELS3, ...labelsOverride };
1550
1497
  const detectedVariables = detectVariables(value.bodyText);
1551
1498
  function set(key, next) {
1552
1499
  onChange({ ...value, [key]: next });
1553
1500
  }
1554
- return /* @__PURE__ */ jsxs15("section", { className: "bg-white dark:bg-gray-800 rounded-xl border border-gray-200 dark:border-gray-700 overflow-hidden", children: [
1555
- /* @__PURE__ */ jsxs15("div", { className: "px-6 py-4 border-b border-gray-100 dark:border-gray-700", children: [
1556
- /* @__PURE__ */ jsx22("h2", { className: "text-sm font-semibold text-gray-900 dark:text-gray-100", children: labels.sectionTitle }),
1557
- /* @__PURE__ */ jsx22("p", { className: "text-xs text-gray-500 dark:text-gray-400 mt-1", children: labels.sectionDescription })
1501
+ return /* @__PURE__ */ jsxs13("section", { className: "bg-white dark:bg-gray-800 rounded-xl border border-gray-200 dark:border-gray-700 overflow-hidden", children: [
1502
+ /* @__PURE__ */ jsxs13("div", { className: "px-6 py-4 border-b border-gray-100 dark:border-gray-700", children: [
1503
+ /* @__PURE__ */ jsx15("h2", { className: "text-sm font-semibold text-gray-900 dark:text-gray-100", children: labels.sectionTitle }),
1504
+ /* @__PURE__ */ jsx15("p", { className: "text-xs text-gray-500 dark:text-gray-400 mt-1", children: labels.sectionDescription })
1558
1505
  ] }),
1559
- /* @__PURE__ */ jsxs15("form", { onSubmit, className: "p-6 space-y-4", children: [
1560
- /* @__PURE__ */ jsxs15("div", { children: [
1561
- /* @__PURE__ */ jsx22("label", { className: "text-sm font-medium text-gray-700 dark:text-gray-300", children: labels.nameLabel }),
1562
- /* @__PURE__ */ jsx22("p", { className: "text-xs text-gray-400 mb-1.5", children: labels.nameHint }),
1563
- /* @__PURE__ */ jsx22(
1506
+ /* @__PURE__ */ jsxs13("form", { onSubmit, className: "p-6 space-y-4", children: [
1507
+ /* @__PURE__ */ jsxs13("div", { children: [
1508
+ /* @__PURE__ */ jsx15("label", { className: "text-sm font-medium text-gray-700 dark:text-gray-300", children: labels.nameLabel }),
1509
+ /* @__PURE__ */ jsx15("p", { className: "text-xs text-gray-400 mb-1.5", children: labels.nameHint }),
1510
+ /* @__PURE__ */ jsx15(
1564
1511
  "input",
1565
1512
  {
1566
1513
  type: "text",
1567
1514
  value: value.name,
1568
1515
  onChange: (e) => set("name", e.target.value.toLowerCase().replace(/[^a-z0-9_]/g, "_")),
1569
- placeholder: "reengajamento_cliente",
1516
+ placeholder: labels.namePlaceholder,
1570
1517
  className: "w-full border border-gray-200 dark:border-gray-600 dark:bg-gray-700 dark:text-gray-100 rounded-xl px-3 py-2.5 text-sm focus:outline-none focus:ring-2 focus:ring-blue-400 focus:border-transparent transition-all",
1571
1518
  required: true
1572
1519
  }
1573
1520
  )
1574
1521
  ] }),
1575
- /* @__PURE__ */ jsxs15("div", { className: "grid grid-cols-2 gap-3", children: [
1576
- /* @__PURE__ */ jsxs15("div", { children: [
1577
- /* @__PURE__ */ jsx22("label", { className: "text-sm font-medium text-gray-700 dark:text-gray-300", children: labels.categoryLabel }),
1578
- /* @__PURE__ */ jsxs15(
1522
+ /* @__PURE__ */ jsxs13("div", { className: "grid grid-cols-2 gap-3", children: [
1523
+ /* @__PURE__ */ jsxs13("div", { children: [
1524
+ /* @__PURE__ */ jsx15("label", { className: "text-sm font-medium text-gray-700 dark:text-gray-300", children: labels.categoryLabel }),
1525
+ /* @__PURE__ */ jsxs13(
1579
1526
  "select",
1580
1527
  {
1581
1528
  value: value.category,
1582
1529
  onChange: (e) => set("category", e.target.value),
1583
1530
  className: "w-full border border-gray-200 dark:border-gray-600 dark:bg-gray-700 dark:text-gray-100 rounded-xl px-3 py-2.5 text-sm focus:outline-none focus:ring-2 focus:ring-blue-400 focus:border-transparent transition-all",
1584
1531
  children: [
1585
- /* @__PURE__ */ jsx22("option", { value: "UTILITY", children: "UTILITY (Transacional)" }),
1586
- /* @__PURE__ */ jsx22("option", { value: "MARKETING", children: "MARKETING (Promocional)" })
1532
+ /* @__PURE__ */ jsx15("option", { value: "UTILITY", children: "UTILITY (Transacional)" }),
1533
+ /* @__PURE__ */ jsx15("option", { value: "MARKETING", children: "MARKETING (Promocional)" })
1587
1534
  ]
1588
1535
  }
1589
1536
  )
1590
1537
  ] }),
1591
- /* @__PURE__ */ jsxs15("div", { children: [
1592
- /* @__PURE__ */ jsx22("label", { className: "text-sm font-medium text-gray-700 dark:text-gray-300", children: labels.languageLabel }),
1593
- /* @__PURE__ */ jsxs15(
1538
+ /* @__PURE__ */ jsxs13("div", { children: [
1539
+ /* @__PURE__ */ jsx15("label", { className: "text-sm font-medium text-gray-700 dark:text-gray-300", children: labels.languageLabel }),
1540
+ /* @__PURE__ */ jsxs13(
1594
1541
  "select",
1595
1542
  {
1596
1543
  value: value.language,
1597
1544
  onChange: (e) => set("language", e.target.value),
1598
1545
  className: "w-full border border-gray-200 dark:border-gray-600 dark:bg-gray-700 dark:text-gray-100 rounded-xl px-3 py-2.5 text-sm focus:outline-none focus:ring-2 focus:ring-blue-400 focus:border-transparent transition-all",
1599
1546
  children: [
1600
- /* @__PURE__ */ jsx22("option", { value: "pt_BR", children: "Portugu\xEAs (Brasil)" }),
1601
- /* @__PURE__ */ jsx22("option", { value: "en_US", children: "Ingl\xEAs (EUA)" }),
1602
- /* @__PURE__ */ jsx22("option", { value: "es_ES", children: "Espanhol" })
1547
+ /* @__PURE__ */ jsx15("option", { value: "pt_BR", children: "Portugu\xEAs (Brasil)" }),
1548
+ /* @__PURE__ */ jsx15("option", { value: "en_US", children: "Ingl\xEAs (EUA)" }),
1549
+ /* @__PURE__ */ jsx15("option", { value: "es_ES", children: "Espanhol" })
1603
1550
  ]
1604
1551
  }
1605
1552
  )
1606
1553
  ] })
1607
1554
  ] }),
1608
- /* @__PURE__ */ jsxs15("div", { children: [
1609
- /* @__PURE__ */ jsx22("label", { className: "text-sm font-medium text-gray-700 dark:text-gray-300", children: labels.headerLabel }),
1610
- /* @__PURE__ */ jsx22("p", { className: "text-xs text-gray-400 mb-1.5", children: labels.headerHint }),
1611
- /* @__PURE__ */ jsxs15("div", { className: "flex gap-2", children: [
1612
- /* @__PURE__ */ jsxs15(
1555
+ /* @__PURE__ */ jsxs13("div", { children: [
1556
+ /* @__PURE__ */ jsx15("label", { className: "text-sm font-medium text-gray-700 dark:text-gray-300", children: labels.headerLabel }),
1557
+ /* @__PURE__ */ jsx15("p", { className: "text-xs text-gray-400 mb-1.5", children: labels.headerHint }),
1558
+ /* @__PURE__ */ jsxs13("div", { className: "flex gap-2", children: [
1559
+ /* @__PURE__ */ jsxs13(
1613
1560
  "select",
1614
1561
  {
1615
1562
  value: value.headerType,
1616
1563
  onChange: (e) => set("headerType", e.target.value),
1617
1564
  className: "border border-gray-200 dark:border-gray-600 dark:bg-gray-700 dark:text-gray-100 rounded-xl px-3 py-2.5 text-sm focus:outline-none focus:ring-2 focus:ring-blue-400 transition-all",
1618
1565
  children: [
1619
- /* @__PURE__ */ jsx22("option", { value: "NONE", children: labels.headerNone }),
1620
- /* @__PURE__ */ jsx22("option", { value: "TEXT", children: labels.headerText })
1566
+ /* @__PURE__ */ jsx15("option", { value: "NONE", children: labels.headerNone }),
1567
+ /* @__PURE__ */ jsx15("option", { value: "TEXT", children: labels.headerText })
1621
1568
  ]
1622
1569
  }
1623
1570
  ),
1624
- value.headerType === "TEXT" && /* @__PURE__ */ jsx22(
1571
+ value.headerType === "TEXT" && /* @__PURE__ */ jsx15(
1625
1572
  "input",
1626
1573
  {
1627
1574
  type: "text",
@@ -1634,10 +1581,10 @@ function WhatsAppCreateTemplateForm({
1634
1581
  )
1635
1582
  ] })
1636
1583
  ] }),
1637
- /* @__PURE__ */ jsxs15("div", { children: [
1638
- /* @__PURE__ */ jsx22("label", { className: "text-sm font-medium text-gray-700 dark:text-gray-300", children: labels.bodyLabel }),
1639
- /* @__PURE__ */ jsx22("p", { className: "text-xs text-gray-400 mb-1.5", children: labels.bodyHint }),
1640
- /* @__PURE__ */ jsx22(
1584
+ /* @__PURE__ */ jsxs13("div", { children: [
1585
+ /* @__PURE__ */ jsx15("label", { className: "text-sm font-medium text-gray-700 dark:text-gray-300", children: labels.bodyLabel }),
1586
+ /* @__PURE__ */ jsx15("p", { className: "text-xs text-gray-400 mb-1.5", children: labels.bodyHint }),
1587
+ /* @__PURE__ */ jsx15(
1641
1588
  "textarea",
1642
1589
  {
1643
1590
  value: value.bodyText,
@@ -1648,26 +1595,26 @@ function WhatsAppCreateTemplateForm({
1648
1595
  required: true
1649
1596
  }
1650
1597
  ),
1651
- detectedVariables.length > 0 && /* @__PURE__ */ jsxs15("div", { className: "mt-2 rounded-lg bg-gray-50 dark:bg-gray-700/50 border border-gray-200 dark:border-gray-600 px-3 py-2", children: [
1652
- /* @__PURE__ */ jsx22("p", { className: "font-medium text-gray-500 dark:text-gray-400 mb-1.5 uppercase tracking-wide text-xs", children: labels.detectedVariables }),
1653
- /* @__PURE__ */ jsx22("div", { className: "flex flex-wrap gap-2", children: detectedVariables.map((num) => /* @__PURE__ */ jsxs15("span", { className: "inline-flex items-center gap-1 text-xs", children: [
1654
- /* @__PURE__ */ jsx22("code", { className: "bg-blue-100 dark:bg-blue-900/40 text-blue-700 dark:text-blue-300 px-1.5 py-0.5 rounded font-mono", children: `{{${num}}}` }),
1655
- /* @__PURE__ */ jsxs15("span", { className: "text-gray-400 dark:text-gray-500", children: [
1598
+ detectedVariables.length > 0 && /* @__PURE__ */ jsxs13("div", { className: "mt-2 rounded-lg bg-gray-50 dark:bg-gray-700/50 border border-gray-200 dark:border-gray-600 px-3 py-2", children: [
1599
+ /* @__PURE__ */ jsx15("p", { className: "font-medium text-gray-500 dark:text-gray-400 mb-1.5 uppercase tracking-wide text-xs", children: labels.detectedVariables }),
1600
+ /* @__PURE__ */ jsx15("div", { className: "flex flex-wrap gap-2", children: detectedVariables.map((num) => /* @__PURE__ */ jsxs13("span", { className: "inline-flex items-center gap-1 text-xs", children: [
1601
+ /* @__PURE__ */ jsx15("code", { className: "bg-blue-100 dark:bg-blue-900/40 text-blue-700 dark:text-blue-300 px-1.5 py-0.5 rounded font-mono", children: `{{${num}}}` }),
1602
+ /* @__PURE__ */ jsxs13("span", { className: "text-gray-400 dark:text-gray-500", children: [
1656
1603
  "= ",
1657
1604
  variableExamples[Number(num) - 1] ?? labels.variableLabel(Number(num))
1658
1605
  ] })
1659
1606
  ] }, num)) }),
1660
- /* @__PURE__ */ jsxs15("p", { className: "text-gray-400 dark:text-gray-500 mt-1.5 text-xs", children: [
1607
+ /* @__PURE__ */ jsxs13("p", { className: "text-gray-400 dark:text-gray-500 mt-1.5 text-xs", children: [
1661
1608
  labels.configureHintPrefix,
1662
- /* @__PURE__ */ jsx22("strong", { children: labels.configureHintLink }),
1609
+ /* @__PURE__ */ jsx15("strong", { children: labels.configureHintLink }),
1663
1610
  labels.configureHintSuffix
1664
1611
  ] })
1665
1612
  ] })
1666
1613
  ] }),
1667
- /* @__PURE__ */ jsxs15("div", { children: [
1668
- /* @__PURE__ */ jsx22("label", { className: "text-sm font-medium text-gray-700 dark:text-gray-300", children: labels.footerLabel }),
1669
- /* @__PURE__ */ jsx22("p", { className: "text-xs text-gray-400 mb-1.5", children: labels.footerHint }),
1670
- /* @__PURE__ */ jsx22(
1614
+ /* @__PURE__ */ jsxs13("div", { children: [
1615
+ /* @__PURE__ */ jsx15("label", { className: "text-sm font-medium text-gray-700 dark:text-gray-300", children: labels.footerLabel }),
1616
+ /* @__PURE__ */ jsx15("p", { className: "text-xs text-gray-400 mb-1.5", children: labels.footerHint }),
1617
+ /* @__PURE__ */ jsx15(
1671
1618
  "input",
1672
1619
  {
1673
1620
  type: "text",
@@ -1679,48 +1626,48 @@ function WhatsAppCreateTemplateForm({
1679
1626
  }
1680
1627
  )
1681
1628
  ] }),
1682
- /* @__PURE__ */ jsxs15("div", { children: [
1683
- /* @__PURE__ */ jsx22("label", { className: "text-sm font-medium text-gray-700 dark:text-gray-300 mb-2 block", children: labels.previewLabel }),
1684
- /* @__PURE__ */ jsxs15("div", { className: "rounded-2xl bg-[#e5ddd5] dark:bg-[#1a1a2e] border border-gray-300 dark:border-gray-600 overflow-hidden shadow-inner", children: [
1685
- /* @__PURE__ */ jsxs15("div", { className: "bg-[#075e54] px-4 py-2.5 flex items-center gap-2", children: [
1686
- /* @__PURE__ */ jsx22("div", { className: "w-8 h-8 rounded-full bg-white/20 flex items-center justify-center text-white text-xs font-bold", children: previewCompanyName.slice(0, 2).toUpperCase() }),
1687
- /* @__PURE__ */ jsxs15("div", { children: [
1688
- /* @__PURE__ */ jsx22("p", { className: "text-white text-xs font-semibold leading-tight", children: previewCompanyName }),
1689
- /* @__PURE__ */ jsx22("p", { className: "text-white/60 text-xs", children: labels.previewOnline })
1629
+ /* @__PURE__ */ jsxs13("div", { children: [
1630
+ /* @__PURE__ */ jsx15("label", { className: "text-sm font-medium text-gray-700 dark:text-gray-300 mb-2 block", children: labels.previewLabel }),
1631
+ /* @__PURE__ */ jsxs13("div", { className: "rounded-2xl bg-[#e5ddd5] dark:bg-[#1a1a2e] border border-gray-300 dark:border-gray-600 overflow-hidden shadow-inner", children: [
1632
+ /* @__PURE__ */ jsxs13("div", { className: "bg-[#075e54] px-4 py-2.5 flex items-center gap-2", children: [
1633
+ /* @__PURE__ */ jsx15("div", { className: "w-8 h-8 rounded-full bg-white/20 flex items-center justify-center text-white text-xs font-bold", children: previewCompanyName.slice(0, 2).toUpperCase() }),
1634
+ /* @__PURE__ */ jsxs13("div", { children: [
1635
+ /* @__PURE__ */ jsx15("p", { className: "text-white text-xs font-semibold leading-tight", children: previewCompanyName }),
1636
+ /* @__PURE__ */ jsx15("p", { className: "text-white/60 text-xs", children: labels.previewOnline })
1690
1637
  ] })
1691
1638
  ] }),
1692
- /* @__PURE__ */ jsxs15("div", { className: "px-3 py-4 space-y-3", style: { minHeight: "120px" }, children: [
1693
- /* @__PURE__ */ jsx22("div", { className: "flex justify-start", style: { maxWidth: "85%" }, children: /* @__PURE__ */ jsxs15("div", { className: "bg-white dark:bg-gray-700 rounded-lg rounded-tl-none px-3 py-2 shadow-sm", children: [
1694
- /* @__PURE__ */ jsx22("p", { className: "text-gray-500 dark:text-gray-400 leading-tight text-sm", children: labels.previewDescription }),
1695
- /* @__PURE__ */ jsx22("p", { className: "text-gray-400 dark:text-gray-500 mt-1 text-right text-[9px]", children: "12:00" })
1639
+ /* @__PURE__ */ jsxs13("div", { className: "px-3 py-4 space-y-3", style: { minHeight: "120px" }, children: [
1640
+ /* @__PURE__ */ jsx15("div", { className: "flex justify-start", style: { maxWidth: "85%" }, children: /* @__PURE__ */ jsxs13("div", { className: "bg-white dark:bg-gray-700 rounded-lg rounded-tl-none px-3 py-2 shadow-sm", children: [
1641
+ /* @__PURE__ */ jsx15("p", { className: "text-gray-500 dark:text-gray-400 leading-tight text-sm", children: labels.previewDescription }),
1642
+ /* @__PURE__ */ jsx15("p", { className: "text-gray-400 dark:text-gray-500 mt-1 text-right text-[9px]", children: "12:00" })
1696
1643
  ] }) }),
1697
- /* @__PURE__ */ jsx22("div", { className: "flex justify-end ml-auto", style: { maxWidth: "85%" }, children: /* @__PURE__ */ jsxs15("div", { className: "bg-[#dcf8c6] dark:bg-[#1b5e20] rounded-lg rounded-tr-none px-3 py-2 shadow-sm", children: [
1698
- value.headerType === "TEXT" && value.headerText && /* @__PURE__ */ jsx22("p", { className: "text-xs font-bold text-gray-700 dark:text-gray-200 mb-1", children: fillPreviewTokens(value.headerText) || labels.previewHeaderFallback }),
1699
- /* @__PURE__ */ jsx22("p", { className: "text-sm text-gray-800 dark:text-gray-100 leading-relaxed", children: value.bodyText ? fillPreviewTokens(value.bodyText) : /* @__PURE__ */ jsx22("span", { className: "text-gray-400 italic", children: labels.previewBodyPlaceholder }) }),
1700
- value.footerText && /* @__PURE__ */ jsx22("p", { className: "text-gray-400 dark:text-gray-400 mt-1 text-xs", children: value.footerText }),
1701
- /* @__PURE__ */ jsx22("p", { className: "text-gray-400 dark:text-gray-400 mt-1 text-right text-[9px]", children: "12:01 \u2713" })
1644
+ /* @__PURE__ */ jsx15("div", { className: "flex justify-end ml-auto", style: { maxWidth: "85%" }, children: /* @__PURE__ */ jsxs13("div", { className: "bg-[#dcf8c6] dark:bg-[#1b5e20] rounded-lg rounded-tr-none px-3 py-2 shadow-sm", children: [
1645
+ value.headerType === "TEXT" && value.headerText && /* @__PURE__ */ jsx15("p", { className: "text-xs font-bold text-gray-700 dark:text-gray-200 mb-1", children: fillPreviewTokens(value.headerText) || labels.previewHeaderFallback }),
1646
+ /* @__PURE__ */ jsx15("p", { className: "text-sm text-gray-800 dark:text-gray-100 leading-relaxed", children: value.bodyText ? fillPreviewTokens(value.bodyText) : /* @__PURE__ */ jsx15("span", { className: "text-gray-400 italic", children: labels.previewBodyPlaceholder }) }),
1647
+ value.footerText && /* @__PURE__ */ jsx15("p", { className: "text-gray-400 dark:text-gray-400 mt-1 text-xs", children: value.footerText }),
1648
+ /* @__PURE__ */ jsx15("p", { className: "text-gray-400 dark:text-gray-400 mt-1 text-right text-[9px]", children: "12:01 \u2713" })
1702
1649
  ] }) })
1703
1650
  ] })
1704
1651
  ] })
1705
1652
  ] }),
1706
- result && /* @__PURE__ */ jsxs15(
1653
+ result && /* @__PURE__ */ jsxs13(
1707
1654
  "div",
1708
1655
  {
1709
1656
  className: `rounded-xl px-4 py-3 text-sm ${result.ok ? "bg-green-50 dark:bg-green-950/40 border border-green-200 dark:border-green-800 text-green-700 dark:text-green-400" : "bg-red-50 dark:bg-red-950/40 border border-red-200 dark:border-red-800 text-red-700 dark:text-red-400"}`,
1710
1657
  children: [
1711
1658
  result.message,
1712
- result.ok && result.status && /* @__PURE__ */ jsx22("p", { className: "text-xs mt-1 opacity-75", children: labels.successStatus(result.status) })
1659
+ result.ok && result.status && /* @__PURE__ */ jsx15("p", { className: "text-xs mt-1 opacity-75", children: labels.successStatus(result.status) })
1713
1660
  ]
1714
1661
  }
1715
1662
  ),
1716
- /* @__PURE__ */ jsx22("div", { className: "flex justify-end", children: /* @__PURE__ */ jsxs15(
1663
+ /* @__PURE__ */ jsx15("div", { className: "flex justify-end", children: /* @__PURE__ */ jsxs13(
1717
1664
  "button",
1718
1665
  {
1719
1666
  type: "submit",
1720
1667
  disabled: submitting || !value.name || !value.bodyText,
1721
1668
  className: "flex items-center gap-2 bg-emerald-600 hover:bg-emerald-700 disabled:bg-emerald-400 text-white font-semibold px-5 py-2.5 rounded-xl transition-colors text-sm",
1722
1669
  children: [
1723
- submitting ? /* @__PURE__ */ jsx22("span", { className: "w-4 h-4 border-2 border-white/40 border-t-white rounded-full animate-spin" }) : /* @__PURE__ */ jsx22(SendHorizonal, { size: 14 }),
1670
+ submitting ? /* @__PURE__ */ jsx15("span", { className: "w-4 h-4 border-2 border-white/40 border-t-white rounded-full animate-spin" }) : /* @__PURE__ */ jsx15(SendHorizonal, { size: 14 }),
1724
1671
  submitting ? labels.sending : labels.sendForApproval
1725
1672
  ]
1726
1673
  }
@@ -1731,8 +1678,8 @@ function WhatsAppCreateTemplateForm({
1731
1678
 
1732
1679
  // src/settings/WelcomeFarewellForm.tsx
1733
1680
  import { LogOut, Mail, Save as Save2 } from "lucide-react";
1734
- import { jsx as jsx23, jsxs as jsxs16 } from "react/jsx-runtime";
1735
- var DEFAULT_LABELS3 = {
1681
+ import { jsx as jsx16, jsxs as jsxs14 } from "react/jsx-runtime";
1682
+ var DEFAULT_LABELS4 = {
1736
1683
  sectionTitle: "Boas-vindas e encerramento",
1737
1684
  welcomeMessage: "Mensagem de boas-vindas",
1738
1685
  welcomeMessagePlaceholder: "Ol\xE1! Sou o assistente virtual da {empresa}...",
@@ -1758,16 +1705,16 @@ function WelcomeFarewellForm({
1758
1705
  farewellPlaceholders = DEFAULT_FAREWELL_PLACEHOLDERS,
1759
1706
  labels: labelsOverride
1760
1707
  }) {
1761
- const labels = { ...DEFAULT_LABELS3, ...labelsOverride };
1762
- return /* @__PURE__ */ jsxs16("section", { className: "bg-white dark:bg-gray-800 rounded-xl border border-gray-200 dark:border-gray-700 overflow-hidden", children: [
1763
- /* @__PURE__ */ jsx23("div", { className: "px-6 py-4 border-b border-gray-100 dark:border-gray-700", children: /* @__PURE__ */ jsx23("h2", { className: "text-sm font-semibold text-gray-900 dark:text-gray-100", children: labels.sectionTitle }) }),
1764
- /* @__PURE__ */ jsxs16("form", { onSubmit: onSave, className: "p-6 space-y-5", children: [
1765
- /* @__PURE__ */ jsxs16("div", { children: [
1766
- /* @__PURE__ */ jsxs16("label", { className: "block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1.5 flex items-center gap-1.5", children: [
1767
- /* @__PURE__ */ jsx23(Mail, { size: 13, className: "text-blue-600 dark:text-blue-400" }),
1708
+ const labels = { ...DEFAULT_LABELS4, ...labelsOverride };
1709
+ return /* @__PURE__ */ jsxs14("section", { className: "bg-white dark:bg-gray-800 rounded-xl border border-gray-200 dark:border-gray-700 overflow-hidden", children: [
1710
+ /* @__PURE__ */ jsx16("div", { className: "px-6 py-4 border-b border-gray-100 dark:border-gray-700", children: /* @__PURE__ */ jsx16("h2", { className: "text-sm font-semibold text-gray-900 dark:text-gray-100", children: labels.sectionTitle }) }),
1711
+ /* @__PURE__ */ jsxs14("form", { onSubmit: onSave, className: "p-6 space-y-5", children: [
1712
+ /* @__PURE__ */ jsxs14("div", { children: [
1713
+ /* @__PURE__ */ jsxs14("label", { className: "block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1.5 flex items-center gap-1.5", children: [
1714
+ /* @__PURE__ */ jsx16(Mail, { size: 13, className: "text-blue-600 dark:text-blue-400" }),
1768
1715
  labels.welcomeMessage
1769
1716
  ] }),
1770
- /* @__PURE__ */ jsx23(
1717
+ /* @__PURE__ */ jsx16(
1771
1718
  WhatsAppMessageEditor,
1772
1719
  {
1773
1720
  value: welcomeMessage,
@@ -1776,14 +1723,14 @@ function WelcomeFarewellForm({
1776
1723
  placeholders: welcomePlaceholders
1777
1724
  }
1778
1725
  ),
1779
- /* @__PURE__ */ jsx23("p", { className: "mt-1.5 text-xs text-gray-500 dark:text-gray-400", children: labels.welcomeMessageHint })
1726
+ /* @__PURE__ */ jsx16("p", { className: "mt-1.5 text-xs text-gray-500 dark:text-gray-400", children: labels.welcomeMessageHint })
1780
1727
  ] }),
1781
- /* @__PURE__ */ jsxs16("div", { children: [
1782
- /* @__PURE__ */ jsxs16("label", { className: "block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1.5 flex items-center gap-1.5", children: [
1783
- /* @__PURE__ */ jsx23(LogOut, { size: 13, className: "text-blue-600 dark:text-blue-400" }),
1728
+ /* @__PURE__ */ jsxs14("div", { children: [
1729
+ /* @__PURE__ */ jsxs14("label", { className: "block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1.5 flex items-center gap-1.5", children: [
1730
+ /* @__PURE__ */ jsx16(LogOut, { size: 13, className: "text-blue-600 dark:text-blue-400" }),
1784
1731
  labels.farewellMessage
1785
1732
  ] }),
1786
- /* @__PURE__ */ jsx23(
1733
+ /* @__PURE__ */ jsx16(
1787
1734
  WhatsAppMessageEditor,
1788
1735
  {
1789
1736
  value: farewellMessage,
@@ -1792,17 +1739,17 @@ function WelcomeFarewellForm({
1792
1739
  placeholders: farewellPlaceholders
1793
1740
  }
1794
1741
  ),
1795
- /* @__PURE__ */ jsx23("p", { className: "mt-1.5 text-xs text-gray-500 dark:text-gray-400", children: labels.farewellMessageHint })
1742
+ /* @__PURE__ */ jsx16("p", { className: "mt-1.5 text-xs text-gray-500 dark:text-gray-400", children: labels.farewellMessageHint })
1796
1743
  ] }),
1797
- saveSuccess && /* @__PURE__ */ jsx23("div", { className: "bg-green-50 dark:bg-green-950/40 border border-green-200 dark:border-green-800 rounded-xl px-4 py-3 text-sm text-green-700 dark:text-green-400", children: labels.saveSuccess }),
1798
- /* @__PURE__ */ jsx23("div", { className: "flex justify-end", children: /* @__PURE__ */ jsxs16(
1744
+ saveSuccess && /* @__PURE__ */ jsx16("div", { className: "bg-green-50 dark:bg-green-950/40 border border-green-200 dark:border-green-800 rounded-xl px-4 py-3 text-sm text-green-700 dark:text-green-400", children: labels.saveSuccess }),
1745
+ /* @__PURE__ */ jsx16("div", { className: "flex justify-end", children: /* @__PURE__ */ jsxs14(
1799
1746
  "button",
1800
1747
  {
1801
1748
  type: "submit",
1802
1749
  disabled: saving,
1803
1750
  className: "flex items-center gap-2 bg-blue-600 hover:bg-blue-700 disabled:bg-blue-400 text-white font-semibold px-5 py-2.5 rounded-xl transition-colors text-sm",
1804
1751
  children: [
1805
- saving ? /* @__PURE__ */ jsx23("span", { className: "w-4 h-4 border-2 border-white/40 border-t-white rounded-full animate-spin" }) : /* @__PURE__ */ jsx23(Save2, { size: 14 }),
1752
+ saving ? /* @__PURE__ */ jsx16("span", { className: "w-4 h-4 border-2 border-white/40 border-t-white rounded-full animate-spin" }) : /* @__PURE__ */ jsx16(Save2, { size: 14 }),
1806
1753
  saving ? labels.saving : labels.save
1807
1754
  ]
1808
1755
  }
@@ -1811,10 +1758,57 @@ function WelcomeFarewellForm({
1811
1758
  ] });
1812
1759
  }
1813
1760
 
1761
+ // src/settings/WhatsAppTemplatesSettings.tsx
1762
+ import { useState as useState8 } from "react";
1763
+ import { jsx as jsx17, jsxs as jsxs15 } from "react/jsx-runtime";
1764
+ var TEMPLATE_SETTINGS_TAB = {
1765
+ SELECT: "select",
1766
+ CREATE: "create"
1767
+ };
1768
+ var DEFAULT_TEMPLATES_SETTINGS_LABELS = {
1769
+ selectTab: "Template de reengajamento",
1770
+ createTab: "Criar template"
1771
+ };
1772
+ function WhatsAppTemplatesSettings({
1773
+ labels: labelsOverride,
1774
+ create,
1775
+ ...settingsProps
1776
+ }) {
1777
+ const labels = { ...DEFAULT_TEMPLATES_SETTINGS_LABELS, ...labelsOverride };
1778
+ const [tab, setTab] = useState8(TEMPLATE_SETTINGS_TAB.SELECT);
1779
+ return /* @__PURE__ */ jsxs15("div", { className: "space-y-4", children: [
1780
+ /* @__PURE__ */ jsxs15("nav", { className: "flex gap-1 border-b", role: "tablist", children: [
1781
+ /* @__PURE__ */ jsx17(
1782
+ "button",
1783
+ {
1784
+ type: "button",
1785
+ role: "tab",
1786
+ "aria-selected": tab === TEMPLATE_SETTINGS_TAB.SELECT,
1787
+ onClick: () => setTab(TEMPLATE_SETTINGS_TAB.SELECT),
1788
+ className: `cv-subtab ${tab === TEMPLATE_SETTINGS_TAB.SELECT ? "cv-subtab--active" : ""}`,
1789
+ children: labels.selectTab
1790
+ }
1791
+ ),
1792
+ create ? /* @__PURE__ */ jsx17(
1793
+ "button",
1794
+ {
1795
+ type: "button",
1796
+ role: "tab",
1797
+ "aria-selected": tab === TEMPLATE_SETTINGS_TAB.CREATE,
1798
+ onClick: () => setTab(TEMPLATE_SETTINGS_TAB.CREATE),
1799
+ className: `cv-subtab ${tab === TEMPLATE_SETTINGS_TAB.CREATE ? "cv-subtab--active" : ""}`,
1800
+ children: labels.createTab
1801
+ }
1802
+ ) : null
1803
+ ] }),
1804
+ tab === TEMPLATE_SETTINGS_TAB.SELECT ? /* @__PURE__ */ jsx17(WhatsAppTemplateSettingsForm, { ...settingsProps }) : create ? /* @__PURE__ */ jsx17(WhatsAppCreateTemplateForm, { ...create }) : null
1805
+ ] });
1806
+ }
1807
+
1814
1808
  // src/settings/TopicsForm.tsx
1815
1809
  import { Megaphone, Save as Save3 } from "lucide-react";
1816
- import { jsx as jsx24, jsxs as jsxs17 } from "react/jsx-runtime";
1817
- var DEFAULT_LABELS4 = {
1810
+ import { jsx as jsx18, jsxs as jsxs16 } from "react/jsx-runtime";
1811
+ var DEFAULT_LABELS5 = {
1818
1812
  sectionTitle: "T\xF3picos intermedi\xE1rios",
1819
1813
  sectionDescription: "Mensagens autom\xE1ticas para assuntos espec\xEDficos, disparadas quando o cliente demonstra interesse.",
1820
1814
  messagePlaceholder: "Digite a mensagem autom\xE1tica para este t\xF3pico...",
@@ -1830,20 +1824,20 @@ function TopicsForm({
1830
1824
  saveSuccess = false,
1831
1825
  labels: labelsOverride
1832
1826
  }) {
1833
- const labels = { ...DEFAULT_LABELS4, ...labelsOverride };
1834
- return /* @__PURE__ */ jsxs17("section", { className: "bg-white dark:bg-gray-800 rounded-xl border border-gray-200 dark:border-gray-700 overflow-hidden", children: [
1835
- /* @__PURE__ */ jsxs17("div", { className: "px-6 py-4 border-b border-gray-100 dark:border-gray-700", children: [
1836
- /* @__PURE__ */ jsxs17("h2", { className: "text-sm font-semibold text-gray-900 dark:text-gray-100 flex items-center gap-2", children: [
1837
- /* @__PURE__ */ jsx24(Megaphone, { size: 16, className: "text-orange-600 dark:text-orange-400" }),
1827
+ const labels = { ...DEFAULT_LABELS5, ...labelsOverride };
1828
+ return /* @__PURE__ */ jsxs16("section", { className: "bg-white dark:bg-gray-800 rounded-xl border border-gray-200 dark:border-gray-700 overflow-hidden", children: [
1829
+ /* @__PURE__ */ jsxs16("div", { className: "px-6 py-4 border-b border-gray-100 dark:border-gray-700", children: [
1830
+ /* @__PURE__ */ jsxs16("h2", { className: "text-sm font-semibold text-gray-900 dark:text-gray-100 flex items-center gap-2", children: [
1831
+ /* @__PURE__ */ jsx18(Megaphone, { size: 16, className: "text-orange-600 dark:text-orange-400" }),
1838
1832
  labels.sectionTitle
1839
1833
  ] }),
1840
- /* @__PURE__ */ jsx24("p", { className: "text-xs text-gray-500 dark:text-gray-400 mt-1", children: labels.sectionDescription })
1834
+ /* @__PURE__ */ jsx18("p", { className: "text-xs text-gray-500 dark:text-gray-400 mt-1", children: labels.sectionDescription })
1841
1835
  ] }),
1842
- /* @__PURE__ */ jsxs17("form", { onSubmit: onSave, className: "p-6 space-y-6", children: [
1843
- topics.map((topic) => /* @__PURE__ */ jsxs17("div", { className: "space-y-2", children: [
1844
- /* @__PURE__ */ jsxs17("div", { className: "flex items-start gap-4", children: [
1845
- /* @__PURE__ */ jsx24("div", { className: "flex-1", children: /* @__PURE__ */ jsx24("p", { className: "text-sm font-medium text-gray-900 dark:text-gray-100", children: topic.label }) }),
1846
- /* @__PURE__ */ jsx24(
1836
+ /* @__PURE__ */ jsxs16("form", { onSubmit: onSave, className: "p-6 space-y-6", children: [
1837
+ topics.map((topic) => /* @__PURE__ */ jsxs16("div", { className: "space-y-2", children: [
1838
+ /* @__PURE__ */ jsxs16("div", { className: "flex items-start gap-4", children: [
1839
+ /* @__PURE__ */ jsx18("div", { className: "flex-1", children: /* @__PURE__ */ jsx18("p", { className: "text-sm font-medium text-gray-900 dark:text-gray-100", children: topic.label }) }),
1840
+ /* @__PURE__ */ jsx18(
1847
1841
  "button",
1848
1842
  {
1849
1843
  type: "button",
@@ -1851,7 +1845,7 @@ function TopicsForm({
1851
1845
  className: `relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none ${topic.enabled ? "bg-blue-600 dark:bg-blue-500" : "bg-gray-200 dark:bg-gray-700"}`,
1852
1846
  role: "switch",
1853
1847
  "aria-checked": topic.enabled,
1854
- children: /* @__PURE__ */ jsx24(
1848
+ children: /* @__PURE__ */ jsx18(
1855
1849
  "span",
1856
1850
  {
1857
1851
  className: `pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out ${topic.enabled ? "translate-x-5" : "translate-x-0"}`
@@ -1860,7 +1854,7 @@ function TopicsForm({
1860
1854
  }
1861
1855
  )
1862
1856
  ] }),
1863
- /* @__PURE__ */ jsx24(
1857
+ /* @__PURE__ */ jsx18(
1864
1858
  "textarea",
1865
1859
  {
1866
1860
  value: topic.message,
@@ -1872,55 +1866,27 @@ function TopicsForm({
1872
1866
  }
1873
1867
  )
1874
1868
  ] }, topic.key)),
1875
- /* @__PURE__ */ jsxs17("div", { className: "flex items-center gap-3", children: [
1876
- /* @__PURE__ */ jsxs17(
1869
+ /* @__PURE__ */ jsxs16("div", { className: "flex items-center gap-3", children: [
1870
+ /* @__PURE__ */ jsxs16(
1877
1871
  "button",
1878
1872
  {
1879
1873
  type: "submit",
1880
1874
  disabled: saving,
1881
1875
  className: "inline-flex items-center gap-2 rounded-lg bg-blue-600 px-4 py-2 text-sm font-medium text-white hover:bg-blue-700 disabled:opacity-50",
1882
1876
  children: [
1883
- /* @__PURE__ */ jsx24(Save3, { size: 14 }),
1877
+ /* @__PURE__ */ jsx18(Save3, { size: 14 }),
1884
1878
  labels.saveButton
1885
1879
  ]
1886
1880
  }
1887
1881
  ),
1888
- saveSuccess && /* @__PURE__ */ jsx24("span", { className: "text-sm text-green-600 dark:text-green-400", children: labels.saveSuccess })
1882
+ saveSuccess && /* @__PURE__ */ jsx18("span", { className: "text-sm text-green-600 dark:text-green-400", children: labels.saveSuccess })
1889
1883
  ] })
1890
1884
  ] })
1891
1885
  ] });
1892
1886
  }
1893
1887
 
1894
1888
  // src/hooks/useConversationMessages.ts
1895
- import { useCallback as useCallback7 } from "react";
1896
-
1897
- // src/hooks/useAsyncResource.ts
1898
- import { useCallback as useCallback6, useEffect as useEffect5, useRef as useRef6, useState as useState10 } from "react";
1899
- function useAsyncResource(fetcher, deps) {
1900
- const [data, setData] = useState10(void 0);
1901
- const [loading, setLoading] = useState10(false);
1902
- const [error, setError] = useState10(void 0);
1903
- const requestIdRef = useRef6(0);
1904
- const load = useCallback6(async () => {
1905
- const requestId = ++requestIdRef.current;
1906
- setLoading(true);
1907
- setError(void 0);
1908
- try {
1909
- const result = await fetcher();
1910
- if (requestId === requestIdRef.current) setData(result);
1911
- } catch (err) {
1912
- if (requestId === requestIdRef.current) setError(err instanceof Error ? err : new Error(String(err)));
1913
- } finally {
1914
- if (requestId === requestIdRef.current) setLoading(false);
1915
- }
1916
- }, deps);
1917
- useEffect5(() => {
1918
- load();
1919
- }, [load]);
1920
- return { data, loading, error, refetch: load };
1921
- }
1922
-
1923
- // src/hooks/useConversationMessages.ts
1889
+ import { useCallback as useCallback4 } from "react";
1924
1890
  function useConversationMessages(conversationId, params) {
1925
1891
  const context = useConversations();
1926
1892
  if (!context) {
@@ -1931,7 +1897,7 @@ function useConversationMessages(conversationId, params) {
1931
1897
  () => api.fetchMessages(conversationId, params),
1932
1898
  [conversationId, params?.limit, params?.before]
1933
1899
  );
1934
- const sendMessage = useCallback7(
1900
+ const sendMessage = useCallback4(
1935
1901
  async (text) => {
1936
1902
  const message = await api.sendMessage(conversationId, text);
1937
1903
  await refetch();
@@ -1939,7 +1905,7 @@ function useConversationMessages(conversationId, params) {
1939
1905
  },
1940
1906
  [api, conversationId, refetch]
1941
1907
  );
1942
- const sendMedia = useCallback7(
1908
+ const sendMedia = useCallback4(
1943
1909
  async (mediaData) => {
1944
1910
  const message = await api.sendMedia(conversationId, mediaData);
1945
1911
  await refetch();
@@ -1947,14 +1913,14 @@ function useConversationMessages(conversationId, params) {
1947
1913
  },
1948
1914
  [api, conversationId, refetch]
1949
1915
  );
1950
- const sendTemplate = useCallback7(
1916
+ const sendTemplate = useCallback4(
1951
1917
  async (templateData) => {
1952
1918
  await api.sendTemplate(conversationId, templateData);
1953
1919
  await refetch();
1954
1920
  },
1955
1921
  [api, conversationId, refetch]
1956
1922
  );
1957
- const markRead = useCallback7(() => api.markRead(conversationId), [api, conversationId]);
1923
+ const markRead = useCallback4(() => api.markRead(conversationId), [api, conversationId]);
1958
1924
  return { messages: data ?? [], loading, error, refetch, sendMessage, sendMedia, sendTemplate, markRead };
1959
1925
  }
1960
1926
 
@@ -1965,11 +1931,15 @@ function useConversationList(params) {
1965
1931
  throw new Error("useConversationList requires an ancestor <ConversationsProvider>");
1966
1932
  }
1967
1933
  const { api } = context;
1934
+ const filtersKey = JSON.stringify(params?.filters ?? {});
1968
1935
  const { data, loading, error, refetch } = useAsyncResource(
1969
1936
  () => api.fetchConversations(params),
1970
- [params?.page, params?.limit, params?.waitingHuman, params?.search]
1937
+ [params?.page, params?.limit, params?.waitingHuman, params?.search, filtersKey]
1971
1938
  );
1972
- return { conversations: data ?? [], loading, error, refetch };
1939
+ if (data === void 0) {
1940
+ return { conversations: [], total: 0, loading, error, refetch };
1941
+ }
1942
+ return { conversations: conversationsOf(data), total: totalOf(data), loading, error, refetch };
1973
1943
  }
1974
1944
 
1975
1945
  // src/hooks/useConversationContext.ts
@@ -1983,27 +1953,13 @@ function useConversationContext(conversationId) {
1983
1953
  return { context: data, loading, error, refetch };
1984
1954
  }
1985
1955
 
1986
- // src/hooks/useConversationDocuments.ts
1987
- function useConversationDocuments(conversationId, params) {
1988
- const context = useConversations();
1989
- if (!context) {
1990
- throw new Error("useConversationDocuments requires an ancestor <ConversationsProvider>");
1991
- }
1992
- const { api } = context;
1993
- const { data, loading, error, refetch } = useAsyncResource(
1994
- () => api.getDocuments(conversationId, params),
1995
- [conversationId, params?.search, params?.page]
1996
- );
1997
- return { documents: data ?? [], loading, error, refetch };
1998
- }
1999
-
2000
1956
  // src/hooks/useConversationRealtime.ts
2001
- import { useEffect as useEffect6, useRef as useRef7 } from "react";
1957
+ import { useEffect as useEffect5, useRef as useRef4 } from "react";
2002
1958
  function useConversationRealtime(conversationId, onEvent) {
2003
1959
  const context = useConversations();
2004
- const onEventRef = useRef7(onEvent);
1960
+ const onEventRef = useRef4(onEvent);
2005
1961
  onEventRef.current = onEvent;
2006
- useEffect6(() => {
1962
+ useEffect5(() => {
2007
1963
  if (!context || !conversationId) return;
2008
1964
  const source = context.sse.connectConversationStream(conversationId);
2009
1965
  const handler = (event) => onEventRef.current(event);
@@ -2016,9 +1972,9 @@ function useConversationRealtime(conversationId, onEvent) {
2016
1972
  }
2017
1973
  function useGlobalRealtime(onEvent) {
2018
1974
  const context = useConversations();
2019
- const onEventRef = useRef7(onEvent);
1975
+ const onEventRef = useRef4(onEvent);
2020
1976
  onEventRef.current = onEvent;
2021
- useEffect6(() => {
1977
+ useEffect5(() => {
2022
1978
  if (!context) return;
2023
1979
  const source = context.sse.connectGlobalStream();
2024
1980
  const handler = (event) => onEventRef.current(event);
@@ -2029,16 +1985,78 @@ function useGlobalRealtime(onEvent) {
2029
1985
  };
2030
1986
  }, [context]);
2031
1987
  }
1988
+
1989
+ // src/hooks/useConversationActions.ts
1990
+ import { useMemo as useMemo2 } from "react";
1991
+ function useConversationActions(conversationId) {
1992
+ const context = useConversations();
1993
+ if (!context) {
1994
+ throw new Error("useConversationActions requires an ancestor <ConversationsProvider>");
1995
+ }
1996
+ const { api } = context;
1997
+ return useMemo2(
1998
+ () => ({
1999
+ takeover: api.takeover ? () => api.takeover(conversationId) : void 0,
2000
+ release: api.release ? () => api.release(conversationId) : void 0,
2001
+ finalize: api.finalize ? () => api.finalize(conversationId) : void 0
2002
+ }),
2003
+ [api, conversationId]
2004
+ );
2005
+ }
2006
+ function useInboxActions() {
2007
+ const context = useConversations();
2008
+ if (!context) {
2009
+ throw new Error("useInboxActions requires an ancestor <ConversationsProvider>");
2010
+ }
2011
+ const { api } = context;
2012
+ return useMemo2(
2013
+ () => ({
2014
+ markAllRead: api.markAllRead ? () => api.markAllRead() : void 0,
2015
+ listTemplates: api.listTemplates ? () => api.listTemplates() : void 0
2016
+ }),
2017
+ [api]
2018
+ );
2019
+ }
2032
2020
  export {
2033
2021
  AudioPlayer,
2034
2022
  Avatar,
2023
+ CHANNEL_BRAND_COLOR,
2024
+ CHANNEL_CAPABILITIES,
2025
+ CHANNEL_FILTER_ALL,
2026
+ CONVERSATION_CHANNEL,
2027
+ CONVERSATION_WINDOW,
2028
+ ChannelIcon,
2029
+ ConversationContextPanel,
2030
+ ConversationDocumentsPanel,
2031
+ ConversationHeader,
2035
2032
  ConversationListItem,
2036
2033
  ConversationLocalesProvider,
2034
+ ConversationRow,
2037
2035
  ConversationWallpaper,
2038
2036
  ConversationsProvider,
2037
+ DEFAULT_ACCEPTED_FILE_TYPES,
2038
+ DEFAULT_AVATAR_LABELS,
2039
+ DEFAULT_CONVERSATION_CHANNEL,
2040
+ DEFAULT_CONVERSATION_CONTEXT_LABELS,
2041
+ DEFAULT_CONVERSATION_DOCUMENTS_LABELS,
2042
+ DEFAULT_CONVERSATION_HEADER_LABELS,
2043
+ DEFAULT_CONVERSATION_LIST_ITEM_LABELS,
2044
+ DEFAULT_DOCUMENTS_LIBRARY_LABELS,
2045
+ DEFAULT_EMOJI_PICKER_LABELS,
2046
+ DEFAULT_INTERACTIVE_MESSAGE_LABELS,
2047
+ DEFAULT_LIGHTBOX_LABELS,
2048
+ DEFAULT_MESSAGE_COMPOSER_LABELS,
2049
+ DEFAULT_TEMPLATES_SETTINGS_LABELS,
2050
+ DEFAULT_WHATSAPP_MESSAGE_EDITOR_LABELS,
2051
+ DEFAULT_WINDOW_EXPIRED_LABELS,
2052
+ DOCUMENT_SOURCE_FILTER,
2039
2053
  DateDivider,
2054
+ DocumentsLibrary,
2055
+ EMOJI_CATEGORIES,
2040
2056
  EmojiPicker,
2041
2057
  FileIcon,
2058
+ HANDLE_KIND,
2059
+ InteractiveMessage,
2042
2060
  Lightbox,
2043
2061
  MediaRenderer,
2044
2062
  MessageBubble,
@@ -2046,21 +2064,43 @@ export {
2046
2064
  MessageTail,
2047
2065
  MessageText,
2048
2066
  MessageTimestamp,
2067
+ NARROW_MAX_WIDTH_PX,
2068
+ REOPEN_MECHANISM,
2049
2069
  SimpleEmojiPicker,
2050
2070
  StatusTicks,
2071
+ TEMPLATE_SETTINGS_TAB,
2051
2072
  ToastProvider,
2052
2073
  TopicsForm,
2074
+ WINDOW_FILTERS,
2053
2075
  WelcomeFarewellForm,
2054
2076
  WhatsAppCreateTemplateForm,
2055
2077
  WhatsAppMessageEditor,
2056
2078
  WhatsAppTemplateSettingsForm,
2079
+ WhatsAppTemplatesSettings,
2080
+ WindowExpiredNotice,
2081
+ applyQuickReplyVariables,
2082
+ buildTranscriptFilename,
2083
+ buildTranscriptText,
2084
+ capabilitiesOf,
2085
+ channelFiltersFor,
2086
+ contactFlag,
2087
+ createMediaUrlResolver,
2088
+ downloadTextFile,
2089
+ formatContactHandle,
2090
+ formatDateTime,
2057
2091
  formatFileSize,
2058
2092
  formatPhone,
2093
+ formatStalledFor,
2059
2094
  formatTimestamp,
2060
2095
  htmlToWA,
2096
+ isSameDay,
2097
+ isWindowBlocking,
2061
2098
  parseWhatsAppFormatting,
2062
2099
  phoneInitials,
2100
+ resolveQuickReply,
2101
+ searchEmojis,
2063
2102
  toast,
2103
+ useConversationActions,
2064
2104
  useConversationContext,
2065
2105
  useConversationDocuments,
2066
2106
  useConversationList,
@@ -2070,8 +2110,12 @@ export {
2070
2110
  useConversations,
2071
2111
  useDarkMode,
2072
2112
  useGlobalRealtime,
2113
+ useInboxActions,
2114
+ useIsDarkTheme,
2115
+ useIsNarrow,
2073
2116
  useToast,
2074
2117
  useWaitingNotifications,
2075
2118
  waToHTML,
2076
- waToHTMLInline
2119
+ waToHTMLInline,
2120
+ windowOf
2077
2121
  };