@adatechnology/conversations-ui 0.1.0-rc.2 → 0.1.0-rc.20

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 (99) hide show
  1. package/dist/{chunk-ZDURDZTM.js → chunk-2AYDBWNE.js} +14 -0
  2. package/dist/chunk-OIDAIVCH.js +1985 -0
  3. package/dist/flows/index.d.ts +14 -2
  4. package/dist/flows/index.js +117 -39
  5. package/dist/index.d.ts +729 -136
  6. package/dist/index.js +1418 -1104
  7. package/dist/preview/index.d.ts +373 -0
  8. package/dist/preview/index.js +1247 -0
  9. package/dist/styles.css +198 -0
  10. package/dist/types-O7kMP1Yn.d.ts +416 -0
  11. package/package.json +10 -3
  12. package/src/AudioRecorderButton.test.tsx +30 -0
  13. package/src/AudioRecorderButton.tsx +248 -0
  14. package/src/Avatar.tsx +30 -4
  15. package/src/ChannelIcon.tsx +87 -0
  16. package/src/ConversationContextPanel.tsx +127 -0
  17. package/src/ConversationDocumentsPanel.tsx +425 -0
  18. package/src/ConversationHeader.tsx +257 -0
  19. package/src/ConversationListItem.tsx +54 -7
  20. package/src/ConversationLocalesProvider.tsx +16 -0
  21. package/src/ConversationRow.tsx +137 -0
  22. package/src/DateDivider.tsx +16 -3
  23. package/src/DocumentsLibrary.tsx +322 -0
  24. package/src/EmojiPicker.tsx +69 -55
  25. package/src/FileIcon.test.ts +83 -0
  26. package/src/FileIcon.tsx +88 -11
  27. package/src/InteractiveMessage.test.tsx +41 -0
  28. package/src/InteractiveMessage.tsx +143 -0
  29. package/src/Lightbox.tsx +18 -3
  30. package/src/MediaRenderer.tsx +64 -21
  31. package/src/MessageBubble.tsx +54 -5
  32. package/src/MessageComposer.test.tsx +35 -0
  33. package/src/MessageComposer.tsx +146 -19
  34. package/src/RichMessageComposer.test.tsx +83 -0
  35. package/src/RichMessageComposer.tsx +380 -0
  36. package/src/Wallpaper.test.tsx +21 -0
  37. package/src/Wallpaper.tsx +54 -6
  38. package/src/WhatsAppMessageEditor.tsx +28 -4
  39. package/src/WindowExpiredNotice.tsx +57 -0
  40. package/src/audioRecorderFormat.test.ts +67 -0
  41. package/src/conversationChannel.test.ts +53 -0
  42. package/src/conversationChannel.ts +146 -0
  43. package/src/conversationTranscript.test.ts +65 -0
  44. package/src/conversationTranscript.ts +64 -0
  45. package/src/conversationWindow.test.ts +90 -0
  46. package/src/conversationWindow.ts +78 -0
  47. package/src/documentTypeLabel.test.ts +57 -0
  48. package/src/emojiCatalog.test.ts +35 -0
  49. package/src/emojiCatalog.ts +189 -0
  50. package/src/flows/FlowGroupHeader.tsx +12 -2
  51. package/src/flows/FlowMapCanvas.tsx +17 -14
  52. package/src/flows/FlowMapNode.tsx +3 -1
  53. package/src/flows/FlowNodeCard.tsx +22 -4
  54. package/src/flows/FlowNodePanel.tsx +132 -35
  55. package/src/flows/FlowPalette.tsx +6 -2
  56. package/src/flows/FlowWhatsAppPreview.tsx +14 -3
  57. package/src/flows/flowGraph.ts +5 -5
  58. package/src/flows/labels.ts +5 -0
  59. package/src/hooks/useConversationActions.ts +56 -0
  60. package/src/hooks/useConversationDocuments.ts +15 -9
  61. package/src/hooks/useConversationList.ts +15 -9
  62. package/src/hooks/useConversationMessages.ts +2 -2
  63. package/src/index.ts +129 -16
  64. package/src/lib/cn.test.ts +29 -0
  65. package/src/lib/cn.ts +15 -0
  66. package/src/lib/createMediaUrlResolver.ts +33 -0
  67. package/src/lib/paginated.test.ts +33 -0
  68. package/src/lib/paginated.ts +26 -0
  69. package/src/lib/phone.ts +34 -0
  70. package/src/preview/ConversationPreview.tsx +325 -0
  71. package/src/preview/MediaTypesPreview.tsx +87 -0
  72. package/src/preview/conversationPreviewFailures.test.ts +64 -0
  73. package/src/preview/createMockConversationsApi.ts +271 -0
  74. package/src/preview/createMockSSEProvider.ts +40 -0
  75. package/src/preview/createPreviewBridgeClient.test.ts +92 -0
  76. package/src/preview/createPreviewBridgeClient.ts +88 -0
  77. package/src/preview/createPreviewWebhookClient.test.ts +105 -0
  78. package/src/preview/createPreviewWebhookClient.ts +131 -0
  79. package/src/preview/index.ts +61 -0
  80. package/src/preview/mediaTypeOf.test.ts +15 -0
  81. package/src/preview/mockDocumentsSearch.test.ts +57 -0
  82. package/src/preview/mockEventSource.ts +53 -0
  83. package/src/preview/preview.test.ts +177 -0
  84. package/src/preview/previewFileSamples.test.ts +151 -0
  85. package/src/preview/previewFileSamples.ts +74 -0
  86. package/src/preview/previewFixtures.ts +440 -0
  87. package/src/preview/previewMediaSource.test.ts +62 -0
  88. package/src/preview/previewMediaSource.ts +91 -0
  89. package/src/preview/previewStore.ts +193 -0
  90. package/src/preview/startPreviewScript.ts +60 -0
  91. package/src/providers/types.ts +163 -11
  92. package/src/quickReply.test.ts +58 -0
  93. package/src/settings/WhatsAppCreateTemplateForm.tsx +3 -1
  94. package/src/settings/WhatsAppTemplatesSettings.tsx +106 -0
  95. package/src/styles.css +136 -0
  96. package/src/types.ts +46 -1
  97. package/src/useDarkMode.ts +26 -0
  98. package/src/useIsNarrow.ts +29 -0
  99. package/src/useWaitingNotifications.ts +74 -29
package/dist/index.js CHANGED
@@ -1,759 +1,67 @@
1
+ import {
2
+ AudioPlayer,
3
+ AudioRecorderButton,
4
+ ConversationDocumentsPanel,
5
+ ConversationLocalesProvider,
6
+ ConversationWallpaper,
7
+ ConversationsProvider,
8
+ DEFAULT_ACCEPTED_FILE_TYPES,
9
+ DEFAULT_AUDIO_RECORDER_BUTTON_LABELS,
10
+ DEFAULT_CONVERSATION_DOCUMENTS_LABELS,
11
+ DEFAULT_DOCUMENTS_LIBRARY_LABELS,
12
+ DEFAULT_EMOJI_PICKER_LABELS,
13
+ DEFAULT_INTERACTIVE_MESSAGE_LABELS,
14
+ DEFAULT_LIGHTBOX_LABELS,
15
+ DEFAULT_MAX_RECORDING_MILLISECONDS,
16
+ DEFAULT_MESSAGE_COMPOSER_LABELS,
17
+ DOCUMENT_SOURCE_FILTER,
18
+ DateDivider,
19
+ DocumentsLibrary,
20
+ EMOJI_CATEGORIES,
21
+ EmojiPicker,
22
+ FileIcon,
23
+ InteractiveMessage,
24
+ Lightbox,
25
+ MediaRenderer,
26
+ MessageBubble,
27
+ MessageComposer,
28
+ StatusTicks,
29
+ applyQuickReplyVariables,
30
+ cn,
31
+ conversationsOf,
32
+ createMediaUrlResolver,
33
+ formatDateTime,
34
+ formatFileSize,
35
+ formatPhone,
36
+ formatTimestamp,
37
+ isSameDay,
38
+ phoneCountryFlag,
39
+ phoneInitials,
40
+ resolveQuickReply,
41
+ searchEmojis,
42
+ totalOf,
43
+ useAsyncResource,
44
+ useConversationDocuments,
45
+ useConversationLocales,
46
+ useConversations
47
+ } from "./chunk-OIDAIVCH.js";
1
48
  import {
2
49
  htmlToWA,
3
50
  parseWhatsAppFormatting,
4
51
  useDarkMode,
52
+ useIsDarkTheme,
5
53
  waToHTML,
6
54
  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
- }
55
+ } from "./chunk-2AYDBWNE.js";
193
56
 
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
- };
645
-
646
- // src/WhatsAppMessageEditor.tsx
647
- import { useRef as useRef3 } from "react";
648
- import { Bold, Italic, Strikethrough } from "lucide-react";
649
- import { Fragment as Fragment3, jsx as jsx11, jsxs as jsxs8 } from "react/jsx-runtime";
650
- var INLINE_TOKEN = /(\*[^*\n]+\*|_[^_\n]+_|~[^~\n]+~|`[^`\n]+`|\{[a-zA-Z_]+\})/g;
651
- function renderLine(line, lineKey) {
652
- const nodes = [];
653
- let lastIndex = 0;
654
- let match;
655
- let tokenIndex = 0;
656
- INLINE_TOKEN.lastIndex = 0;
657
- while ((match = INLINE_TOKEN.exec(line)) !== null) {
658
- if (match.index > lastIndex) nodes.push(line.slice(lastIndex, match.index));
659
- const token = match[0];
660
- 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));
666
- lastIndex = INLINE_TOKEN.lastIndex;
667
- }
668
- if (lastIndex < line.length) nodes.push(line.slice(lastIndex));
669
- return nodes;
670
- }
671
- function renderWhatsApp(text) {
672
- const lines = text.split("\n");
673
- return lines.map((line, index) => /* @__PURE__ */ jsxs8("span", { children: [
674
- renderLine(line, `ln-${index}`),
675
- index < lines.length - 1 ? /* @__PURE__ */ jsx11("br", {}) : null
676
- ] }, `ln-${index}`));
677
- }
678
- function WhatsAppMessageEditor({
679
- value,
680
- onChange,
681
- placeholder,
682
- placeholders = [],
683
- rows = 4,
684
- previewLabel = "Pr\xE9via (como aparece no WhatsApp)",
685
- emptyPreviewText = "Sua mensagem aparecer\xE1 aqui\u2026"
686
- }) {
687
- const textareaRef = useRef3(null);
688
- function wrapSelection(marker) {
689
- const textarea = textareaRef.current;
690
- if (!textarea) return;
691
- const start = textarea.selectionStart;
692
- const end = textarea.selectionEnd;
693
- const selected = value.slice(start, end) || "texto";
694
- const next = value.slice(0, start) + marker + selected + marker + value.slice(end);
695
- onChange(next);
696
- requestAnimationFrame(() => {
697
- textarea.focus();
698
- textarea.setSelectionRange(start + marker.length, start + marker.length + selected.length);
699
- });
700
- }
701
- function insertAtCursor(snippet) {
702
- const textarea = textareaRef.current;
703
- if (!textarea) return;
704
- const start = textarea.selectionStart;
705
- const end = textarea.selectionEnd;
706
- const next = value.slice(0, start) + snippet + value.slice(end);
707
- onChange(next);
708
- requestAnimationFrame(() => {
709
- textarea.focus();
710
- const caret = start + snippet.length;
711
- textarea.setSelectionRange(caret, caret);
712
- });
713
- }
714
- 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(
723
- "button",
724
- {
725
- type: "button",
726
- onClick: () => insertAtCursor(token),
727
- 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}`,
729
- children: token
730
- },
731
- token
732
- ))
733
- ] })
734
- ] }),
735
- /* @__PURE__ */ jsx11(
736
- "textarea",
737
- {
738
- ref: textareaRef,
739
- value,
740
- onChange: (event) => onChange(event.target.value),
741
- placeholder,
742
- rows,
743
- 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
- }
745
- ),
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 }) }) })
749
- ] })
750
- ] });
751
- }
57
+ // src/RichMessageComposer.tsx
58
+ import { forwardRef, useCallback, useEffect as useEffect2, useImperativeHandle, useRef as useRef2, useState as useState2 } from "react";
59
+ import { Bold, Italic, Strikethrough, Code, Paperclip, SendHorizonal, Braces } from "lucide-react";
752
60
 
753
61
  // src/SimpleEmojiPicker.tsx
754
- import { useEffect as useEffect2, useRef as useRef4, useState as useState6 } from "react";
62
+ import { useEffect, useRef, useState } from "react";
755
63
  import { Smile } from "lucide-react";
756
- import { jsx as jsx12, jsxs as jsxs9 } from "react/jsx-runtime";
64
+ import { jsx, jsxs } from "react/jsx-runtime";
757
65
  var EMOJIS = [
758
66
  "\u{1F600}",
759
67
  "\u{1F602}",
@@ -837,9 +145,9 @@ var EMOJIS = [
837
145
  "\u{1F6E1}\uFE0F"
838
146
  ];
839
147
  function SimpleEmojiPicker({ onSelect, label = "Emojis", pickerWidth = "280px", pickerMaxHeight = "220px" }) {
840
- const [open, setOpen] = useState6(false);
841
- const containerRef = useRef4(null);
842
- useEffect2(() => {
148
+ const [open, setOpen] = useState(false);
149
+ const containerRef = useRef(null);
150
+ useEffect(() => {
843
151
  if (!open) return;
844
152
  const handleClickOutside = (e) => {
845
153
  if (containerRef.current && !containerRef.current.contains(e.target)) {
@@ -849,58 +157,412 @@ function SimpleEmojiPicker({ onSelect, label = "Emojis", pickerWidth = "280px",
849
157
  document.addEventListener("mousedown", handleClickOutside);
850
158
  return () => document.removeEventListener("mousedown", handleClickOutside);
851
159
  }, [open]);
852
- return /* @__PURE__ */ jsxs9("div", { ref: containerRef, className: "relative flex-shrink-0", children: [
853
- /* @__PURE__ */ jsx12(
160
+ return /* @__PURE__ */ jsxs("div", { ref: containerRef, className: "relative flex-shrink-0", children: [
161
+ /* @__PURE__ */ jsx(
854
162
  "button",
855
163
  {
856
164
  type: "button",
857
165
  onClick: () => setOpen((v) => !v),
858
166
  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
167
  title: label,
860
- children: /* @__PURE__ */ jsx12(Smile, { size: 15 })
168
+ children: /* @__PURE__ */ jsx(Smile, { size: 15 })
861
169
  }
862
170
  ),
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(
171
+ open && /* @__PURE__ */ jsx("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__ */ jsx(
864
172
  "div",
865
173
  {
866
174
  className: "grid grid-cols-10 gap-0.5 overflow-y-auto",
867
175
  style: { width: pickerWidth, maxHeight: pickerMaxHeight },
868
- children: EMOJIS.map((emoji) => /* @__PURE__ */ jsx12(
176
+ children: EMOJIS.map((emoji) => /* @__PURE__ */ jsx(
177
+ "button",
178
+ {
179
+ type: "button",
180
+ onClick: () => {
181
+ onSelect(emoji);
182
+ setOpen(false);
183
+ },
184
+ className: "w-7 h-7 flex items-center justify-center rounded hover:bg-gray-100 dark:hover:bg-gray-700 text-lg leading-none transition-colors",
185
+ children: emoji
186
+ },
187
+ emoji
188
+ ))
189
+ }
190
+ ) })
191
+ ] });
192
+ }
193
+
194
+ // src/RichMessageComposer.tsx
195
+ import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
196
+ var RICH_COMPOSER_ACTION = {
197
+ BOLD: "bold",
198
+ ITALIC: "italic",
199
+ STRIKETHROUGH: "strikethrough",
200
+ MONOSPACE: "monospace",
201
+ EMOJI: "emoji",
202
+ ATTACH: "attach",
203
+ VARIABLES: "variables"
204
+ };
205
+ var DEFAULT_RICH_COMPOSER_TOOLTIPS = {
206
+ bold: "Negrito",
207
+ italic: "It\xE1lico",
208
+ strikethrough: "Tachado",
209
+ monospace: "Monoespa\xE7ado",
210
+ emoji: "Emojis",
211
+ attach: "Anexar arquivo",
212
+ send: "Enviar",
213
+ variables: "Vari\xE1veis dispon\xEDveis"
214
+ };
215
+ var FORMATTING_ELEMENT = {
216
+ bold: "strong",
217
+ italic: "em",
218
+ strikethrough: "del",
219
+ monospace: "code"
220
+ };
221
+ var MONOSPACE_CLASS = "bg-black/5 dark:bg-white/10 rounded px-0.5 font-mono text-sm";
222
+ var RichMessageComposer = forwardRef(function RichMessageComposer2({
223
+ value,
224
+ onChange,
225
+ onSend,
226
+ onAttachFiles,
227
+ quickReplies,
228
+ variables,
229
+ toolbar,
230
+ tooltips,
231
+ placeholder,
232
+ disabled = false,
233
+ isSending = false,
234
+ idleAction,
235
+ attachmentsPreview,
236
+ acceptedFileTypes = DEFAULT_ACCEPTED_FILE_TYPES,
237
+ className
238
+ }, ref) {
239
+ const editorRef = useRef2(null);
240
+ const fileInputRef = useRef2(null);
241
+ const variablesRef = useRef2(null);
242
+ const [isVariablesOpen, setIsVariablesOpen] = useState2(false);
243
+ useEffect2(() => {
244
+ if (!isVariablesOpen) return;
245
+ const closeOnOutsideClick = (event) => {
246
+ if (!variablesRef.current?.contains(event.target)) setIsVariablesOpen(false);
247
+ };
248
+ document.addEventListener("mousedown", closeOnOutsideClick);
249
+ return () => document.removeEventListener("mousedown", closeOnOutsideClick);
250
+ }, [isVariablesOpen]);
251
+ const tooltipOf = (action) => tooltips?.[action] ?? DEFAULT_RICH_COMPOSER_TOOLTIPS[action];
252
+ const shows = (action) => toolbar?.[action] !== false;
253
+ const replaceContent = useCallback((text) => {
254
+ const editor = editorRef.current;
255
+ if (!editor) return;
256
+ editor.innerHTML = waToHTML(text);
257
+ onChange(htmlToWA(editor.innerHTML));
258
+ editor.focus();
259
+ }, [onChange]);
260
+ useImperativeHandle(ref, () => ({
261
+ setContent: replaceContent,
262
+ clear: () => {
263
+ const editor = editorRef.current;
264
+ if (!editor) return;
265
+ editor.innerHTML = "";
266
+ onChange("");
267
+ },
268
+ focus: () => editorRef.current?.focus()
269
+ }), [onChange, replaceContent]);
270
+ const currentRange = useCallback(() => {
271
+ const editor = editorRef.current;
272
+ if (!editor) return void 0;
273
+ editor.focus();
274
+ const selection = window.getSelection();
275
+ if (!selection?.rangeCount || !editor.contains(selection.anchorNode)) return void 0;
276
+ return selection.getRangeAt(0);
277
+ }, []);
278
+ const commitRange = useCallback((range, node) => {
279
+ const selection = window.getSelection();
280
+ range.setStartAfter(node);
281
+ range.collapse(true);
282
+ selection?.removeAllRanges();
283
+ selection?.addRange(range);
284
+ onChange(htmlToWA(editorRef.current?.innerHTML ?? ""));
285
+ }, [onChange]);
286
+ const insertAtCursor = useCallback((fragment) => {
287
+ const range = currentRange();
288
+ if (!range) return;
289
+ range.deleteContents();
290
+ const textNode = document.createTextNode(fragment);
291
+ range.insertNode(textNode);
292
+ commitRange(range, textNode);
293
+ }, [commitRange, currentRange]);
294
+ const wrapSelection = useCallback((action) => {
295
+ const range = currentRange();
296
+ const selectedText = range?.toString();
297
+ if (!range || !selectedText) return;
298
+ range.deleteContents();
299
+ const wrapper = document.createElement(FORMATTING_ELEMENT[action]);
300
+ if (action === "monospace") wrapper.className = MONOSPACE_CLASS;
301
+ wrapper.textContent = selectedText;
302
+ range.insertNode(wrapper);
303
+ commitRange(range, wrapper);
304
+ }, [commitRange, currentRange]);
305
+ const handleInput = useCallback((event) => {
306
+ onChange(htmlToWA(event.currentTarget.innerHTML));
307
+ }, [onChange]);
308
+ const handleKeyDown = useCallback((event) => {
309
+ if (event.key === "Enter" && !event.shiftKey) {
310
+ event.preventDefault();
311
+ if (!disabled) onSend();
312
+ }
313
+ }, [disabled, onSend]);
314
+ const handleFileChange = useCallback((event) => {
315
+ if (event.target.files?.length) onAttachFiles?.(event.target.files);
316
+ if (fileInputRef.current) fileInputRef.current.value = "";
317
+ }, [onAttachFiles]);
318
+ const canSend = value.trim().length > 0;
319
+ return /* @__PURE__ */ jsxs2("div", { className: cn("flex flex-col", className), children: [
320
+ attachmentsPreview,
321
+ quickReplies?.length ? (
322
+ /* Uma linha só, rolando na horizontal. Deixar quebrar em várias linhas faz a barra crescer
323
+ conforme o número de respostas rápidas e empurrar a conversa para cima — o composer
324
+ precisa ter altura previsível, independente de quantos atalhos o host configurou. */
325
+ /* @__PURE__ */ jsx2("div", { className: "mb-2 flex flex-nowrap gap-1 overflow-x-auto [scrollbar-width:none] [&::-webkit-scrollbar]:hidden", children: quickReplies.map((quickReply) => /* @__PURE__ */ jsx2(
326
+ "button",
327
+ {
328
+ type: "button",
329
+ title: quickReply.tooltip,
330
+ onClick: () => replaceContent(quickReply.text),
331
+ className: "flex-shrink-0 whitespace-nowrap rounded-full border border-gray-200 bg-white px-2 py-1 text-xs text-gray-600 transition-colors hover:border-teal-200 hover:bg-teal-50 hover:text-teal-700 dark:border-gray-600 dark:bg-gray-800 dark:text-gray-400 dark:hover:border-teal-800 dark:hover:bg-teal-950/40 dark:hover:text-teal-400",
332
+ children: quickReply.label
333
+ },
334
+ quickReply.id
335
+ )) })
336
+ ) : null,
337
+ /* @__PURE__ */ jsxs2("div", { className: "flex flex-wrap items-end gap-2", children: [
338
+ /* @__PURE__ */ jsx2("div", { className: "mb-0.5 hidden items-center gap-0.5 sm:flex", children: [
339
+ [RICH_COMPOSER_ACTION.BOLD, Bold],
340
+ [RICH_COMPOSER_ACTION.ITALIC, Italic],
341
+ [RICH_COMPOSER_ACTION.STRIKETHROUGH, Strikethrough],
342
+ [RICH_COMPOSER_ACTION.MONOSPACE, Code]
343
+ ].filter(([action]) => shows(action)).map(([action, Icon]) => /* @__PURE__ */ jsx2(
344
+ "button",
345
+ {
346
+ type: "button",
347
+ onClick: () => wrapSelection(action),
348
+ title: tooltipOf(action),
349
+ "aria-label": tooltipOf(action),
350
+ className: "flex h-7 w-7 flex-shrink-0 items-center justify-center rounded-lg text-gray-400 transition-colors hover:bg-teal-50 hover:text-teal-600 dark:text-gray-500 dark:hover:bg-teal-900/30 dark:hover:text-teal-400",
351
+ children: /* @__PURE__ */ jsx2(Icon, { size: 15 })
352
+ },
353
+ action
354
+ )) }),
355
+ shows(RICH_COMPOSER_ACTION.EMOJI) ? /* @__PURE__ */ jsx2(SimpleEmojiPicker, { onSelect: insertAtCursor, label: tooltipOf("emoji") }) : null,
356
+ variables?.length && shows(RICH_COMPOSER_ACTION.VARIABLES) ? /* @__PURE__ */ jsxs2("div", { ref: variablesRef, className: "relative", children: [
357
+ /* @__PURE__ */ jsx2(
358
+ "button",
359
+ {
360
+ type: "button",
361
+ onClick: () => setIsVariablesOpen((open) => !open),
362
+ title: tooltipOf("variables"),
363
+ "aria-label": tooltipOf("variables"),
364
+ "aria-expanded": isVariablesOpen,
365
+ className: "flex h-9 w-9 flex-shrink-0 items-center justify-center rounded-full text-gray-400 transition-colors hover:bg-teal-50 hover:text-teal-600 dark:text-gray-500 dark:hover:bg-teal-900/30 dark:hover:text-teal-400",
366
+ children: /* @__PURE__ */ jsx2(Braces, { size: 18 })
367
+ }
368
+ ),
369
+ isVariablesOpen ? /* @__PURE__ */ jsx2("div", { className: "absolute bottom-full left-0 z-20 mb-2 max-h-56 w-64 overflow-y-auto rounded-xl border border-gray-200 bg-white py-1 shadow-lg dark:border-gray-700 dark:bg-gray-800", children: variables.map((variable) => /* @__PURE__ */ jsxs2(
370
+ "button",
371
+ {
372
+ type: "button",
373
+ title: variable.tooltip ?? variable.value,
374
+ onClick: () => {
375
+ insertAtCursor(variable.value);
376
+ setIsVariablesOpen(false);
377
+ },
378
+ className: "flex w-full flex-col items-start px-3 py-1.5 text-left hover:bg-teal-50 dark:hover:bg-teal-950/40",
379
+ children: [
380
+ /* @__PURE__ */ jsx2("span", { className: "text-sm text-gray-800 dark:text-gray-100", children: variable.label }),
381
+ /* @__PURE__ */ jsx2("span", { className: "font-mono text-xs text-gray-400 dark:text-gray-500", children: variable.value })
382
+ ]
383
+ },
384
+ variable.id
385
+ )) }) : null
386
+ ] }) : null,
387
+ onAttachFiles && shows(RICH_COMPOSER_ACTION.ATTACH) ? /* @__PURE__ */ jsxs2(Fragment, { children: [
388
+ /* @__PURE__ */ jsx2(
389
+ "input",
390
+ {
391
+ ref: fileInputRef,
392
+ type: "file",
393
+ multiple: true,
394
+ accept: acceptedFileTypes,
395
+ onChange: handleFileChange,
396
+ className: "hidden"
397
+ }
398
+ ),
399
+ /* @__PURE__ */ jsx2(
400
+ "button",
401
+ {
402
+ type: "button",
403
+ onClick: () => fileInputRef.current?.click(),
404
+ title: tooltipOf("attach"),
405
+ "aria-label": tooltipOf("attach"),
406
+ className: "flex h-9 w-9 flex-shrink-0 items-center justify-center rounded-full text-gray-400 transition-colors hover:bg-teal-50 hover:text-teal-600 dark:text-gray-500 dark:hover:bg-teal-900/30 dark:hover:text-teal-400",
407
+ children: /* @__PURE__ */ jsx2(Paperclip, { size: 18 })
408
+ }
409
+ )
410
+ ] }) : null,
411
+ /* @__PURE__ */ jsx2(
412
+ "div",
413
+ {
414
+ ref: editorRef,
415
+ contentEditable: !disabled,
416
+ suppressContentEditableWarning: true,
417
+ role: "textbox",
418
+ "aria-multiline": "true",
419
+ "aria-label": placeholder,
420
+ "data-placeholder": placeholder,
421
+ onInput: handleInput,
422
+ onKeyDown: handleKeyDown,
423
+ style: { minHeight: "36px", maxHeight: "120px" },
424
+ className: "flex-1 min-w-[180px] overflow-y-auto rounded-2xl border border-gray-200 bg-gray-50 px-4 py-2 text-sm text-gray-800 transition-all focus:border-transparent focus:outline-none focus:ring-2 focus:ring-teal-300 dark:border-gray-700 dark:bg-gray-700 dark:text-gray-100 empty:before:content-[attr(data-placeholder)] empty:before:text-gray-400 dark:empty:before:text-gray-500 before:pointer-events-none [&_strong]:font-semibold [&_em]:italic [&_del]:line-through [&_code]:rounded [&_code]:bg-black/5 [&_code]:px-0.5 [&_code]:font-mono [&_code]:text-sm dark:[&_code]:bg-white/10"
425
+ }
426
+ ),
427
+ !canSend && idleAction ? /* @__PURE__ */ jsx2("div", { className: "flex-shrink-0", children: idleAction }) : /* @__PURE__ */ jsx2(
428
+ "button",
429
+ {
430
+ type: "button",
431
+ onClick: onSend,
432
+ disabled: disabled || isSending || !canSend,
433
+ title: tooltipOf("send"),
434
+ "aria-label": tooltipOf("send"),
435
+ className: "flex h-9 w-9 flex-shrink-0 items-center justify-center rounded-full bg-teal-600 text-white transition-colors hover:bg-teal-700 disabled:cursor-not-allowed disabled:opacity-40",
436
+ children: isSending ? /* @__PURE__ */ jsx2("span", { className: "text-xs", children: "\u23F3" }) : /* @__PURE__ */ jsx2(SendHorizonal, { size: 16 })
437
+ }
438
+ )
439
+ ] })
440
+ ] });
441
+ });
442
+
443
+ // src/WhatsAppMessageEditor.tsx
444
+ import { useRef as useRef3 } from "react";
445
+ import { Bold as Bold2, Italic as Italic2, Strikethrough as Strikethrough2 } from "lucide-react";
446
+ import { Fragment as Fragment2, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
447
+ var DEFAULT_WHATSAPP_MESSAGE_EDITOR_LABELS = {
448
+ bold: "Negrito",
449
+ boldHint: "Negrito (*texto*)",
450
+ italic: "It\xE1lico",
451
+ italicHint: "It\xE1lico (_texto_)",
452
+ strikethrough: "Tachado",
453
+ strikethroughHint: "Tachado (~texto~)",
454
+ insertPlaceholder: (token) => `Inserir ${token}`
455
+ };
456
+ var INLINE_TOKEN = /(\*[^*\n]+\*|_[^_\n]+_|~[^~\n]+~|`[^`\n]+`|\{[a-zA-Z_]+\})/g;
457
+ function renderLine(line, lineKey) {
458
+ const nodes = [];
459
+ let lastIndex = 0;
460
+ let match;
461
+ let tokenIndex = 0;
462
+ INLINE_TOKEN.lastIndex = 0;
463
+ while ((match = INLINE_TOKEN.exec(line)) !== null) {
464
+ if (match.index > lastIndex) nodes.push(line.slice(lastIndex, match.index));
465
+ const token = match[0];
466
+ const key = `${lineKey}-${tokenIndex++}`;
467
+ if (token.startsWith("*")) nodes.push(/* @__PURE__ */ jsx3("strong", { children: token.slice(1, -1) }, key));
468
+ else if (token.startsWith("_")) nodes.push(/* @__PURE__ */ jsx3("em", { children: token.slice(1, -1) }, key));
469
+ else if (token.startsWith("~")) nodes.push(/* @__PURE__ */ jsx3("s", { children: token.slice(1, -1) }, key));
470
+ else if (token.startsWith("`")) nodes.push(/* @__PURE__ */ jsx3("code", { className: "font-mono text-[0.85em]", children: token.slice(1, -1) }, key));
471
+ else nodes.push(/* @__PURE__ */ jsx3("span", { className: "rounded bg-blue-100 dark:bg-blue-900/50 text-blue-700 dark:text-blue-300 px-1", children: token }, key));
472
+ lastIndex = INLINE_TOKEN.lastIndex;
473
+ }
474
+ if (lastIndex < line.length) nodes.push(line.slice(lastIndex));
475
+ return nodes;
476
+ }
477
+ function renderWhatsApp(text) {
478
+ const lines = text.split("\n");
479
+ return lines.map((line, index) => /* @__PURE__ */ jsxs3("span", { children: [
480
+ renderLine(line, `ln-${index}`),
481
+ index < lines.length - 1 ? /* @__PURE__ */ jsx3("br", {}) : null
482
+ ] }, `ln-${index}`));
483
+ }
484
+ function WhatsAppMessageEditor({
485
+ value,
486
+ onChange,
487
+ placeholder,
488
+ placeholders = [],
489
+ rows = 4,
490
+ previewLabel = "Pr\xE9via (como aparece no WhatsApp)",
491
+ emptyPreviewText = "Sua mensagem aparecer\xE1 aqui\u2026",
492
+ labels
493
+ }) {
494
+ const editorLabels = { ...DEFAULT_WHATSAPP_MESSAGE_EDITOR_LABELS, ...labels };
495
+ const textareaRef = useRef3(null);
496
+ function wrapSelection(marker) {
497
+ const textarea = textareaRef.current;
498
+ if (!textarea) return;
499
+ const start = textarea.selectionStart;
500
+ const end = textarea.selectionEnd;
501
+ const selected = value.slice(start, end) || "texto";
502
+ const next = value.slice(0, start) + marker + selected + marker + value.slice(end);
503
+ onChange(next);
504
+ requestAnimationFrame(() => {
505
+ textarea.focus();
506
+ textarea.setSelectionRange(start + marker.length, start + marker.length + selected.length);
507
+ });
508
+ }
509
+ function insertAtCursor(snippet) {
510
+ const textarea = textareaRef.current;
511
+ if (!textarea) return;
512
+ const start = textarea.selectionStart;
513
+ const end = textarea.selectionEnd;
514
+ const next = value.slice(0, start) + snippet + value.slice(end);
515
+ onChange(next);
516
+ requestAnimationFrame(() => {
517
+ textarea.focus();
518
+ const caret = start + snippet.length;
519
+ textarea.setSelectionRange(caret, caret);
520
+ });
521
+ }
522
+ 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";
523
+ return /* @__PURE__ */ jsxs3("div", { children: [
524
+ /* @__PURE__ */ jsxs3("div", { className: "flex flex-wrap items-center gap-1.5 mb-2", children: [
525
+ /* @__PURE__ */ jsx3("button", { type: "button", onClick: () => wrapSelection("*"), className: toolbarButtonClass, title: editorLabels.boldHint, "aria-label": editorLabels.bold, children: /* @__PURE__ */ jsx3(Bold2, { size: 15 }) }),
526
+ /* @__PURE__ */ jsx3("button", { type: "button", onClick: () => wrapSelection("_"), className: toolbarButtonClass, title: editorLabels.italicHint, "aria-label": editorLabels.italic, children: /* @__PURE__ */ jsx3(Italic2, { size: 15 }) }),
527
+ /* @__PURE__ */ jsx3("button", { type: "button", onClick: () => wrapSelection("~"), className: toolbarButtonClass, title: editorLabels.strikethroughHint, "aria-label": editorLabels.strikethrough, children: /* @__PURE__ */ jsx3(Strikethrough2, { size: 15 }) }),
528
+ placeholders.length > 0 && /* @__PURE__ */ jsxs3(Fragment2, { children: [
529
+ /* @__PURE__ */ jsx3("span", { className: "mx-1 h-5 w-px bg-gray-200 dark:bg-gray-600" }),
530
+ placeholders.map((token) => /* @__PURE__ */ jsx3(
869
531
  "button",
870
532
  {
871
533
  type: "button",
872
- onClick: () => {
873
- onSelect(emoji);
874
- setOpen(false);
875
- },
876
- className: "w-7 h-7 flex items-center justify-center rounded hover:bg-gray-100 dark:hover:bg-gray-700 text-lg leading-none transition-colors",
877
- children: emoji
534
+ onClick: () => insertAtCursor(token),
535
+ 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",
536
+ title: editorLabels.insertPlaceholder(token),
537
+ children: token
878
538
  },
879
- emoji
539
+ token
880
540
  ))
541
+ ] })
542
+ ] }),
543
+ /* @__PURE__ */ jsx3(
544
+ "textarea",
545
+ {
546
+ ref: textareaRef,
547
+ value,
548
+ onChange: (event) => onChange(event.target.value),
549
+ placeholder,
550
+ rows,
551
+ 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"
881
552
  }
882
- ) })
553
+ ),
554
+ /* @__PURE__ */ jsxs3("div", { className: "mt-2", children: [
555
+ /* @__PURE__ */ jsx3("p", { className: "text-xs text-gray-500 dark:text-gray-400 mb-1", children: previewLabel }),
556
+ /* @__PURE__ */ jsx3("div", { className: "rounded-xl bg-[#e5ded8] dark:bg-gray-900 p-3", children: /* @__PURE__ */ jsx3("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__ */ jsx3("span", { className: "text-gray-400 dark:text-gray-500", children: emptyPreviewText }) }) })
557
+ ] })
883
558
  ] });
884
559
  }
885
560
 
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
561
  // src/Avatar.tsx
903
- import { jsx as jsx14 } from "react/jsx-runtime";
562
+ import { jsx as jsx4 } from "react/jsx-runtime";
563
+ var DEFAULT_AVATAR_LABELS = {
564
+ unnamedContact: "Contato sem nome"
565
+ };
904
566
  var sizeClasses = {
905
567
  sm: "w-6 h-6 text-xs",
906
568
  md: "w-8 h-8 text-sm",
@@ -926,10 +588,11 @@ function getBgColor(name) {
926
588
  const hash = name.charCodeAt(0) + (name.charCodeAt(name.length - 1) || 0);
927
589
  return bgColors[hash % bgColors.length];
928
590
  }
929
- function Avatar({ name, avatarUrl, size = "md", className = "" }) {
591
+ function Avatar({ name, avatarUrl, size = "md", className = "", labels }) {
930
592
  const sizeClass = sizeClasses[size];
593
+ const unnamedContactLabel = labels?.unnamedContact ?? DEFAULT_AVATAR_LABELS.unnamedContact;
931
594
  if (avatarUrl) {
932
- return /* @__PURE__ */ jsx14(
595
+ return /* @__PURE__ */ jsx4(
933
596
  "img",
934
597
  {
935
598
  src: avatarUrl,
@@ -938,14 +601,24 @@ function Avatar({ name, avatarUrl, size = "md", className = "" }) {
938
601
  }
939
602
  );
940
603
  }
941
- const initials = getInitials(name);
942
604
  const bgColor = getBgColor(name);
943
- return /* @__PURE__ */ jsx14(
605
+ if (!name) {
606
+ return /* @__PURE__ */ jsx4(
607
+ "div",
608
+ {
609
+ className: `${sizeClass} ${bgColor} rounded-full flex items-center justify-center text-white flex-shrink-0 ${className}`,
610
+ role: "img",
611
+ "aria-label": unnamedContactLabel,
612
+ children: /* @__PURE__ */ jsx4("svg", { viewBox: "0 0 24 24", fill: "currentColor", className: "h-[60%] w-[60%]", "aria-hidden": true, children: /* @__PURE__ */ jsx4("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" }) })
613
+ }
614
+ );
615
+ }
616
+ return /* @__PURE__ */ jsx4(
944
617
  "div",
945
618
  {
946
619
  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
620
+ title: name,
621
+ children: getInitials(name)
949
622
  }
950
623
  );
951
624
  }
@@ -953,30 +626,91 @@ function Avatar({ name, avatarUrl, size = "md", className = "" }) {
953
626
  // src/ConversationListItem.tsx
954
627
  import { useMemo } from "react";
955
628
 
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)}`;
629
+ // src/conversationChannel.ts
630
+ var CONVERSATION_CHANNEL = {
631
+ WHATSAPP: "whatsapp",
632
+ MESSENGER: "messenger",
633
+ INSTAGRAM: "instagram",
634
+ WEBCHAT: "webchat"
635
+ };
636
+ var DEFAULT_CONVERSATION_CHANNEL = CONVERSATION_CHANNEL.WHATSAPP;
637
+ var REOPEN_MECHANISM = {
638
+ TEMPLATE: "template",
639
+ TAG: "tag",
640
+ NONE: "none"
641
+ };
642
+ var HANDLE_KIND = {
643
+ PHONE: "phone",
644
+ USERNAME: "username",
645
+ SESSION: "session"
646
+ };
647
+ var CHANNEL_CAPABILITIES = {
648
+ [CONVERSATION_CHANNEL.WHATSAPP]: {
649
+ label: "WhatsApp",
650
+ icon: "\u{1F4AC}",
651
+ hasSessionWindow: true,
652
+ windowHours: 24,
653
+ reopenMechanism: REOPEN_MECHANISM.TEMPLATE,
654
+ handleKind: HANDLE_KIND.PHONE
655
+ },
656
+ [CONVERSATION_CHANNEL.MESSENGER]: {
657
+ // Messenger também tem 24h, mas reabre com message tag — não com template aprovado.
658
+ label: "Messenger",
659
+ icon: "\u{1F4E8}",
660
+ hasSessionWindow: true,
661
+ windowHours: 24,
662
+ reopenMechanism: REOPEN_MECHANISM.TAG,
663
+ handleKind: HANDLE_KIND.USERNAME
664
+ },
665
+ [CONVERSATION_CHANNEL.INSTAGRAM]: {
666
+ label: "Instagram",
667
+ icon: "\u{1F4F7}",
668
+ hasSessionWindow: true,
669
+ windowHours: 24,
670
+ reopenMechanism: REOPEN_MECHANISM.TAG,
671
+ handleKind: HANDLE_KIND.USERNAME
672
+ },
673
+ [CONVERSATION_CHANNEL.WEBCHAT]: {
674
+ // Chat próprio: sem intermediário, sem janela. Bloquear o composer aqui seria inventar limite.
675
+ label: "Chat do site",
676
+ icon: "\u{1F310}",
677
+ hasSessionWindow: false,
678
+ windowHours: 0,
679
+ reopenMechanism: REOPEN_MECHANISM.NONE,
680
+ handleKind: HANDLE_KIND.SESSION
970
681
  }
971
- return number;
682
+ };
683
+ function capabilitiesOf(channel) {
684
+ return CHANNEL_CAPABILITIES[channel ?? DEFAULT_CONVERSATION_CHANNEL];
685
+ }
686
+ var CHANNEL_FILTER_ALL = "all";
687
+ function channelFiltersFor(conversations) {
688
+ const present = new Set(
689
+ conversations.map((conversation) => conversation.channel ?? DEFAULT_CONVERSATION_CHANNEL)
690
+ );
691
+ if (present.size < 2) return [];
692
+ const ordered = Object.keys(CHANNEL_CAPABILITIES).filter((channel) => present.has(channel));
693
+ return [
694
+ { value: CHANNEL_FILTER_ALL, label: "Todos" },
695
+ ...ordered.map((channel) => ({ value: channel, label: CHANNEL_CAPABILITIES[channel].label }))
696
+ ];
697
+ }
698
+ function formatContactHandle(params) {
699
+ const { handleKind } = capabilitiesOf(params.channel);
700
+ if (handleKind === HANDLE_KIND.PHONE) return formatPhone(params.handle);
701
+ if (handleKind === HANDLE_KIND.USERNAME) return params.handle.startsWith("@") ? params.handle : `@${params.handle}`;
702
+ return `Visitante ${params.handle.slice(-6)}`;
972
703
  }
973
- function phoneInitials(number) {
974
- const digits = number.replace(/\D/g, "");
975
- return digits.slice(-2);
704
+ function contactFlag(params) {
705
+ return capabilitiesOf(params.channel).handleKind === HANDLE_KIND.PHONE ? phoneCountryFlag(params.handle) : "";
976
706
  }
977
707
 
978
708
  // src/ConversationListItem.tsx
979
- import { jsx as jsx15, jsxs as jsxs10 } from "react/jsx-runtime";
709
+ import { jsx as jsx5, jsxs as jsxs4 } from "react/jsx-runtime";
710
+ var DEFAULT_CONVERSATION_LIST_ITEM_LABELS = {
711
+ expiredWindow: "Janela expirada",
712
+ warningWindow: "Janela pr\xF3xima do fim"
713
+ };
980
714
  function formatRelativeTime(dateStr) {
981
715
  try {
982
716
  const date = new Date(dateStr);
@@ -1035,50 +769,63 @@ var ConversationListItem = ({
1035
769
  active = false,
1036
770
  selected = false,
1037
771
  onClick,
1038
- onSelect
772
+ onSelect,
773
+ showDivider = true,
774
+ highlightActive = true,
775
+ labels
1039
776
  }) => {
777
+ const expiredWindowLabel = labels?.expiredWindow ?? DEFAULT_CONVERSATION_LIST_ITEM_LABELS.expiredWindow;
778
+ const warningWindowLabel = labels?.warningWindow ?? DEFAULT_CONVERSATION_LIST_ITEM_LABELS.warningWindow;
1040
779
  const isActive = active || selected;
1041
780
  const windowStatus = useMemo(() => getWindowStatus(conversation.lastInboundAt), [conversation.lastInboundAt]);
1042
781
  const preview = useMemo(() => lastMessagePreview(conversation), [conversation.lastContent]);
1043
- const name = conversation.clientName || formatPhone(conversation.whatsappNumber) || conversation.whatsappNumber;
782
+ const handle = conversation.contactId ?? conversation.whatsappNumber;
783
+ const displayHandle = formatContactHandle({ handle, channel: conversation.channel });
784
+ const name = conversation.clientName || displayHandle || handle;
1044
785
  const timestamp = formatRelativeTime(conversation.lastAt);
786
+ const flag = contactFlag({ handle, channel: conversation.channel });
1045
787
  const isInbound = conversation.lastDirection === "inbound";
1046
788
  const handleClick = () => {
1047
789
  onClick?.();
1048
790
  onSelect?.(conversation.id);
1049
791
  };
1050
- return /* @__PURE__ */ jsxs10(
792
+ return /* @__PURE__ */ jsxs4(
1051
793
  "button",
1052
794
  {
1053
795
  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]",
796
+ 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
797
  style: {
1056
- backgroundColor: isActive ? "#f0f2f5" : "transparent"
798
+ backgroundColor: isActive && highlightActive ? "#f0f2f5" : "transparent"
1057
799
  },
1058
800
  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" })
801
+ /* @__PURE__ */ jsxs4("div", { className: "relative flex-shrink-0", children: [
802
+ /* @__PURE__ */ jsx5(Avatar, { name: conversation.clientName, size: "lg" }),
803
+ conversation.waitingHuman && /* @__PURE__ */ jsx5("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
804
  ] }),
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" })
805
+ /* @__PURE__ */ jsxs4("div", { className: "flex-1 min-w-0", children: [
806
+ /* @__PURE__ */ jsxs4("div", { className: "flex items-center justify-between gap-2", children: [
807
+ /* @__PURE__ */ jsxs4("div", { className: "flex items-center gap-1.5 min-w-0", children: [
808
+ !conversation.clientName && flag ? /* @__PURE__ */ jsx5("span", { "aria-hidden": true, children: flag }) : null,
809
+ /* @__PURE__ */ jsx5("span", { className: "text-[16px] text-[#111b21] truncate", children: name }),
810
+ windowStatus && windowStatus.label === "expired" && /* @__PURE__ */ jsx5("span", { className: "w-2 h-2 rounded-full bg-red-500 flex-shrink-0", title: expiredWindowLabel }),
811
+ windowStatus && windowStatus.label === "warning" && /* @__PURE__ */ jsx5("span", { className: "w-2 h-2 rounded-full bg-orange-500 flex-shrink-0", title: warningWindowLabel })
1069
812
  ] }),
1070
- timestamp && /* @__PURE__ */ jsx15("span", { className: "text-xs text-[#667781] flex-shrink-0", children: timestamp })
813
+ timestamp && /* @__PURE__ */ jsx5("span", { className: "text-xs text-[#667781] flex-shrink-0", children: timestamp })
1071
814
  ] }),
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 }),
815
+ conversation.clientName ? /* @__PURE__ */ jsxs4("div", { className: "flex items-center gap-1 text-xs text-[#667781]", children: [
816
+ flag ? /* @__PURE__ */ jsx5("span", { "aria-hidden": true, children: flag }) : null,
817
+ /* @__PURE__ */ jsx5("span", { className: "truncate", children: displayHandle })
818
+ ] }) : null,
819
+ /* @__PURE__ */ jsxs4("div", { className: "flex items-center justify-between mt-0.5", children: [
820
+ /* @__PURE__ */ jsxs4("span", { className: "text-sm text-[#667781] truncate max-w-[180px]", children: [
821
+ preview.icon && /* @__PURE__ */ jsx5("span", { className: "mr-1", children: preview.icon }),
1075
822
  preview.preview || "Sem mensagens"
1076
823
  ] }),
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 })
824
+ conversation.unread > 0 && /* @__PURE__ */ jsx5("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
825
  ] }),
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" })
826
+ /* @__PURE__ */ jsxs4("div", { className: "flex items-center gap-1.5 mt-1", children: [
827
+ conversation.mode === "human" && /* @__PURE__ */ jsx5("span", { className: "text-[10px] px-1.5 py-0.5 rounded font-medium bg-purple-100 text-purple-700", children: "Humano" }),
828
+ conversation.mode === "bot" && conversation.waitingHuman && /* @__PURE__ */ jsx5("span", { className: "text-[10px] px-1.5 py-0.5 rounded font-medium bg-amber-100 text-amber-700", children: "Aguardando" })
1082
829
  ] })
1083
830
  ] })
1084
831
  ]
@@ -1087,9 +834,9 @@ var ConversationListItem = ({
1087
834
  };
1088
835
 
1089
836
  // 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);
837
+ import { createContext, useContext, useState as useState3, useCallback as useCallback2, useEffect as useEffect3 } from "react";
838
+ import { jsx as jsx6, jsxs as jsxs5 } from "react/jsx-runtime";
839
+ var ToastContext = createContext(null);
1093
840
  var nextId = 0;
1094
841
  var singletonShow = null;
1095
842
  var toast = {
@@ -1105,19 +852,19 @@ var typeStyles = {
1105
852
  info: "bg-blue-600"
1106
853
  };
1107
854
  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" })
855
+ success: /* @__PURE__ */ jsx6("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ jsx6("polyline", { points: "20 6 9 17 4 12" }) }),
856
+ error: /* @__PURE__ */ jsxs5("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
857
+ /* @__PURE__ */ jsx6("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
858
+ /* @__PURE__ */ jsx6("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
1112
859
  ] }),
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" })
860
+ info: /* @__PURE__ */ jsxs5("svg", { width: "18", height: "18", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
861
+ /* @__PURE__ */ jsx6("circle", { cx: "12", cy: "12", r: "10" }),
862
+ /* @__PURE__ */ jsx6("line", { x1: "12", y1: "16", x2: "12", y2: "12" }),
863
+ /* @__PURE__ */ jsx6("line", { x1: "12", y1: "8", x2: "12.01", y2: "8" })
1117
864
  ] })
1118
865
  };
1119
866
  function ToastItemComponent({ item, onRemove }) {
1120
- const [exiting, setExiting] = useState7(false);
867
+ const [exiting, setExiting] = useState3(false);
1121
868
  useEffect3(() => {
1122
869
  const timer = setTimeout(() => {
1123
870
  setExiting(true);
@@ -1125,14 +872,14 @@ function ToastItemComponent({ item, onRemove }) {
1125
872
  }, 5e3);
1126
873
  return () => clearTimeout(timer);
1127
874
  }, [item.id, onRemove]);
1128
- return /* @__PURE__ */ jsxs11(
875
+ return /* @__PURE__ */ jsxs5(
1129
876
  "div",
1130
877
  {
1131
878
  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
879
  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(
880
+ /* @__PURE__ */ jsx6("span", { className: "flex-shrink-0", children: typeIcons[item.type] }),
881
+ /* @__PURE__ */ jsx6("span", { className: "flex-1", children: item.message }),
882
+ /* @__PURE__ */ jsx6(
1136
883
  "button",
1137
884
  {
1138
885
  onClick: () => {
@@ -1140,9 +887,9 @@ function ToastItemComponent({ item, onRemove }) {
1140
887
  setTimeout(() => onRemove(item.id), 300);
1141
888
  },
1142
889
  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" })
890
+ children: /* @__PURE__ */ jsxs5("svg", { width: "14", height: "14", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
891
+ /* @__PURE__ */ jsx6("line", { x1: "18", y1: "6", x2: "6", y2: "18" }),
892
+ /* @__PURE__ */ jsx6("line", { x1: "6", y1: "6", x2: "18", y2: "18" })
1146
893
  ] })
1147
894
  }
1148
895
  )
@@ -1151,12 +898,12 @@ function ToastItemComponent({ item, onRemove }) {
1151
898
  );
1152
899
  }
1153
900
  function ToastProvider({ children }) {
1154
- const [items, setItems] = useState7([]);
1155
- const show = useCallback3((type, message) => {
901
+ const [items, setItems] = useState3([]);
902
+ const show = useCallback2((type, message) => {
1156
903
  const id = ++nextId;
1157
904
  setItems((prev) => [...prev, { id, type, message }]);
1158
905
  }, []);
1159
- const remove = useCallback3((id) => {
906
+ const remove = useCallback2((id) => {
1160
907
  setItems((prev) => prev.filter((item) => item.id !== id));
1161
908
  }, []);
1162
909
  useEffect3(() => {
@@ -1165,10 +912,10 @@ function ToastProvider({ children }) {
1165
912
  singletonShow = null;
1166
913
  };
1167
914
  }, [show]);
1168
- return /* @__PURE__ */ jsxs11(ToastContext.Provider, { value: { show }, children: [
915
+ return /* @__PURE__ */ jsxs5(ToastContext.Provider, { value: { show }, children: [
1169
916
  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: `
917
+ /* @__PURE__ */ jsx6("div", { className: "fixed top-4 right-4 z-[100] flex flex-col gap-2", children: items.map((item) => /* @__PURE__ */ jsx6(ToastItemComponent, { item, onRemove: remove }, item.id)) }),
918
+ /* @__PURE__ */ jsx6("style", { children: `
1172
919
  @keyframes slide-in {
1173
920
  from { transform: translateX(100%); opacity: 0; }
1174
921
  to { transform: translateX(0); opacity: 1; }
@@ -1183,7 +930,7 @@ function ToastProvider({ children }) {
1183
930
  ] });
1184
931
  }
1185
932
  function useToast() {
1186
- const context = useContext2(ToastContext);
933
+ const context = useContext(ToastContext);
1187
934
  if (!context) {
1188
935
  throw new Error("useToast must be used within a ToastProvider");
1189
936
  }
@@ -1191,11 +938,11 @@ function useToast() {
1191
938
  }
1192
939
 
1193
940
  // 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";
941
+ import { useCallback as useCallback3, useState as useState4 } from "react";
942
+ import { jsx as jsx7, jsxs as jsxs6 } from "react/jsx-runtime";
1196
943
  function MessageText({ message }) {
1197
- const [copied, setCopied] = useState8(false);
1198
- const handleCopy = useCallback4(async () => {
944
+ const [copied, setCopied] = useState4(false);
945
+ const handleCopy = useCallback3(async () => {
1199
946
  if (!message.content) return;
1200
947
  try {
1201
948
  await navigator.clipboard.writeText(message.content);
@@ -1205,102 +952,565 @@ function MessageText({ message }) {
1205
952
  }
1206
953
  }, [message.content]);
1207
954
  if (message.type === "template")
1208
- return /* @__PURE__ */ jsx17("p", { className: "text-sm italic text-[#667781]", children: message.content ?? "Template message" });
1209
- return /* @__PURE__ */ jsxs12(
955
+ return /* @__PURE__ */ jsx7("p", { className: "text-sm italic text-[#667781]", children: message.content ?? "Template message" });
956
+ return /* @__PURE__ */ jsxs6(
1210
957
  "div",
1211
958
  {
1212
959
  onClick: handleCopy,
1213
960
  className: "text-[14.2px] leading-[19px] whitespace-pre-wrap break-words select-all [&_strong]:font-bold [&_em]:italic [&_del]:line-through",
1214
961
  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!" })
962
+ /* @__PURE__ */ jsx7("span", { children: parseWhatsAppFormatting(message.content ?? "") }),
963
+ copied && /* @__PURE__ */ jsx7("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
964
  ]
1218
965
  }
1219
966
  );
1220
967
  }
1221
968
 
1222
969
  // src/MessageTimestamp.tsx
1223
- import { jsx as jsx18, jsxs as jsxs13 } from "react/jsx-runtime";
970
+ import { jsx as jsx8, jsxs as jsxs7 } from "react/jsx-runtime";
1224
971
  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 }) })
972
+ return /* @__PURE__ */ jsxs7("div", { className: "flex items-center justify-end gap-[3px]", children: [
973
+ /* @__PURE__ */ jsx8("span", { className: "text-[11px] text-[#667781] leading-[15px] select-none", children: timestamp }),
974
+ isOutbound && status && /* @__PURE__ */ jsx8("span", { className: "flex items-center -mr-[2px]", children: /* @__PURE__ */ jsx8(StatusTicks, { status }) })
1228
975
  ] });
1229
976
  }
1230
977
 
1231
978
  // src/MessageTail.tsx
1232
- import { jsx as jsx19 } from "react/jsx-runtime";
979
+ import { jsx as jsx9 } from "react/jsx-runtime";
1233
980
  function MessageTail({ isOutbound }) {
1234
981
  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" }) });
982
+ return /* @__PURE__ */ jsx9("svg", { className: "absolute top-0 -right-[5px] w-[5px] h-[11px]", viewBox: "0 0 5 11", fill: "#d9fdd3", children: /* @__PURE__ */ jsx9("path", { d: "M5 0H0v11c1.5-2 4.5-2 5-4V0z" }) });
983
+ return /* @__PURE__ */ jsx9("svg", { className: "absolute top-0 -left-[5px] w-[5px] h-[11px]", viewBox: "0 0 5 11", fill: "white", children: /* @__PURE__ */ jsx9("path", { d: "M0 0h5v11C3.5 9 .5 9 0 7V0z" }) });
1237
984
  }
1238
985
 
1239
- // src/useWaitingNotifications.ts
1240
- import { useState as useState9, useEffect as useEffect4, useRef as useRef5, useCallback as useCallback5 } from "react";
986
+ // src/conversationWindow.ts
987
+ var MINUTE_MS = 6e4;
988
+ var HOUR_MS = 60 * MINUTE_MS;
989
+ var DAY_MS = 24 * HOUR_MS;
990
+ var CONVERSATION_WINDOW = {
991
+ ALL: "all",
992
+ FRESH: "fresh",
993
+ WARNING: "warning",
994
+ CRITICAL: "critical",
995
+ EXPIRED: "expired"
996
+ };
997
+ var WINDOW_FILTERS = [
998
+ { value: CONVERSATION_WINDOW.ALL, label: "Todas", dotClass: "" },
999
+ { value: CONVERSATION_WINDOW.FRESH, label: "<12h", dotClass: "bg-green-500" },
1000
+ { value: CONVERSATION_WINDOW.WARNING, label: "12-21h", dotClass: "bg-yellow-500" },
1001
+ { value: CONVERSATION_WINDOW.CRITICAL, label: "21-24h", dotClass: "bg-red-500" },
1002
+ { value: CONVERSATION_WINDOW.EXPIRED, label: ">24h", dotClass: "bg-gray-400" }
1003
+ ];
1004
+ var WARNING_RATIO = 0.5;
1005
+ var CRITICAL_RATIO = 0.875;
1006
+ function windowOf(params) {
1007
+ const capabilities = capabilitiesOf(params.channel);
1008
+ if (!capabilities.hasSessionWindow) return CONVERSATION_WINDOW.FRESH;
1009
+ if (!params.lastInboundAt) return CONVERSATION_WINDOW.EXPIRED;
1010
+ const windowMs = capabilities.windowHours * HOUR_MS;
1011
+ const elapsed = params.now - new Date(params.lastInboundAt).getTime();
1012
+ if (elapsed >= windowMs) return CONVERSATION_WINDOW.EXPIRED;
1013
+ if (elapsed >= windowMs * CRITICAL_RATIO) return CONVERSATION_WINDOW.CRITICAL;
1014
+ if (elapsed >= windowMs * WARNING_RATIO) return CONVERSATION_WINDOW.WARNING;
1015
+ return CONVERSATION_WINDOW.FRESH;
1016
+ }
1017
+ function formatStalledFor(lastAt, now) {
1018
+ const elapsed = Math.max(0, now - new Date(lastAt).getTime());
1019
+ const days = Math.floor(elapsed / DAY_MS);
1020
+ const hours = Math.floor(elapsed % DAY_MS / HOUR_MS);
1021
+ const minutes = Math.floor(elapsed % HOUR_MS / MINUTE_MS);
1022
+ if (days > 0) return `${days}d ${hours}h:${String(minutes).padStart(2, "0")}m`;
1023
+ if (hours > 0) return `${hours}h:${String(minutes).padStart(2, "0")}m`;
1024
+ return `${minutes}m`;
1025
+ }
1026
+
1027
+ // src/ChannelIcon.tsx
1028
+ import { Fragment as Fragment3, jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
1029
+ var CHANNEL_BRAND_COLOR = {
1030
+ [CONVERSATION_CHANNEL.WHATSAPP]: "#25D366",
1031
+ [CONVERSATION_CHANNEL.MESSENGER]: "#0084FF",
1032
+ [CONVERSATION_CHANNEL.INSTAGRAM]: "#E4405F",
1033
+ [CONVERSATION_CHANNEL.WEBCHAT]: "#64748B"
1034
+ };
1035
+ function Glyph({ channel }) {
1036
+ const color = CHANNEL_BRAND_COLOR[channel];
1037
+ if (channel === CONVERSATION_CHANNEL.WHATSAPP) {
1038
+ return /* @__PURE__ */ jsxs8(Fragment3, { children: [
1039
+ /* @__PURE__ */ jsx10("circle", { cx: "12", cy: "12", r: "11", fill: color }),
1040
+ /* @__PURE__ */ jsx10(
1041
+ "path",
1042
+ {
1043
+ 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",
1044
+ fill: "#fff"
1045
+ }
1046
+ )
1047
+ ] });
1048
+ }
1049
+ if (channel === CONVERSATION_CHANNEL.MESSENGER) {
1050
+ return /* @__PURE__ */ jsxs8(Fragment3, { children: [
1051
+ /* @__PURE__ */ jsx10("circle", { cx: "12", cy: "12", r: "11", fill: color }),
1052
+ /* @__PURE__ */ jsx10("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" })
1053
+ ] });
1054
+ }
1055
+ if (channel === CONVERSATION_CHANNEL.INSTAGRAM) {
1056
+ return /* @__PURE__ */ jsxs8(Fragment3, { children: [
1057
+ /* @__PURE__ */ jsx10("rect", { x: "2", y: "2", width: "20", height: "20", rx: "6", fill: color }),
1058
+ /* @__PURE__ */ jsx10("rect", { x: "6.5", y: "6.5", width: "11", height: "11", rx: "3.5", fill: "none", stroke: "#fff", strokeWidth: "1.6" }),
1059
+ /* @__PURE__ */ jsx10("circle", { cx: "12", cy: "12", r: "2.6", fill: "none", stroke: "#fff", strokeWidth: "1.6" }),
1060
+ /* @__PURE__ */ jsx10("circle", { cx: "16.4", cy: "7.6", r: "1", fill: "#fff" })
1061
+ ] });
1062
+ }
1063
+ return /* @__PURE__ */ jsxs8(Fragment3, { children: [
1064
+ /* @__PURE__ */ jsx10("circle", { cx: "12", cy: "12", r: "11", fill: color }),
1065
+ /* @__PURE__ */ jsx10("path", { d: "M6.5 8.5h11v7h-5.5L8.5 18v-2.5h-2v-7Z", fill: "#fff" })
1066
+ ] });
1067
+ }
1068
+ function ChannelIcon({ channel, size = 14, className = "" }) {
1069
+ const resolved = channel ?? CONVERSATION_CHANNEL.WHATSAPP;
1070
+ const { label } = capabilitiesOf(resolved);
1071
+ return /* @__PURE__ */ jsx10(
1072
+ "svg",
1073
+ {
1074
+ viewBox: "0 0 24 24",
1075
+ width: size,
1076
+ height: size,
1077
+ className: `inline-block shrink-0 ${className}`.trim(),
1078
+ role: "img",
1079
+ "aria-label": label,
1080
+ children: /* @__PURE__ */ jsx10(Glyph, { channel: resolved })
1081
+ }
1082
+ );
1083
+ }
1084
+
1085
+ // src/ConversationRow.tsx
1086
+ import { jsx as jsx11, jsxs as jsxs9 } from "react/jsx-runtime";
1087
+ var WINDOW_TITLE = {
1088
+ [CONVERSATION_WINDOW.ALL]: "",
1089
+ [CONVERSATION_WINDOW.FRESH]: "Janela de 24h aberta (menos de 12h)",
1090
+ [CONVERSATION_WINDOW.WARNING]: "Janela de 24h fechando (12-21h)",
1091
+ [CONVERSATION_WINDOW.CRITICAL]: "Janela de 24h quase expirada (21-24h)",
1092
+ [CONVERSATION_WINDOW.EXPIRED]: "Janela de 24h expirada \u2014 s\xF3 template"
1093
+ };
1094
+ var STALLED_THRESHOLD_MS = 60 * 60 * 1e3;
1095
+ var WINDOW_BAR_CLASS = {
1096
+ [CONVERSATION_WINDOW.ALL]: "bg-transparent",
1097
+ [CONVERSATION_WINDOW.FRESH]: "bg-green-500",
1098
+ [CONVERSATION_WINDOW.WARNING]: "bg-yellow-500",
1099
+ [CONVERSATION_WINDOW.CRITICAL]: "bg-red-500",
1100
+ [CONVERSATION_WINDOW.EXPIRED]: "bg-gray-300"
1101
+ };
1102
+ function ConversationRow({
1103
+ conversation,
1104
+ active,
1105
+ selected,
1106
+ now,
1107
+ busy,
1108
+ onOpen,
1109
+ onToggleSelected,
1110
+ onTakeover,
1111
+ className,
1112
+ classNames
1113
+ }) {
1114
+ const capabilities = capabilitiesOf(conversation.channel);
1115
+ const window2 = windowOf({ lastInboundAt: conversation.lastInboundAt, now, channel: conversation.channel });
1116
+ const stalledMs = now - new Date(conversation.lastAt).getTime();
1117
+ const isStalled = stalledMs > STALLED_THRESHOLD_MS;
1118
+ const isWaiting = conversation.mode === "bot" && conversation.waitingHuman;
1119
+ return /* @__PURE__ */ jsxs9("div", { className: cn("cv-row flex border-b", active && "cv-row--active", classNames?.root, className), children: [
1120
+ /* @__PURE__ */ jsx11(
1121
+ "span",
1122
+ {
1123
+ className: cn("w-1 shrink-0", WINDOW_BAR_CLASS[window2], classNames?.windowBar),
1124
+ title: WINDOW_TITLE[window2],
1125
+ "aria-label": WINDOW_TITLE[window2]
1126
+ }
1127
+ ),
1128
+ /* @__PURE__ */ jsxs9("div", { className: "min-w-0 flex-1", children: [
1129
+ /* @__PURE__ */ jsxs9("div", { className: "flex items-start gap-2 px-2 pt-2", children: [
1130
+ /* @__PURE__ */ jsx11(
1131
+ "input",
1132
+ {
1133
+ type: "checkbox",
1134
+ checked: selected,
1135
+ onChange: onToggleSelected,
1136
+ "aria-label": `Selecionar conversa ${conversation.whatsappNumber}`,
1137
+ className: "mt-3"
1138
+ }
1139
+ ),
1140
+ /* @__PURE__ */ jsx11("div", { className: "min-w-0 flex-1", children: /* @__PURE__ */ jsx11(
1141
+ ConversationListItem,
1142
+ {
1143
+ conversation,
1144
+ active,
1145
+ onClick: onOpen,
1146
+ showDivider: false,
1147
+ highlightActive: false
1148
+ }
1149
+ ) })
1150
+ ] }),
1151
+ /* @__PURE__ */ jsxs9("div", { className: "flex flex-wrap items-center gap-1.5 px-4 pb-2 pl-8", children: [
1152
+ /* @__PURE__ */ jsxs9("span", { className: "cv-pill", children: [
1153
+ /* @__PURE__ */ jsx11(ChannelIcon, { channel: conversation.channel }),
1154
+ " ",
1155
+ capabilities.label
1156
+ ] }),
1157
+ conversation.mode === "human" ? /* @__PURE__ */ jsx11("span", { className: "cv-pill cv-pill--success", children: "\u{1F9D1}\u200D\u{1F4BC} em atendimento" }) : null,
1158
+ isWaiting ? /* @__PURE__ */ jsx11("span", { className: "cv-pill cv-pill--warning", children: "\u23F3 aguardando atendimento" }) : null,
1159
+ !isWaiting && conversation.mode === "bot" ? /* @__PURE__ */ jsx11("span", { className: "cv-pill", children: "\u{1F916} bot ativo" }) : null,
1160
+ isStalled ? /* @__PURE__ */ jsxs9("span", { className: "cv-pill cv-pill--danger", children: [
1161
+ "\u23F1\uFE0F parada h\xE1 ",
1162
+ formatStalledFor(conversation.lastAt, now)
1163
+ ] }) : null,
1164
+ isWaiting ? /* @__PURE__ */ jsx11("button", { type: "button", onClick: onTakeover, disabled: busy, className: "cv-header-action", children: "\u25B6\uFE0F Continuar Atendimento" }) : null
1165
+ ] })
1166
+ ] })
1167
+ ] });
1168
+ }
1169
+
1170
+ // src/ConversationHeader.tsx
1171
+ import { useState as useState5 } from "react";
1172
+ import { jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
1173
+ var DEFAULT_CONVERSATION_HEADER_LABELS = {
1174
+ botMode: "\u{1F916} atendimento autom\xE1tico",
1175
+ humanMode: "\u{1F9D1}\u200D\u{1F4BC} atendimento humano",
1176
+ returnToBot: "Devolver ao bot",
1177
+ finish: "Finalizar",
1178
+ takeover: "Assumir atendimento",
1179
+ download: "Baixar conversa",
1180
+ documents: "Arquivos da conversa",
1181
+ back: "Voltar para a lista",
1182
+ moreActions: "Mais a\xE7\xF5es"
1183
+ };
1184
+ function ConversationHeader({
1185
+ conversation,
1186
+ busy = false,
1187
+ onTakeover,
1188
+ onReturnToBot,
1189
+ onFinish,
1190
+ onDownload,
1191
+ onOpenDocuments,
1192
+ documentsOpen = false,
1193
+ extraUtilities,
1194
+ onBack,
1195
+ labels: labelsOverride,
1196
+ className,
1197
+ classNames
1198
+ }) {
1199
+ const labels = { ...DEFAULT_CONVERSATION_HEADER_LABELS, ...labelsOverride };
1200
+ const isHuman = conversation.mode === "human";
1201
+ const handle = conversation.contactId ?? conversation.whatsappNumber;
1202
+ const displayHandle = formatContactHandle({ handle, channel: conversation.channel });
1203
+ const flag = contactFlag({ handle, channel: conversation.channel });
1204
+ const capabilities = capabilitiesOf(conversation.channel);
1205
+ const [menuOpen, setMenuOpen] = useState5(false);
1206
+ const utilities = [
1207
+ onOpenDocuments ? { key: "documents", icon: "\u{1F4C4}", label: labels.documents, run: onOpenDocuments, active: documentsOpen } : void 0,
1208
+ onDownload ? { key: "download", icon: "\u2B07\uFE0F", label: labels.download, run: onDownload, active: false } : void 0,
1209
+ ...(extraUtilities ?? []).map((utility) => ({ ...utility, active: utility.active ?? false }))
1210
+ ].filter(
1211
+ (utility) => Boolean(utility)
1212
+ );
1213
+ const actions = [
1214
+ !isHuman && onTakeover ? { key: "takeover", label: labels.takeover, run: onTakeover, className: "cv-header-action--primary" } : void 0,
1215
+ isHuman && onReturnToBot ? { key: "release", label: labels.returnToBot, run: onReturnToBot, className: "" } : void 0,
1216
+ isHuman && onFinish ? { key: "finish", label: `\u2715 ${labels.finish}`, run: onFinish, className: "cv-header-action--danger" } : void 0
1217
+ ].filter((action) => Boolean(action));
1218
+ const menuItems = [
1219
+ ...utilities.map((utility) => ({
1220
+ key: utility.key,
1221
+ label: `${utility.icon} ${utility.label}`,
1222
+ run: utility.run,
1223
+ className: ""
1224
+ })),
1225
+ ...actions
1226
+ ];
1227
+ return /* @__PURE__ */ jsxs10(
1228
+ "header",
1229
+ {
1230
+ className: cn(
1231
+ "flex flex-nowrap items-center justify-between gap-3 border-b px-4 py-3",
1232
+ classNames?.root,
1233
+ className
1234
+ ),
1235
+ children: [
1236
+ /* @__PURE__ */ jsxs10("div", { className: cn("flex min-w-0 flex-1 items-center gap-3", classNames?.identity), children: [
1237
+ onBack ? /* @__PURE__ */ jsx12("button", { type: "button", onClick: onBack, className: "cv-back cv-touch", "aria-label": labels.back, title: labels.back, children: "\u2190" }) : null,
1238
+ /* @__PURE__ */ jsx12(Avatar, { name: conversation.clientName, size: "md" }),
1239
+ /* @__PURE__ */ jsxs10("div", { className: "min-w-0", children: [
1240
+ /* @__PURE__ */ jsx12("p", { className: cn("truncate font-medium", classNames?.name), children: conversation.clientName ?? displayHandle }),
1241
+ /* @__PURE__ */ jsxs10("p", { className: cn("truncate text-xs text-gray-500", classNames?.meta), children: [
1242
+ flag ? `${flag} ` : "",
1243
+ displayHandle,
1244
+ /* @__PURE__ */ jsxs10("span", { className: "ml-2 inline-flex items-center gap-1", children: [
1245
+ /* @__PURE__ */ jsx12(ChannelIcon, { channel: conversation.channel }),
1246
+ /* @__PURE__ */ jsx12("span", { className: "cv-only-wide", children: capabilities.label })
1247
+ ] }),
1248
+ /* @__PURE__ */ jsx12("span", { className: "ml-2 cv-only-wide", children: isHuman ? labels.humanMode : labels.botMode }),
1249
+ /* @__PURE__ */ jsx12("span", { className: "ml-1 cv-only-narrow", title: isHuman ? labels.humanMode : labels.botMode, children: isHuman ? "\u{1F9D1}\u200D\u{1F4BC}" : "\u{1F916}" })
1250
+ ] })
1251
+ ] })
1252
+ ] }),
1253
+ /* @__PURE__ */ jsxs10("div", { className: cn("flex shrink-0 items-center gap-2", classNames?.actions), children: [
1254
+ /* @__PURE__ */ jsxs10("div", { className: cn("hidden items-center gap-2 lg:flex", classNames?.desktopActions), children: [
1255
+ utilities.map((utility) => /* @__PURE__ */ jsx12(
1256
+ "button",
1257
+ {
1258
+ type: "button",
1259
+ onClick: utility.run,
1260
+ disabled: busy,
1261
+ "aria-pressed": utility.active,
1262
+ title: utility.label,
1263
+ "aria-label": utility.label,
1264
+ className: `cv-header-icon ${utility.active ? "cv-header-icon--active" : ""}`,
1265
+ children: utility.icon
1266
+ },
1267
+ utility.key
1268
+ )),
1269
+ actions.map((action) => /* @__PURE__ */ jsx12(
1270
+ "button",
1271
+ {
1272
+ type: "button",
1273
+ onClick: action.run,
1274
+ disabled: busy,
1275
+ className: `cv-header-action ${action.className}`,
1276
+ children: action.label
1277
+ },
1278
+ action.key
1279
+ ))
1280
+ ] }),
1281
+ menuItems.length > 0 ? /* @__PURE__ */ jsxs10("div", { className: cn("cv-menu lg:hidden", classNames?.mobileMenu), children: [
1282
+ /* @__PURE__ */ jsx12(
1283
+ "button",
1284
+ {
1285
+ type: "button",
1286
+ onClick: () => setMenuOpen(!menuOpen),
1287
+ "aria-expanded": menuOpen,
1288
+ "aria-label": labels.moreActions,
1289
+ title: labels.moreActions,
1290
+ className: "cv-header-icon cv-touch",
1291
+ children: "\u22EE"
1292
+ }
1293
+ ),
1294
+ menuOpen ? /* @__PURE__ */ jsx12("div", { className: "cv-menu-panel", role: "menu", children: menuItems.map((item) => /* @__PURE__ */ jsx12(
1295
+ "button",
1296
+ {
1297
+ type: "button",
1298
+ role: "menuitem",
1299
+ onClick: () => {
1300
+ setMenuOpen(false);
1301
+ item.run();
1302
+ },
1303
+ disabled: busy,
1304
+ className: `cv-header-action cv-touch ${item.className}`,
1305
+ children: item.label
1306
+ },
1307
+ item.key
1308
+ )) }) : null
1309
+ ] }) : null
1310
+ ] })
1311
+ ]
1312
+ }
1313
+ );
1314
+ }
1315
+
1316
+ // src/ConversationContextPanel.tsx
1317
+ import { useState as useState7 } from "react";
1318
+
1319
+ // src/useIsNarrow.ts
1320
+ import { useEffect as useEffect4, useState as useState6 } from "react";
1321
+ var NARROW_MAX_WIDTH_PX = 1023;
1322
+ function useIsNarrow() {
1323
+ const [isNarrow, setIsNarrow] = useState6(
1324
+ () => typeof window !== "undefined" && window.matchMedia(`(max-width: ${NARROW_MAX_WIDTH_PX}px)`).matches
1325
+ );
1326
+ useEffect4(() => {
1327
+ const query = window.matchMedia(`(max-width: ${NARROW_MAX_WIDTH_PX}px)`);
1328
+ const handleChange = (event) => setIsNarrow(event.matches);
1329
+ query.addEventListener("change", handleChange);
1330
+ setIsNarrow(query.matches);
1331
+ return () => query.removeEventListener("change", handleChange);
1332
+ }, []);
1333
+ return isNarrow;
1334
+ }
1335
+
1336
+ // src/ConversationContextPanel.tsx
1337
+ import { jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
1338
+ var DEFAULT_CONVERSATION_CONTEXT_LABELS = {
1339
+ title: "\u{1F4CB} Suas Sele\xE7\xF5es",
1340
+ empty: "Nada coletado ainda nesta conversa.",
1341
+ collapse: "fechar",
1342
+ expand: "abrir"
1343
+ };
1344
+ function ConversationContextPanel({
1345
+ entries,
1346
+ defaultOpen,
1347
+ labels: labelsOverride,
1348
+ className,
1349
+ classNames
1350
+ }) {
1351
+ const labels = { ...DEFAULT_CONVERSATION_CONTEXT_LABELS, ...labelsOverride };
1352
+ const filled = entries.filter((entry) => Boolean(entry.value));
1353
+ const isNarrow = useIsNarrow();
1354
+ const [manualOpen, setManualOpen] = useState7(void 0);
1355
+ const open = manualOpen ?? defaultOpen ?? (!isNarrow && filled.length > 0);
1356
+ return /* @__PURE__ */ jsxs11("section", { className: cn("border-b", classNames?.root, className), children: [
1357
+ /* @__PURE__ */ jsxs11(
1358
+ "button",
1359
+ {
1360
+ type: "button",
1361
+ onClick: () => setManualOpen(!open),
1362
+ "aria-expanded": open,
1363
+ title: open ? labels.collapse : labels.expand,
1364
+ className: cn(
1365
+ "flex w-full cursor-pointer items-center gap-2 px-4 py-3 text-left text-sm font-medium transition-colors hover:bg-gray-50 dark:hover:bg-gray-800",
1366
+ classNames?.toggle
1367
+ ),
1368
+ children: [
1369
+ /* @__PURE__ */ jsx13("span", { "aria-hidden": true, className: "text-xs", children: open ? "\u25BE" : "\u25B8" }),
1370
+ /* @__PURE__ */ jsx13("span", { children: labels.title }),
1371
+ /* @__PURE__ */ jsxs11("span", { className: cn("rounded-full bg-gray-200 px-2 text-xs dark:bg-gray-700", classNames?.counter), children: [
1372
+ filled.length,
1373
+ "/",
1374
+ entries.length
1375
+ ] }),
1376
+ /* @__PURE__ */ jsx13("span", { "aria-hidden": true, className: "ml-auto text-xs text-gray-500", children: open ? labels.collapse : labels.expand })
1377
+ ]
1378
+ }
1379
+ ),
1380
+ open ? /* @__PURE__ */ jsx13("div", { className: cn("px-4 pb-3", classNames?.body), children: entries.length === 0 ? /* @__PURE__ */ jsx13("p", { className: "text-xs text-gray-500", children: labels.empty }) : /* @__PURE__ */ jsx13("dl", { className: "grid grid-cols-2 gap-2 text-xs", children: entries.map((entry) => /* @__PURE__ */ jsxs11("div", { className: "min-w-0", children: [
1381
+ /* @__PURE__ */ jsxs11("dt", { className: "truncate text-gray-500", children: [
1382
+ entry.icon ? `${entry.icon} ` : "",
1383
+ entry.label
1384
+ ] }),
1385
+ /* @__PURE__ */ jsx13("dd", { className: "truncate font-medium", children: entry.value ? /* @__PURE__ */ jsxs11("span", { className: "text-green-700 dark:text-green-400", children: [
1386
+ "\u2713 ",
1387
+ entry.value
1388
+ ] }) : "\u2014" })
1389
+ ] }, entry.key)) }) }) : null
1390
+ ] });
1391
+ }
1241
1392
 
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
1393
+ // src/WindowExpiredNotice.tsx
1394
+ import { jsx as jsx14, jsxs as jsxs12 } from "react/jsx-runtime";
1395
+ var DEFAULT_WINDOW_EXPIRED_LABELS = {
1396
+ title: "\u23F0 Janela de 24h expirada \u2014 o WhatsApp n\xE3o aceita mensagens livres.",
1397
+ description: "Envie uma mensagem de template para reabrir a conversa com o cliente.",
1398
+ sendTemplate: "\u{1F4E8} Enviar Template (HSM)"
1399
+ };
1400
+ function WindowExpiredNotice({
1401
+ onSendTemplate,
1402
+ disabled = false,
1403
+ labels: labelsOverride,
1404
+ className
1250
1405
  }) {
1251
- return /* @__PURE__ */ jsx20(ConversationsContext.Provider, { value: { api, sse }, children });
1406
+ const labels = { ...DEFAULT_WINDOW_EXPIRED_LABELS, ...labelsOverride };
1407
+ return /* @__PURE__ */ jsxs12("div", { role: "status", className: cn("border-t bg-yellow-50 px-4 py-3 text-sm dark:bg-yellow-950", className), children: [
1408
+ /* @__PURE__ */ jsx14("p", { className: "font-medium text-yellow-900 dark:text-yellow-200", children: labels.title }),
1409
+ /* @__PURE__ */ jsx14("p", { className: "text-yellow-800 dark:text-yellow-300", children: labels.description }),
1410
+ onSendTemplate ? /* @__PURE__ */ jsx14("button", { type: "button", onClick: onSendTemplate, disabled, className: "cv-header-action mt-2", children: labels.sendTemplate }) : null
1411
+ ] });
1412
+ }
1413
+ function isWindowBlocking(window2) {
1414
+ return window2 === CONVERSATION_WINDOW.EXPIRED;
1415
+ }
1416
+
1417
+ // src/conversationTranscript.ts
1418
+ var SENDER_LABEL = {
1419
+ customer: "Cliente",
1420
+ bot: "Bot",
1421
+ agent: "Atendente"
1422
+ };
1423
+ function buildTranscriptText(params) {
1424
+ const header = [
1425
+ `Conversa: ${params.clientName ?? params.whatsappNumber}`,
1426
+ `N\xFAmero: ${params.whatsappNumber}`,
1427
+ `Mensagens: ${params.messages.length}`,
1428
+ ""
1429
+ ];
1430
+ const lines = params.messages.map((message) => {
1431
+ const stamp = new Date(message.timestamp).toLocaleString("pt-BR");
1432
+ const author = message.agentName ?? SENDER_LABEL[message.sender];
1433
+ const body = message.content ?? message.caption ?? `<${message.type}>`;
1434
+ return `[${stamp}] ${author}: ${body}`;
1435
+ });
1436
+ return [...header, ...lines].join("\n");
1252
1437
  }
1253
- function useConversations() {
1254
- return useContext3(ConversationsContext);
1438
+ function buildTranscriptFilename(whatsappNumber, generatedAt) {
1439
+ const stamp = generatedAt.toISOString().slice(0, 10);
1440
+ return `conversa-${whatsappNumber}-${stamp}.txt`;
1441
+ }
1442
+ function downloadTextFile(filename, content) {
1443
+ const blob = new Blob([content], { type: "text/plain;charset=utf-8" });
1444
+ const url = URL.createObjectURL(blob);
1445
+ const anchor = document.createElement("a");
1446
+ anchor.href = url;
1447
+ anchor.download = filename;
1448
+ anchor.click();
1449
+ URL.revokeObjectURL(url);
1255
1450
  }
1256
1451
 
1257
1452
  // 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;
1453
+ import { useState as useState8, useEffect as useEffect5, useRef as useRef4, useCallback as useCallback4 } from "react";
1454
+ var DEFAULT_POLL_INTERVAL_MS = 1e4;
1455
+ var DEFAULT_LIMIT = 50;
1456
+ var DEFAULT_LABELS = {
1457
+ title: (conversation) => conversation.clientName ?? conversation.whatsappNumber,
1458
+ body: (conversation) => conversation.lastContent ?? "Nova mensagem"
1459
+ };
1460
+ function useWaitingNotifications(params) {
1461
+ const conversationsContext = useConversations();
1462
+ const [conversations, setConversations] = useState8([]);
1463
+ const previousUnreadMap = useRef4(/* @__PURE__ */ new Map());
1464
+ const notifiedIds = useRef4(/* @__PURE__ */ new Set());
1465
+ const isPollingRef = useRef4(false);
1466
+ const isEnabled = params?.enabled ?? true;
1467
+ const intervalMs = params?.intervalMs ?? DEFAULT_POLL_INTERVAL_MS;
1468
+ const icon = params?.icon;
1469
+ const listParams = params?.params;
1470
+ const labelTitle = params?.labels?.title ?? DEFAULT_LABELS.title;
1471
+ const labelBody = params?.labels?.body ?? DEFAULT_LABELS.body;
1472
+ const refresh = useCallback4(async () => {
1473
+ if (!conversationsContext || isPollingRef.current) return;
1474
+ isPollingRef.current = true;
1265
1475
  try {
1266
- const result = await conversationsCtx.api.fetchConversations({ limit: 50 });
1476
+ const result = conversationsOf(
1477
+ await conversationsContext.api.fetchConversations({ limit: DEFAULT_LIMIT, ...listParams })
1478
+ );
1267
1479
  setConversations(result);
1268
1480
  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
- }
1481
+ for (const conversation of result) currentMap.set(conversation.id, conversation.unread);
1482
+ for (const conversation of result) {
1483
+ if (conversation.unread <= 0) continue;
1484
+ const previousUnread = previousUnreadMap.current.get(conversation.id) ?? 0;
1485
+ if (conversation.unread <= previousUnread || notifiedIds.current.has(conversation.id)) continue;
1486
+ notifiedIds.current.add(conversation.id);
1487
+ if (typeof window !== "undefined" && "Notification" in window && Notification.permission === "granted") {
1488
+ new Notification(labelTitle(conversation), { body: labelBody(conversation), ...icon ? { icon } : {} });
1282
1489
  }
1283
1490
  }
1284
1491
  previousUnreadMap.current = currentMap;
1285
1492
  } catch {
1493
+ } finally {
1494
+ isPollingRef.current = false;
1286
1495
  }
1287
- }, [conversationsCtx]);
1288
- useEffect4(() => {
1496
+ }, [conversationsContext, listParams, icon, labelTitle, labelBody]);
1497
+ useEffect5(() => {
1498
+ if (!isEnabled) return;
1289
1499
  if (typeof window !== "undefined" && "Notification" in window && Notification.permission === "default") {
1290
1500
  Notification.requestPermission();
1291
1501
  }
1292
- pollConversations();
1293
- const interval = setInterval(pollConversations, 1e4);
1502
+ void refresh();
1503
+ const interval = setInterval(() => void refresh(), intervalMs);
1294
1504
  return () => clearInterval(interval);
1295
- }, [pollConversations]);
1296
- const unreadCount = conversations.reduce((sum, conv) => sum + conv.unread, 0);
1297
- return { unreadCount, conversations };
1505
+ }, [refresh, isEnabled, intervalMs]);
1506
+ const unreadCount = conversations.reduce((sum, conversation) => sum + conversation.unread, 0);
1507
+ return { unreadCount, conversations, refresh };
1298
1508
  }
1299
1509
 
1300
1510
  // src/settings/WhatsAppTemplateSettingsForm.tsx
1301
1511
  import { Plus, RefreshCw, Save, Trash2 } from "lucide-react";
1302
- import { jsx as jsx21, jsxs as jsxs14 } from "react/jsx-runtime";
1303
- var DEFAULT_LABELS = {
1512
+ import { jsx as jsx15, jsxs as jsxs13 } from "react/jsx-runtime";
1513
+ var DEFAULT_LABELS2 = {
1304
1514
  sectionTitle: "WhatsApp",
1305
1515
  sectionDescription: "Template usado para reabrir a janela de 24h com o cliente.",
1306
1516
  templateLabel: "Template aprovado",
@@ -1334,7 +1544,7 @@ function WhatsAppTemplateSettingsForm({
1334
1544
  onSave,
1335
1545
  labels: labelsOverride
1336
1546
  }) {
1337
- const labels = { ...DEFAULT_LABELS, ...labelsOverride };
1547
+ const labels = { ...DEFAULT_LABELS2, ...labelsOverride };
1338
1548
  const selected = templates.find((template) => template.name === selectedTemplateName);
1339
1549
  function handleSelect(name) {
1340
1550
  const template = templates.find((t) => t.name === name);
@@ -1358,16 +1568,16 @@ function WhatsAppTemplateSettingsForm({
1358
1568
  }
1359
1569
  onVariablesChange([...variables, token]);
1360
1570
  }
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 })
1571
+ return /* @__PURE__ */ jsxs13("section", { className: "bg-white dark:bg-gray-800 rounded-xl border border-gray-200 dark:border-gray-700 overflow-hidden", children: [
1572
+ /* @__PURE__ */ jsxs13("div", { className: "px-6 py-4 border-b border-gray-100 dark:border-gray-700", children: [
1573
+ /* @__PURE__ */ jsx15("h2", { className: "text-sm font-semibold text-gray-900 dark:text-gray-100", children: labels.sectionTitle }),
1574
+ /* @__PURE__ */ jsx15("p", { className: "text-xs text-gray-500 dark:text-gray-400 mt-1", children: labels.sectionDescription })
1365
1575
  ] }),
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(
1576
+ /* @__PURE__ */ jsxs13("form", { onSubmit: onSave, className: "p-6 space-y-4", children: [
1577
+ /* @__PURE__ */ jsxs13("div", { children: [
1578
+ /* @__PURE__ */ jsxs13("div", { className: "flex items-center justify-between mb-1.5", children: [
1579
+ /* @__PURE__ */ jsx15("label", { className: "text-sm font-medium text-gray-700 dark:text-gray-300", children: labels.templateLabel }),
1580
+ /* @__PURE__ */ jsxs13(
1371
1581
  "button",
1372
1582
  {
1373
1583
  type: "button",
@@ -1375,31 +1585,31 @@ function WhatsAppTemplateSettingsForm({
1375
1585
  disabled: loadingTemplates,
1376
1586
  className: "flex items-center gap-1 text-xs text-blue-600 dark:text-blue-400 hover:underline disabled:opacity-50",
1377
1587
  children: [
1378
- /* @__PURE__ */ jsx21(RefreshCw, { size: 11, className: loadingTemplates ? "animate-spin" : "" }),
1588
+ /* @__PURE__ */ jsx15(RefreshCw, { size: 11, className: loadingTemplates ? "animate-spin" : "" }),
1379
1589
  labels.refreshList
1380
1590
  ]
1381
1591
  }
1382
1592
  )
1383
1593
  ] }),
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: [
1594
+ templatesError && /* @__PURE__ */ jsxs13("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
1595
  labels.errorLoading,
1386
- /* @__PURE__ */ jsx21("br", {}),
1387
- /* @__PURE__ */ jsxs14("span", { className: "text-gray-500 dark:text-gray-400 mt-0.5 block", children: [
1596
+ /* @__PURE__ */ jsx15("br", {}),
1597
+ /* @__PURE__ */ jsxs13("span", { className: "text-gray-500 dark:text-gray-400 mt-0.5 block", children: [
1388
1598
  labels.currentNameSaved,
1389
1599
  " ",
1390
- /* @__PURE__ */ jsx21("strong", { children: selectedTemplateName || "\u2014" })
1600
+ /* @__PURE__ */ jsx15("strong", { children: selectedTemplateName || "\u2014" })
1391
1601
  ] })
1392
1602
  ] }),
1393
- loadingTemplates && /* @__PURE__ */ jsx21("div", { className: "h-10 rounded-xl bg-gray-100 dark:bg-gray-700 animate-pulse" }),
1394
- !loadingTemplates && !templatesError && /* @__PURE__ */ jsxs14(
1603
+ loadingTemplates && /* @__PURE__ */ jsx15("div", { className: "h-10 rounded-xl bg-gray-100 dark:bg-gray-700 animate-pulse" }),
1604
+ !loadingTemplates && !templatesError && /* @__PURE__ */ jsxs13(
1395
1605
  "select",
1396
1606
  {
1397
1607
  value: selectedTemplateName,
1398
1608
  onChange: (e) => handleSelect(e.target.value),
1399
1609
  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
1610
  children: [
1401
- /* @__PURE__ */ jsx21("option", { value: "", children: labels.selectPlaceholder }),
1402
- templates.map((template) => /* @__PURE__ */ jsxs14("option", { value: template.name, disabled: template.status !== "APPROVED", children: [
1611
+ /* @__PURE__ */ jsx15("option", { value: "", children: labels.selectPlaceholder }),
1612
+ templates.map((template) => /* @__PURE__ */ jsxs13("option", { value: template.name, disabled: template.status !== "APPROVED", children: [
1403
1613
  template.displayName,
1404
1614
  " \xB7 ",
1405
1615
  template.shortId,
@@ -1411,27 +1621,27 @@ function WhatsAppTemplateSettingsForm({
1411
1621
  ]
1412
1622
  }
1413
1623
  ),
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 })
1624
+ selected?.bodyText && /* @__PURE__ */ jsx15("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
1625
  ] }),
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(
1626
+ /* @__PURE__ */ jsxs13("div", { children: [
1627
+ /* @__PURE__ */ jsxs13("div", { className: "flex items-center justify-between mb-1.5", children: [
1628
+ /* @__PURE__ */ jsx15("label", { className: "text-sm font-medium text-gray-700 dark:text-gray-300", children: labels.variablesLabel }),
1629
+ /* @__PURE__ */ jsxs13(
1420
1630
  "button",
1421
1631
  {
1422
1632
  type: "button",
1423
1633
  onClick: () => onVariablesChange([...variables, ""]),
1424
1634
  className: "flex items-center gap-1 text-xs text-blue-600 dark:text-blue-400 hover:underline",
1425
1635
  children: [
1426
- /* @__PURE__ */ jsx21(Plus, { size: 12 }),
1636
+ /* @__PURE__ */ jsx15(Plus, { size: 12 }),
1427
1637
  labels.addVariable
1428
1638
  ]
1429
1639
  }
1430
1640
  )
1431
1641
  ] }),
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(
1642
+ availableVariables.length > 0 && /* @__PURE__ */ jsxs13("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: [
1643
+ /* @__PURE__ */ jsx15("p", { className: "text-xs font-medium text-gray-600 dark:text-gray-300 mb-1.5", children: labels.variablesAvailableTitle }),
1644
+ /* @__PURE__ */ jsx15("div", { className: "flex flex-wrap gap-1.5", children: availableVariables.map((available) => /* @__PURE__ */ jsxs13(
1435
1645
  "button",
1436
1646
  {
1437
1647
  type: "button",
@@ -1439,8 +1649,8 @@ function WhatsAppTemplateSettingsForm({
1439
1649
  title: `Inserir ${available.token}`,
1440
1650
  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
1651
  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: [
1652
+ /* @__PURE__ */ jsx15("code", { className: "text-blue-600 dark:text-blue-300", children: available.token }),
1653
+ /* @__PURE__ */ jsxs13("span", { className: "text-gray-400 dark:text-gray-500", children: [
1444
1654
  "\xB7 ",
1445
1655
  available.label
1446
1656
  ] })
@@ -1448,13 +1658,13 @@ function WhatsAppTemplateSettingsForm({
1448
1658
  },
1449
1659
  available.token
1450
1660
  )) }),
1451
- /* @__PURE__ */ jsx21("p", { className: "mt-1.5 text-[11px] text-gray-400 dark:text-gray-500", children: labels.variablesAvailableHint })
1661
+ /* @__PURE__ */ jsx15("p", { className: "mt-1.5 text-[11px] text-gray-400 dark:text-gray-500", children: labels.variablesAvailableHint })
1452
1662
  ] }),
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(
1663
+ /* @__PURE__ */ jsx15("p", { className: "text-xs text-gray-400 dark:text-gray-500 mb-2", children: labels.variablesMappingHint }),
1664
+ variables.length === 0 && /* @__PURE__ */ jsx15("p", { className: "text-xs text-gray-400 dark:text-gray-500 italic", children: labels.noVariables }),
1665
+ /* @__PURE__ */ jsx15("div", { className: "space-y-2", children: variables.map((variable, index) => /* @__PURE__ */ jsxs13("div", { className: "flex items-center gap-2", children: [
1666
+ /* @__PURE__ */ jsx15("span", { className: "text-xs text-gray-400 dark:text-gray-500 w-6 text-right flex-shrink-0", children: `{{${index + 1}}}` }),
1667
+ /* @__PURE__ */ jsx15(
1458
1668
  "input",
1459
1669
  {
1460
1670
  type: "text",
@@ -1464,27 +1674,27 @@ function WhatsAppTemplateSettingsForm({
1464
1674
  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
1675
  }
1466
1676
  ),
1467
- /* @__PURE__ */ jsx21(
1677
+ /* @__PURE__ */ jsx15(
1468
1678
  "button",
1469
1679
  {
1470
1680
  type: "button",
1471
1681
  onClick: () => removeVariable(index),
1472
1682
  className: "text-gray-400 hover:text-red-500 dark:hover:text-red-400 transition-colors flex-shrink-0",
1473
1683
  title: labels.removeVariable,
1474
- children: /* @__PURE__ */ jsx21(Trash2, { size: 14 })
1684
+ children: /* @__PURE__ */ jsx15(Trash2, { size: 14 })
1475
1685
  }
1476
1686
  )
1477
1687
  ] }, index)) })
1478
1688
  ] }),
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(
1689
+ saveSuccess && /* @__PURE__ */ jsx15("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 }),
1690
+ /* @__PURE__ */ jsx15("div", { className: "flex justify-end", children: /* @__PURE__ */ jsxs13(
1481
1691
  "button",
1482
1692
  {
1483
1693
  type: "submit",
1484
1694
  disabled: saving || !selectedTemplateName.trim(),
1485
1695
  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
1696
  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 }),
1697
+ saving ? /* @__PURE__ */ jsx15("span", { className: "w-4 h-4 border-2 border-white/40 border-t-white rounded-full animate-spin" }) : /* @__PURE__ */ jsx15(Save, { size: 14 }),
1488
1698
  saving ? labels.saving : labels.save
1489
1699
  ]
1490
1700
  }
@@ -1494,13 +1704,14 @@ function WhatsAppTemplateSettingsForm({
1494
1704
  }
1495
1705
 
1496
1706
  // src/settings/WhatsAppCreateTemplateForm.tsx
1497
- import { SendHorizonal } from "lucide-react";
1498
- import { jsx as jsx22, jsxs as jsxs15 } from "react/jsx-runtime";
1499
- var DEFAULT_LABELS2 = {
1707
+ import { SendHorizonal as SendHorizonal2 } from "lucide-react";
1708
+ import { jsx as jsx16, jsxs as jsxs14 } from "react/jsx-runtime";
1709
+ var DEFAULT_LABELS3 = {
1500
1710
  sectionTitle: "Criar novo template",
1501
1711
  sectionDescription: "Envia um template para aprova\xE7\xE3o da Meta.",
1502
1712
  nameLabel: "Nome do template",
1503
1713
  nameHint: "Somente letras min\xFAsculas, n\xFAmeros e underscore.",
1714
+ namePlaceholder: "reengajamento_cliente",
1504
1715
  categoryLabel: "Categoria",
1505
1716
  languageLabel: "Idioma",
1506
1717
  headerLabel: "Cabe\xE7alho",
@@ -1546,82 +1757,82 @@ function WhatsAppCreateTemplateForm({
1546
1757
  labels: labelsOverride,
1547
1758
  variableExamples = DEFAULT_VARIABLE_EXAMPLES
1548
1759
  }) {
1549
- const labels = { ...DEFAULT_LABELS2, ...labelsOverride };
1760
+ const labels = { ...DEFAULT_LABELS3, ...labelsOverride };
1550
1761
  const detectedVariables = detectVariables(value.bodyText);
1551
1762
  function set(key, next) {
1552
1763
  onChange({ ...value, [key]: next });
1553
1764
  }
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 })
1765
+ return /* @__PURE__ */ jsxs14("section", { className: "bg-white dark:bg-gray-800 rounded-xl border border-gray-200 dark:border-gray-700 overflow-hidden", children: [
1766
+ /* @__PURE__ */ jsxs14("div", { className: "px-6 py-4 border-b border-gray-100 dark:border-gray-700", children: [
1767
+ /* @__PURE__ */ jsx16("h2", { className: "text-sm font-semibold text-gray-900 dark:text-gray-100", children: labels.sectionTitle }),
1768
+ /* @__PURE__ */ jsx16("p", { className: "text-xs text-gray-500 dark:text-gray-400 mt-1", children: labels.sectionDescription })
1558
1769
  ] }),
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(
1770
+ /* @__PURE__ */ jsxs14("form", { onSubmit, className: "p-6 space-y-4", children: [
1771
+ /* @__PURE__ */ jsxs14("div", { children: [
1772
+ /* @__PURE__ */ jsx16("label", { className: "text-sm font-medium text-gray-700 dark:text-gray-300", children: labels.nameLabel }),
1773
+ /* @__PURE__ */ jsx16("p", { className: "text-xs text-gray-400 mb-1.5", children: labels.nameHint }),
1774
+ /* @__PURE__ */ jsx16(
1564
1775
  "input",
1565
1776
  {
1566
1777
  type: "text",
1567
1778
  value: value.name,
1568
1779
  onChange: (e) => set("name", e.target.value.toLowerCase().replace(/[^a-z0-9_]/g, "_")),
1569
- placeholder: "reengajamento_cliente",
1780
+ placeholder: labels.namePlaceholder,
1570
1781
  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
1782
  required: true
1572
1783
  }
1573
1784
  )
1574
1785
  ] }),
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(
1786
+ /* @__PURE__ */ jsxs14("div", { className: "grid grid-cols-2 gap-3", children: [
1787
+ /* @__PURE__ */ jsxs14("div", { children: [
1788
+ /* @__PURE__ */ jsx16("label", { className: "text-sm font-medium text-gray-700 dark:text-gray-300", children: labels.categoryLabel }),
1789
+ /* @__PURE__ */ jsxs14(
1579
1790
  "select",
1580
1791
  {
1581
1792
  value: value.category,
1582
1793
  onChange: (e) => set("category", e.target.value),
1583
1794
  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
1795
  children: [
1585
- /* @__PURE__ */ jsx22("option", { value: "UTILITY", children: "UTILITY (Transacional)" }),
1586
- /* @__PURE__ */ jsx22("option", { value: "MARKETING", children: "MARKETING (Promocional)" })
1796
+ /* @__PURE__ */ jsx16("option", { value: "UTILITY", children: "UTILITY (Transacional)" }),
1797
+ /* @__PURE__ */ jsx16("option", { value: "MARKETING", children: "MARKETING (Promocional)" })
1587
1798
  ]
1588
1799
  }
1589
1800
  )
1590
1801
  ] }),
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(
1802
+ /* @__PURE__ */ jsxs14("div", { children: [
1803
+ /* @__PURE__ */ jsx16("label", { className: "text-sm font-medium text-gray-700 dark:text-gray-300", children: labels.languageLabel }),
1804
+ /* @__PURE__ */ jsxs14(
1594
1805
  "select",
1595
1806
  {
1596
1807
  value: value.language,
1597
1808
  onChange: (e) => set("language", e.target.value),
1598
1809
  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
1810
  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" })
1811
+ /* @__PURE__ */ jsx16("option", { value: "pt_BR", children: "Portugu\xEAs (Brasil)" }),
1812
+ /* @__PURE__ */ jsx16("option", { value: "en_US", children: "Ingl\xEAs (EUA)" }),
1813
+ /* @__PURE__ */ jsx16("option", { value: "es_ES", children: "Espanhol" })
1603
1814
  ]
1604
1815
  }
1605
1816
  )
1606
1817
  ] })
1607
1818
  ] }),
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(
1819
+ /* @__PURE__ */ jsxs14("div", { children: [
1820
+ /* @__PURE__ */ jsx16("label", { className: "text-sm font-medium text-gray-700 dark:text-gray-300", children: labels.headerLabel }),
1821
+ /* @__PURE__ */ jsx16("p", { className: "text-xs text-gray-400 mb-1.5", children: labels.headerHint }),
1822
+ /* @__PURE__ */ jsxs14("div", { className: "flex gap-2", children: [
1823
+ /* @__PURE__ */ jsxs14(
1613
1824
  "select",
1614
1825
  {
1615
1826
  value: value.headerType,
1616
1827
  onChange: (e) => set("headerType", e.target.value),
1617
1828
  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
1829
  children: [
1619
- /* @__PURE__ */ jsx22("option", { value: "NONE", children: labels.headerNone }),
1620
- /* @__PURE__ */ jsx22("option", { value: "TEXT", children: labels.headerText })
1830
+ /* @__PURE__ */ jsx16("option", { value: "NONE", children: labels.headerNone }),
1831
+ /* @__PURE__ */ jsx16("option", { value: "TEXT", children: labels.headerText })
1621
1832
  ]
1622
1833
  }
1623
1834
  ),
1624
- value.headerType === "TEXT" && /* @__PURE__ */ jsx22(
1835
+ value.headerType === "TEXT" && /* @__PURE__ */ jsx16(
1625
1836
  "input",
1626
1837
  {
1627
1838
  type: "text",
@@ -1634,10 +1845,10 @@ function WhatsAppCreateTemplateForm({
1634
1845
  )
1635
1846
  ] })
1636
1847
  ] }),
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(
1848
+ /* @__PURE__ */ jsxs14("div", { children: [
1849
+ /* @__PURE__ */ jsx16("label", { className: "text-sm font-medium text-gray-700 dark:text-gray-300", children: labels.bodyLabel }),
1850
+ /* @__PURE__ */ jsx16("p", { className: "text-xs text-gray-400 mb-1.5", children: labels.bodyHint }),
1851
+ /* @__PURE__ */ jsx16(
1641
1852
  "textarea",
1642
1853
  {
1643
1854
  value: value.bodyText,
@@ -1648,26 +1859,26 @@ function WhatsAppCreateTemplateForm({
1648
1859
  required: true
1649
1860
  }
1650
1861
  ),
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: [
1862
+ detectedVariables.length > 0 && /* @__PURE__ */ jsxs14("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: [
1863
+ /* @__PURE__ */ jsx16("p", { className: "font-medium text-gray-500 dark:text-gray-400 mb-1.5 uppercase tracking-wide text-xs", children: labels.detectedVariables }),
1864
+ /* @__PURE__ */ jsx16("div", { className: "flex flex-wrap gap-2", children: detectedVariables.map((num) => /* @__PURE__ */ jsxs14("span", { className: "inline-flex items-center gap-1 text-xs", children: [
1865
+ /* @__PURE__ */ jsx16("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}}}` }),
1866
+ /* @__PURE__ */ jsxs14("span", { className: "text-gray-400 dark:text-gray-500", children: [
1656
1867
  "= ",
1657
1868
  variableExamples[Number(num) - 1] ?? labels.variableLabel(Number(num))
1658
1869
  ] })
1659
1870
  ] }, num)) }),
1660
- /* @__PURE__ */ jsxs15("p", { className: "text-gray-400 dark:text-gray-500 mt-1.5 text-xs", children: [
1871
+ /* @__PURE__ */ jsxs14("p", { className: "text-gray-400 dark:text-gray-500 mt-1.5 text-xs", children: [
1661
1872
  labels.configureHintPrefix,
1662
- /* @__PURE__ */ jsx22("strong", { children: labels.configureHintLink }),
1873
+ /* @__PURE__ */ jsx16("strong", { children: labels.configureHintLink }),
1663
1874
  labels.configureHintSuffix
1664
1875
  ] })
1665
1876
  ] })
1666
1877
  ] }),
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(
1878
+ /* @__PURE__ */ jsxs14("div", { children: [
1879
+ /* @__PURE__ */ jsx16("label", { className: "text-sm font-medium text-gray-700 dark:text-gray-300", children: labels.footerLabel }),
1880
+ /* @__PURE__ */ jsx16("p", { className: "text-xs text-gray-400 mb-1.5", children: labels.footerHint }),
1881
+ /* @__PURE__ */ jsx16(
1671
1882
  "input",
1672
1883
  {
1673
1884
  type: "text",
@@ -1679,48 +1890,48 @@ function WhatsAppCreateTemplateForm({
1679
1890
  }
1680
1891
  )
1681
1892
  ] }),
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 })
1893
+ /* @__PURE__ */ jsxs14("div", { children: [
1894
+ /* @__PURE__ */ jsx16("label", { className: "text-sm font-medium text-gray-700 dark:text-gray-300 mb-2 block", children: labels.previewLabel }),
1895
+ /* @__PURE__ */ jsxs14("div", { className: "rounded-2xl bg-[#e5ddd5] dark:bg-[#1a1a2e] border border-gray-300 dark:border-gray-600 overflow-hidden shadow-inner", children: [
1896
+ /* @__PURE__ */ jsxs14("div", { className: "bg-[#075e54] px-4 py-2.5 flex items-center gap-2", children: [
1897
+ /* @__PURE__ */ jsx16("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() }),
1898
+ /* @__PURE__ */ jsxs14("div", { children: [
1899
+ /* @__PURE__ */ jsx16("p", { className: "text-white text-xs font-semibold leading-tight", children: previewCompanyName }),
1900
+ /* @__PURE__ */ jsx16("p", { className: "text-white/60 text-xs", children: labels.previewOnline })
1690
1901
  ] })
1691
1902
  ] }),
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" })
1903
+ /* @__PURE__ */ jsxs14("div", { className: "px-3 py-4 space-y-3", style: { minHeight: "120px" }, children: [
1904
+ /* @__PURE__ */ jsx16("div", { className: "flex justify-start", style: { maxWidth: "85%" }, children: /* @__PURE__ */ jsxs14("div", { className: "bg-white dark:bg-gray-700 rounded-lg rounded-tl-none px-3 py-2 shadow-sm", children: [
1905
+ /* @__PURE__ */ jsx16("p", { className: "text-gray-500 dark:text-gray-400 leading-tight text-sm", children: labels.previewDescription }),
1906
+ /* @__PURE__ */ jsx16("p", { className: "text-gray-400 dark:text-gray-500 mt-1 text-right text-[9px]", children: "12:00" })
1696
1907
  ] }) }),
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" })
1908
+ /* @__PURE__ */ jsx16("div", { className: "flex justify-end ml-auto", style: { maxWidth: "85%" }, children: /* @__PURE__ */ jsxs14("div", { className: "bg-[#dcf8c6] dark:bg-[#1b5e20] rounded-lg rounded-tr-none px-3 py-2 shadow-sm", children: [
1909
+ value.headerType === "TEXT" && value.headerText && /* @__PURE__ */ jsx16("p", { className: "text-xs font-bold text-gray-700 dark:text-gray-200 mb-1", children: fillPreviewTokens(value.headerText) || labels.previewHeaderFallback }),
1910
+ /* @__PURE__ */ jsx16("p", { className: "text-sm text-gray-800 dark:text-gray-100 leading-relaxed", children: value.bodyText ? fillPreviewTokens(value.bodyText) : /* @__PURE__ */ jsx16("span", { className: "text-gray-400 italic", children: labels.previewBodyPlaceholder }) }),
1911
+ value.footerText && /* @__PURE__ */ jsx16("p", { className: "text-gray-400 dark:text-gray-400 mt-1 text-xs", children: value.footerText }),
1912
+ /* @__PURE__ */ jsx16("p", { className: "text-gray-400 dark:text-gray-400 mt-1 text-right text-[9px]", children: "12:01 \u2713" })
1702
1913
  ] }) })
1703
1914
  ] })
1704
1915
  ] })
1705
1916
  ] }),
1706
- result && /* @__PURE__ */ jsxs15(
1917
+ result && /* @__PURE__ */ jsxs14(
1707
1918
  "div",
1708
1919
  {
1709
1920
  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
1921
  children: [
1711
1922
  result.message,
1712
- result.ok && result.status && /* @__PURE__ */ jsx22("p", { className: "text-xs mt-1 opacity-75", children: labels.successStatus(result.status) })
1923
+ result.ok && result.status && /* @__PURE__ */ jsx16("p", { className: "text-xs mt-1 opacity-75", children: labels.successStatus(result.status) })
1713
1924
  ]
1714
1925
  }
1715
1926
  ),
1716
- /* @__PURE__ */ jsx22("div", { className: "flex justify-end", children: /* @__PURE__ */ jsxs15(
1927
+ /* @__PURE__ */ jsx16("div", { className: "flex justify-end", children: /* @__PURE__ */ jsxs14(
1717
1928
  "button",
1718
1929
  {
1719
1930
  type: "submit",
1720
1931
  disabled: submitting || !value.name || !value.bodyText,
1721
1932
  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
1933
  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 }),
1934
+ submitting ? /* @__PURE__ */ jsx16("span", { className: "w-4 h-4 border-2 border-white/40 border-t-white rounded-full animate-spin" }) : /* @__PURE__ */ jsx16(SendHorizonal2, { size: 14 }),
1724
1935
  submitting ? labels.sending : labels.sendForApproval
1725
1936
  ]
1726
1937
  }
@@ -1731,8 +1942,8 @@ function WhatsAppCreateTemplateForm({
1731
1942
 
1732
1943
  // src/settings/WelcomeFarewellForm.tsx
1733
1944
  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 = {
1945
+ import { jsx as jsx17, jsxs as jsxs15 } from "react/jsx-runtime";
1946
+ var DEFAULT_LABELS4 = {
1736
1947
  sectionTitle: "Boas-vindas e encerramento",
1737
1948
  welcomeMessage: "Mensagem de boas-vindas",
1738
1949
  welcomeMessagePlaceholder: "Ol\xE1! Sou o assistente virtual da {empresa}...",
@@ -1758,16 +1969,16 @@ function WelcomeFarewellForm({
1758
1969
  farewellPlaceholders = DEFAULT_FAREWELL_PLACEHOLDERS,
1759
1970
  labels: labelsOverride
1760
1971
  }) {
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" }),
1972
+ const labels = { ...DEFAULT_LABELS4, ...labelsOverride };
1973
+ return /* @__PURE__ */ jsxs15("section", { className: "bg-white dark:bg-gray-800 rounded-xl border border-gray-200 dark:border-gray-700 overflow-hidden", children: [
1974
+ /* @__PURE__ */ jsx17("div", { className: "px-6 py-4 border-b border-gray-100 dark:border-gray-700", children: /* @__PURE__ */ jsx17("h2", { className: "text-sm font-semibold text-gray-900 dark:text-gray-100", children: labels.sectionTitle }) }),
1975
+ /* @__PURE__ */ jsxs15("form", { onSubmit: onSave, className: "p-6 space-y-5", children: [
1976
+ /* @__PURE__ */ jsxs15("div", { children: [
1977
+ /* @__PURE__ */ jsxs15("label", { className: "block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1.5 flex items-center gap-1.5", children: [
1978
+ /* @__PURE__ */ jsx17(Mail, { size: 13, className: "text-blue-600 dark:text-blue-400" }),
1768
1979
  labels.welcomeMessage
1769
1980
  ] }),
1770
- /* @__PURE__ */ jsx23(
1981
+ /* @__PURE__ */ jsx17(
1771
1982
  WhatsAppMessageEditor,
1772
1983
  {
1773
1984
  value: welcomeMessage,
@@ -1776,14 +1987,14 @@ function WelcomeFarewellForm({
1776
1987
  placeholders: welcomePlaceholders
1777
1988
  }
1778
1989
  ),
1779
- /* @__PURE__ */ jsx23("p", { className: "mt-1.5 text-xs text-gray-500 dark:text-gray-400", children: labels.welcomeMessageHint })
1990
+ /* @__PURE__ */ jsx17("p", { className: "mt-1.5 text-xs text-gray-500 dark:text-gray-400", children: labels.welcomeMessageHint })
1780
1991
  ] }),
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" }),
1992
+ /* @__PURE__ */ jsxs15("div", { children: [
1993
+ /* @__PURE__ */ jsxs15("label", { className: "block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1.5 flex items-center gap-1.5", children: [
1994
+ /* @__PURE__ */ jsx17(LogOut, { size: 13, className: "text-blue-600 dark:text-blue-400" }),
1784
1995
  labels.farewellMessage
1785
1996
  ] }),
1786
- /* @__PURE__ */ jsx23(
1997
+ /* @__PURE__ */ jsx17(
1787
1998
  WhatsAppMessageEditor,
1788
1999
  {
1789
2000
  value: farewellMessage,
@@ -1792,17 +2003,17 @@ function WelcomeFarewellForm({
1792
2003
  placeholders: farewellPlaceholders
1793
2004
  }
1794
2005
  ),
1795
- /* @__PURE__ */ jsx23("p", { className: "mt-1.5 text-xs text-gray-500 dark:text-gray-400", children: labels.farewellMessageHint })
2006
+ /* @__PURE__ */ jsx17("p", { className: "mt-1.5 text-xs text-gray-500 dark:text-gray-400", children: labels.farewellMessageHint })
1796
2007
  ] }),
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(
2008
+ saveSuccess && /* @__PURE__ */ jsx17("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 }),
2009
+ /* @__PURE__ */ jsx17("div", { className: "flex justify-end", children: /* @__PURE__ */ jsxs15(
1799
2010
  "button",
1800
2011
  {
1801
2012
  type: "submit",
1802
2013
  disabled: saving,
1803
2014
  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
2015
  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 }),
2016
+ saving ? /* @__PURE__ */ jsx17("span", { className: "w-4 h-4 border-2 border-white/40 border-t-white rounded-full animate-spin" }) : /* @__PURE__ */ jsx17(Save2, { size: 14 }),
1806
2017
  saving ? labels.saving : labels.save
1807
2018
  ]
1808
2019
  }
@@ -1811,10 +2022,57 @@ function WelcomeFarewellForm({
1811
2022
  ] });
1812
2023
  }
1813
2024
 
2025
+ // src/settings/WhatsAppTemplatesSettings.tsx
2026
+ import { useState as useState9 } from "react";
2027
+ import { jsx as jsx18, jsxs as jsxs16 } from "react/jsx-runtime";
2028
+ var TEMPLATE_SETTINGS_TAB = {
2029
+ SELECT: "select",
2030
+ CREATE: "create"
2031
+ };
2032
+ var DEFAULT_TEMPLATES_SETTINGS_LABELS = {
2033
+ selectTab: "Template de reengajamento",
2034
+ createTab: "Criar template"
2035
+ };
2036
+ function WhatsAppTemplatesSettings({
2037
+ labels: labelsOverride,
2038
+ create,
2039
+ ...settingsProps
2040
+ }) {
2041
+ const labels = { ...DEFAULT_TEMPLATES_SETTINGS_LABELS, ...labelsOverride };
2042
+ const [tab, setTab] = useState9(TEMPLATE_SETTINGS_TAB.SELECT);
2043
+ return /* @__PURE__ */ jsxs16("div", { className: "space-y-4", children: [
2044
+ /* @__PURE__ */ jsxs16("nav", { className: "flex gap-1 border-b", role: "tablist", children: [
2045
+ /* @__PURE__ */ jsx18(
2046
+ "button",
2047
+ {
2048
+ type: "button",
2049
+ role: "tab",
2050
+ "aria-selected": tab === TEMPLATE_SETTINGS_TAB.SELECT,
2051
+ onClick: () => setTab(TEMPLATE_SETTINGS_TAB.SELECT),
2052
+ className: `cv-subtab ${tab === TEMPLATE_SETTINGS_TAB.SELECT ? "cv-subtab--active" : ""}`,
2053
+ children: labels.selectTab
2054
+ }
2055
+ ),
2056
+ create ? /* @__PURE__ */ jsx18(
2057
+ "button",
2058
+ {
2059
+ type: "button",
2060
+ role: "tab",
2061
+ "aria-selected": tab === TEMPLATE_SETTINGS_TAB.CREATE,
2062
+ onClick: () => setTab(TEMPLATE_SETTINGS_TAB.CREATE),
2063
+ className: `cv-subtab ${tab === TEMPLATE_SETTINGS_TAB.CREATE ? "cv-subtab--active" : ""}`,
2064
+ children: labels.createTab
2065
+ }
2066
+ ) : null
2067
+ ] }),
2068
+ tab === TEMPLATE_SETTINGS_TAB.SELECT ? /* @__PURE__ */ jsx18(WhatsAppTemplateSettingsForm, { ...settingsProps }) : create ? /* @__PURE__ */ jsx18(WhatsAppCreateTemplateForm, { ...create }) : null
2069
+ ] });
2070
+ }
2071
+
1814
2072
  // src/settings/TopicsForm.tsx
1815
2073
  import { Megaphone, Save as Save3 } from "lucide-react";
1816
- import { jsx as jsx24, jsxs as jsxs17 } from "react/jsx-runtime";
1817
- var DEFAULT_LABELS4 = {
2074
+ import { jsx as jsx19, jsxs as jsxs17 } from "react/jsx-runtime";
2075
+ var DEFAULT_LABELS5 = {
1818
2076
  sectionTitle: "T\xF3picos intermedi\xE1rios",
1819
2077
  sectionDescription: "Mensagens autom\xE1ticas para assuntos espec\xEDficos, disparadas quando o cliente demonstra interesse.",
1820
2078
  messagePlaceholder: "Digite a mensagem autom\xE1tica para este t\xF3pico...",
@@ -1830,20 +2088,20 @@ function TopicsForm({
1830
2088
  saveSuccess = false,
1831
2089
  labels: labelsOverride
1832
2090
  }) {
1833
- const labels = { ...DEFAULT_LABELS4, ...labelsOverride };
2091
+ const labels = { ...DEFAULT_LABELS5, ...labelsOverride };
1834
2092
  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
2093
  /* @__PURE__ */ jsxs17("div", { className: "px-6 py-4 border-b border-gray-100 dark:border-gray-700", children: [
1836
2094
  /* @__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" }),
2095
+ /* @__PURE__ */ jsx19(Megaphone, { size: 16, className: "text-orange-600 dark:text-orange-400" }),
1838
2096
  labels.sectionTitle
1839
2097
  ] }),
1840
- /* @__PURE__ */ jsx24("p", { className: "text-xs text-gray-500 dark:text-gray-400 mt-1", children: labels.sectionDescription })
2098
+ /* @__PURE__ */ jsx19("p", { className: "text-xs text-gray-500 dark:text-gray-400 mt-1", children: labels.sectionDescription })
1841
2099
  ] }),
1842
2100
  /* @__PURE__ */ jsxs17("form", { onSubmit: onSave, className: "p-6 space-y-6", children: [
1843
2101
  topics.map((topic) => /* @__PURE__ */ jsxs17("div", { className: "space-y-2", children: [
1844
2102
  /* @__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(
2103
+ /* @__PURE__ */ jsx19("div", { className: "flex-1", children: /* @__PURE__ */ jsx19("p", { className: "text-sm font-medium text-gray-900 dark:text-gray-100", children: topic.label }) }),
2104
+ /* @__PURE__ */ jsx19(
1847
2105
  "button",
1848
2106
  {
1849
2107
  type: "button",
@@ -1851,7 +2109,7 @@ function TopicsForm({
1851
2109
  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
2110
  role: "switch",
1853
2111
  "aria-checked": topic.enabled,
1854
- children: /* @__PURE__ */ jsx24(
2112
+ children: /* @__PURE__ */ jsx19(
1855
2113
  "span",
1856
2114
  {
1857
2115
  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 +2118,7 @@ function TopicsForm({
1860
2118
  }
1861
2119
  )
1862
2120
  ] }),
1863
- /* @__PURE__ */ jsx24(
2121
+ /* @__PURE__ */ jsx19(
1864
2122
  "textarea",
1865
2123
  {
1866
2124
  value: topic.message,
@@ -1880,47 +2138,19 @@ function TopicsForm({
1880
2138
  disabled: saving,
1881
2139
  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
2140
  children: [
1883
- /* @__PURE__ */ jsx24(Save3, { size: 14 }),
2141
+ /* @__PURE__ */ jsx19(Save3, { size: 14 }),
1884
2142
  labels.saveButton
1885
2143
  ]
1886
2144
  }
1887
2145
  ),
1888
- saveSuccess && /* @__PURE__ */ jsx24("span", { className: "text-sm text-green-600 dark:text-green-400", children: labels.saveSuccess })
2146
+ saveSuccess && /* @__PURE__ */ jsx19("span", { className: "text-sm text-green-600 dark:text-green-400", children: labels.saveSuccess })
1889
2147
  ] })
1890
2148
  ] })
1891
2149
  ] });
1892
2150
  }
1893
2151
 
1894
2152
  // 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
2153
+ import { useCallback as useCallback5 } from "react";
1924
2154
  function useConversationMessages(conversationId, params) {
1925
2155
  const context = useConversations();
1926
2156
  if (!context) {
@@ -1931,7 +2161,7 @@ function useConversationMessages(conversationId, params) {
1931
2161
  () => api.fetchMessages(conversationId, params),
1932
2162
  [conversationId, params?.limit, params?.before]
1933
2163
  );
1934
- const sendMessage = useCallback7(
2164
+ const sendMessage = useCallback5(
1935
2165
  async (text) => {
1936
2166
  const message = await api.sendMessage(conversationId, text);
1937
2167
  await refetch();
@@ -1939,7 +2169,7 @@ function useConversationMessages(conversationId, params) {
1939
2169
  },
1940
2170
  [api, conversationId, refetch]
1941
2171
  );
1942
- const sendMedia = useCallback7(
2172
+ const sendMedia = useCallback5(
1943
2173
  async (mediaData) => {
1944
2174
  const message = await api.sendMedia(conversationId, mediaData);
1945
2175
  await refetch();
@@ -1947,14 +2177,14 @@ function useConversationMessages(conversationId, params) {
1947
2177
  },
1948
2178
  [api, conversationId, refetch]
1949
2179
  );
1950
- const sendTemplate = useCallback7(
2180
+ const sendTemplate = useCallback5(
1951
2181
  async (templateData) => {
1952
2182
  await api.sendTemplate(conversationId, templateData);
1953
2183
  await refetch();
1954
2184
  },
1955
2185
  [api, conversationId, refetch]
1956
2186
  );
1957
- const markRead = useCallback7(() => api.markRead(conversationId), [api, conversationId]);
2187
+ const markRead = useCallback5(() => api.markRead(conversationId), [api, conversationId]);
1958
2188
  return { messages: data ?? [], loading, error, refetch, sendMessage, sendMedia, sendTemplate, markRead };
1959
2189
  }
1960
2190
 
@@ -1965,11 +2195,15 @@ function useConversationList(params) {
1965
2195
  throw new Error("useConversationList requires an ancestor <ConversationsProvider>");
1966
2196
  }
1967
2197
  const { api } = context;
2198
+ const filtersKey = JSON.stringify(params?.filters ?? {});
1968
2199
  const { data, loading, error, refetch } = useAsyncResource(
1969
2200
  () => api.fetchConversations(params),
1970
- [params?.page, params?.limit, params?.waitingHuman, params?.search]
2201
+ [params?.page, params?.limit, params?.waitingHuman, params?.search, filtersKey]
1971
2202
  );
1972
- return { conversations: data ?? [], loading, error, refetch };
2203
+ if (data === void 0) {
2204
+ return { conversations: [], total: 0, loading, error, refetch };
2205
+ }
2206
+ return { conversations: conversationsOf(data), total: totalOf(data), loading, error, refetch };
1973
2207
  }
1974
2208
 
1975
2209
  // src/hooks/useConversationContext.ts
@@ -1983,25 +2217,11 @@ function useConversationContext(conversationId) {
1983
2217
  return { context: data, loading, error, refetch };
1984
2218
  }
1985
2219
 
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
2220
  // src/hooks/useConversationRealtime.ts
2001
- import { useEffect as useEffect6, useRef as useRef7 } from "react";
2221
+ import { useEffect as useEffect6, useRef as useRef5 } from "react";
2002
2222
  function useConversationRealtime(conversationId, onEvent) {
2003
2223
  const context = useConversations();
2004
- const onEventRef = useRef7(onEvent);
2224
+ const onEventRef = useRef5(onEvent);
2005
2225
  onEventRef.current = onEvent;
2006
2226
  useEffect6(() => {
2007
2227
  if (!context || !conversationId) return;
@@ -2016,7 +2236,7 @@ function useConversationRealtime(conversationId, onEvent) {
2016
2236
  }
2017
2237
  function useGlobalRealtime(onEvent) {
2018
2238
  const context = useConversations();
2019
- const onEventRef = useRef7(onEvent);
2239
+ const onEventRef = useRef5(onEvent);
2020
2240
  onEventRef.current = onEvent;
2021
2241
  useEffect6(() => {
2022
2242
  if (!context) return;
@@ -2029,16 +2249,82 @@ function useGlobalRealtime(onEvent) {
2029
2249
  };
2030
2250
  }, [context]);
2031
2251
  }
2252
+
2253
+ // src/hooks/useConversationActions.ts
2254
+ import { useMemo as useMemo2 } from "react";
2255
+ function useConversationActions(conversationId) {
2256
+ const context = useConversations();
2257
+ if (!context) {
2258
+ throw new Error("useConversationActions requires an ancestor <ConversationsProvider>");
2259
+ }
2260
+ const { api } = context;
2261
+ return useMemo2(
2262
+ () => ({
2263
+ takeover: api.takeover ? () => api.takeover(conversationId) : void 0,
2264
+ release: api.release ? () => api.release(conversationId) : void 0,
2265
+ finalize: api.finalize ? () => api.finalize(conversationId) : void 0
2266
+ }),
2267
+ [api, conversationId]
2268
+ );
2269
+ }
2270
+ function useInboxActions() {
2271
+ const context = useConversations();
2272
+ if (!context) {
2273
+ throw new Error("useInboxActions requires an ancestor <ConversationsProvider>");
2274
+ }
2275
+ const { api } = context;
2276
+ return useMemo2(
2277
+ () => ({
2278
+ markAllRead: api.markAllRead ? () => api.markAllRead() : void 0,
2279
+ listTemplates: api.listTemplates ? () => api.listTemplates() : void 0
2280
+ }),
2281
+ [api]
2282
+ );
2283
+ }
2032
2284
  export {
2033
2285
  AudioPlayer,
2286
+ AudioRecorderButton,
2034
2287
  Avatar,
2288
+ CHANNEL_BRAND_COLOR,
2289
+ CHANNEL_CAPABILITIES,
2290
+ CHANNEL_FILTER_ALL,
2291
+ CONVERSATION_CHANNEL,
2292
+ CONVERSATION_WINDOW,
2293
+ ChannelIcon,
2294
+ ConversationContextPanel,
2295
+ ConversationDocumentsPanel,
2296
+ ConversationHeader,
2035
2297
  ConversationListItem,
2036
2298
  ConversationLocalesProvider,
2299
+ ConversationRow,
2037
2300
  ConversationWallpaper,
2038
2301
  ConversationsProvider,
2302
+ DEFAULT_ACCEPTED_FILE_TYPES,
2303
+ DEFAULT_AUDIO_RECORDER_BUTTON_LABELS,
2304
+ DEFAULT_AVATAR_LABELS,
2305
+ DEFAULT_CONVERSATION_CHANNEL,
2306
+ DEFAULT_CONVERSATION_CONTEXT_LABELS,
2307
+ DEFAULT_CONVERSATION_DOCUMENTS_LABELS,
2308
+ DEFAULT_CONVERSATION_HEADER_LABELS,
2309
+ DEFAULT_CONVERSATION_LIST_ITEM_LABELS,
2310
+ DEFAULT_DOCUMENTS_LIBRARY_LABELS,
2311
+ DEFAULT_EMOJI_PICKER_LABELS,
2312
+ DEFAULT_INTERACTIVE_MESSAGE_LABELS,
2313
+ DEFAULT_LIGHTBOX_LABELS,
2314
+ DEFAULT_MAX_RECORDING_MILLISECONDS,
2315
+ DEFAULT_MESSAGE_COMPOSER_LABELS,
2316
+ DEFAULT_RICH_COMPOSER_TOOLTIPS,
2317
+ DEFAULT_TEMPLATES_SETTINGS_LABELS,
2318
+ DEFAULT_WHATSAPP_MESSAGE_EDITOR_LABELS,
2319
+ DEFAULT_WINDOW_EXPIRED_LABELS,
2320
+ DOCUMENT_SOURCE_FILTER,
2039
2321
  DateDivider,
2322
+ DocumentsLibrary,
2323
+ EMOJI_CATEGORIES,
2040
2324
  EmojiPicker,
2041
2325
  FileIcon,
2326
+ HANDLE_KIND,
2327
+ InteractiveMessage,
2042
2328
  Lightbox,
2043
2329
  MediaRenderer,
2044
2330
  MessageBubble,
@@ -2046,21 +2332,45 @@ export {
2046
2332
  MessageTail,
2047
2333
  MessageText,
2048
2334
  MessageTimestamp,
2335
+ NARROW_MAX_WIDTH_PX,
2336
+ REOPEN_MECHANISM,
2337
+ RICH_COMPOSER_ACTION,
2338
+ RichMessageComposer,
2049
2339
  SimpleEmojiPicker,
2050
2340
  StatusTicks,
2341
+ TEMPLATE_SETTINGS_TAB,
2051
2342
  ToastProvider,
2052
2343
  TopicsForm,
2344
+ WINDOW_FILTERS,
2053
2345
  WelcomeFarewellForm,
2054
2346
  WhatsAppCreateTemplateForm,
2055
2347
  WhatsAppMessageEditor,
2056
2348
  WhatsAppTemplateSettingsForm,
2349
+ WhatsAppTemplatesSettings,
2350
+ WindowExpiredNotice,
2351
+ applyQuickReplyVariables,
2352
+ buildTranscriptFilename,
2353
+ buildTranscriptText,
2354
+ capabilitiesOf,
2355
+ channelFiltersFor,
2356
+ contactFlag,
2357
+ createMediaUrlResolver,
2358
+ downloadTextFile,
2359
+ formatContactHandle,
2360
+ formatDateTime,
2057
2361
  formatFileSize,
2058
2362
  formatPhone,
2363
+ formatStalledFor,
2059
2364
  formatTimestamp,
2060
2365
  htmlToWA,
2366
+ isSameDay,
2367
+ isWindowBlocking,
2061
2368
  parseWhatsAppFormatting,
2062
2369
  phoneInitials,
2370
+ resolveQuickReply,
2371
+ searchEmojis,
2063
2372
  toast,
2373
+ useConversationActions,
2064
2374
  useConversationContext,
2065
2375
  useConversationDocuments,
2066
2376
  useConversationList,
@@ -2070,8 +2380,12 @@ export {
2070
2380
  useConversations,
2071
2381
  useDarkMode,
2072
2382
  useGlobalRealtime,
2383
+ useInboxActions,
2384
+ useIsDarkTheme,
2385
+ useIsNarrow,
2073
2386
  useToast,
2074
2387
  useWaitingNotifications,
2075
2388
  waToHTML,
2076
- waToHTMLInline
2389
+ waToHTMLInline,
2390
+ windowOf
2077
2391
  };