@adatechnology/conversations-ui 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/dist/channel/index.d.ts +2 -0
  2. package/dist/channel/index.js +0 -0
  3. package/dist/chunk-ZDURDZTM.js +199 -0
  4. package/dist/flows/index.d.ts +246 -0
  5. package/dist/flows/index.js +1110 -0
  6. package/dist/index.d.ts +542 -0
  7. package/dist/index.js +2077 -0
  8. package/dist/styles.css +11 -0
  9. package/dist/styles.d.ts +2 -0
  10. package/package.json +52 -0
  11. package/src/AudioPlayer.tsx +103 -0
  12. package/src/Avatar.tsx +63 -0
  13. package/src/ConversationListItem.tsx +147 -0
  14. package/src/ConversationLocalesProvider.tsx +76 -0
  15. package/src/DateDivider.tsx +32 -0
  16. package/src/EmojiPicker.tsx +77 -0
  17. package/src/FileIcon.tsx +33 -0
  18. package/src/Lightbox.tsx +17 -0
  19. package/src/MediaRenderer.tsx +158 -0
  20. package/src/MessageBubble.tsx +129 -0
  21. package/src/MessageComposer.tsx +211 -0
  22. package/src/MessageTail.tsx +18 -0
  23. package/src/MessageText.tsx +42 -0
  24. package/src/MessageTimestamp.tsx +22 -0
  25. package/src/SimpleEmojiPicker.tsx +72 -0
  26. package/src/StatusTicks.tsx +39 -0
  27. package/src/Toast.tsx +140 -0
  28. package/src/Wallpaper.tsx +13 -0
  29. package/src/WhatsAppMessageEditor.tsx +142 -0
  30. package/src/channel/index.ts +1 -0
  31. package/src/conversations/index.ts +1 -0
  32. package/src/flows/FlowGroupFrame.tsx +21 -0
  33. package/src/flows/FlowGroupHeader.tsx +31 -0
  34. package/src/flows/FlowMapCanvas.tsx +76 -0
  35. package/src/flows/FlowMapNode.tsx +39 -0
  36. package/src/flows/FlowNodeCard.tsx +150 -0
  37. package/src/flows/FlowNodePanel.tsx +356 -0
  38. package/src/flows/FlowPalette.tsx +137 -0
  39. package/src/flows/FlowPortalNode.tsx +30 -0
  40. package/src/flows/FlowWhatsAppPreview.tsx +67 -0
  41. package/src/flows/flowGraph.ts +391 -0
  42. package/src/flows/index.ts +55 -0
  43. package/src/flows/labels.ts +187 -0
  44. package/src/hooks/useAsyncResource.ts +38 -0
  45. package/src/hooks/useConversationContext.ts +23 -0
  46. package/src/hooks/useConversationDocuments.ts +33 -0
  47. package/src/hooks/useConversationList.ts +32 -0
  48. package/src/hooks/useConversationMessages.ts +64 -0
  49. package/src/hooks/useConversationRealtime.ts +50 -0
  50. package/src/index.ts +87 -0
  51. package/src/lib/format.ts +32 -0
  52. package/src/lib/phone.ts +26 -0
  53. package/src/lib/whatsapp-formatting.tsx +215 -0
  54. package/src/providers/ConversationsProvider.tsx +29 -0
  55. package/src/providers/types.ts +54 -0
  56. package/src/settings/TopicsForm.tsx +109 -0
  57. package/src/settings/WelcomeFarewellForm.tsx +118 -0
  58. package/src/settings/WhatsAppCreateTemplateForm.tsx +309 -0
  59. package/src/settings/WhatsAppTemplateSettingsForm.tsx +264 -0
  60. package/src/styles.css +21 -0
  61. package/src/theme.ts +30 -0
  62. package/src/types.ts +44 -0
  63. package/src/useDarkMode.ts +48 -0
  64. package/src/useWaitingNotifications.ts +64 -0
  65. package/tsconfig.json +15 -0
package/dist/index.js ADDED
@@ -0,0 +1,2077 @@
1
+ import {
2
+ htmlToWA,
3
+ parseWhatsAppFormatting,
4
+ useDarkMode,
5
+ waToHTML,
6
+ waToHTMLInline
7
+ } from "./chunk-ZDURDZTM.js";
8
+
9
+ // src/MessageBubble.tsx
10
+ import { useState as useState3 } from "react";
11
+ import { Check } from "lucide-react";
12
+
13
+ // src/ConversationLocalesProvider.tsx
14
+ import { createContext, useContext } from "react";
15
+ import { jsx } from "react/jsx-runtime";
16
+ var DEFAULT_LOCALES = {
17
+ bubble: {
18
+ customer: "Cliente",
19
+ bot: "Bot",
20
+ agent: "Atendente",
21
+ document: "documento",
22
+ media: "m\xEDdia",
23
+ templateLabel: "Template",
24
+ readAt: "Lida \xE0s ",
25
+ windowExpired: "Janela de 24h expirada",
26
+ viewImage: "Ver imagem",
27
+ listenAudio: "Ouvir \xE1udio",
28
+ viewVideo: "Ver v\xEDdeo"
29
+ },
30
+ selection: {
31
+ select: "Selecionar"
32
+ },
33
+ dateDivider: {
34
+ today: "Hoje",
35
+ yesterday: "Ontem"
36
+ }
37
+ };
38
+ var ConversationLocalesContext = createContext(DEFAULT_LOCALES);
39
+ function ConversationLocalesProvider({ children, locales }) {
40
+ const merged = {
41
+ bubble: { ...DEFAULT_LOCALES.bubble, ...locales?.bubble },
42
+ selection: { ...DEFAULT_LOCALES.selection, ...locales?.selection },
43
+ dateDivider: { ...DEFAULT_LOCALES.dateDivider, ...locales?.dateDivider }
44
+ };
45
+ return /* @__PURE__ */ jsx(ConversationLocalesContext.Provider, { value: merged, children });
46
+ }
47
+ function useConversationLocales() {
48
+ return useContext(ConversationLocalesContext);
49
+ }
50
+
51
+ // src/StatusTicks.tsx
52
+ import { AlertTriangle } from "lucide-react";
53
+ import { jsx as jsx2, jsxs } from "react/jsx-runtime";
54
+ var STATUS_COLOR_CLASS = {
55
+ sent: "text-black/40 dark:text-white/40",
56
+ delivered: "text-black/40 dark:text-white/40",
57
+ read: "text-sky-500",
58
+ failed: "text-red-500"
59
+ };
60
+ function Ticks({ double }) {
61
+ return /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 20 12", width: "15", height: "9", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [
62
+ double && /* @__PURE__ */ jsx2("path", { d: "M1 6.5L4.5 10L11 2", stroke: "currentColor", strokeWidth: "1.6", strokeLinecap: "round", strokeLinejoin: "round" }),
63
+ /* @__PURE__ */ jsx2(
64
+ "path",
65
+ {
66
+ d: double ? "M6 6.5L9.5 10L19 1" : "M1 6.5L5 10.5L14.5 1",
67
+ stroke: "currentColor",
68
+ strokeWidth: "1.6",
69
+ strokeLinecap: "round",
70
+ strokeLinejoin: "round"
71
+ }
72
+ )
73
+ ] });
74
+ }
75
+ function StatusTicks({ status, title }) {
76
+ const colorClass = STATUS_COLOR_CLASS[status] ?? STATUS_COLOR_CLASS.sent;
77
+ return /* @__PURE__ */ jsx2("span", { className: `cursor-help leading-none flex items-center ${colorClass}`, title, children: status === "failed" ? /* @__PURE__ */ jsx2(AlertTriangle, { size: 11 }) : /* @__PURE__ */ jsx2(Ticks, { double: status !== "sent" }) });
78
+ }
79
+
80
+ // src/MediaRenderer.tsx
81
+ import { useState as useState2 } from "react";
82
+
83
+ // src/AudioPlayer.tsx
84
+ import { useEffect, useRef, useState } from "react";
85
+ import { Pause, Play } from "lucide-react";
86
+ import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
87
+ var BARS = Array.from({ length: 30 }, (_, i) => {
88
+ const heights = [3, 5, 8, 12, 7, 10, 14, 9, 6, 11, 15, 8, 4, 13, 7, 10, 6, 12, 9, 5, 14, 8, 11, 6, 13, 7, 10, 5, 9, 4];
89
+ return heights[i % heights.length];
90
+ });
91
+ function fmt(sec) {
92
+ if (!isFinite(sec)) return "0:00";
93
+ const m = Math.floor(sec / 60);
94
+ const s = Math.floor(sec % 60);
95
+ return `${m}:${s.toString().padStart(2, "0")}`;
96
+ }
97
+ function AudioPlayer({ src, isMine = false }) {
98
+ const audioRef = useRef(null);
99
+ const [playing, setPlaying] = useState(false);
100
+ const [progress, setProgress] = useState(0);
101
+ const [duration, setDuration] = useState(0);
102
+ const [current, setCurrent] = useState(0);
103
+ useEffect(() => {
104
+ const audio = audioRef.current;
105
+ if (!audio) return;
106
+ const onTime = () => {
107
+ setCurrent(audio.currentTime);
108
+ setProgress(audio.duration ? audio.currentTime / audio.duration : 0);
109
+ };
110
+ const onMeta = () => setDuration(audio.duration);
111
+ const onEnd = () => {
112
+ setPlaying(false);
113
+ setProgress(0);
114
+ setCurrent(0);
115
+ };
116
+ audio.addEventListener("timeupdate", onTime);
117
+ audio.addEventListener("loadedmetadata", onMeta);
118
+ audio.addEventListener("ended", onEnd);
119
+ return () => {
120
+ audio.removeEventListener("timeupdate", onTime);
121
+ audio.removeEventListener("loadedmetadata", onMeta);
122
+ audio.removeEventListener("ended", onEnd);
123
+ };
124
+ }, []);
125
+ const toggle = () => {
126
+ const audio = audioRef.current;
127
+ if (!audio) return;
128
+ if (playing) {
129
+ audio.pause();
130
+ setPlaying(false);
131
+ } else {
132
+ audio.play();
133
+ setPlaying(true);
134
+ }
135
+ };
136
+ const seek = (e) => {
137
+ const audio = audioRef.current;
138
+ if (!audio || !audio.duration) return;
139
+ const rect = e.currentTarget.getBoundingClientRect();
140
+ const ratio = (e.clientX - rect.left) / rect.width;
141
+ audio.currentTime = ratio * audio.duration;
142
+ };
143
+ const activeBars = Math.round(progress * BARS.length);
144
+ const playBtn = isMine ? "bg-green-600 hover:bg-green-700 text-white" : "bg-gray-600 hover:bg-gray-700 text-white";
145
+ const activeBar = isMine ? "bg-green-700" : "bg-gray-700";
146
+ const inactiveBar = isMine ? "bg-green-300" : "bg-gray-300";
147
+ return /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-2 w-full", style: { minWidth: "200px", maxWidth: "260px" }, children: [
148
+ /* @__PURE__ */ jsx3("audio", { ref: audioRef, src, preload: "metadata" }),
149
+ /* @__PURE__ */ jsx3(
150
+ "button",
151
+ {
152
+ onClick: toggle,
153
+ className: `flex-shrink-0 w-9 h-9 rounded-full flex items-center justify-center transition-colors ${playBtn}`,
154
+ children: playing ? /* @__PURE__ */ jsx3(Pause, { size: 16 }) : /* @__PURE__ */ jsx3(Play, { size: 16, className: "translate-x-0.5" })
155
+ }
156
+ ),
157
+ /* @__PURE__ */ jsxs2("div", { className: "flex-1 flex flex-col gap-1", children: [
158
+ /* @__PURE__ */ jsx3("div", { className: "flex items-end gap-0.5 h-8 cursor-pointer", onClick: seek, children: BARS.map((h, i) => /* @__PURE__ */ jsx3(
159
+ "div",
160
+ {
161
+ className: `flex-1 rounded-full transition-colors ${i < activeBars ? activeBar : inactiveBar}`,
162
+ style: { height: `${h * 2}px` }
163
+ },
164
+ i
165
+ )) }),
166
+ /* @__PURE__ */ jsx3("span", { className: "text-gray-400 tabular-nums text-xs", children: playing || current > 0 ? fmt(current) : fmt(duration) })
167
+ ] })
168
+ ] });
169
+ }
170
+
171
+ // src/FileIcon.tsx
172
+ import { FileArchive, FileSpreadsheet, FileText, File as FileGeneric } from "lucide-react";
173
+ import { jsx as jsx4 } from "react/jsx-runtime";
174
+ var EXTENSION_STYLE = {
175
+ pdf: { Icon: FileText, colorClass: "text-red-500" },
176
+ doc: { Icon: FileText, colorClass: "text-blue-500" },
177
+ docx: { Icon: FileText, colorClass: "text-blue-500" },
178
+ xls: { Icon: FileSpreadsheet, colorClass: "text-green-600" },
179
+ xlsx: { Icon: FileSpreadsheet, colorClass: "text-green-600" },
180
+ zip: { Icon: FileArchive, colorClass: "text-orange-500" }
181
+ };
182
+ function getExtension(filename, mimeType) {
183
+ const fromFilename = filename?.split(".").pop()?.toLowerCase();
184
+ if (fromFilename && EXTENSION_STYLE[fromFilename]) return fromFilename;
185
+ return mimeType?.split("/")[1]?.toLowerCase() ?? "";
186
+ }
187
+ function FileIcon({ filename, mimeType, size = 20, className = "" }) {
188
+ const extension = getExtension(filename, mimeType);
189
+ const style = EXTENSION_STYLE[extension] ?? { Icon: FileGeneric, colorClass: "text-gray-500" };
190
+ const { Icon, colorClass } = style;
191
+ return /* @__PURE__ */ jsx4(Icon, { size, className: `${colorClass} ${className}`.trim() });
192
+ }
193
+
194
+ // src/lib/format.ts
195
+ function formatTimestamp(timestamp) {
196
+ try {
197
+ const date = new Date(timestamp);
198
+ const hours = date.getHours().toString().padStart(2, "0");
199
+ const minutes = date.getMinutes().toString().padStart(2, "0");
200
+ return `${hours}:${minutes}`;
201
+ } catch {
202
+ return timestamp;
203
+ }
204
+ }
205
+ function formatDateTime(iso) {
206
+ const d = new Date(iso);
207
+ return d.toLocaleString("pt-BR", { day: "2-digit", month: "2-digit", hour: "2-digit", minute: "2-digit" });
208
+ }
209
+ function isSameDay(a, b) {
210
+ return a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();
211
+ }
212
+ var FILE_SIZE_UNITS = ["B", "KB", "MB", "GB"];
213
+ function formatFileSize(bytes) {
214
+ if (!isFinite(bytes) || bytes < 0) return "";
215
+ if (bytes < 1) return "0 B";
216
+ const exponent = Math.min(Math.floor(Math.log(bytes) / Math.log(1024)), FILE_SIZE_UNITS.length - 1);
217
+ const value = bytes / 1024 ** exponent;
218
+ const formatted = exponent === 0 ? value.toString() : value.toFixed(value < 10 ? 1 : 0);
219
+ return `${formatted} ${FILE_SIZE_UNITS[exponent]}`;
220
+ }
221
+
222
+ // src/MediaRenderer.tsx
223
+ import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
224
+ function resolveMediaSource(message) {
225
+ if (message.mediaUrl) return message.mediaUrl;
226
+ if (message.base64) {
227
+ const prefix = message.mimeType ? `data:${message.mimeType};base64,` : "data:application/octet-stream;base64,";
228
+ return prefix + message.base64;
229
+ }
230
+ return null;
231
+ }
232
+ function hasLazyRef(message) {
233
+ return Boolean(message.uploadId || message.mediaId);
234
+ }
235
+ function useLazyMediaUrl(message, onResolveUrl) {
236
+ const [url, setUrl] = useState2(null);
237
+ const [loading, setLoading] = useState2(false);
238
+ const [error, setError] = useState2(false);
239
+ const load = async () => {
240
+ if (url || loading || !onResolveUrl) return;
241
+ setLoading(true);
242
+ setError(false);
243
+ try {
244
+ const resolved = await onResolveUrl(message);
245
+ if (resolved) setUrl(resolved);
246
+ else setError(true);
247
+ } catch {
248
+ setError(true);
249
+ } finally {
250
+ setLoading(false);
251
+ }
252
+ };
253
+ return { url, loading, error, load };
254
+ }
255
+ function MediaRenderer({ message, onLightbox, onResolveUrl }) {
256
+ const { bubble } = useConversationLocales();
257
+ const eagerSrc = resolveMediaSource(message);
258
+ const lazy = useLazyMediaUrl(message, onResolveUrl);
259
+ const src = eagerSrc ?? lazy.url;
260
+ const canLazyLoad = !eagerSrc && hasLazyRef(message) && Boolean(onResolveUrl);
261
+ const lazyButtonClass = "text-xs text-blue-600 underline flex items-center gap-1";
262
+ switch (message.type) {
263
+ case "image":
264
+ case "sticker": {
265
+ if (!src && canLazyLoad) {
266
+ return /* @__PURE__ */ jsx5("button", { onClick: lazy.load, className: lazyButtonClass, children: lazy.loading ? "Carregando..." : lazy.error ? "Erro \u2014 tentar novamente" : bubble.viewImage });
267
+ }
268
+ return /* @__PURE__ */ jsx5("div", { className: "min-w-[200px]", children: src ? /* @__PURE__ */ jsx5("img", { src, alt: message.caption ?? "Image", className: "w-full max-h-80 object-cover cursor-pointer hover:opacity-90 transition-opacity", onClick: () => onLightbox(src), loading: "lazy" }) : /* @__PURE__ */ jsx5("div", { className: "w-full h-40 bg-gray-200 flex items-center justify-center text-gray-400", children: /* @__PURE__ */ jsxs3("svg", { width: "32", height: "32", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.5", children: [
269
+ /* @__PURE__ */ jsx5("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2", ry: "2" }),
270
+ /* @__PURE__ */ jsx5("circle", { cx: "8.5", cy: "8.5", r: "1.5" }),
271
+ /* @__PURE__ */ jsx5("polyline", { points: "21 15 16 10 5 21" })
272
+ ] }) }) });
273
+ }
274
+ case "video": {
275
+ if (!src && canLazyLoad) {
276
+ return /* @__PURE__ */ jsx5("button", { onClick: lazy.load, className: lazyButtonClass, children: lazy.loading ? "Carregando..." : lazy.error ? "Erro \u2014 tentar novamente" : bubble.viewVideo });
277
+ }
278
+ return /* @__PURE__ */ jsx5("div", { className: "min-w-[200px]", children: src ? /* @__PURE__ */ jsx5("video", { src, className: "w-full max-h-80 rounded-lg", controls: true, preload: "metadata", children: /* @__PURE__ */ jsx5("track", { kind: "captions" }) }) : /* @__PURE__ */ jsx5("div", { className: "w-full h-32 bg-gray-200 rounded-lg flex items-center justify-center text-gray-400", children: /* @__PURE__ */ jsxs3("svg", { width: "32", height: "32", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.5", children: [
279
+ /* @__PURE__ */ jsx5("polygon", { points: "23 7 16 12 23 17 23 7" }),
280
+ /* @__PURE__ */ jsx5("rect", { x: "1", y: "5", width: "15", height: "14", rx: "2", ry: "2" })
281
+ ] }) }) });
282
+ }
283
+ case "audio": {
284
+ if (!src && canLazyLoad) {
285
+ return /* @__PURE__ */ jsx5("button", { onClick: lazy.load, className: lazyButtonClass, children: lazy.loading ? "Carregando..." : lazy.error ? "Erro \u2014 tentar novamente" : bubble.listenAudio });
286
+ }
287
+ return /* @__PURE__ */ jsx5("div", { className: "min-w-[200px]", children: src ? /* @__PURE__ */ jsx5(AudioPlayer, { src, isMine: message.direction === "outbound" }) : /* @__PURE__ */ jsx5("div", { className: "h-12 bg-gray-200 rounded-lg flex items-center justify-center text-gray-400 text-xs", children: "Audio unavailable" }) });
288
+ }
289
+ case "document": {
290
+ const typeLabel = message.mimeType?.split("/")[1]?.toUpperCase() ?? "FILE";
291
+ const sizeLabel = message.sizeBytes ? formatFileSize(message.sizeBytes) : null;
292
+ return /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-3 min-w-[200px]", children: [
293
+ /* @__PURE__ */ jsx5("div", { className: "w-10 h-10 bg-gray-200 rounded-lg flex items-center justify-center flex-shrink-0", children: /* @__PURE__ */ jsx5(FileIcon, { filename: message.filename, mimeType: message.mimeType }) }),
294
+ /* @__PURE__ */ jsxs3("div", { className: "flex-1 min-w-0", children: [
295
+ /* @__PURE__ */ jsx5("p", { className: "text-sm font-medium truncate", children: message.filename ?? "Document" }),
296
+ /* @__PURE__ */ jsx5("p", { className: "text-xs text-gray-500", children: sizeLabel ? `${typeLabel} \xB7 ${sizeLabel}` : typeLabel })
297
+ ] }),
298
+ src ? /* @__PURE__ */ jsx5("a", { href: src, download: message.filename, className: "w-8 h-8 flex items-center justify-center rounded-full bg-gray-200 hover:bg-gray-300 flex-shrink-0 transition-colors", "aria-label": "Download", children: /* @__PURE__ */ jsxs3("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", className: "text-gray-600", children: [
299
+ /* @__PURE__ */ jsx5("path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" }),
300
+ /* @__PURE__ */ jsx5("polyline", { points: "7 10 12 15 17 10" }),
301
+ /* @__PURE__ */ jsx5("line", { x1: "12", y1: "15", x2: "12", y2: "3" })
302
+ ] }) }) : canLazyLoad ? /* @__PURE__ */ jsx5(
303
+ "button",
304
+ {
305
+ onClick: lazy.load,
306
+ disabled: lazy.loading,
307
+ className: "w-8 h-8 flex items-center justify-center rounded-full bg-gray-200 hover:bg-gray-300 flex-shrink-0 transition-colors disabled:opacity-50",
308
+ "aria-label": "Download",
309
+ children: /* @__PURE__ */ jsxs3("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", className: "text-gray-600", children: [
310
+ /* @__PURE__ */ jsx5("path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" }),
311
+ /* @__PURE__ */ jsx5("polyline", { points: "7 10 12 15 17 10" }),
312
+ /* @__PURE__ */ jsx5("line", { x1: "12", y1: "15", x2: "12", y2: "3" })
313
+ ] })
314
+ }
315
+ ) : null,
316
+ lazy.error && /* @__PURE__ */ jsx5("span", { className: "text-xs text-red-500 flex-shrink-0", children: "Erro" })
317
+ ] });
318
+ }
319
+ default:
320
+ return null;
321
+ }
322
+ }
323
+
324
+ // src/Lightbox.tsx
325
+ import { jsx as jsx6, jsxs as jsxs4 } from "react/jsx-runtime";
326
+ function Lightbox({ imageUrl, caption, onClose }) {
327
+ return /* @__PURE__ */ jsx6("div", { className: "fixed inset-0 z-50 bg-black/85 flex items-center justify-center p-4", onClick: onClose, children: /* @__PURE__ */ jsxs4("div", { className: "max-w-[90vw] max-h-[90vh] flex flex-col items-center", onClick: (e) => e.stopPropagation(), children: [
328
+ /* @__PURE__ */ jsx6("img", { src: imageUrl, alt: caption ?? "Image", className: "max-w-full max-h-[80vh] object-contain rounded-lg" }),
329
+ caption && /* @__PURE__ */ jsx6("p", { className: "text-white text-sm mt-3 text-center", children: caption }),
330
+ /* @__PURE__ */ jsx6("button", { onClick: onClose, className: "mt-4 px-4 py-2 bg-white/20 text-white rounded-lg hover:bg-white/30 transition-colors", children: "Fechar" })
331
+ ] }) });
332
+ }
333
+
334
+ // src/MessageBubble.tsx
335
+ import { Fragment, jsx as jsx7, jsxs as jsxs5 } from "react/jsx-runtime";
336
+ var BUBBLE_COLOR = {
337
+ agent: "bg-[#d9fdd3] dark:bg-[#005c4b]",
338
+ bot: "bg-[#d7f0ec] dark:bg-[#0a3d3a]",
339
+ customer: "bg-white dark:bg-[#202c33]"
340
+ };
341
+ var MEDIA_TYPES = /* @__PURE__ */ new Set(["image", "audio", "video", "document", "sticker"]);
342
+ function MessageBubble({
343
+ message,
344
+ isMine,
345
+ senderName,
346
+ isFirstInGroup = true,
347
+ isSelecting = false,
348
+ isSelected = false,
349
+ onToggleSelect,
350
+ onResolveMediaUrl
351
+ }) {
352
+ const { bubble, selection } = useConversationLocales();
353
+ const [lightboxSrc, setLightboxSrc] = useState3(null);
354
+ const bubbleColor = BUBBLE_COLOR[message.sender] ?? BUBBLE_COLOR.customer;
355
+ const hasError = message.status === "failed";
356
+ const isMedia = MEDIA_TYPES.has(message.type);
357
+ const isTemplate = message.type === "template";
358
+ const displayName = message.sender === "agent" && senderName ? senderName : bubble[message.sender] ?? message.sender;
359
+ const tooltipText = message.status === "read" && message.readAt ? `${bubble.readAt}${formatDateTime(message.readAt)}` : message.status === "failed" ? bubble.windowExpired : void 0;
360
+ const tailCornerClass = isFirstInGroup ? isMine ? "rounded-tr-md" : "rounded-tl-md" : "";
361
+ const checkbox = /* @__PURE__ */ jsx7(
362
+ "button",
363
+ {
364
+ onClick: (e) => {
365
+ e.stopPropagation();
366
+ onToggleSelect?.();
367
+ },
368
+ title: selection.select,
369
+ className: `
370
+ flex-shrink-0 self-end mb-1 w-5 h-5 rounded-full border-2 flex items-center justify-center transition-all
371
+ ${isSelected ? "bg-teal-600 border-teal-600" : "bg-white/80 dark:bg-black/40 border-black/20 dark:border-white/30"}
372
+ ${isSelecting ? "opacity-100" : "opacity-0 group-hover:opacity-100"}
373
+ `,
374
+ children: isSelected && /* @__PURE__ */ jsx7(Check, { size: 12, className: "text-white", strokeWidth: 3 })
375
+ }
376
+ );
377
+ return /* @__PURE__ */ jsxs5("div", { className: `flex items-end gap-1.5 ${isMine ? "justify-end" : "justify-start"} group ${isFirstInGroup ? "mt-2" : "mt-0.5"}`, children: [
378
+ isMine && checkbox,
379
+ /* @__PURE__ */ jsxs5(
380
+ "div",
381
+ {
382
+ onClick: isSelecting ? onToggleSelect : void 0,
383
+ className: `
384
+ max-w-[75%] sm:max-w-[65%] rounded-2xl ${tailCornerClass} px-2.5 py-1.5 shadow-sm
385
+ ${bubbleColor}
386
+ ${hasError ? "ring-1 ring-inset ring-red-400" : ""}
387
+ ${isSelecting ? "cursor-pointer" : ""}
388
+ ${isSelected ? "ring-2 ring-teal-500" : ""}
389
+ relative
390
+ `,
391
+ children: [
392
+ isMine && isFirstInGroup && (message.sender === "bot" || message.sender === "agent" && senderName) && /* @__PURE__ */ jsx7("div", { className: "text-xs font-semibold mb-0.5 text-teal-700 dark:text-teal-400", children: displayName }),
393
+ isMedia ? /* @__PURE__ */ jsx7(MediaRenderer, { message, onLightbox: setLightboxSrc, onResolveUrl: onResolveMediaUrl }) : /* @__PURE__ */ jsxs5(Fragment, { children: [
394
+ isTemplate && /* @__PURE__ */ jsxs5("p", { className: "text-xs text-gray-500 dark:text-gray-400 flex items-center gap-1 mb-0.5", children: [
395
+ /* @__PURE__ */ jsx7("span", { children: "\u{1F4E8}" }),
396
+ /* @__PURE__ */ jsxs5("span", { className: "font-medium", children: [
397
+ bubble.templateLabel,
398
+ message.templateName ? ` \u2014 ${message.templateName}` : ""
399
+ ] })
400
+ ] }),
401
+ /* @__PURE__ */ jsx7("div", { className: "text-sm text-gray-900 dark:text-gray-100 whitespace-pre-wrap break-words leading-[19px]", children: parseWhatsAppFormatting(message.content ?? "") })
402
+ ] }),
403
+ /* @__PURE__ */ jsxs5("div", { className: "flex items-center justify-end gap-1 mt-0.5 select-none", children: [
404
+ /* @__PURE__ */ jsx7("span", { className: "text-xs text-black/40 dark:text-white/40 font-medium", children: formatTimestamp(message.timestamp) }),
405
+ isMine && message.status && /* @__PURE__ */ jsx7(StatusTicks, { status: message.status, title: tooltipText })
406
+ ] })
407
+ ]
408
+ }
409
+ ),
410
+ !isMine && checkbox,
411
+ lightboxSrc && /* @__PURE__ */ jsx7(Lightbox, { imageUrl: lightboxSrc, onClose: () => setLightboxSrc(null) })
412
+ ] });
413
+ }
414
+
415
+ // src/Wallpaper.tsx
416
+ import { jsx as jsx8 } from "react/jsx-runtime";
417
+ function ConversationWallpaper({ children, className = "" }) {
418
+ return /* @__PURE__ */ jsx8("div", { className: `cv-wallpaper ${className}`.trim(), children });
419
+ }
420
+
421
+ // src/EmojiPicker.tsx
422
+ import { useState as useState4, useCallback } from "react";
423
+ import { jsx as jsx9, jsxs as jsxs6 } from "react/jsx-runtime";
424
+ var EMOJI_CATEGORIES = [
425
+ {
426
+ name: "Smileys",
427
+ emojis: ["\u{1F600}", "\u{1F603}", "\u{1F604}", "\u{1F601}", "\u{1F605}", "\u{1F602}", "\u{1F923}", "\u{1F60A}", "\u{1F607}", "\u{1F642}", "\u{1F609}", "\u{1F60C}", "\u{1F60D}", "\u{1F970}", "\u{1F618}", "\u{1F617}", "\u{1F60B}", "\u{1F61B}", "\u{1F61C}", "\u{1F92A}"]
428
+ },
429
+ {
430
+ name: "Gestures",
431
+ emojis: ["\u{1F44D}", "\u{1F44E}", "\u{1F44C}", "\u270C\uFE0F", "\u{1F91E}", "\u{1F91F}", "\u{1F918}", "\u{1F919}", "\u{1F44B}", "\u{1F91A}", "\u{1F590}\uFE0F", "\u270B", "\u{1F596}", "\u{1F44F}", "\u{1F64C}", "\u{1F91D}", "\u{1F64F}", "\u270D\uFE0F", "\u{1F485}", "\u{1F933}"]
432
+ },
433
+ {
434
+ name: "Hearts",
435
+ emojis: ["\u2764\uFE0F", "\u{1F9E1}", "\u{1F49B}", "\u{1F49A}", "\u{1F499}", "\u{1F49C}", "\u{1F5A4}", "\u{1F90D}", "\u{1F90E}", "\u{1F494}", "\u2763\uFE0F", "\u{1F495}", "\u{1F49E}", "\u{1F493}", "\u{1F497}", "\u{1F496}", "\u{1F498}", "\u{1F49D}", "\u{1F49F}", "\u2665\uFE0F"]
436
+ },
437
+ {
438
+ name: "Food",
439
+ emojis: ["\u{1F354}", "\u{1F35F}", "\u{1F355}", "\u{1F32D}", "\u{1F37F}", "\u{1F9C2}", "\u{1F953}", "\u{1F95A}", "\u{1F373}", "\u{1F9C7}", "\u{1F95E}", "\u{1F9C8}", "\u{1F35E}", "\u{1F950}", "\u{1F968}", "\u{1F96F}", "\u{1F956}", "\u{1F9C0}", "\u{1F957}", "\u{1F959}"]
440
+ },
441
+ {
442
+ name: "Drinks",
443
+ emojis: ["\u2615", "\u{1F375}", "\u{1F376}", "\u{1F37E}", "\u{1F377}", "\u{1F378}", "\u{1F379}", "\u{1F37A}", "\u{1F37B}", "\u{1F942}", "\u{1F943}", "\u{1F964}", "\u{1F9CB}", "\u{1F9C3}", "\u{1F9C9}", "\u{1F9CA}", "\u{1F962}", "\u{1F37D}\uFE0F", "\u{1F374}", "\u{1F944}"]
444
+ },
445
+ {
446
+ name: "Objects",
447
+ emojis: ["\u{1F381}", "\u{1F382}", "\u{1F388}", "\u{1F389}", "\u{1F38A}", "\u{1F380}", "\u{1F4F1}", "\u{1F4BB}", "\u231A", "\u{1F4F7}", "\u{1F511}", "\u{1F4B0}", "\u{1F4B3}", "\u{1F4DD}", "\u{1F4CC}", "\u{1F4CD}", "\u2702\uFE0F", "\u{1F50D}", "\u{1F4A1}", "\u{1F514}"]
448
+ }
449
+ ];
450
+ var EmojiPicker = ({ onSelect, className = "" }) => {
451
+ const [activeCategory, setActiveCategory] = useState4(0);
452
+ const handleSelect = useCallback(
453
+ (emoji) => {
454
+ onSelect(emoji);
455
+ },
456
+ [onSelect]
457
+ );
458
+ return /* @__PURE__ */ jsxs6("div", { className: `bg-white border border-gray-200 rounded-lg shadow-lg overflow-hidden ${className}`, children: [
459
+ /* @__PURE__ */ jsx9("div", { className: "flex border-b border-gray-200 overflow-x-auto", children: EMOJI_CATEGORIES.map((category, index) => /* @__PURE__ */ jsxs6(
460
+ "button",
461
+ {
462
+ onClick: () => setActiveCategory(index),
463
+ className: `px-3 py-2 text-xs font-medium whitespace-nowrap border-b-2 transition-colors ${activeCategory === index ? "border-blue-500 text-blue-600" : "border-transparent text-gray-500 hover:text-gray-700"}`,
464
+ children: [
465
+ category.emojis[0],
466
+ " ",
467
+ category.name
468
+ ]
469
+ },
470
+ category.name
471
+ )) }),
472
+ /* @__PURE__ */ jsx9("div", { className: "grid grid-cols-10 gap-0.5 p-2 max-h-[240px] overflow-y-auto", children: EMOJI_CATEGORIES[activeCategory].emojis.map((emoji) => /* @__PURE__ */ jsx9(
473
+ "button",
474
+ {
475
+ onClick: () => handleSelect(emoji),
476
+ className: "w-8 h-8 flex items-center justify-center text-lg hover:bg-gray-100 rounded transition-colors cursor-pointer",
477
+ "aria-label": emoji,
478
+ children: emoji
479
+ },
480
+ emoji
481
+ )) })
482
+ ] });
483
+ };
484
+
485
+ // src/MessageComposer.tsx
486
+ import { useState as useState5, useRef as useRef2, useCallback as useCallback2 } from "react";
487
+ import { Fragment as Fragment2, jsx as jsx10, jsxs as jsxs7 } from "react/jsx-runtime";
488
+ var DEFAULT_ACCEPTED_FILE_TYPES = "image/*,video/*,audio/*,.pdf,.doc,.docx,.xls,.xlsx,.zip";
489
+ var MessageComposer = ({
490
+ onSend,
491
+ onAttach,
492
+ value: externalValue,
493
+ onChange: externalOnChange,
494
+ features,
495
+ placeholder = "Digite uma mensagem...",
496
+ maxLength,
497
+ disabled = false,
498
+ acceptedFileTypes = DEFAULT_ACCEPTED_FILE_TYPES
499
+ }) => {
500
+ const [internalText, setInternalText] = useState5("");
501
+ const [showEmoji, setShowEmoji] = useState5(false);
502
+ const [attachments, setAttachments] = useState5([]);
503
+ const textareaRef = useRef2(null);
504
+ const fileInputRef = useRef2(null);
505
+ const isControlled = externalValue !== void 0;
506
+ const text = isControlled ? externalValue : internalText;
507
+ const setText = useCallback2((newText) => {
508
+ if (isControlled) {
509
+ externalOnChange?.(newText);
510
+ } else {
511
+ setInternalText(newText);
512
+ }
513
+ }, [isControlled, externalOnChange]);
514
+ const showEmojiButton = features?.emoji !== false;
515
+ const showAttachButton = features?.documents !== false;
516
+ const sendMessage = useCallback2(() => {
517
+ const trimmed = text.trim();
518
+ if (!trimmed && attachments.length === 0) return;
519
+ if (trimmed) onSend(trimmed);
520
+ for (const a of attachments) {
521
+ onAttach?.(a.file);
522
+ URL.revokeObjectURL(a.previewUrl);
523
+ }
524
+ if (!isControlled) setInternalText("");
525
+ setAttachments([]);
526
+ setShowEmoji(false);
527
+ if (textareaRef.current) textareaRef.current.style.height = "auto";
528
+ }, [text, attachments, onSend, onAttach, isControlled]);
529
+ const handleKeyDown = useCallback2((e) => {
530
+ if (e.key === "Enter" && !e.shiftKey) {
531
+ e.preventDefault();
532
+ if (!disabled) sendMessage();
533
+ }
534
+ }, [sendMessage, disabled]);
535
+ const handleInput = useCallback2(() => {
536
+ const ta = textareaRef.current;
537
+ if (!ta) return;
538
+ ta.style.height = "auto";
539
+ ta.style.height = `${Math.min(ta.scrollHeight, 100)}px`;
540
+ }, []);
541
+ const handleEmojiSelect = useCallback2((emoji) => {
542
+ const ta = textareaRef.current;
543
+ if (!ta) {
544
+ setText(text + emoji);
545
+ return;
546
+ }
547
+ const start = ta.selectionStart;
548
+ const end = ta.selectionEnd;
549
+ const newText = text.slice(0, start) + emoji + text.slice(end);
550
+ setText(newText);
551
+ requestAnimationFrame(() => {
552
+ ta.focus();
553
+ ta.setSelectionRange(start + emoji.length, start + emoji.length);
554
+ handleInput();
555
+ });
556
+ }, [text, setText, handleInput]);
557
+ const handleFileChange = useCallback2((e) => {
558
+ const files = e.target.files;
559
+ if (!files) return;
560
+ const previews = [];
561
+ for (let i = 0; i < files.length; i++) {
562
+ const file = files[i];
563
+ previews.push({ file, previewUrl: file.type.startsWith("image/") ? URL.createObjectURL(file) : "" });
564
+ }
565
+ setAttachments((prev) => [...prev, ...previews]);
566
+ if (fileInputRef.current) fileInputRef.current.value = "";
567
+ }, []);
568
+ const removeAttachment = useCallback2((index) => {
569
+ setAttachments((prev) => {
570
+ const next = [...prev];
571
+ if (next[index].previewUrl) URL.revokeObjectURL(next[index].previewUrl);
572
+ next.splice(index, 1);
573
+ return next;
574
+ });
575
+ }, []);
576
+ const insertFormatting = useCallback2((marker) => {
577
+ const ta = textareaRef.current;
578
+ if (!ta) return;
579
+ const start = ta.selectionStart;
580
+ const end = ta.selectionEnd;
581
+ const sel = text.slice(start, end);
582
+ if (sel) {
583
+ setText(text.slice(0, start) + marker + sel + marker + text.slice(end));
584
+ requestAnimationFrame(() => {
585
+ ta.focus();
586
+ ta.setSelectionRange(start + marker.length + sel.length + marker.length, start + marker.length + sel.length + marker.length);
587
+ });
588
+ }
589
+ }, [text, setText]);
590
+ const canSend = text.trim().length > 0 || attachments.length > 0;
591
+ const remaining = maxLength ? maxLength - text.length : null;
592
+ return /* @__PURE__ */ jsxs7("div", { children: [
593
+ attachments.length > 0 && /* @__PURE__ */ jsx10("div", { className: "flex gap-2 px-1 pb-2 overflow-x-auto", children: attachments.map((a, i) => /* @__PURE__ */ jsxs7("div", { className: "relative flex-shrink-0", children: [
594
+ a.previewUrl ? /* @__PURE__ */ jsx10("img", { src: a.previewUrl, alt: "", className: "w-16 h-16 object-cover rounded-lg border border-gray-200" }) : /* @__PURE__ */ jsx10("div", { className: "w-16 h-16 bg-gray-100 rounded-lg border border-gray-200 flex items-center justify-center", children: /* @__PURE__ */ jsxs7("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", stroke: "#9ca3af", strokeWidth: "1.5", children: [
595
+ /* @__PURE__ */ jsx10("path", { d: "M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" }),
596
+ /* @__PURE__ */ jsx10("polyline", { points: "14 2 14 8 20 8" })
597
+ ] }) }),
598
+ /* @__PURE__ */ jsx10("button", { onClick: () => removeAttachment(i), className: "absolute -top-2 -right-2 w-5 h-5 bg-gray-600 text-white rounded-full flex items-center justify-center hover:bg-gray-800 text-xs", children: "\u2715" })
599
+ ] }, i)) }),
600
+ /* @__PURE__ */ jsxs7("div", { className: "flex items-end gap-1.5 bg-[#f0f2f5] rounded-xl px-3 py-2", children: [
601
+ showEmojiButton && /* @__PURE__ */ jsxs7("div", { className: "relative flex-shrink-0", children: [
602
+ /* @__PURE__ */ jsx10("button", { onClick: () => setShowEmoji((v) => !v), className: "w-9 h-9 flex items-center justify-center rounded-full text-gray-500 hover:bg-gray-200 transition-colors", "aria-label": "Emoji", children: /* @__PURE__ */ jsxs7("svg", { width: "22", height: "22", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: [
603
+ /* @__PURE__ */ jsx10("circle", { cx: "12", cy: "12", r: "10" }),
604
+ /* @__PURE__ */ jsx10("path", { d: "M8 14s1.5 2 4 2 4-2 4-2" }),
605
+ /* @__PURE__ */ jsx10("circle", { cx: "9", cy: "9", r: "0.5", fill: "currentColor" }),
606
+ /* @__PURE__ */ jsx10("circle", { cx: "15", cy: "9", r: "0.5", fill: "currentColor" })
607
+ ] }) }),
608
+ showEmoji && /* @__PURE__ */ jsx10("div", { className: "absolute bottom-full left-0 mb-2 z-10", children: /* @__PURE__ */ jsx10(EmojiPicker, { onSelect: handleEmojiSelect }) })
609
+ ] }),
610
+ /* @__PURE__ */ jsx10(
611
+ "textarea",
612
+ {
613
+ ref: textareaRef,
614
+ value: text,
615
+ onChange: (e) => {
616
+ setText(e.target.value);
617
+ handleInput();
618
+ },
619
+ onKeyDown: handleKeyDown,
620
+ placeholder,
621
+ rows: 1,
622
+ disabled,
623
+ className: "flex-1 resize-none bg-transparent text-[15px] text-[#3b4a54] placeholder-[#8696a0] outline-none py-1.5 max-h-[100px] leading-relaxed",
624
+ style: { fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif" }
625
+ }
626
+ ),
627
+ showAttachButton && /* @__PURE__ */ jsxs7(Fragment2, { children: [
628
+ /* @__PURE__ */ jsx10("input", { ref: fileInputRef, type: "file", multiple: true, accept: acceptedFileTypes, onChange: handleFileChange, className: "hidden" }),
629
+ /* @__PURE__ */ jsx10("button", { onClick: () => fileInputRef.current?.click(), className: "w-9 h-9 flex items-center justify-center rounded-full text-gray-500 hover:bg-gray-200 flex-shrink-0 transition-colors", "aria-label": "Anexar", children: /* @__PURE__ */ jsx10("svg", { width: "22", height: "22", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", children: /* @__PURE__ */ jsx10("path", { d: "M21.44 11.05l-9.19 9.19a6 6 0 0 1-8.49-8.49l9.19-9.19a4 4 0 0 1 5.66 5.66l-9.2 9.19a2 2 0 0 1-2.83-2.83l8.49-8.48" }) }) })
630
+ ] }),
631
+ /* @__PURE__ */ jsx10(
632
+ "button",
633
+ {
634
+ onClick: sendMessage,
635
+ disabled: !canSend || disabled,
636
+ className: `w-10 h-10 flex items-center justify-center rounded-full flex-shrink-0 transition-all ${canSend && !disabled ? "bg-[#00a884] text-white hover:bg-[#06cf9c] shadow-sm" : "bg-gray-200 text-gray-400 cursor-not-allowed"}`,
637
+ "aria-label": "Enviar",
638
+ children: /* @__PURE__ */ jsx10("svg", { width: "20", height: "20", viewBox: "0 0 24 24", fill: "currentColor", children: /* @__PURE__ */ jsx10("path", { d: "M2.01 21L23 12 2.01 3 2 10l15 2-15 2z" }) })
639
+ }
640
+ )
641
+ ] }),
642
+ remaining !== null && /* @__PURE__ */ jsx10("div", { className: "flex justify-end mt-1 pr-1", children: /* @__PURE__ */ jsx10("span", { className: `text-xs ${remaining < 20 ? "text-red-500" : "text-gray-400"}`, children: remaining }) })
643
+ ] });
644
+ };
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
+ }
752
+
753
+ // src/SimpleEmojiPicker.tsx
754
+ import { useEffect as useEffect2, useRef as useRef4, useState as useState6 } from "react";
755
+ import { Smile } from "lucide-react";
756
+ import { jsx as jsx12, jsxs as jsxs9 } from "react/jsx-runtime";
757
+ var EMOJIS = [
758
+ "\u{1F600}",
759
+ "\u{1F602}",
760
+ "\u{1F923}",
761
+ "\u{1F60A}",
762
+ "\u{1F60D}",
763
+ "\u{1F970}",
764
+ "\u{1F618}",
765
+ "\u{1F61C}",
766
+ "\u{1F92A}",
767
+ "\u{1F60E}",
768
+ "\u{1F929}",
769
+ "\u{1F607}",
770
+ "\u{1F642}",
771
+ "\u{1F60F}",
772
+ "\u{1F60C}",
773
+ "\u{1F614}",
774
+ "\u{1F622}",
775
+ "\u{1F62D}",
776
+ "\u{1F624}",
777
+ "\u{1F621}",
778
+ "\u{1F97A}",
779
+ "\u{1F630}",
780
+ "\u{1F631}",
781
+ "\u{1F973}",
782
+ "\u{1F914}",
783
+ "\u{1F917}",
784
+ "\u{1F44D}",
785
+ "\u{1F44E}",
786
+ "\u{1F44F}",
787
+ "\u{1F64C}",
788
+ "\u{1F91D}",
789
+ "\u{1F4AA}",
790
+ "\u{1F64F}",
791
+ "\u2764\uFE0F",
792
+ "\u{1F9E1}",
793
+ "\u{1F49B}",
794
+ "\u{1F49A}",
795
+ "\u{1F499}",
796
+ "\u{1F49C}",
797
+ "\u{1F5A4}",
798
+ "\u{1F90D}",
799
+ "\u{1F494}",
800
+ "\u{1F525}",
801
+ "\u2B50",
802
+ "\u{1F389}",
803
+ "\u2728",
804
+ "\u{1F4AF}",
805
+ "\u2705",
806
+ "\u274C",
807
+ "\u26A0\uFE0F",
808
+ "\u{1F680}",
809
+ "\u{1F4A1}",
810
+ "\u{1F4CC}",
811
+ "\u{1F4CE}",
812
+ "\u{1F4DD}",
813
+ "\u{1F4CA}",
814
+ "\u{1F4C8}",
815
+ "\u{1F4B0}",
816
+ "\u{1F4B3}",
817
+ "\u{1F3E0}",
818
+ "\u{1F3E6}",
819
+ "\u{1F697}",
820
+ "\u2708\uFE0F",
821
+ "\u23F0",
822
+ "\u{1F4C5}",
823
+ "\u{1F514}",
824
+ "\u{1F4DE}",
825
+ "\u{1F4AC}",
826
+ "\u{1F5E8}\uFE0F",
827
+ "\u{1F44B}",
828
+ "\u{1F916}",
829
+ "\u{1F3AF}",
830
+ "\u{1F3C6}",
831
+ "\u{1F4CB}",
832
+ "\u{1F4C4}",
833
+ "\u{1F50D}",
834
+ "\u{1F393}",
835
+ "\u{1F31F}",
836
+ "\u{1F4BC}",
837
+ "\u{1F6E1}\uFE0F"
838
+ ];
839
+ function SimpleEmojiPicker({ onSelect, label = "Emojis", pickerWidth = "280px", pickerMaxHeight = "220px" }) {
840
+ const [open, setOpen] = useState6(false);
841
+ const containerRef = useRef4(null);
842
+ useEffect2(() => {
843
+ if (!open) return;
844
+ const handleClickOutside = (e) => {
845
+ if (containerRef.current && !containerRef.current.contains(e.target)) {
846
+ setOpen(false);
847
+ }
848
+ };
849
+ document.addEventListener("mousedown", handleClickOutside);
850
+ return () => document.removeEventListener("mousedown", handleClickOutside);
851
+ }, [open]);
852
+ return /* @__PURE__ */ jsxs9("div", { ref: containerRef, className: "relative flex-shrink-0", children: [
853
+ /* @__PURE__ */ jsx12(
854
+ "button",
855
+ {
856
+ type: "button",
857
+ onClick: () => setOpen((v) => !v),
858
+ 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
+ title: label,
860
+ children: /* @__PURE__ */ jsx12(Smile, { size: 15 })
861
+ }
862
+ ),
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(
864
+ "div",
865
+ {
866
+ className: "grid grid-cols-10 gap-0.5 overflow-y-auto",
867
+ style: { width: pickerWidth, maxHeight: pickerMaxHeight },
868
+ children: EMOJIS.map((emoji) => /* @__PURE__ */ jsx12(
869
+ "button",
870
+ {
871
+ 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
878
+ },
879
+ emoji
880
+ ))
881
+ }
882
+ ) })
883
+ ] });
884
+ }
885
+
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
+ // src/Avatar.tsx
903
+ import { jsx as jsx14 } from "react/jsx-runtime";
904
+ var sizeClasses = {
905
+ sm: "w-6 h-6 text-xs",
906
+ md: "w-8 h-8 text-sm",
907
+ lg: "w-10 h-10 text-base"
908
+ };
909
+ var bgColors = [
910
+ "bg-blue-500",
911
+ "bg-green-500",
912
+ "bg-purple-500",
913
+ "bg-pink-500",
914
+ "bg-yellow-500",
915
+ "bg-red-500",
916
+ "bg-indigo-500"
917
+ ];
918
+ function getInitials(name) {
919
+ if (!name) return "?";
920
+ const parts = name.trim().split(" ");
921
+ if (parts.length >= 2) return (parts[0][0] + parts[parts.length - 1][0]).toUpperCase();
922
+ return (parts[0]?.substring(0, 2) || "?").toUpperCase();
923
+ }
924
+ function getBgColor(name) {
925
+ if (!name) return "bg-gray-400";
926
+ const hash = name.charCodeAt(0) + (name.charCodeAt(name.length - 1) || 0);
927
+ return bgColors[hash % bgColors.length];
928
+ }
929
+ function Avatar({ name, avatarUrl, size = "md", className = "" }) {
930
+ const sizeClass = sizeClasses[size];
931
+ if (avatarUrl) {
932
+ return /* @__PURE__ */ jsx14(
933
+ "img",
934
+ {
935
+ src: avatarUrl,
936
+ alt: name || void 0,
937
+ className: `${sizeClass} rounded-full object-cover flex-shrink-0 ${className}`
938
+ }
939
+ );
940
+ }
941
+ const initials = getInitials(name);
942
+ const bgColor = getBgColor(name);
943
+ return /* @__PURE__ */ jsx14(
944
+ "div",
945
+ {
946
+ 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
949
+ }
950
+ );
951
+ }
952
+
953
+ // src/ConversationListItem.tsx
954
+ import { useMemo } from "react";
955
+
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)}`;
970
+ }
971
+ return number;
972
+ }
973
+ function phoneInitials(number) {
974
+ const digits = number.replace(/\D/g, "");
975
+ return digits.slice(-2);
976
+ }
977
+
978
+ // src/ConversationListItem.tsx
979
+ import { jsx as jsx15, jsxs as jsxs10 } from "react/jsx-runtime";
980
+ function formatRelativeTime(dateStr) {
981
+ try {
982
+ const date = new Date(dateStr);
983
+ const now = /* @__PURE__ */ new Date();
984
+ const diffMs = now.getTime() - date.getTime();
985
+ const diffMins = Math.floor(diffMs / 6e4);
986
+ const diffHours = Math.floor(diffMs / 36e5);
987
+ const diffDays = Math.floor(diffMs / 864e5);
988
+ if (diffMins < 1) return "Agora";
989
+ if (diffMins < 60) return `${diffMins}m`;
990
+ if (diffHours < 24) return `${diffHours}h`;
991
+ if (diffDays < 7) return `${diffDays}d`;
992
+ const day = date.getDate().toString().padStart(2, "0");
993
+ const month = (date.getMonth() + 1).toString().padStart(2, "0");
994
+ return `${day}/${month}`;
995
+ } catch {
996
+ return "";
997
+ }
998
+ }
999
+ function getWindowStatus(lastInboundAt) {
1000
+ if (!lastInboundAt) return null;
1001
+ try {
1002
+ const lastInbound = new Date(lastInboundAt).getTime();
1003
+ const now = Date.now();
1004
+ const hoursElapsed = (now - lastInbound) / 36e5;
1005
+ if (hoursElapsed <= 12) return { label: "active", color: "#22c55e" };
1006
+ if (hoursElapsed <= 21) return { label: "approaching", color: "#eab308" };
1007
+ if (hoursElapsed <= 24) return { label: "warning", color: "#f97316" };
1008
+ return { label: "expired", color: "#ef4444" };
1009
+ } catch {
1010
+ return null;
1011
+ }
1012
+ }
1013
+ function lastMessagePreview(conversation) {
1014
+ if (!conversation.lastContent) return { icon: null, preview: "" };
1015
+ const content = conversation.lastContent;
1016
+ if (content.startsWith("[image]") || content.startsWith("[Image]")) {
1017
+ return { icon: "\u{1F5BC}", preview: "Imagem" };
1018
+ }
1019
+ if (content.startsWith("[video]") || content.startsWith("[Video]")) {
1020
+ return { icon: "\u{1F3AC}", preview: "V\xEDdeo" };
1021
+ }
1022
+ if (content.startsWith("[audio]") || content.startsWith("[Audio]")) {
1023
+ return { icon: "\u{1F3A4}", preview: "\xC1udio" };
1024
+ }
1025
+ if (content.startsWith("[document]") || content.startsWith("[Document]")) {
1026
+ return { icon: "\u{1F4C4}", preview: "Documento" };
1027
+ }
1028
+ if (content.startsWith("[sticker]") || content.startsWith("[Sticker]")) {
1029
+ return { icon: "\u{1F3AF}", preview: "Sticker" };
1030
+ }
1031
+ return { icon: null, preview: content };
1032
+ }
1033
+ var ConversationListItem = ({
1034
+ conversation,
1035
+ active = false,
1036
+ selected = false,
1037
+ onClick,
1038
+ onSelect
1039
+ }) => {
1040
+ const isActive = active || selected;
1041
+ const windowStatus = useMemo(() => getWindowStatus(conversation.lastInboundAt), [conversation.lastInboundAt]);
1042
+ const preview = useMemo(() => lastMessagePreview(conversation), [conversation.lastContent]);
1043
+ const name = conversation.clientName || formatPhone(conversation.whatsappNumber) || conversation.whatsappNumber;
1044
+ const timestamp = formatRelativeTime(conversation.lastAt);
1045
+ const isInbound = conversation.lastDirection === "inbound";
1046
+ const handleClick = () => {
1047
+ onClick?.();
1048
+ onSelect?.(conversation.id);
1049
+ };
1050
+ return /* @__PURE__ */ jsxs10(
1051
+ "button",
1052
+ {
1053
+ 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]",
1055
+ style: {
1056
+ backgroundColor: isActive ? "#f0f2f5" : "transparent"
1057
+ },
1058
+ 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" })
1062
+ ] }),
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" })
1069
+ ] }),
1070
+ timestamp && /* @__PURE__ */ jsx15("span", { className: "text-xs text-[#667781] flex-shrink-0", children: timestamp })
1071
+ ] }),
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 }),
1075
+ preview.preview || "Sem mensagens"
1076
+ ] }),
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 })
1078
+ ] }),
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" })
1082
+ ] })
1083
+ ] })
1084
+ ]
1085
+ }
1086
+ );
1087
+ };
1088
+
1089
+ // 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);
1093
+ var nextId = 0;
1094
+ var singletonShow = null;
1095
+ var toast = {
1096
+ show(type, message) {
1097
+ if (singletonShow) {
1098
+ singletonShow(type, message);
1099
+ }
1100
+ }
1101
+ };
1102
+ var typeStyles = {
1103
+ success: "bg-green-600",
1104
+ error: "bg-red-600",
1105
+ info: "bg-blue-600"
1106
+ };
1107
+ 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" })
1112
+ ] }),
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" })
1117
+ ] })
1118
+ };
1119
+ function ToastItemComponent({ item, onRemove }) {
1120
+ const [exiting, setExiting] = useState7(false);
1121
+ useEffect3(() => {
1122
+ const timer = setTimeout(() => {
1123
+ setExiting(true);
1124
+ setTimeout(() => onRemove(item.id), 300);
1125
+ }, 5e3);
1126
+ return () => clearTimeout(timer);
1127
+ }, [item.id, onRemove]);
1128
+ return /* @__PURE__ */ jsxs11(
1129
+ "div",
1130
+ {
1131
+ 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
+ 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(
1136
+ "button",
1137
+ {
1138
+ onClick: () => {
1139
+ setExiting(true);
1140
+ setTimeout(() => onRemove(item.id), 300);
1141
+ },
1142
+ 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" })
1146
+ ] })
1147
+ }
1148
+ )
1149
+ ]
1150
+ }
1151
+ );
1152
+ }
1153
+ function ToastProvider({ children }) {
1154
+ const [items, setItems] = useState7([]);
1155
+ const show = useCallback3((type, message) => {
1156
+ const id = ++nextId;
1157
+ setItems((prev) => [...prev, { id, type, message }]);
1158
+ }, []);
1159
+ const remove = useCallback3((id) => {
1160
+ setItems((prev) => prev.filter((item) => item.id !== id));
1161
+ }, []);
1162
+ useEffect3(() => {
1163
+ singletonShow = show;
1164
+ return () => {
1165
+ singletonShow = null;
1166
+ };
1167
+ }, [show]);
1168
+ return /* @__PURE__ */ jsxs11(ToastContext.Provider, { value: { show }, children: [
1169
+ 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: `
1172
+ @keyframes slide-in {
1173
+ from { transform: translateX(100%); opacity: 0; }
1174
+ to { transform: translateX(0); opacity: 1; }
1175
+ }
1176
+ @keyframes slide-out {
1177
+ from { transform: translateX(0); opacity: 1; }
1178
+ to { transform: translateX(100%); opacity: 0; }
1179
+ }
1180
+ .animate-slide-in { animation: slide-in 0.3s ease-out; }
1181
+ .animate-slide-out { animation: slide-out 0.3s ease-in forwards; }
1182
+ ` })
1183
+ ] });
1184
+ }
1185
+ function useToast() {
1186
+ const context = useContext2(ToastContext);
1187
+ if (!context) {
1188
+ throw new Error("useToast must be used within a ToastProvider");
1189
+ }
1190
+ return context;
1191
+ }
1192
+
1193
+ // 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";
1196
+ function MessageText({ message }) {
1197
+ const [copied, setCopied] = useState8(false);
1198
+ const handleCopy = useCallback4(async () => {
1199
+ if (!message.content) return;
1200
+ try {
1201
+ await navigator.clipboard.writeText(message.content);
1202
+ setCopied(true);
1203
+ setTimeout(() => setCopied(false), 2e3);
1204
+ } catch {
1205
+ }
1206
+ }, [message.content]);
1207
+ if (message.type === "template")
1208
+ return /* @__PURE__ */ jsx17("p", { className: "text-sm italic text-[#667781]", children: message.content ?? "Template message" });
1209
+ return /* @__PURE__ */ jsxs12(
1210
+ "div",
1211
+ {
1212
+ onClick: handleCopy,
1213
+ className: "text-[14.2px] leading-[19px] whitespace-pre-wrap break-words select-all [&_strong]:font-bold [&_em]:italic [&_del]:line-through",
1214
+ 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!" })
1217
+ ]
1218
+ }
1219
+ );
1220
+ }
1221
+
1222
+ // src/MessageTimestamp.tsx
1223
+ import { jsx as jsx18, jsxs as jsxs13 } from "react/jsx-runtime";
1224
+ 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 }) })
1228
+ ] });
1229
+ }
1230
+
1231
+ // src/MessageTail.tsx
1232
+ import { jsx as jsx19 } from "react/jsx-runtime";
1233
+ function MessageTail({ isOutbound }) {
1234
+ 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" }) });
1237
+ }
1238
+
1239
+ // src/useWaitingNotifications.ts
1240
+ import { useState as useState9, useEffect as useEffect4, useRef as useRef5, useCallback as useCallback5 } from "react";
1241
+
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
1250
+ }) {
1251
+ return /* @__PURE__ */ jsx20(ConversationsContext.Provider, { value: { api, sse }, children });
1252
+ }
1253
+ function useConversations() {
1254
+ return useContext3(ConversationsContext);
1255
+ }
1256
+
1257
+ // 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;
1265
+ try {
1266
+ const result = await conversationsCtx.api.fetchConversations({ limit: 50 });
1267
+ setConversations(result);
1268
+ 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
+ }
1282
+ }
1283
+ }
1284
+ previousUnreadMap.current = currentMap;
1285
+ } catch {
1286
+ }
1287
+ }, [conversationsCtx]);
1288
+ useEffect4(() => {
1289
+ if (typeof window !== "undefined" && "Notification" in window && Notification.permission === "default") {
1290
+ Notification.requestPermission();
1291
+ }
1292
+ pollConversations();
1293
+ const interval = setInterval(pollConversations, 1e4);
1294
+ return () => clearInterval(interval);
1295
+ }, [pollConversations]);
1296
+ const unreadCount = conversations.reduce((sum, conv) => sum + conv.unread, 0);
1297
+ return { unreadCount, conversations };
1298
+ }
1299
+
1300
+ // src/settings/WhatsAppTemplateSettingsForm.tsx
1301
+ import { Plus, RefreshCw, Save, Trash2 } from "lucide-react";
1302
+ import { jsx as jsx21, jsxs as jsxs14 } from "react/jsx-runtime";
1303
+ var DEFAULT_LABELS = {
1304
+ sectionTitle: "WhatsApp",
1305
+ sectionDescription: "Template usado para reabrir a janela de 24h com o cliente.",
1306
+ templateLabel: "Template aprovado",
1307
+ refreshList: "Atualizar lista",
1308
+ errorLoading: "N\xE3o foi poss\xEDvel carregar os templates da Meta.",
1309
+ currentNameSaved: "Nome salvo atualmente:",
1310
+ selectPlaceholder: "Selecione um template\u2026",
1311
+ variablesLabel: "Vari\xE1veis do template",
1312
+ addVariable: "Adicionar",
1313
+ removeVariable: "Remover vari\xE1vel",
1314
+ variablesAvailableTitle: "Vari\xE1veis dispon\xEDveis",
1315
+ variablesAvailableHint: "Clique para inserir na pr\xF3xima vari\xE1vel vazia.",
1316
+ variablesMappingHint: "A ordem das vari\xE1veis abaixo deve corresponder \xE0 ordem {{1}}, {{2}}... do template.",
1317
+ noVariables: "Este template n\xE3o tem vari\xE1veis.",
1318
+ save: "Salvar",
1319
+ saving: "Salvando...",
1320
+ saveSuccess: "Configura\xE7\xE3o salva com sucesso."
1321
+ };
1322
+ function WhatsAppTemplateSettingsForm({
1323
+ templates,
1324
+ loadingTemplates = false,
1325
+ templatesError = false,
1326
+ onRefreshTemplates,
1327
+ selectedTemplateName,
1328
+ onSelectTemplate,
1329
+ variables,
1330
+ onVariablesChange,
1331
+ availableVariables = [],
1332
+ saving = false,
1333
+ saveSuccess = false,
1334
+ onSave,
1335
+ labels: labelsOverride
1336
+ }) {
1337
+ const labels = { ...DEFAULT_LABELS, ...labelsOverride };
1338
+ const selected = templates.find((template) => template.name === selectedTemplateName);
1339
+ function handleSelect(name) {
1340
+ const template = templates.find((t) => t.name === name);
1341
+ onSelectTemplate(name, template);
1342
+ }
1343
+ function updateVariable(index, value) {
1344
+ const next = [...variables];
1345
+ next[index] = value;
1346
+ onVariablesChange(next);
1347
+ }
1348
+ function removeVariable(index) {
1349
+ onVariablesChange(variables.filter((_, i) => i !== index));
1350
+ }
1351
+ function insertVariable(token) {
1352
+ const emptyIndex = variables.findIndex((value) => !value.trim());
1353
+ if (emptyIndex >= 0) {
1354
+ const next = [...variables];
1355
+ next[emptyIndex] = token;
1356
+ onVariablesChange(next);
1357
+ return;
1358
+ }
1359
+ onVariablesChange([...variables, token]);
1360
+ }
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 })
1365
+ ] }),
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(
1371
+ "button",
1372
+ {
1373
+ type: "button",
1374
+ onClick: onRefreshTemplates,
1375
+ disabled: loadingTemplates,
1376
+ className: "flex items-center gap-1 text-xs text-blue-600 dark:text-blue-400 hover:underline disabled:opacity-50",
1377
+ children: [
1378
+ /* @__PURE__ */ jsx21(RefreshCw, { size: 11, className: loadingTemplates ? "animate-spin" : "" }),
1379
+ labels.refreshList
1380
+ ]
1381
+ }
1382
+ )
1383
+ ] }),
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: [
1385
+ labels.errorLoading,
1386
+ /* @__PURE__ */ jsx21("br", {}),
1387
+ /* @__PURE__ */ jsxs14("span", { className: "text-gray-500 dark:text-gray-400 mt-0.5 block", children: [
1388
+ labels.currentNameSaved,
1389
+ " ",
1390
+ /* @__PURE__ */ jsx21("strong", { children: selectedTemplateName || "\u2014" })
1391
+ ] })
1392
+ ] }),
1393
+ loadingTemplates && /* @__PURE__ */ jsx21("div", { className: "h-10 rounded-xl bg-gray-100 dark:bg-gray-700 animate-pulse" }),
1394
+ !loadingTemplates && !templatesError && /* @__PURE__ */ jsxs14(
1395
+ "select",
1396
+ {
1397
+ value: selectedTemplateName,
1398
+ onChange: (e) => handleSelect(e.target.value),
1399
+ 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
+ children: [
1401
+ /* @__PURE__ */ jsx21("option", { value: "", children: labels.selectPlaceholder }),
1402
+ templates.map((template) => /* @__PURE__ */ jsxs14("option", { value: template.name, disabled: template.status !== "APPROVED", children: [
1403
+ template.displayName,
1404
+ " \xB7 ",
1405
+ template.shortId,
1406
+ " (",
1407
+ template.language,
1408
+ ") ",
1409
+ template.status !== "APPROVED" ? `[${template.status}]` : ""
1410
+ ] }, `${template.name}-${template.language}`))
1411
+ ]
1412
+ }
1413
+ ),
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 })
1415
+ ] }),
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(
1420
+ "button",
1421
+ {
1422
+ type: "button",
1423
+ onClick: () => onVariablesChange([...variables, ""]),
1424
+ className: "flex items-center gap-1 text-xs text-blue-600 dark:text-blue-400 hover:underline",
1425
+ children: [
1426
+ /* @__PURE__ */ jsx21(Plus, { size: 12 }),
1427
+ labels.addVariable
1428
+ ]
1429
+ }
1430
+ )
1431
+ ] }),
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(
1435
+ "button",
1436
+ {
1437
+ type: "button",
1438
+ onClick: () => insertVariable(available.token),
1439
+ title: `Inserir ${available.token}`,
1440
+ 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
+ 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: [
1444
+ "\xB7 ",
1445
+ available.label
1446
+ ] })
1447
+ ]
1448
+ },
1449
+ available.token
1450
+ )) }),
1451
+ /* @__PURE__ */ jsx21("p", { className: "mt-1.5 text-[11px] text-gray-400 dark:text-gray-500", children: labels.variablesAvailableHint })
1452
+ ] }),
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(
1458
+ "input",
1459
+ {
1460
+ type: "text",
1461
+ value: variable,
1462
+ onChange: (e) => updateVariable(index, e.target.value),
1463
+ placeholder: "Ex: {clientName}",
1464
+ 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
+ }
1466
+ ),
1467
+ /* @__PURE__ */ jsx21(
1468
+ "button",
1469
+ {
1470
+ type: "button",
1471
+ onClick: () => removeVariable(index),
1472
+ className: "text-gray-400 hover:text-red-500 dark:hover:text-red-400 transition-colors flex-shrink-0",
1473
+ title: labels.removeVariable,
1474
+ children: /* @__PURE__ */ jsx21(Trash2, { size: 14 })
1475
+ }
1476
+ )
1477
+ ] }, index)) })
1478
+ ] }),
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(
1481
+ "button",
1482
+ {
1483
+ type: "submit",
1484
+ disabled: saving || !selectedTemplateName.trim(),
1485
+ 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
+ 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 }),
1488
+ saving ? labels.saving : labels.save
1489
+ ]
1490
+ }
1491
+ ) })
1492
+ ] })
1493
+ ] });
1494
+ }
1495
+
1496
+ // 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 = {
1500
+ sectionTitle: "Criar novo template",
1501
+ sectionDescription: "Envia um template para aprova\xE7\xE3o da Meta.",
1502
+ nameLabel: "Nome do template",
1503
+ nameHint: "Somente letras min\xFAsculas, n\xFAmeros e underscore.",
1504
+ categoryLabel: "Categoria",
1505
+ languageLabel: "Idioma",
1506
+ headerLabel: "Cabe\xE7alho",
1507
+ headerHint: "Opcional \u2014 texto fixo exibido acima do corpo.",
1508
+ headerNone: "Sem cabe\xE7alho",
1509
+ headerText: "Texto",
1510
+ headerPlaceholder: "Ex: Ol\xE1 {{1}}!",
1511
+ bodyLabel: "Corpo da mensagem",
1512
+ bodyHint: "Use {{1}}, {{2}}... para vari\xE1veis din\xE2micas.",
1513
+ bodyPlaceholder: "Ex: Ol\xE1 {{1}}, sua simula\xE7\xE3o para {{2}} est\xE1 pronta.",
1514
+ detectedVariables: "Vari\xE1veis detectadas",
1515
+ variableLabel: (index) => `Vari\xE1vel ${index}`,
1516
+ configureHintPrefix: "Configure o mapeamento em ",
1517
+ configureHintLink: "Template usado para reabrir a janela de 24h",
1518
+ configureHintSuffix: " depois de aprovado.",
1519
+ footerLabel: "Rodap\xE9",
1520
+ footerHint: "Opcional \u2014 texto fixo exibido abaixo do corpo.",
1521
+ footerPlaceholder: "Ex: Empresa LTDA",
1522
+ previewLabel: "Pr\xE9via",
1523
+ previewOnline: "online",
1524
+ previewDescription: "Assim aparecer\xE1 para o cliente no WhatsApp.",
1525
+ previewHeaderFallback: "Cabe\xE7alho",
1526
+ previewBodyPlaceholder: "Corpo da mensagem aparecer\xE1 aqui\u2026",
1527
+ sendForApproval: "Enviar para aprova\xE7\xE3o",
1528
+ sending: "Enviando...",
1529
+ successStatus: (status) => `Status: ${status}`
1530
+ };
1531
+ var DEFAULT_VARIABLE_EXAMPLES = ["Nome do cliente", "Produto", "Valor", "Prazo", "Banco"];
1532
+ function detectVariables(bodyText) {
1533
+ const matches = bodyText.match(/\{\{(\d+)\}\}/g) ?? [];
1534
+ return [...new Set(matches.map((token) => token.replace(/[{}]/g, "")))].sort();
1535
+ }
1536
+ function fillPreviewTokens(text) {
1537
+ return text.replace(/\{\{1\}\}/g, "Jo\xE3o").replace(/\{\{2\}\}/g, "Im\xF3vel").replace(/\{\{3\}\}/g, "R$ 1.500");
1538
+ }
1539
+ function WhatsAppCreateTemplateForm({
1540
+ value,
1541
+ onChange,
1542
+ onSubmit,
1543
+ submitting = false,
1544
+ result,
1545
+ previewCompanyName = "WhatsApp Bot",
1546
+ labels: labelsOverride,
1547
+ variableExamples = DEFAULT_VARIABLE_EXAMPLES
1548
+ }) {
1549
+ const labels = { ...DEFAULT_LABELS2, ...labelsOverride };
1550
+ const detectedVariables = detectVariables(value.bodyText);
1551
+ function set(key, next) {
1552
+ onChange({ ...value, [key]: next });
1553
+ }
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 })
1558
+ ] }),
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(
1564
+ "input",
1565
+ {
1566
+ type: "text",
1567
+ value: value.name,
1568
+ onChange: (e) => set("name", e.target.value.toLowerCase().replace(/[^a-z0-9_]/g, "_")),
1569
+ placeholder: "reengajamento_cliente",
1570
+ 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
+ required: true
1572
+ }
1573
+ )
1574
+ ] }),
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(
1579
+ "select",
1580
+ {
1581
+ value: value.category,
1582
+ onChange: (e) => set("category", e.target.value),
1583
+ 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
+ children: [
1585
+ /* @__PURE__ */ jsx22("option", { value: "UTILITY", children: "UTILITY (Transacional)" }),
1586
+ /* @__PURE__ */ jsx22("option", { value: "MARKETING", children: "MARKETING (Promocional)" })
1587
+ ]
1588
+ }
1589
+ )
1590
+ ] }),
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(
1594
+ "select",
1595
+ {
1596
+ value: value.language,
1597
+ onChange: (e) => set("language", e.target.value),
1598
+ 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
+ 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" })
1603
+ ]
1604
+ }
1605
+ )
1606
+ ] })
1607
+ ] }),
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(
1613
+ "select",
1614
+ {
1615
+ value: value.headerType,
1616
+ onChange: (e) => set("headerType", e.target.value),
1617
+ 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
+ children: [
1619
+ /* @__PURE__ */ jsx22("option", { value: "NONE", children: labels.headerNone }),
1620
+ /* @__PURE__ */ jsx22("option", { value: "TEXT", children: labels.headerText })
1621
+ ]
1622
+ }
1623
+ ),
1624
+ value.headerType === "TEXT" && /* @__PURE__ */ jsx22(
1625
+ "input",
1626
+ {
1627
+ type: "text",
1628
+ value: value.headerText,
1629
+ onChange: (e) => set("headerText", e.target.value),
1630
+ placeholder: labels.headerPlaceholder,
1631
+ maxLength: 60,
1632
+ className: "flex-1 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"
1633
+ }
1634
+ )
1635
+ ] })
1636
+ ] }),
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(
1641
+ "textarea",
1642
+ {
1643
+ value: value.bodyText,
1644
+ onChange: (e) => set("bodyText", e.target.value),
1645
+ placeholder: labels.bodyPlaceholder,
1646
+ rows: 4,
1647
+ 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 resize-none placeholder:text-gray-400 dark:placeholder:text-gray-500",
1648
+ required: true
1649
+ }
1650
+ ),
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: [
1656
+ "= ",
1657
+ variableExamples[Number(num) - 1] ?? labels.variableLabel(Number(num))
1658
+ ] })
1659
+ ] }, num)) }),
1660
+ /* @__PURE__ */ jsxs15("p", { className: "text-gray-400 dark:text-gray-500 mt-1.5 text-xs", children: [
1661
+ labels.configureHintPrefix,
1662
+ /* @__PURE__ */ jsx22("strong", { children: labels.configureHintLink }),
1663
+ labels.configureHintSuffix
1664
+ ] })
1665
+ ] })
1666
+ ] }),
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(
1671
+ "input",
1672
+ {
1673
+ type: "text",
1674
+ value: value.footerText,
1675
+ onChange: (e) => set("footerText", e.target.value),
1676
+ placeholder: labels.footerPlaceholder,
1677
+ maxLength: 60,
1678
+ 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"
1679
+ }
1680
+ )
1681
+ ] }),
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 })
1690
+ ] })
1691
+ ] }),
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" })
1696
+ ] }) }),
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" })
1702
+ ] }) })
1703
+ ] })
1704
+ ] })
1705
+ ] }),
1706
+ result && /* @__PURE__ */ jsxs15(
1707
+ "div",
1708
+ {
1709
+ 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
+ children: [
1711
+ result.message,
1712
+ result.ok && result.status && /* @__PURE__ */ jsx22("p", { className: "text-xs mt-1 opacity-75", children: labels.successStatus(result.status) })
1713
+ ]
1714
+ }
1715
+ ),
1716
+ /* @__PURE__ */ jsx22("div", { className: "flex justify-end", children: /* @__PURE__ */ jsxs15(
1717
+ "button",
1718
+ {
1719
+ type: "submit",
1720
+ disabled: submitting || !value.name || !value.bodyText,
1721
+ 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
+ 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 }),
1724
+ submitting ? labels.sending : labels.sendForApproval
1725
+ ]
1726
+ }
1727
+ ) })
1728
+ ] })
1729
+ ] });
1730
+ }
1731
+
1732
+ // src/settings/WelcomeFarewellForm.tsx
1733
+ 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 = {
1736
+ sectionTitle: "Boas-vindas e encerramento",
1737
+ welcomeMessage: "Mensagem de boas-vindas",
1738
+ welcomeMessagePlaceholder: "Ol\xE1! Sou o assistente virtual da {empresa}...",
1739
+ welcomeMessageHint: "Enviada automaticamente no primeiro contato do cliente.",
1740
+ farewellMessage: "Mensagem de encerramento",
1741
+ farewellMessagePlaceholder: "Obrigado pelo contato! Se precisar de algo, \xE9 s\xF3 chamar.",
1742
+ farewellMessageHint: "Enviada quando o atendente encerra a conversa.",
1743
+ save: "Salvar",
1744
+ saving: "Salvando...",
1745
+ saveSuccess: "Mensagens salvas com sucesso."
1746
+ };
1747
+ var DEFAULT_WELCOME_PLACEHOLDERS = ["{empresa}", "{telefone}", "{email}", "{endereco}", "{maps}"];
1748
+ var DEFAULT_FAREWELL_PLACEHOLDERS = ["{empresa}", "{telefone}", "{email}"];
1749
+ function WelcomeFarewellForm({
1750
+ welcomeMessage,
1751
+ onWelcomeMessageChange,
1752
+ farewellMessage,
1753
+ onFarewellMessageChange,
1754
+ onSave,
1755
+ saving = false,
1756
+ saveSuccess = false,
1757
+ welcomePlaceholders = DEFAULT_WELCOME_PLACEHOLDERS,
1758
+ farewellPlaceholders = DEFAULT_FAREWELL_PLACEHOLDERS,
1759
+ labels: labelsOverride
1760
+ }) {
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" }),
1768
+ labels.welcomeMessage
1769
+ ] }),
1770
+ /* @__PURE__ */ jsx23(
1771
+ WhatsAppMessageEditor,
1772
+ {
1773
+ value: welcomeMessage,
1774
+ onChange: onWelcomeMessageChange,
1775
+ placeholder: labels.welcomeMessagePlaceholder,
1776
+ placeholders: welcomePlaceholders
1777
+ }
1778
+ ),
1779
+ /* @__PURE__ */ jsx23("p", { className: "mt-1.5 text-xs text-gray-500 dark:text-gray-400", children: labels.welcomeMessageHint })
1780
+ ] }),
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" }),
1784
+ labels.farewellMessage
1785
+ ] }),
1786
+ /* @__PURE__ */ jsx23(
1787
+ WhatsAppMessageEditor,
1788
+ {
1789
+ value: farewellMessage,
1790
+ onChange: onFarewellMessageChange,
1791
+ placeholder: labels.farewellMessagePlaceholder,
1792
+ placeholders: farewellPlaceholders
1793
+ }
1794
+ ),
1795
+ /* @__PURE__ */ jsx23("p", { className: "mt-1.5 text-xs text-gray-500 dark:text-gray-400", children: labels.farewellMessageHint })
1796
+ ] }),
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(
1799
+ "button",
1800
+ {
1801
+ type: "submit",
1802
+ disabled: saving,
1803
+ 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
+ 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 }),
1806
+ saving ? labels.saving : labels.save
1807
+ ]
1808
+ }
1809
+ ) })
1810
+ ] })
1811
+ ] });
1812
+ }
1813
+
1814
+ // src/settings/TopicsForm.tsx
1815
+ import { Megaphone, Save as Save3 } from "lucide-react";
1816
+ import { jsx as jsx24, jsxs as jsxs17 } from "react/jsx-runtime";
1817
+ var DEFAULT_LABELS4 = {
1818
+ sectionTitle: "T\xF3picos intermedi\xE1rios",
1819
+ sectionDescription: "Mensagens autom\xE1ticas para assuntos espec\xEDficos, disparadas quando o cliente demonstra interesse.",
1820
+ messagePlaceholder: "Digite a mensagem autom\xE1tica para este t\xF3pico...",
1821
+ saveButton: "Salvar t\xF3picos",
1822
+ saveSuccess: "T\xF3picos salvos com sucesso."
1823
+ };
1824
+ function TopicsForm({
1825
+ topics,
1826
+ onToggle,
1827
+ onMessageChange,
1828
+ onSave,
1829
+ saving = false,
1830
+ saveSuccess = false,
1831
+ labels: labelsOverride
1832
+ }) {
1833
+ const labels = { ...DEFAULT_LABELS4, ...labelsOverride };
1834
+ return /* @__PURE__ */ jsxs17("section", { className: "bg-white dark:bg-gray-800 rounded-xl border border-gray-200 dark:border-gray-700 overflow-hidden", children: [
1835
+ /* @__PURE__ */ jsxs17("div", { className: "px-6 py-4 border-b border-gray-100 dark:border-gray-700", children: [
1836
+ /* @__PURE__ */ jsxs17("h2", { className: "text-sm font-semibold text-gray-900 dark:text-gray-100 flex items-center gap-2", children: [
1837
+ /* @__PURE__ */ jsx24(Megaphone, { size: 16, className: "text-orange-600 dark:text-orange-400" }),
1838
+ labels.sectionTitle
1839
+ ] }),
1840
+ /* @__PURE__ */ jsx24("p", { className: "text-xs text-gray-500 dark:text-gray-400 mt-1", children: labels.sectionDescription })
1841
+ ] }),
1842
+ /* @__PURE__ */ jsxs17("form", { onSubmit: onSave, className: "p-6 space-y-6", children: [
1843
+ topics.map((topic) => /* @__PURE__ */ jsxs17("div", { className: "space-y-2", children: [
1844
+ /* @__PURE__ */ jsxs17("div", { className: "flex items-start gap-4", children: [
1845
+ /* @__PURE__ */ jsx24("div", { className: "flex-1", children: /* @__PURE__ */ jsx24("p", { className: "text-sm font-medium text-gray-900 dark:text-gray-100", children: topic.label }) }),
1846
+ /* @__PURE__ */ jsx24(
1847
+ "button",
1848
+ {
1849
+ type: "button",
1850
+ onClick: () => onToggle(topic.key),
1851
+ 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
+ role: "switch",
1853
+ "aria-checked": topic.enabled,
1854
+ children: /* @__PURE__ */ jsx24(
1855
+ "span",
1856
+ {
1857
+ 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"}`
1858
+ }
1859
+ )
1860
+ }
1861
+ )
1862
+ ] }),
1863
+ /* @__PURE__ */ jsx24(
1864
+ "textarea",
1865
+ {
1866
+ value: topic.message,
1867
+ onChange: (e) => onMessageChange(topic.key, e.target.value),
1868
+ placeholder: labels.messagePlaceholder,
1869
+ maxLength: 300,
1870
+ rows: 2,
1871
+ className: "w-full rounded-lg border border-gray-300 dark:border-gray-600 bg-white dark:bg-gray-900 px-3 py-2 text-sm text-gray-900 dark:text-gray-100 focus:outline-none focus:ring-2 focus:ring-blue-500"
1872
+ }
1873
+ )
1874
+ ] }, topic.key)),
1875
+ /* @__PURE__ */ jsxs17("div", { className: "flex items-center gap-3", children: [
1876
+ /* @__PURE__ */ jsxs17(
1877
+ "button",
1878
+ {
1879
+ type: "submit",
1880
+ disabled: saving,
1881
+ 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
+ children: [
1883
+ /* @__PURE__ */ jsx24(Save3, { size: 14 }),
1884
+ labels.saveButton
1885
+ ]
1886
+ }
1887
+ ),
1888
+ saveSuccess && /* @__PURE__ */ jsx24("span", { className: "text-sm text-green-600 dark:text-green-400", children: labels.saveSuccess })
1889
+ ] })
1890
+ ] })
1891
+ ] });
1892
+ }
1893
+
1894
+ // 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
1924
+ function useConversationMessages(conversationId, params) {
1925
+ const context = useConversations();
1926
+ if (!context) {
1927
+ throw new Error("useConversationMessages requires an ancestor <ConversationsProvider>");
1928
+ }
1929
+ const { api } = context;
1930
+ const { data, loading, error, refetch } = useAsyncResource(
1931
+ () => api.fetchMessages(conversationId, params),
1932
+ [conversationId, params?.limit, params?.before]
1933
+ );
1934
+ const sendMessage = useCallback7(
1935
+ async (text) => {
1936
+ const message = await api.sendMessage(conversationId, text);
1937
+ await refetch();
1938
+ return message;
1939
+ },
1940
+ [api, conversationId, refetch]
1941
+ );
1942
+ const sendMedia = useCallback7(
1943
+ async (mediaData) => {
1944
+ const message = await api.sendMedia(conversationId, mediaData);
1945
+ await refetch();
1946
+ return message;
1947
+ },
1948
+ [api, conversationId, refetch]
1949
+ );
1950
+ const sendTemplate = useCallback7(
1951
+ async (templateData) => {
1952
+ await api.sendTemplate(conversationId, templateData);
1953
+ await refetch();
1954
+ },
1955
+ [api, conversationId, refetch]
1956
+ );
1957
+ const markRead = useCallback7(() => api.markRead(conversationId), [api, conversationId]);
1958
+ return { messages: data ?? [], loading, error, refetch, sendMessage, sendMedia, sendTemplate, markRead };
1959
+ }
1960
+
1961
+ // src/hooks/useConversationList.ts
1962
+ function useConversationList(params) {
1963
+ const context = useConversations();
1964
+ if (!context) {
1965
+ throw new Error("useConversationList requires an ancestor <ConversationsProvider>");
1966
+ }
1967
+ const { api } = context;
1968
+ const { data, loading, error, refetch } = useAsyncResource(
1969
+ () => api.fetchConversations(params),
1970
+ [params?.page, params?.limit, params?.waitingHuman, params?.search]
1971
+ );
1972
+ return { conversations: data ?? [], loading, error, refetch };
1973
+ }
1974
+
1975
+ // src/hooks/useConversationContext.ts
1976
+ function useConversationContext(conversationId) {
1977
+ const conversationsContext = useConversations();
1978
+ if (!conversationsContext) {
1979
+ throw new Error("useConversationContext requires an ancestor <ConversationsProvider>");
1980
+ }
1981
+ const { api } = conversationsContext;
1982
+ const { data, loading, error, refetch } = useAsyncResource(() => api.getContext(conversationId), [conversationId]);
1983
+ return { context: data, loading, error, refetch };
1984
+ }
1985
+
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
+ // src/hooks/useConversationRealtime.ts
2001
+ import { useEffect as useEffect6, useRef as useRef7 } from "react";
2002
+ function useConversationRealtime(conversationId, onEvent) {
2003
+ const context = useConversations();
2004
+ const onEventRef = useRef7(onEvent);
2005
+ onEventRef.current = onEvent;
2006
+ useEffect6(() => {
2007
+ if (!context || !conversationId) return;
2008
+ const source = context.sse.connectConversationStream(conversationId);
2009
+ const handler = (event) => onEventRef.current(event);
2010
+ source.addEventListener("message", handler);
2011
+ return () => {
2012
+ source.removeEventListener("message", handler);
2013
+ source.close();
2014
+ };
2015
+ }, [context, conversationId]);
2016
+ }
2017
+ function useGlobalRealtime(onEvent) {
2018
+ const context = useConversations();
2019
+ const onEventRef = useRef7(onEvent);
2020
+ onEventRef.current = onEvent;
2021
+ useEffect6(() => {
2022
+ if (!context) return;
2023
+ const source = context.sse.connectGlobalStream();
2024
+ const handler = (event) => onEventRef.current(event);
2025
+ source.addEventListener("message", handler);
2026
+ return () => {
2027
+ source.removeEventListener("message", handler);
2028
+ source.close();
2029
+ };
2030
+ }, [context]);
2031
+ }
2032
+ export {
2033
+ AudioPlayer,
2034
+ Avatar,
2035
+ ConversationListItem,
2036
+ ConversationLocalesProvider,
2037
+ ConversationWallpaper,
2038
+ ConversationsProvider,
2039
+ DateDivider,
2040
+ EmojiPicker,
2041
+ FileIcon,
2042
+ Lightbox,
2043
+ MediaRenderer,
2044
+ MessageBubble,
2045
+ MessageComposer,
2046
+ MessageTail,
2047
+ MessageText,
2048
+ MessageTimestamp,
2049
+ SimpleEmojiPicker,
2050
+ StatusTicks,
2051
+ ToastProvider,
2052
+ TopicsForm,
2053
+ WelcomeFarewellForm,
2054
+ WhatsAppCreateTemplateForm,
2055
+ WhatsAppMessageEditor,
2056
+ WhatsAppTemplateSettingsForm,
2057
+ formatFileSize,
2058
+ formatPhone,
2059
+ formatTimestamp,
2060
+ htmlToWA,
2061
+ parseWhatsAppFormatting,
2062
+ phoneInitials,
2063
+ toast,
2064
+ useConversationContext,
2065
+ useConversationDocuments,
2066
+ useConversationList,
2067
+ useConversationLocales,
2068
+ useConversationMessages,
2069
+ useConversationRealtime,
2070
+ useConversations,
2071
+ useDarkMode,
2072
+ useGlobalRealtime,
2073
+ useToast,
2074
+ useWaitingNotifications,
2075
+ waToHTML,
2076
+ waToHTMLInline
2077
+ };