@veploy/ploychat 0.1.2 → 0.1.3

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.
package/dist/index.js CHANGED
@@ -46,7 +46,7 @@ function createPloyClient(config = {}) {
46
46
  });
47
47
  }
48
48
  const live = !!(config.apiBase && config.workspaceId && config.getToken);
49
- return { apiFetch, supabase, live };
49
+ return { apiFetch, supabase, live, agentName: config.agentName || null };
50
50
  }
51
51
 
52
52
  // src/lib/context.jsx
@@ -56,7 +56,7 @@ function PloyConfigProvider({ config, children }) {
56
56
  const value = useMemo(() => {
57
57
  const client = createPloyClient(config);
58
58
  return { config, client };
59
- }, [config.apiBase, config.workspaceId, config.getToken, config.supabase, config.supabaseUrl, config.supabaseAnonKey]);
59
+ }, [config.apiBase, config.workspaceId, config.getToken, config.supabase, config.supabaseUrl, config.supabaseAnonKey, config.agentName]);
60
60
  return /* @__PURE__ */ jsx(PloyContext.Provider, { value, children });
61
61
  }
62
62
  function usePloy() {
@@ -100,18 +100,21 @@ function relTime(iso) {
100
100
  if (h < 24) return `${h}h`;
101
101
  return `${Math.floor(h / 24)}d`;
102
102
  }
103
- function nowHHMM() {
103
+ function nowHHMM2() {
104
104
  const d = /* @__PURE__ */ new Date();
105
105
  return `${pad(d.getHours())}:${pad(d.getMinutes())}`;
106
106
  }
107
107
  var STATUS_MAP = { open: "open", pending: "in_progress", resolved: "resolved", snoozed: "waiting" };
108
108
  function mapConversation(c) {
109
109
  const contact = c.contact || {};
110
- const name = contact.name || contact.push_name || contact.phone_e164 || contact.external_id || "Contato";
110
+ const isGroup = String(contact.external_id || "").endsWith("@g.us");
111
+ const name = contact.name || (isGroup ? "Grupo" : contact.push_name || contact.phone_e164 || contact.external_id || "Contato");
111
112
  return {
112
113
  id: c.id,
113
114
  name,
114
- phone: contact.phone_e164 || "",
115
+ isGroup,
116
+ phone: isGroup ? "" : contact.phone_e164 || "",
117
+ avatar: contact.profile_pic_url || null,
115
118
  avatarColor: colorFor(c.id),
116
119
  preview: c.last_message_preview || "",
117
120
  waitFor: relTime(c.last_message_at),
@@ -119,12 +122,17 @@ function mapConversation(c) {
119
122
  sla: "ok",
120
123
  priority: c.priority || "normal",
121
124
  status: STATUS_MAP[c.status] || "open",
122
- ticket: c.property_ref ? `REF ${c.property_ref}` : `#${String(c.id).slice(0, 4)}`,
125
+ // só mostra "ticket" quando REF real de imóvel; senão nada (o #id era ruído)
126
+ ticket: c.property_ref ? `REF ${c.property_ref}` : null,
123
127
  starred: false,
128
+ aiSummary: c.ai_summary || "",
129
+ // resumo IA persistido (tarja)
130
+ aiSummaryCount: c.ai_summary_msg_count || 0,
124
131
  _raw: c
125
132
  };
126
133
  }
127
134
  function mapMessage(m) {
135
+ var _a, _b;
128
136
  const ts = m.sent_at || m.created_at;
129
137
  if (m.kind === "note") {
130
138
  return { id: m.id, side: "note", kind: "text", text: m.text || "", time: timeOf(ts), day: dayOf(ts), author: m.author_name, _raw: m };
@@ -144,8 +152,11 @@ function mapMessage(m) {
144
152
  // 'in' | 'out'
145
153
  kind,
146
154
  text,
155
+ // em grupos, quem enviou (participante) — vem do pushName no payload bruto
156
+ author: m.direction === "in" ? ((_a = m.raw) == null ? void 0 : _a.pushName) || ((_b = m.raw) == null ? void 0 : _b.push_name) || "" : "",
147
157
  caption: m.caption || "",
148
158
  meta: m.media_mime || "",
159
+ filename: m.media_filename || "",
149
160
  mediaUrl: m.media_url || "",
150
161
  time: timeOf(ts),
151
162
  day: dayOf(ts),
@@ -161,14 +172,21 @@ function useInbox() {
161
172
  const [conversations2, setConversations] = useState([]);
162
173
  const [activeId, setActiveId] = useState(null);
163
174
  const [messages, setMessages] = useState([]);
175
+ const [messagesLoading, setMessagesLoading] = useState(false);
164
176
  const [channels, setChannels] = useState([]);
177
+ const [channelsLoaded, setChannelsLoaded] = useState(false);
165
178
  const [agents, setAgents] = useState([]);
166
179
  const [me, setMe] = useState(null);
180
+ const [canDelete, setCanDelete] = useState(false);
181
+ const [scope, setScope] = useState("all");
182
+ const [draftConv, setDraftConv] = useState(null);
167
183
  const [tenantId, setTenantId] = useState(null);
184
+ const [typingConvId, setTypingConvId] = useState(null);
168
185
  const [loading, setLoading] = useState(live);
169
186
  const [error, setError] = useState(null);
170
187
  const activeIdRef = useRef(null);
171
188
  activeIdRef.current = activeId;
189
+ const typingTimerRef = useRef(null);
172
190
  const api = client.apiFetch;
173
191
  useEffect(() => {
174
192
  if (!live) return;
@@ -176,6 +194,8 @@ function useInbox() {
176
194
  var _a;
177
195
  if ((_a = m == null ? void 0 : m.user) == null ? void 0 : _a.id) setMe(m.user.id);
178
196
  if (m == null ? void 0 : m.tenant_id) setTenantId(m.tenant_id);
197
+ setCanDelete(!!(m == null ? void 0 : m.can_delete));
198
+ if (m == null ? void 0 : m.inbox_scope) setScope(m.inbox_scope);
179
199
  }).catch(() => {
180
200
  });
181
201
  }, [live, api]);
@@ -195,13 +215,28 @@ function useInbox() {
195
215
  setLoading(false);
196
216
  }
197
217
  }, [live, api]);
198
- const loadMessages = useCallback(async (convId) => {
218
+ const msgCacheRef = useRef({});
219
+ const loadMessages = useCallback(async (convId, opts = {}) => {
199
220
  if (!live || !convId) return;
221
+ if (!opts.silent) {
222
+ const cached = msgCacheRef.current[convId];
223
+ if (cached) {
224
+ setMessages(cached);
225
+ setMessagesLoading(false);
226
+ } else {
227
+ setMessages([]);
228
+ setMessagesLoading(true);
229
+ }
230
+ }
200
231
  try {
201
232
  const res = await api(`/v1/conversations/${convId}/messages?limit=50`);
202
- setMessages(((res == null ? void 0 : res.messages) || []).slice().reverse().map(mapMessage));
233
+ const mapped = ((res == null ? void 0 : res.messages) || []).slice().reverse().map(mapMessage);
234
+ msgCacheRef.current[convId] = mapped;
235
+ if (activeIdRef.current === convId) setMessages(mapped);
203
236
  } catch (e) {
204
237
  setError(e.message);
238
+ } finally {
239
+ if (activeIdRef.current === convId) setMessagesLoading(false);
205
240
  }
206
241
  }, [live, api]);
207
242
  const loadChannels = useCallback(async () => {
@@ -212,6 +247,8 @@ function useInbox() {
212
247
  return l;
213
248
  } catch {
214
249
  return [];
250
+ } finally {
251
+ setChannelsLoaded(true);
215
252
  }
216
253
  }, [live, api]);
217
254
  const loadAgents = useCallback(async () => {
@@ -232,6 +269,9 @@ function useInbox() {
232
269
  useEffect(() => {
233
270
  loadMessages(activeId);
234
271
  }, [activeId, loadMessages]);
272
+ useEffect(() => {
273
+ if (draftConv && conversations2.some((c) => c.id === draftConv.id)) setDraftConv(null);
274
+ }, [conversations2, draftConv]);
235
275
  const markRead = useCallback(async (convId) => {
236
276
  if (!live || !convId) return;
237
277
  setConversations((cs) => cs.map((c) => c.id === convId ? { ...c, unread: 0 } : c));
@@ -252,12 +292,13 @@ function useInbox() {
252
292
  const send = useCallback(async (text, isNote = false) => {
253
293
  const t = (text || "").trim();
254
294
  if (!t || !live || !activeIdRef.current) return;
255
- const tmpId = `tmp${Date.now()}`;
256
- setMessages((ms) => [...ms, isNote ? { id: tmpId, side: "note", kind: "text", text: t, time: nowHHMM() } : { id: tmpId, side: "out", kind: "text", text: t, time: nowHHMM(), status: "pending" }]);
295
+ const cid = typeof crypto !== "undefined" && crypto.randomUUID ? crypto.randomUUID() : null;
296
+ const tmpId = cid || `tmp${Date.now()}`;
297
+ setMessages((ms) => [...ms, isNote ? { id: tmpId, side: "note", kind: "text", text: t, time: nowHHMM2() } : { id: tmpId, side: "out", kind: "text", text: t, time: nowHHMM2(), status: "pending" }]);
257
298
  try {
258
299
  const saved = await api(`/v1/conversations/${activeIdRef.current}/messages`, {
259
300
  method: "POST",
260
- body: isNote ? { text: t, note: true } : { text: t }
301
+ body: isNote ? { text: t, note: true, client_id: cid || void 0 } : { text: t, author_name: client.agentName || void 0, client_id: cid || void 0 }
261
302
  });
262
303
  setMessages((ms) => ms.map((m) => m.id === tmpId ? mapMessage(saved) : m));
263
304
  } catch {
@@ -266,12 +307,13 @@ function useInbox() {
266
307
  }, [live, api]);
267
308
  const sendMedia = useCallback(async ({ data, mime, kind, filename, previewUrl, caption }) => {
268
309
  if (!live || !activeIdRef.current || !data) return;
269
- const tmpId = `tmp${Date.now()}`;
270
- setMessages((ms) => [...ms, { id: tmpId, side: "out", kind, mediaUrl: previewUrl || "", caption: caption || "", meta: filename || mime || "", time: nowHHMM(), status: "pending" }]);
310
+ const cid = typeof crypto !== "undefined" && crypto.randomUUID ? crypto.randomUUID() : null;
311
+ const tmpId = cid || `tmp${Date.now()}`;
312
+ setMessages((ms) => [...ms, { id: tmpId, side: "out", kind, mediaUrl: previewUrl || "", caption: caption || "", filename: filename || "", meta: mime || "", time: nowHHMM2(), status: "pending" }]);
271
313
  try {
272
314
  const saved = await api(`/v1/conversations/${activeIdRef.current}/messages`, {
273
315
  method: "POST",
274
- body: { media: { data, mime, kind, filename }, caption }
316
+ body: { media: { data, mime, kind, filename }, caption, author_name: client.agentName || void 0, client_id: cid || void 0 }
275
317
  });
276
318
  const mapped = mapMessage(saved);
277
319
  setMessages((ms) => ms.map((m) => m.id === tmpId ? { ...mapped, mediaUrl: mapped.mediaUrl || previewUrl || "" } : m));
@@ -279,6 +321,43 @@ function useInbox() {
279
321
  setMessages((ms) => ms.map((m) => m.id === tmpId ? { ...m, status: "failed" } : m));
280
322
  }
281
323
  }, [live, api]);
324
+ const deleteMessage = useCallback(async (messageId) => {
325
+ if (!live || !messageId || !activeIdRef.current) return;
326
+ const convId = activeIdRef.current;
327
+ setMessages((ms) => ms.filter((m) => m.id !== messageId));
328
+ try {
329
+ await api(`/v1/conversations/${convId}/messages/${messageId}`, { method: "DELETE" });
330
+ } catch (e) {
331
+ setError(e.message);
332
+ loadMessages(convId);
333
+ }
334
+ }, [live, api, loadMessages]);
335
+ const setTyping = useCallback((convId, state) => {
336
+ if (!live || !convId) return;
337
+ api(`/v1/conversations/${convId}/typing`, { method: "POST", body: { state } }).catch(() => {
338
+ });
339
+ }, [live, api]);
340
+ const createConversation = useCallback(async (phone, text) => {
341
+ if (!live) return null;
342
+ const conv = await api("/v1/conversations", {
343
+ method: "POST",
344
+ body: { phone, text: text || void 0, author_name: client.agentName || void 0 }
345
+ });
346
+ const mapped = mapConversation(conv);
347
+ if (conv == null ? void 0 : conv.last_message_at) {
348
+ setDraftConv(null);
349
+ await loadConversations();
350
+ } else {
351
+ setDraftConv(mapped);
352
+ }
353
+ if (mapped == null ? void 0 : mapped.id) setActiveId(mapped.id);
354
+ return conv;
355
+ }, [live, api, loadConversations]);
356
+ const summarize = useCallback(async (convId) => {
357
+ if (!live || !convId) return "";
358
+ const r = await api(`/v1/ai/conversations/${convId}/summary`, { method: "POST" });
359
+ return (r == null ? void 0 : r.summary) || "";
360
+ }, [live, api]);
282
361
  const createChannel = useCallback((displayName) => api("/v1/channels", { method: "POST", body: { display_name: displayName } }), [api]);
283
362
  const connect = useCallback((id) => api(`/v1/channels/${id}/connect`, { method: "POST" }), [api]);
284
363
  const channelStatus = useCallback((id) => api(`/v1/channels/${id}/status`), [api]);
@@ -287,17 +366,26 @@ function useInbox() {
287
366
  const sb = client.supabase;
288
367
  if (!sb) return;
289
368
  const ch = sb.channel(`inbox:${tenantId}`).on("broadcast", { event: "message.created" }, ({ payload }) => {
290
- if ((payload == null ? void 0 : payload.conversation_id) === activeIdRef.current) {
291
- setMessages((ms) => ms.some((m) => m.id === payload.id) ? ms : [...ms, mapMessage(payload)]);
292
- }
369
+ if ((payload == null ? void 0 : payload.conversation_id) === activeIdRef.current) loadMessages(activeIdRef.current, { silent: true });
293
370
  loadConversations();
294
371
  }).on("broadcast", { event: "message.updated" }, ({ payload }) => {
295
372
  setMessages((ms) => ms.map((m) => m.id === (payload == null ? void 0 : payload.id) ? { ...m, status: payload.status || m.status } : m));
296
- }).on("broadcast", { event: "conversation.updated" }, () => loadConversations()).on("broadcast", { event: "channel.updated" }, () => loadChannels()).subscribe();
373
+ }).on("broadcast", { event: "conversation.updated" }, () => loadConversations()).on("broadcast", { event: "channel.updated" }, () => loadChannels()).on("broadcast", { event: "message.deleted" }, ({ payload }) => {
374
+ setMessages((ms) => ms.filter((m) => m.id !== (payload == null ? void 0 : payload.id)));
375
+ }).on("broadcast", { event: "presence.update" }, ({ payload }) => {
376
+ if ((payload == null ? void 0 : payload.state) === "composing") {
377
+ setTypingConvId(payload.conversation_id);
378
+ clearTimeout(typingTimerRef.current);
379
+ typingTimerRef.current = setTimeout(() => setTypingConvId(null), 6e3);
380
+ } else {
381
+ setTypingConvId((cur) => cur === (payload == null ? void 0 : payload.conversation_id) ? null : cur);
382
+ }
383
+ }).subscribe();
297
384
  return () => {
298
385
  sb.removeChannel(ch);
386
+ clearTimeout(typingTimerRef.current);
299
387
  };
300
- }, [live, tenantId, client, loadConversations, loadChannels]);
388
+ }, [live, tenantId, client, loadConversations, loadChannels, loadMessages]);
301
389
  return {
302
390
  live,
303
391
  loading,
@@ -306,6 +394,9 @@ function useInbox() {
306
394
  activeId,
307
395
  setActiveId,
308
396
  messages,
397
+ messagesLoading,
398
+ scope,
399
+ draftConv,
309
400
  send,
310
401
  sendMedia,
311
402
  markRead,
@@ -314,10 +405,17 @@ function useInbox() {
314
405
  agents,
315
406
  loadAgents,
316
407
  channels,
408
+ channelsLoaded,
317
409
  loadChannels,
318
410
  createChannel,
411
+ createConversation,
319
412
  connect,
320
- channelStatus
413
+ channelStatus,
414
+ setTyping,
415
+ typingConvId,
416
+ deleteMessage,
417
+ canDelete,
418
+ summarize
321
419
  };
322
420
  }
323
421
 
@@ -517,6 +615,7 @@ function TransferModal({ inbox, conversationId, currentAssignee, onClose }) {
517
615
 
518
616
  // src/components/App.jsx
519
617
  import { Fragment, jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
618
+ var REGEN_EVERY = 3;
520
619
  var Icon = ({ d, className = "w-4 h-4", stroke = 1.8, fill = "none" }) => /* @__PURE__ */ jsx5("svg", { viewBox: "0 0 24 24", className, fill, stroke: "currentColor", strokeWidth: stroke, strokeLinecap: "round", strokeLinejoin: "round", children: d });
521
620
  var I = {
522
621
  inbox: /* @__PURE__ */ jsx5(Icon, { d: /* @__PURE__ */ jsxs3(Fragment, { children: [
@@ -745,14 +844,29 @@ var I = {
745
844
  /* @__PURE__ */ jsx5("path", { d: "M14 21h.01" }),
746
845
  /* @__PURE__ */ jsx5("path", { d: "M21 21v-3.5" }),
747
846
  /* @__PURE__ */ jsx5("path", { d: "M17.5 21h.01" })
847
+ ] }) }),
848
+ back: /* @__PURE__ */ jsx5(Icon, { d: /* @__PURE__ */ jsxs3(Fragment, { children: [
849
+ /* @__PURE__ */ jsx5("path", { d: "M19 12H5" }),
850
+ /* @__PURE__ */ jsx5("path", { d: "M12 19l-7-7 7-7" })
851
+ ] }) }),
852
+ trash: /* @__PURE__ */ jsx5(Icon, { d: /* @__PURE__ */ jsxs3(Fragment, { children: [
853
+ /* @__PURE__ */ jsx5("polyline", { points: "3 6 5 6 21 6" }),
854
+ /* @__PURE__ */ jsx5("path", { d: "M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" })
855
+ ] }) }),
856
+ sparkle: /* @__PURE__ */ jsx5(Icon, { d: /* @__PURE__ */ jsx5("path", { d: "M12 3l1.9 5.1L19 10l-5.1 1.9L12 17l-1.9-5.1L5 10l5.1-1.9L12 3Z" }) }),
857
+ warn: /* @__PURE__ */ jsx5(Icon, { d: /* @__PURE__ */ jsxs3(Fragment, { children: [
858
+ /* @__PURE__ */ jsx5("path", { d: "M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0Z" }),
859
+ /* @__PURE__ */ jsx5("line", { x1: "12", y1: "9", x2: "12", y2: "13" }),
860
+ /* @__PURE__ */ jsx5("line", { x1: "12", y1: "17", x2: "12.01", y2: "17" })
748
861
  ] }) })
749
862
  };
750
- var Avatar = ({ name, size = "md", color = "bg-violet-500", online }) => {
751
- const sizes = { sm: "w-7 h-7 text-[11px]", md: "w-9 h-9 text-xs", lg: "w-11 h-11 text-sm", xl: "w-14 h-14 text-base" };
752
- const initials = name.split(" ").map((p) => p[0]).slice(0, 2).join("").toUpperCase();
863
+ var Avatar = ({ name = "", src, size = "md", color = "bg-violet-500", online, dot }) => {
864
+ const sizes = { xs: "w-5 h-5 text-[9px]", sm: "w-7 h-7 text-[11px]", md: "w-9 h-9 text-xs", lg: "w-11 h-11 text-sm", xl: "w-14 h-14 text-base" };
865
+ const initials = (name || "").split(" ").filter(Boolean).map((p) => p[0]).slice(0, 2).join("").toUpperCase() || "?";
866
+ const dotColor = dot || (online ? "bg-emerald-500" : null);
753
867
  return /* @__PURE__ */ jsxs3("div", { className: "relative inline-flex", children: [
754
- /* @__PURE__ */ jsx5("div", { className: `${sizes[size]} ${color} rounded-full text-white font-semibold inline-flex items-center justify-center select-none shrink-0`, children: initials }),
755
- online && /* @__PURE__ */ jsx5("span", { className: "absolute bottom-0 right-0 w-2.5 h-2.5 rounded-full bg-emerald-500 ring-2 ring-white" })
868
+ src ? /* @__PURE__ */ jsx5("img", { src, alt: name, className: `${sizes[size]} rounded-full object-cover select-none shrink-0` }) : /* @__PURE__ */ jsx5("div", { className: `${sizes[size]} ${color} rounded-full text-white font-semibold inline-flex items-center justify-center select-none shrink-0`, children: initials }),
869
+ dotColor && /* @__PURE__ */ jsx5("span", { className: `absolute bottom-0 right-0 w-2.5 h-2.5 rounded-full ${dotColor} ring-2 ring-white` })
756
870
  ] });
757
871
  };
758
872
  var Pill = ({ children, tone = "slate" }) => {
@@ -878,15 +992,12 @@ var quickReplies = [
878
992
  { id: "q3", label: "Agendar visita", text: "Posso j\xE1 agendar a visita pra voc\xEA. Qual o melhor dia e hor\xE1rio essa semana?" },
879
993
  { id: "q4", label: "Documentos", text: "Pra avan\xE7ar com a proposta, vou precisar de RG, CPF, comprovante de renda e resid\xEAncia. Pode me enviar por aqui mesmo." }
880
994
  ];
881
- var statusInfo = {
882
- open: { tone: "sky", label: "Aberto" },
883
- in_progress: { tone: "amber", label: "Em atendimento" },
884
- waiting: { tone: "violet", label: "Aguardando cliente" },
885
- resolved: { tone: "emerald", label: "Resolvido" }
886
- };
887
- function App() {
995
+ function App({ onBack, agent, onUnreadChange }) {
888
996
  var _a;
889
997
  const inbox = useInbox();
998
+ const [available, setAvailable] = useState4(true);
999
+ const typingSentRef = useRef3(0);
1000
+ const typingStopRef = useRef3(null);
890
1001
  const live = inbox.live;
891
1002
  const [mockActiveId, setMockActiveId] = useState4("c1");
892
1003
  const activeId = live ? inbox.activeId : mockActiveId;
@@ -894,6 +1005,16 @@ function App() {
894
1005
  const [connectOpen, setConnectOpen] = useState4(false);
895
1006
  const [transferOpen, setTransferOpen] = useState4(false);
896
1007
  const [emojiOpen, setEmojiOpen] = useState4(false);
1008
+ const emojiRef = useRef3(null);
1009
+ const [mediaPreview, setMediaPreview] = useState4(null);
1010
+ const [selectMode, setSelectMode] = useState4(false);
1011
+ const [selectedMsgs, setSelectedMsgs] = useState4(() => /* @__PURE__ */ new Set());
1012
+ const [confirmDelete, setConfirmDelete] = useState4(false);
1013
+ const [newConv, setNewConv] = useState4(null);
1014
+ const [aiSummary, setAiSummary] = useState4(null);
1015
+ const [convSummary, setConvSummary] = useState4(null);
1016
+ const curConvRef = useRef3(null);
1017
+ const summarizingRef = useRef3(null);
897
1018
  const [filterOpen, setFilterOpen] = useState4(false);
898
1019
  const [unreadOnly, setUnreadOnly] = useState4(false);
899
1020
  const [mineOnly, setMineOnly] = useState4(false);
@@ -902,7 +1023,7 @@ function App() {
902
1023
  const [composeMode, setComposeMode] = useState4("reply");
903
1024
  const [text, setText] = useState4("");
904
1025
  const [filter, setFilter] = useState4("all");
905
- const [detailsOpen, setDetailsOpen] = useState4(true);
1026
+ const [detailsOpen, setDetailsOpen] = useState4(false);
906
1027
  const [quickOpen, setQuickOpen] = useState4(false);
907
1028
  const [searchOpen, setSearchOpen] = useState4(false);
908
1029
  const [searchQuery, setSearchQuery] = useState4("");
@@ -974,27 +1095,91 @@ function App() {
974
1095
  setEmojiOpen(false);
975
1096
  (_a2 = textareaRef.current) == null ? void 0 : _a2.focus();
976
1097
  };
977
- const sendMediaFile = async (file, kind) => {
1098
+ const enterSelect = (id) => {
1099
+ setSelectMode(true);
1100
+ setSelectedMsgs(/* @__PURE__ */ new Set([id]));
1101
+ };
1102
+ const toggleSelect = (id) => setSelectedMsgs((s) => {
1103
+ const n = new Set(s);
1104
+ n.has(id) ? n.delete(id) : n.add(id);
1105
+ if (n.size === 0) setSelectMode(false);
1106
+ return n;
1107
+ });
1108
+ const exitSelect = () => {
1109
+ setSelectMode(false);
1110
+ setSelectedMsgs(/* @__PURE__ */ new Set());
1111
+ };
1112
+ const doDeleteSelected = async () => {
1113
+ const ids = [...selectedMsgs];
1114
+ setConfirmDelete(false);
1115
+ exitSelect();
1116
+ await Promise.all(ids.map((id) => inbox.deleteMessage(id)));
1117
+ };
1118
+ const handleSummarize = async () => {
1119
+ if (!active) return;
1120
+ setAiSummary({ loading: true });
1121
+ try {
1122
+ const s = await inbox.summarize(active.id);
1123
+ setAiSummary({ text: s });
1124
+ } catch (e) {
1125
+ setAiSummary({ error: e.message || "Falha ao gerar o resumo" });
1126
+ }
1127
+ };
1128
+ const submitNewConv = async () => {
1129
+ const phone = ((newConv == null ? void 0 : newConv.phone) || "").trim();
1130
+ if (!phone) return;
1131
+ setNewConv((c) => ({ ...c, busy: true, error: "" }));
1132
+ try {
1133
+ await inbox.createConversation(phone, newConv.text);
1134
+ setNewConv(null);
1135
+ } catch (e) {
1136
+ setNewConv((c) => ({ ...c, busy: false, error: e.message || "Falha ao iniciar conversa" }));
1137
+ }
1138
+ };
1139
+ useEffect4(() => {
1140
+ if (!emojiOpen) return;
1141
+ const onDoc = (e) => {
1142
+ if (emojiRef.current && !emojiRef.current.contains(e.target)) setEmojiOpen(false);
1143
+ };
1144
+ document.addEventListener("mousedown", onDoc);
1145
+ return () => document.removeEventListener("mousedown", onDoc);
1146
+ }, [emojiOpen]);
1147
+ useEffect4(() => {
1148
+ setSelectMode(false);
1149
+ setSelectedMsgs(/* @__PURE__ */ new Set());
1150
+ }, [activeId]);
1151
+ useEffect4(() => {
1152
+ if (typeof onUnreadChange !== "function") return;
1153
+ onUnreadChange((convList || []).reduce((s, c) => s + (c.unread || 0), 0));
1154
+ }, [convList, onUnreadChange]);
1155
+ const openMediaPreview = (file, kind) => {
978
1156
  if (!file) return;
979
- const previewUrl = URL.createObjectURL(file);
980
- const data = await fileToDataUrl(file);
1157
+ setMediaPreview({ file, kind, previewUrl: URL.createObjectURL(file), caption: "" });
1158
+ };
1159
+ const confirmMediaSend = async () => {
1160
+ var _a2;
1161
+ const p = mediaPreview;
1162
+ if (!p) return;
1163
+ setMediaPreview(null);
1164
+ const data = await fileToDataUrl(p.file);
1165
+ const caption = ((_a2 = p.caption) == null ? void 0 : _a2.trim()) || void 0;
981
1166
  if (live) {
982
- inbox.sendMedia({ data, mime: file.type, kind, filename: file.name, previewUrl });
1167
+ inbox.sendMedia({ data, mime: p.file.type, kind: p.kind, filename: p.file.name, previewUrl: p.previewUrl, caption });
983
1168
  } else {
984
- setMessages((ms) => [...ms, { id: `m${Date.now()}`, side: "out", kind, mediaUrl: previewUrl, meta: file.name, time: formatNow(), status: "sent" }]);
1169
+ setMessages((ms) => [...ms, { id: `m${Date.now()}`, side: "out", kind: p.kind, mediaUrl: p.previewUrl, filename: p.file.name, caption: caption || "", time: nowHHMM(), status: "sent" }]);
985
1170
  }
986
1171
  };
987
- const onPickImage = async (e) => {
1172
+ const onPickImage = (e) => {
988
1173
  var _a2;
989
1174
  const f = (_a2 = e.target.files) == null ? void 0 : _a2[0];
990
1175
  e.target.value = "";
991
- await sendMediaFile(f, "image");
1176
+ openMediaPreview(f, "image");
992
1177
  };
993
- const onPickFile = async (e) => {
1178
+ const onPickFile = (e) => {
994
1179
  var _a2;
995
1180
  const f = (_a2 = e.target.files) == null ? void 0 : _a2[0];
996
1181
  e.target.value = "";
997
- await sendMediaFile(f, "document");
1182
+ openMediaPreview(f, "document");
998
1183
  };
999
1184
  const startRecording = async () => {
1000
1185
  try {
@@ -1113,19 +1298,50 @@ function App() {
1113
1298
  };
1114
1299
  const nextMatch = () => searchMatches.length && setSearchIndex((i) => (i + 1) % searchMatches.length);
1115
1300
  const prevMatch = () => searchMatches.length && setSearchIndex((i) => (i - 1 + searchMatches.length) % searchMatches.length);
1116
- const active = convList.find((c) => c.id === activeId);
1301
+ const active = convList.find((c) => c.id === activeId) || (inbox.draftConv && inbox.draftConv.id === activeId ? inbox.draftConv : null);
1302
+ useEffect4(() => {
1303
+ curConvRef.current = activeId;
1304
+ if (!live || !active || threadMessages.length === 0) {
1305
+ setConvSummary(null);
1306
+ return;
1307
+ }
1308
+ const convId = activeId;
1309
+ const saved = active.aiSummary || "";
1310
+ const savedCount = active.aiSummaryCount || 0;
1311
+ const count = threadMessages.length;
1312
+ setConvSummary({ convId, text: saved });
1313
+ const stale = !saved || count - savedCount >= REGEN_EVERY;
1314
+ if (!stale || summarizingRef.current === convId) return;
1315
+ summarizingRef.current = convId;
1316
+ setConvSummary({ convId, loading: true, text: saved });
1317
+ inbox.summarize(convId).then((text2) => {
1318
+ summarizingRef.current = null;
1319
+ if (curConvRef.current !== convId || !text2) return;
1320
+ setConvSummary({ convId, text: text2 });
1321
+ inbox.loadConversations();
1322
+ }).catch((e) => {
1323
+ summarizingRef.current = null;
1324
+ if (curConvRef.current === convId) setConvSummary({ convId, text: saved, error: e.message });
1325
+ });
1326
+ }, [activeId, threadMessages.length, live, active == null ? void 0 : active.aiSummary, active == null ? void 0 : active.aiSummaryCount]);
1117
1327
  useEffect4(() => {
1118
1328
  if (scrollRef.current) scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
1119
1329
  }, [activeId, threadMessages.length]);
1120
1330
  const counts = useMemo2(() => ({
1121
- open: convList.filter((c) => c.status !== "resolved" && c.status !== "waiting").length,
1122
- pending: convList.filter((c) => c.status === "waiting").length,
1123
- all: convList.length
1124
- }), [convList]);
1331
+ all: convList.length,
1332
+ unread: convList.filter((c) => c.unread > 0).length,
1333
+ mine: convList.filter((c) => {
1334
+ var _a2;
1335
+ return ((_a2 = c._raw) == null ? void 0 : _a2.assignee_user_id) && c._raw.assignee_user_id === inbox.me;
1336
+ }).length
1337
+ }), [convList, inbox.me]);
1125
1338
  const filtered = useMemo2(() => {
1126
1339
  let list = convList;
1127
- if (filter === "pending") list = list.filter((c) => c.status === "waiting");
1128
- else if (filter !== "all") list = list.filter((c) => c.status !== "resolved" && c.status !== "waiting");
1340
+ if (filter === "unread") list = list.filter((c) => c.unread > 0);
1341
+ else if (filter === "mine") list = list.filter((c) => {
1342
+ var _a2;
1343
+ return ((_a2 = c._raw) == null ? void 0 : _a2.assignee_user_id) && c._raw.assignee_user_id === inbox.me;
1344
+ });
1129
1345
  if (unreadOnly) list = list.filter((c) => c.unread > 0);
1130
1346
  if (mineOnly) list = list.filter((c) => {
1131
1347
  var _a2;
@@ -1133,55 +1349,100 @@ function App() {
1133
1349
  });
1134
1350
  return list;
1135
1351
  }, [filter, convList, unreadOnly, mineOnly, inbox.me]);
1136
- return /* @__PURE__ */ jsxs3("div", { className: "h-full w-full bg-slate-100 flex text-slate-900", children: [
1137
- /* @__PURE__ */ jsxs3("main", { className: "flex-1 flex overflow-hidden", children: [
1352
+ const channelList = inbox.channels || [];
1353
+ const isConnected = channelList.some((c) => c.status === "connected");
1354
+ const hasChannel = channelList.length > 0;
1355
+ const channelsReady = inbox.channelsLoaded;
1356
+ const needsReconnect = live && channelsReady && hasChannel && !isConnected;
1357
+ const needsConnect = live && channelsReady && !hasChannel;
1358
+ const agentOf = (id) => id ? (inbox.agents || []).find((a) => a.user_id === id) : null;
1359
+ const notifyTyping = () => {
1360
+ if (!live || !activeId || composeMode !== "reply") return;
1361
+ const now = Date.now();
1362
+ if (now - typingSentRef.current > 2500) {
1363
+ typingSentRef.current = now;
1364
+ inbox.setTyping(activeId, "composing");
1365
+ }
1366
+ clearTimeout(typingStopRef.current);
1367
+ typingStopRef.current = setTimeout(() => {
1368
+ inbox.setTyping(activeId, "paused");
1369
+ typingSentRef.current = 0;
1370
+ }, 3e3);
1371
+ };
1372
+ return /* @__PURE__ */ jsxs3("div", { className: "h-full w-full bg-slate-100 flex flex-col text-slate-900", children: [
1373
+ needsReconnect && /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-2 px-4 py-2 bg-amber-50 border-b border-amber-200 text-amber-800 text-[13px] shrink-0", children: [
1374
+ /* @__PURE__ */ jsx5("span", { className: "shrink-0", children: I.warn }),
1375
+ /* @__PURE__ */ jsx5("span", { className: "min-w-0", children: "WhatsApp desconectado. Reconecte o n\xFAmero para continuar recebendo mensagens." }),
1376
+ /* @__PURE__ */ jsx5(
1377
+ "button",
1378
+ {
1379
+ onClick: () => setConnectOpen(true),
1380
+ className: "ml-auto shrink-0 font-medium bg-amber-500 hover:bg-amber-600 text-white px-3 py-1 rounded-lg",
1381
+ children: "Reconectar"
1382
+ }
1383
+ )
1384
+ ] }),
1385
+ needsConnect && /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-2 px-4 py-2 bg-emerald-50 border-b border-emerald-200 text-emerald-800 text-[13px] shrink-0", children: [
1386
+ /* @__PURE__ */ jsx5("span", { className: "min-w-0", children: "Conecte um n\xFAmero de WhatsApp para come\xE7ar a atender." }),
1387
+ /* @__PURE__ */ jsx5(
1388
+ "button",
1389
+ {
1390
+ onClick: () => setConnectOpen(true),
1391
+ className: "ml-auto shrink-0 font-medium bg-emerald-600 hover:bg-emerald-700 text-white px-3 py-1 rounded-lg",
1392
+ children: "Conectar"
1393
+ }
1394
+ )
1395
+ ] }),
1396
+ /* @__PURE__ */ jsxs3("main", { className: "flex-1 flex overflow-hidden min-h-0", children: [
1138
1397
  /* @__PURE__ */ jsxs3("section", { style: { width: listWidth }, className: "relative bg-white border-r border-slate-200 flex flex-col shrink-0", children: [
1139
1398
  /* @__PURE__ */ jsxs3("header", { className: "px-4 pt-4 pb-3 border-b border-slate-100", children: [
1140
- /* @__PURE__ */ jsxs3("div", { className: "flex items-center justify-between mb-3", children: [
1141
- /* @__PURE__ */ jsx5("h1", { className: "font-semibold text-[15px]", children: "Caixa de entrada" }),
1142
- /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-1", children: [
1143
- live && /* @__PURE__ */ jsx5(
1399
+ /* @__PURE__ */ jsxs3("div", { className: "flex items-center justify-between mb-3 gap-2", children: [
1400
+ /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-1.5 min-w-0", children: [
1401
+ onBack && /* @__PURE__ */ jsx5(
1144
1402
  "button",
1145
1403
  {
1146
- onClick: () => setConnectOpen(true),
1147
- title: "Conectar WhatsApp",
1148
- className: "p-1.5 rounded-md hover:bg-emerald-50 text-emerald-600",
1149
- children: I.qrcode
1404
+ onClick: onBack,
1405
+ title: "Voltar",
1406
+ className: "-ml-1.5 p-1.5 rounded-md text-slate-500 hover:bg-slate-100 shrink-0",
1407
+ children: I.back
1150
1408
  }
1151
1409
  ),
1152
- /* @__PURE__ */ jsxs3("div", { className: "relative", children: [
1153
- /* @__PURE__ */ jsx5(
1154
- "button",
1155
- {
1156
- onClick: () => setFilterOpen((v) => !v),
1157
- title: "Filtros",
1158
- className: `p-1.5 rounded-md ${unreadOnly || mineOnly ? "bg-slate-900 text-white" : "hover:bg-slate-100 text-slate-500"}`,
1159
- children: I.filter
1160
- }
1161
- ),
1162
- filterOpen && /* @__PURE__ */ jsxs3("div", { className: "absolute right-0 top-[calc(100%+6px)] z-20 w-52 bg-white border border-slate-200 rounded-xl shadow-lg py-1.5 text-[13px]", children: [
1163
- /* @__PURE__ */ jsxs3("label", { className: "flex items-center gap-2 px-3 py-1.5 hover:bg-slate-50 cursor-pointer", children: [
1164
- /* @__PURE__ */ jsx5("input", { type: "checkbox", checked: unreadOnly, onChange: (e) => setUnreadOnly(e.target.checked) }),
1165
- " S\xF3 n\xE3o lidas"
1166
- ] }),
1167
- live && /* @__PURE__ */ jsxs3("label", { className: "flex items-center gap-2 px-3 py-1.5 hover:bg-slate-50 cursor-pointer", children: [
1168
- /* @__PURE__ */ jsx5("input", { type: "checkbox", checked: mineOnly, onChange: (e) => setMineOnly(e.target.checked) }),
1169
- " Atribu\xEDdas a mim"
1170
- ] }),
1171
- (unreadOnly || mineOnly) && /* @__PURE__ */ jsx5(
1172
- "button",
1173
- {
1174
- onClick: () => {
1175
- setUnreadOnly(false);
1176
- setMineOnly(false);
1177
- },
1178
- className: "w-full text-left px-3 py-1.5 text-slate-500 hover:bg-slate-50 border-t border-slate-100 mt-1",
1179
- children: "Limpar filtros"
1180
- }
1181
- )
1182
- ] })
1183
- ] }),
1184
- /* @__PURE__ */ jsx5("button", { className: "p-1.5 rounded-md text-slate-300 cursor-not-allowed", title: "Ordenar (em breve)", children: I.sort })
1410
+ /* @__PURE__ */ jsx5("h1", { className: "font-semibold text-[15px] truncate", children: "Caixa de entrada" }),
1411
+ live && /* @__PURE__ */ jsxs3(
1412
+ "button",
1413
+ {
1414
+ onClick: () => setAvailable((v) => !v),
1415
+ title: `${(agent == null ? void 0 : agent.name) || "Voc\xEA"} \u2014 ${available ? "Dispon\xEDvel (clique para pausar)" : "Pausado (clique para voltar a atender)"}`,
1416
+ className: "shrink-0 inline-flex items-center gap-1.5 pl-0.5 pr-2 py-0.5 rounded-full hover:bg-slate-100 transition-colors",
1417
+ children: [
1418
+ /* @__PURE__ */ jsx5(Avatar, { name: (agent == null ? void 0 : agent.name) || "Voc\xEA", src: agent == null ? void 0 : agent.avatar, size: "xs", dot: available ? "bg-emerald-500" : "bg-slate-400" }),
1419
+ /* @__PURE__ */ jsx5("span", { className: "text-[11.5px] font-medium text-slate-600 truncate max-w-[84px]", children: (agent == null ? void 0 : agent.name) || "Voc\xEA" })
1420
+ ]
1421
+ }
1422
+ )
1423
+ ] }),
1424
+ /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-1 shrink-0", children: [
1425
+ live && isConnected && /* @__PURE__ */ jsx5(
1426
+ "button",
1427
+ {
1428
+ onClick: () => setNewConv({ phone: "", text: "", busy: false, error: "" }),
1429
+ title: "Nova conversa",
1430
+ className: "p-1.5 rounded-md text-slate-500 hover:bg-slate-100 hover:text-slate-900",
1431
+ children: I.plus
1432
+ }
1433
+ ),
1434
+ live && channelsReady && !isConnected && /* @__PURE__ */ jsxs3(
1435
+ "button",
1436
+ {
1437
+ onClick: () => setConnectOpen(true),
1438
+ title: needsReconnect ? "WhatsApp desconectado \u2014 reconectar" : "Conectar WhatsApp",
1439
+ className: "relative p-1.5 rounded-md hover:bg-emerald-50 text-emerald-600",
1440
+ children: [
1441
+ I.qrcode,
1442
+ needsReconnect && /* @__PURE__ */ jsx5("span", { className: "absolute -top-1 -right-1 w-3.5 h-3.5 rounded-full bg-amber-500 text-white text-[9px] font-bold inline-flex items-center justify-center ring-2 ring-white", children: "!" })
1443
+ ]
1444
+ }
1445
+ )
1185
1446
  ] })
1186
1447
  ] }),
1187
1448
  /* @__PURE__ */ jsxs3("div", { className: "relative", children: [
@@ -1190,8 +1451,8 @@ function App() {
1190
1451
  ] }),
1191
1452
  /* @__PURE__ */ jsx5("div", { className: "flex items-center gap-2 mt-3 text-[12px]", children: [
1192
1453
  ["all", "Todas", counts.all],
1193
- ["pending", "Pendentes", counts.pending],
1194
- ["open", "Abertas", counts.open]
1454
+ ["unread", "N\xE3o lidas", counts.unread],
1455
+ ["mine", "Minhas", counts.mine]
1195
1456
  ].map(([k, l, n]) => /* @__PURE__ */ jsxs3(
1196
1457
  "button",
1197
1458
  {
@@ -1206,17 +1467,22 @@ function App() {
1206
1467
  k
1207
1468
  )) })
1208
1469
  ] }),
1209
- /* @__PURE__ */ jsx5("div", { className: "flex-1 overflow-y-auto", children: filtered.map((c) => /* @__PURE__ */ jsx5(ConversationItem, { c, active: c.id === activeId, onClick: () => {
1210
- setActiveId(c.id);
1211
- if (live) inbox.markRead(c.id);
1212
- } }, c.id)) }),
1213
- /* @__PURE__ */ jsxs3("footer", { className: "border-t border-slate-100 px-3 py-2 flex items-center justify-between text-[11px] text-slate-500", children: [
1214
- /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-1.5", children: [
1215
- /* @__PURE__ */ jsx5("span", { className: "w-1.5 h-1.5 rounded-full bg-emerald-500 animate-pulse" }),
1216
- "Dispon\xEDvel para atender"
1217
- ] }),
1218
- /* @__PURE__ */ jsx5("button", { className: "hover:text-slate-700", children: "Pausar" })
1219
- ] }),
1470
+ /* @__PURE__ */ jsx5("div", { className: "flex-1 overflow-y-auto", children: live && inbox.loading && filtered.length === 0 ? [...Array(7)].map((_, i) => /* @__PURE__ */ jsx5(ConversationSkeleton, {}, i)) : filtered.map((c) => {
1471
+ var _a2;
1472
+ return /* @__PURE__ */ jsx5(
1473
+ ConversationItem,
1474
+ {
1475
+ c,
1476
+ active: c.id === activeId,
1477
+ assignee: inbox.scope === "all" ? agentOf((_a2 = c._raw) == null ? void 0 : _a2.assignee_user_id) : null,
1478
+ onClick: () => {
1479
+ setActiveId(c.id);
1480
+ if (live) inbox.markRead(c.id);
1481
+ }
1482
+ },
1483
+ c.id
1484
+ );
1485
+ }) }),
1220
1486
  /* @__PURE__ */ jsx5(
1221
1487
  "div",
1222
1488
  {
@@ -1226,8 +1492,43 @@ function App() {
1226
1492
  }
1227
1493
  )
1228
1494
  ] }),
1229
- /* @__PURE__ */ jsxs3("section", { className: "flex-1 flex flex-col bg-slate-50 min-w-0", children: [
1230
- active && /* @__PURE__ */ jsx5(ChatHeader, { c: active, onToggleDetails: () => setDetailsOpen((v) => !v), detailsOpen, onOpenSearch: openSearch, onTransfer: () => setTransferOpen(true) }),
1495
+ /* @__PURE__ */ jsx5("section", { className: "flex-1 flex flex-col bg-slate-50 min-w-0", children: active ? /* @__PURE__ */ jsxs3(Fragment, { children: [
1496
+ /* @__PURE__ */ jsx5(ChatHeader, { c: active, onToggleDetails: () => setDetailsOpen((v) => !v), detailsOpen, onOpenSearch: openSearch, onTransfer: () => setTransferOpen(true), typing: live && inbox.typingConvId === (active == null ? void 0 : active.id), onSummarize: live ? handleSummarize : null }),
1497
+ selectMode && /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-3 px-5 py-2.5 bg-slate-900 text-white", children: [
1498
+ /* @__PURE__ */ jsx5("button", { onClick: exitSelect, title: "Cancelar", className: "p-1 rounded hover:bg-white/10", children: I.x }),
1499
+ /* @__PURE__ */ jsxs3("span", { className: "text-[14px] font-medium flex-1", children: [
1500
+ selectedMsgs.size,
1501
+ " selecionada",
1502
+ selectedMsgs.size === 1 ? "" : "s"
1503
+ ] }),
1504
+ /* @__PURE__ */ jsx5(
1505
+ "button",
1506
+ {
1507
+ onClick: () => selectedMsgs.size && setConfirmDelete(true),
1508
+ disabled: !selectedMsgs.size,
1509
+ title: "Apagar selecionadas",
1510
+ className: "p-1.5 rounded-md hover:bg-white/10 disabled:opacity-40",
1511
+ children: I.trash
1512
+ }
1513
+ )
1514
+ ] }),
1515
+ !selectMode && convSummary && (convSummary.text || convSummary.loading) && /* @__PURE__ */ jsxs3(
1516
+ "button",
1517
+ {
1518
+ onClick: () => convSummary.text && setAiSummary({ text: convSummary.text }),
1519
+ title: "Ver resumo completo",
1520
+ className: "w-full text-left flex items-start gap-2 px-5 py-2 bg-violet-50 border-b border-violet-100 hover:bg-violet-100/70 transition-colors",
1521
+ children: [
1522
+ /* @__PURE__ */ jsx5("span", { className: "text-violet-500 shrink-0 mt-0.5", children: I.sparkle }),
1523
+ convSummary.loading && !convSummary.text ? /* @__PURE__ */ jsx5("span", { className: "text-[12.5px] text-violet-400 animate-pulse", children: "Resumindo a conversa\u2026" }) : /* @__PURE__ */ jsxs3("span", { className: "text-[12.5px] text-violet-900 leading-snug line-clamp-2 flex-1", children: [
1524
+ /* @__PURE__ */ jsx5("strong", { className: "font-semibold text-violet-700", children: "Resumo:" }),
1525
+ " ",
1526
+ convSummary.text
1527
+ ] }),
1528
+ convSummary.loading && convSummary.text && /* @__PURE__ */ jsx5("span", { className: "text-[11px] text-violet-400 shrink-0", children: "atualizando\u2026" })
1529
+ ]
1530
+ }
1531
+ ),
1231
1532
  searchOpen && /* @__PURE__ */ jsx5(
1232
1533
  SearchBar,
1233
1534
  {
@@ -1240,24 +1541,27 @@ function App() {
1240
1541
  onClose: closeSearch
1241
1542
  }
1242
1543
  ),
1243
- /* @__PURE__ */ jsxs3("div", { ref: scrollRef, className: "flex-1 overflow-y-auto px-6 py-5 space-y-1", children: [
1244
- /* @__PURE__ */ jsx5(DayDivider, { label: "Hoje" }),
1544
+ /* @__PURE__ */ jsxs3("div", { ref: scrollRef, className: "flex-1 overflow-y-auto px-8 py-6 space-y-1.5", children: [
1545
+ live && inbox.messagesLoading && threadMessages.length === 0 && /* @__PURE__ */ jsx5("div", { className: "space-y-3 py-2", children: [["in", "w-40"], ["in", "w-56"], ["out", "w-48"], ["in", "w-32"], ["out", "w-60"], ["out", "w-36"]].map(([side, w], i) => /* @__PURE__ */ jsx5("div", { className: `flex ${side === "out" ? "justify-end" : "justify-start"}`, children: /* @__PURE__ */ jsx5("div", { className: `h-9 ${w} max-w-[70%] rounded-2xl animate-pulse ${side === "out" ? "bg-emerald-200/70" : "bg-slate-200"}` }) }, i)) }),
1546
+ threadMessages.length > 0 && /* @__PURE__ */ jsx5(DayDivider, { label: "Hoje" }),
1245
1547
  threadMessages.map((m, i) => /* @__PURE__ */ jsx5(
1246
1548
  Message,
1247
1549
  {
1248
1550
  m,
1249
1551
  prev: threadMessages[i - 1],
1250
1552
  searchQuery: searchOpen ? searchQuery : "",
1251
- isCurrentMatch: m.id === currentMatchId
1553
+ isCurrentMatch: m.id === currentMatchId,
1554
+ isGroup: !!(active == null ? void 0 : active.isGroup),
1555
+ selectable: live && inbox.canDelete,
1556
+ selectMode,
1557
+ selected: selectedMsgs.has(m.id),
1558
+ onEnterSelect: () => enterSelect(m.id),
1559
+ onToggleSelect: () => toggleSelect(m.id)
1252
1560
  },
1253
1561
  m.id
1254
- )),
1255
- /* @__PURE__ */ jsx5("div", { className: "text-center mt-4", children: /* @__PURE__ */ jsxs3("span", { className: "text-[11px] text-slate-400 inline-flex items-center gap-1", children: [
1256
- /* @__PURE__ */ jsx5(Avatar, { name: (active == null ? void 0 : active.name) || "", size: "sm", color: active == null ? void 0 : active.avatarColor }),
1257
- " Conversa criada por voc\xEA \xB7 agora"
1258
- ] }) })
1562
+ ))
1259
1563
  ] }),
1260
- /* @__PURE__ */ jsx5("div", { className: "px-6 pb-5", children: /* @__PURE__ */ jsxs3("div", { className: "bg-white border border-slate-200 rounded-2xl shadow-soft", children: [
1564
+ /* @__PURE__ */ jsx5("div", { className: "px-8 pb-6", children: /* @__PURE__ */ jsxs3("div", { className: "bg-white border border-slate-200 rounded-2xl shadow-soft", children: [
1261
1565
  /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-1 px-3 pt-2 text-[12px]", children: [
1262
1566
  [
1263
1567
  ["reply", "Mensagem"],
@@ -1298,14 +1602,17 @@ function App() {
1298
1602
  ref: textareaRef,
1299
1603
  rows: 3,
1300
1604
  value: text,
1301
- onChange: (e) => setText(e.target.value),
1605
+ onChange: (e) => {
1606
+ setText(e.target.value);
1607
+ notifyTyping();
1608
+ },
1302
1609
  onKeyDown: (e) => {
1303
1610
  if (e.key === "Enter" && !e.shiftKey) {
1304
1611
  e.preventDefault();
1305
1612
  handleSend();
1306
1613
  }
1307
1614
  },
1308
- placeholder: composeMode === "note" ? "Adicione uma nota interna \u2014 vis\xEDvel s\xF3 para o time\u2026" : "Digite uma mensagem para Samir\u2026",
1615
+ placeholder: composeMode === "note" ? "Adicione uma nota interna \u2014 vis\xEDvel s\xF3 para o time\u2026" : "Digite uma mensagem\u2026",
1309
1616
  className: `w-full text-[14px] resize-none bg-transparent outline-none placeholder:text-slate-400 ${composeMode === "note" ? "text-yellow-900 placeholder:text-yellow-600" : ""}`
1310
1617
  }
1311
1618
  ) }),
@@ -1321,7 +1628,7 @@ function App() {
1321
1628
  /* @__PURE__ */ jsx5("span", { className: "opacity-90", children: I.send })
1322
1629
  ] })
1323
1630
  ] }) : /* @__PURE__ */ jsxs3(Fragment, { children: [
1324
- /* @__PURE__ */ jsxs3("div", { className: "relative", children: [
1631
+ /* @__PURE__ */ jsxs3("div", { className: "relative", ref: emojiRef, children: [
1325
1632
  /* @__PURE__ */ jsx5(
1326
1633
  "button",
1327
1634
  {
@@ -1367,21 +1674,125 @@ function App() {
1367
1674
  /* @__PURE__ */ jsx5("input", { ref: fileInputRef, type: "file", className: "hidden", onChange: onPickFile })
1368
1675
  ] })
1369
1676
  ] }) })
1370
- ] }),
1677
+ ] }) : /* @__PURE__ */ jsx5(EmptyConversation, {}) }),
1371
1678
  detailsOpen && active && /* @__PURE__ */ jsx5(DetailsPanel, { c: active, onClose: () => setDetailsOpen(false) })
1372
1679
  ] }),
1373
1680
  connectOpen && /* @__PURE__ */ jsx5(ConnectChannel, { inbox, onClose: () => setConnectOpen(false) }),
1374
- transferOpen && active && /* @__PURE__ */ jsx5(TransferModal, { inbox, conversationId: active.id, currentAssignee: (_a = active._raw) == null ? void 0 : _a.assignee_user_id, onClose: () => setTransferOpen(false) })
1681
+ transferOpen && active && /* @__PURE__ */ jsx5(TransferModal, { inbox, conversationId: active.id, currentAssignee: (_a = active._raw) == null ? void 0 : _a.assignee_user_id, onClose: () => setTransferOpen(false) }),
1682
+ mediaPreview && /* @__PURE__ */ jsx5("div", { className: "fixed inset-0 z-50 bg-black/50 flex items-center justify-center p-4", onClick: () => setMediaPreview(null), children: /* @__PURE__ */ jsxs3("div", { className: "bg-white rounded-2xl shadow-xl w-[420px] max-w-full p-4", onClick: (e) => e.stopPropagation(), children: [
1683
+ /* @__PURE__ */ jsxs3("div", { className: "flex items-center justify-between mb-3", children: [
1684
+ /* @__PURE__ */ jsxs3("h3", { className: "font-semibold text-[15px]", children: [
1685
+ "Enviar ",
1686
+ mediaPreview.kind === "image" ? "imagem" : "arquivo"
1687
+ ] }),
1688
+ /* @__PURE__ */ jsx5("button", { onClick: () => setMediaPreview(null), className: "text-slate-400 hover:text-slate-700 text-xl leading-none", children: "\xD7" })
1689
+ ] }),
1690
+ mediaPreview.kind === "image" ? /* @__PURE__ */ jsx5("img", { src: mediaPreview.previewUrl, alt: "", className: "w-full max-h-72 object-contain rounded-lg bg-slate-50" }) : /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-3 bg-slate-50 rounded-lg p-3", children: [
1691
+ /* @__PURE__ */ jsx5("span", { className: "text-slate-500", children: I.file }),
1692
+ /* @__PURE__ */ jsx5("span", { className: "text-[13px] truncate flex-1", children: mediaPreview.file.name })
1693
+ ] }),
1694
+ /* @__PURE__ */ jsx5(
1695
+ "input",
1696
+ {
1697
+ autoFocus: true,
1698
+ value: mediaPreview.caption,
1699
+ onChange: (e) => setMediaPreview((p) => ({ ...p, caption: e.target.value })),
1700
+ onKeyDown: (e) => {
1701
+ if (e.key === "Enter") confirmMediaSend();
1702
+ if (e.key === "Escape") setMediaPreview(null);
1703
+ },
1704
+ placeholder: "Legenda (opcional)\u2026",
1705
+ className: "w-full mt-3 px-3 py-2 text-[14px] rounded-lg border border-slate-200 outline-none focus:border-slate-300"
1706
+ }
1707
+ ),
1708
+ /* @__PURE__ */ jsxs3("div", { className: "flex justify-end gap-2 mt-3", children: [
1709
+ /* @__PURE__ */ jsx5("button", { onClick: () => setMediaPreview(null), className: "text-[13px] px-3 py-1.5 rounded-lg text-slate-600 hover:bg-slate-100", children: "Cancelar" }),
1710
+ /* @__PURE__ */ jsxs3("button", { onClick: confirmMediaSend, className: "text-[13px] font-medium bg-emerald-600 hover:bg-emerald-700 text-white px-4 py-1.5 rounded-lg inline-flex items-center gap-1.5", children: [
1711
+ "Enviar ",
1712
+ /* @__PURE__ */ jsx5("span", { className: "opacity-90", children: I.send })
1713
+ ] })
1714
+ ] })
1715
+ ] }) }),
1716
+ confirmDelete && /* @__PURE__ */ jsx5("div", { className: "fixed inset-0 z-50 bg-black/50 flex items-center justify-center p-4", onClick: () => setConfirmDelete(false), children: /* @__PURE__ */ jsxs3("div", { className: "bg-white rounded-2xl shadow-xl w-[360px] max-w-full p-5", onClick: (e) => e.stopPropagation(), children: [
1717
+ /* @__PURE__ */ jsxs3("h3", { className: "font-semibold text-[16px] text-slate-900", children: [
1718
+ "Apagar ",
1719
+ selectedMsgs.size,
1720
+ " ",
1721
+ selectedMsgs.size === 1 ? "mensagem" : "mensagens",
1722
+ "?"
1723
+ ] }),
1724
+ /* @__PURE__ */ jsx5("p", { className: "text-[13px] text-slate-500 mt-1 mb-4", children: "Ser\xE1 removida para todos no WhatsApp e do seu inbox. N\xE3o d\xE1 pra desfazer." }),
1725
+ /* @__PURE__ */ jsxs3("div", { className: "flex justify-end gap-2", children: [
1726
+ /* @__PURE__ */ jsx5("button", { onClick: () => setConfirmDelete(false), className: "text-[13px] px-3.5 py-2 rounded-lg text-slate-600 hover:bg-slate-100", children: "Cancelar" }),
1727
+ /* @__PURE__ */ jsx5("button", { onClick: doDeleteSelected, className: "text-[13px] font-medium bg-rose-600 hover:bg-rose-700 text-white px-4 py-2 rounded-lg", children: "Apagar" })
1728
+ ] })
1729
+ ] }) }),
1730
+ newConv && /* @__PURE__ */ jsx5("div", { className: "fixed inset-0 z-50 bg-black/50 flex items-center justify-center p-4", onClick: () => !newConv.busy && setNewConv(null), children: /* @__PURE__ */ jsxs3("div", { className: "bg-white rounded-2xl shadow-xl w-[400px] max-w-full p-5", onClick: (e) => e.stopPropagation(), children: [
1731
+ /* @__PURE__ */ jsxs3("div", { className: "flex items-center justify-between mb-3", children: [
1732
+ /* @__PURE__ */ jsx5("h3", { className: "font-semibold text-[15px]", children: "Nova conversa" }),
1733
+ /* @__PURE__ */ jsx5("button", { onClick: () => setNewConv(null), className: "text-slate-400 hover:text-slate-700 text-xl leading-none", children: "\xD7" })
1734
+ ] }),
1735
+ newConv.error && /* @__PURE__ */ jsx5("div", { className: "text-[12px] text-rose-600 mb-2 break-words", children: newConv.error }),
1736
+ /* @__PURE__ */ jsx5("label", { className: "block text-[12px] text-slate-500 mb-1", children: "N\xFAmero (DDD + n\xFAmero; o DDI 55 \xE9 adicionado automaticamente)" }),
1737
+ /* @__PURE__ */ jsx5(
1738
+ "input",
1739
+ {
1740
+ autoFocus: true,
1741
+ value: newConv.phone,
1742
+ onChange: (e) => setNewConv((c) => ({ ...c, phone: e.target.value })),
1743
+ placeholder: "11 99999-9999",
1744
+ className: "w-full px-3 py-2 text-[14px] rounded-lg border border-slate-200 outline-none focus:border-slate-300"
1745
+ }
1746
+ ),
1747
+ /* @__PURE__ */ jsx5("label", { className: "block text-[12px] text-slate-500 mt-3 mb-1", children: "Primeira mensagem (opcional)" }),
1748
+ /* @__PURE__ */ jsx5(
1749
+ "textarea",
1750
+ {
1751
+ rows: 3,
1752
+ value: newConv.text,
1753
+ onChange: (e) => setNewConv((c) => ({ ...c, text: e.target.value })),
1754
+ placeholder: "Ol\xE1! ...",
1755
+ className: "w-full px-3 py-2 text-[14px] rounded-lg border border-slate-200 outline-none focus:border-slate-300 resize-none"
1756
+ }
1757
+ ),
1758
+ /* @__PURE__ */ jsxs3("div", { className: "flex justify-end gap-2 mt-4", children: [
1759
+ /* @__PURE__ */ jsx5("button", { onClick: () => setNewConv(null), disabled: newConv.busy, className: "text-[13px] px-3.5 py-2 rounded-lg text-slate-600 hover:bg-slate-100", children: "Cancelar" }),
1760
+ /* @__PURE__ */ jsx5(
1761
+ "button",
1762
+ {
1763
+ onClick: submitNewConv,
1764
+ disabled: newConv.busy || !newConv.phone.trim(),
1765
+ className: "text-[13px] font-medium bg-emerald-600 hover:bg-emerald-700 disabled:opacity-50 text-white px-4 py-2 rounded-lg",
1766
+ children: newConv.busy ? "Iniciando\u2026" : "Iniciar"
1767
+ }
1768
+ )
1769
+ ] })
1770
+ ] }) }),
1771
+ aiSummary && /* @__PURE__ */ jsx5("div", { className: "fixed inset-0 z-50 bg-black/50 flex items-center justify-center p-4", onClick: () => setAiSummary(null), children: /* @__PURE__ */ jsxs3("div", { className: "bg-white rounded-2xl shadow-xl w-[440px] max-w-full p-5", onClick: (e) => e.stopPropagation(), children: [
1772
+ /* @__PURE__ */ jsxs3("div", { className: "flex items-center justify-between mb-3", children: [
1773
+ /* @__PURE__ */ jsxs3("h3", { className: "font-semibold text-[15px] inline-flex items-center gap-1.5 text-violet-700", children: [
1774
+ I.sparkle,
1775
+ " Resumo da conversa"
1776
+ ] }),
1777
+ /* @__PURE__ */ jsx5("button", { onClick: () => setAiSummary(null), className: "text-slate-400 hover:text-slate-700 text-xl leading-none", children: "\xD7" })
1778
+ ] }),
1779
+ aiSummary.loading && /* @__PURE__ */ jsx5("div", { className: "py-8 flex items-center justify-center text-violet-300", children: /* @__PURE__ */ jsxs3("svg", { className: "animate-spin w-6 h-6", viewBox: "0 0 24 24", fill: "none", children: [
1780
+ /* @__PURE__ */ jsx5("circle", { cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "3", opacity: "0.25" }),
1781
+ /* @__PURE__ */ jsx5("path", { d: "M22 12a10 10 0 0 1-10 10", stroke: "currentColor", strokeWidth: "3", strokeLinecap: "round" })
1782
+ ] }) }),
1783
+ aiSummary.error && /* @__PURE__ */ jsx5("div", { className: "text-[13px] text-rose-600", children: aiSummary.error }),
1784
+ aiSummary.text != null && !aiSummary.loading && /* @__PURE__ */ jsx5("div", { className: "text-[13.5px] text-slate-700 whitespace-pre-wrap leading-relaxed max-h-80 overflow-y-auto", children: aiSummary.text })
1785
+ ] }) })
1375
1786
  ] });
1376
1787
  }
1377
- function ConversationItem({ c, active, onClick }) {
1788
+ function ConversationItem({ c, active, onClick, assignee }) {
1378
1789
  return /* @__PURE__ */ jsxs3(
1379
1790
  "button",
1380
1791
  {
1381
1792
  onClick,
1382
1793
  className: `w-full text-left px-3 py-3 border-l-2 transition-colors flex gap-3 ${active ? "bg-slate-50 border-emerald-500" : "border-transparent hover:bg-slate-50"}`,
1383
1794
  children: [
1384
- /* @__PURE__ */ jsx5(Avatar, { name: c.name, color: c.avatarColor }),
1795
+ /* @__PURE__ */ jsx5(Avatar, { name: c.name, src: c.avatar, color: c.avatarColor }),
1385
1796
  /* @__PURE__ */ jsxs3("div", { className: "flex-1 min-w-0", children: [
1386
1797
  /* @__PURE__ */ jsxs3("div", { className: "flex items-center justify-between gap-2", children: [
1387
1798
  /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-1.5 min-w-0", children: [
@@ -1391,16 +1802,26 @@ function ConversationItem({ c, active, onClick }) {
1391
1802
  /* @__PURE__ */ jsx5("span", { className: `text-[11px] shrink-0 ${c.unread ? "text-emerald-600 font-semibold" : "text-slate-400"}`, children: c.waitFor })
1392
1803
  ] }),
1393
1804
  /* @__PURE__ */ jsxs3("div", { className: "flex items-center justify-between gap-2 mt-0.5", children: [
1394
- /* @__PURE__ */ jsx5("p", { className: `text-[12.5px] truncate ${c.unread ? "text-slate-700" : "text-slate-500"}`, children: c.preview }),
1805
+ /* @__PURE__ */ jsx5("p", { className: `text-[12.5px] truncate ${c.unread ? "text-slate-700" : "text-slate-500"} ${c.preview ? "" : "italic text-slate-400"}`, children: c.preview || "Sem mensagens" }),
1395
1806
  c.unread > 0 && /* @__PURE__ */ jsx5("span", { className: "shrink-0 min-w-[18px] h-[18px] px-1.5 rounded-full bg-emerald-500 text-white text-[10px] font-bold inline-flex items-center justify-center", children: c.unread })
1396
1807
  ] }),
1397
- /* @__PURE__ */ jsx5("div", { className: "flex items-center gap-1.5 mt-1.5", children: /* @__PURE__ */ jsx5(Pill, { tone: "slate", children: c.ticket }) })
1398
- ] })
1808
+ c.ticket && /* @__PURE__ */ jsx5("div", { className: "flex items-center gap-1.5 mt-1.5", children: /* @__PURE__ */ jsx5(Pill, { tone: "slate", children: c.ticket }) })
1809
+ ] }),
1810
+ assignee && /* @__PURE__ */ jsx5("span", { className: "self-center shrink-0", title: `Respons\xE1vel: ${assignee.name}`, children: /* @__PURE__ */ jsx5(Avatar, { name: assignee.name, src: assignee.avatar, size: "xs" }) })
1399
1811
  ]
1400
1812
  }
1401
1813
  );
1402
1814
  }
1403
- function ChatHeader({ c, onToggleDetails, detailsOpen, onOpenSearch, onTransfer }) {
1815
+ function ConversationSkeleton() {
1816
+ return /* @__PURE__ */ jsxs3("div", { className: "px-3 py-3 flex gap-3 items-center", children: [
1817
+ /* @__PURE__ */ jsx5("div", { className: "w-9 h-9 rounded-full bg-slate-200 animate-pulse shrink-0" }),
1818
+ /* @__PURE__ */ jsxs3("div", { className: "flex-1 min-w-0 space-y-2", children: [
1819
+ /* @__PURE__ */ jsx5("div", { className: "h-3 w-1/3 rounded bg-slate-200 animate-pulse" }),
1820
+ /* @__PURE__ */ jsx5("div", { className: "h-2.5 w-2/3 rounded bg-slate-100 animate-pulse" })
1821
+ ] })
1822
+ ] });
1823
+ }
1824
+ function ChatHeader({ c, onToggleDetails, detailsOpen, onOpenSearch, onTransfer, typing, onSummarize }) {
1404
1825
  const [menuOpen, setMenuOpen] = useState4(false);
1405
1826
  const menuRef = useRef3(null);
1406
1827
  useEffect4(() => {
@@ -1412,40 +1833,25 @@ function ChatHeader({ c, onToggleDetails, detailsOpen, onOpenSearch, onTransfer
1412
1833
  return () => document.removeEventListener("mousedown", onDoc);
1413
1834
  }, [menuOpen]);
1414
1835
  return /* @__PURE__ */ jsxs3("header", { className: "bg-white border-b border-slate-200 px-5 py-3 flex items-center gap-3 min-w-0", children: [
1415
- /* @__PURE__ */ jsx5(Avatar, { name: c.name, color: c.avatarColor, size: "lg", online: true }),
1836
+ /* @__PURE__ */ jsx5(Avatar, { name: c.name, src: c.avatar, color: c.avatarColor, size: "lg" }),
1416
1837
  /* @__PURE__ */ jsxs3("div", { className: "flex-1 min-w-0", children: [
1417
1838
  /* @__PURE__ */ jsx5("div", { className: "flex items-center gap-2 min-w-0", children: /* @__PURE__ */ jsx5("h2", { className: "font-semibold text-[15px] truncate", children: c.name }) }),
1418
- /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-2 text-[12px] text-slate-500 mt-0.5 min-w-0", children: [
1419
- /* @__PURE__ */ jsxs3("span", { className: "inline-flex items-center gap-1 shrink-0", children: [
1420
- I.phone,
1421
- " ",
1422
- c.phone
1423
- ] }),
1424
- /* @__PURE__ */ jsx5("span", { className: "hidden lg:inline text-slate-300 shrink-0", children: "\xB7" }),
1425
- /* @__PURE__ */ jsxs3("span", { className: "hidden lg:inline-flex items-center gap-1 shrink-0", children: [
1426
- I.tag,
1427
- " ",
1428
- c.ticket
1429
- ] })
1430
- ] })
1431
- ] }),
1432
- /* @__PURE__ */ jsxs3("div", { className: "hidden 2xl:flex items-center gap-4 text-[12px] pr-3 border-r border-slate-200 shrink-0", children: [
1433
- /* @__PURE__ */ jsxs3("div", { className: "flex flex-col items-end leading-tight", children: [
1434
- /* @__PURE__ */ jsx5("span", { className: "text-[9.5px] uppercase tracking-wide text-slate-400", children: "SLA" }),
1435
- /* @__PURE__ */ jsx5("span", { className: "font-semibold text-amber-600 tabular-nums", children: "02:14" })
1436
- ] }),
1437
- /* @__PURE__ */ jsxs3("div", { className: "flex flex-col items-end leading-tight", children: [
1438
- /* @__PURE__ */ jsx5("span", { className: "text-[9.5px] uppercase tracking-wide text-slate-400", children: "1\xAA resposta" }),
1439
- /* @__PURE__ */ jsx5("span", { className: "font-semibold text-emerald-600 tabular-nums", children: "00:48" })
1440
- ] })
1441
- ] }),
1442
- /* @__PURE__ */ jsxs3("button", { className: "shrink-0 text-[12.5px] font-medium bg-emerald-600 hover:bg-emerald-700 text-white px-3 py-1.5 rounded-lg inline-flex items-center gap-1.5", children: [
1443
- I.check,
1444
- " ",
1445
- /* @__PURE__ */ jsx5("span", { className: "hidden lg:inline", children: "Resolver" })
1839
+ /* @__PURE__ */ jsx5("div", { className: "flex items-center gap-2 text-[12px] mt-0.5 min-w-0", children: typing ? /* @__PURE__ */ jsx5("span", { className: "text-emerald-600 font-medium", children: "digitando\u2026" }) : c.phone ? /* @__PURE__ */ jsxs3("span", { className: "inline-flex items-center gap-1 shrink-0 text-slate-500", children: [
1840
+ I.phone,
1841
+ " ",
1842
+ c.phone
1843
+ ] }) : null })
1446
1844
  ] }),
1447
- /* @__PURE__ */ jsx5("div", { className: "w-px h-6 bg-slate-200 shrink-0" }),
1448
1845
  /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-0.5 shrink-0", children: [
1846
+ onSummarize && /* @__PURE__ */ jsx5(
1847
+ "button",
1848
+ {
1849
+ onClick: onSummarize,
1850
+ title: "Resumo IA da conversa",
1851
+ className: "p-1.5 rounded-md text-violet-500 hover:bg-violet-50",
1852
+ children: I.sparkle
1853
+ }
1854
+ ),
1449
1855
  /* @__PURE__ */ jsx5(
1450
1856
  "button",
1451
1857
  {
@@ -1466,22 +1872,14 @@ function ChatHeader({ c, onToggleDetails, detailsOpen, onOpenSearch, onTransfer
1466
1872
  }
1467
1873
  ),
1468
1874
  menuOpen && /* @__PURE__ */ jsxs3("div", { className: "absolute right-0 top-[calc(100%+6px)] z-20 w-56 bg-white border border-slate-200 rounded-xl shadow-lg py-1.5 text-[13px]", children: [
1469
- /* @__PURE__ */ jsx5(MenuItem, { icon: I.phone, label: "Ligar", onClick: () => setMenuOpen(false) }),
1470
1875
  /* @__PURE__ */ jsx5(MenuItem, { icon: I.forward, label: "Transferir", onClick: () => {
1471
1876
  setMenuOpen(false);
1472
1877
  onTransfer == null ? void 0 : onTransfer();
1473
1878
  } }),
1474
- /* @__PURE__ */ jsx5(MenuItem, { icon: I.userPlus, label: "Adicionar participantes", onClick: () => setMenuOpen(false) }),
1475
- /* @__PURE__ */ jsx5("div", { className: "my-1 h-px bg-slate-100" }),
1476
1879
  /* @__PURE__ */ jsx5(MenuItem, { icon: I.search, label: "Procurar na conversa", onClick: () => {
1477
1880
  setMenuOpen(false);
1478
1881
  onOpenSearch == null ? void 0 : onOpenSearch();
1479
- } }),
1480
- /* @__PURE__ */ jsx5(MenuItem, { icon: I.star, label: "Marcar com estrela", onClick: () => setMenuOpen(false) }),
1481
- /* @__PURE__ */ jsx5(MenuItem, { icon: I.tag, label: "Editar tags", onClick: () => setMenuOpen(false) }),
1482
- /* @__PURE__ */ jsx5(MenuItem, { icon: I.link, label: "Copiar link da conversa", onClick: () => setMenuOpen(false) }),
1483
- /* @__PURE__ */ jsx5("div", { className: "my-1 h-px bg-slate-100" }),
1484
- /* @__PURE__ */ jsx5(MenuItem, { icon: I.x, label: "Encerrar sem resolver", tone: "danger", onClick: () => setMenuOpen(false) })
1882
+ } })
1485
1883
  ] })
1486
1884
  ] })
1487
1885
  ] })
@@ -1589,13 +1987,31 @@ function highlightText(text, query, isCurrent) {
1589
1987
  function DayDivider({ label }) {
1590
1988
  return /* @__PURE__ */ jsx5("div", { className: "flex items-center justify-center my-3", children: /* @__PURE__ */ jsx5("span", { className: "text-[11px] font-medium text-slate-600 bg-white border border-slate-200 rounded-full px-2.5 py-0.5 shadow-soft", children: label }) });
1591
1989
  }
1990
+ function EmptyConversation() {
1991
+ return /* @__PURE__ */ jsxs3("div", { className: "flex-1 flex flex-col items-center justify-center text-center px-8 select-none", children: [
1992
+ /* @__PURE__ */ jsx5("div", { className: "w-20 h-20 rounded-full bg-slate-100 flex items-center justify-center text-slate-300 mb-5", children: /* @__PURE__ */ jsx5(
1993
+ Icon,
1994
+ {
1995
+ d: /* @__PURE__ */ jsx5("path", { d: "M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5Z" }),
1996
+ className: "w-9 h-9"
1997
+ }
1998
+ ) }),
1999
+ /* @__PURE__ */ jsx5("h3", { className: "text-[15px] font-semibold text-slate-700", children: "Nenhuma conversa aberta" }),
2000
+ /* @__PURE__ */ jsx5("p", { className: "text-[13px] text-slate-400 mt-1 max-w-xs leading-relaxed", children: "Selecione uma conversa na lista ao lado para ver as mensagens e responder." })
2001
+ ] });
2002
+ }
1592
2003
  function Ticks({ status }) {
1593
- if (status === "sent") return /* @__PURE__ */ jsx5("span", { className: "text-slate-400", children: "\u2713" });
1594
- if (status === "delivered") return /* @__PURE__ */ jsx5("span", { className: "text-slate-400", children: "\u2713\u2713" });
1595
- if (status === "read") return /* @__PURE__ */ jsx5("span", { className: "text-sky-500", children: "\u2713\u2713" });
2004
+ if (status === "pending" || !status) return /* @__PURE__ */ jsxs3("svg", { className: "inline w-3 h-3 opacity-70", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [
2005
+ /* @__PURE__ */ jsx5("circle", { cx: "12", cy: "12", r: "9" }),
2006
+ /* @__PURE__ */ jsx5("path", { d: "M12 7.5V12l3 1.5" })
2007
+ ] });
2008
+ if (status === "failed") return /* @__PURE__ */ jsx5("span", { className: "text-rose-200 font-bold", title: "Falha ao enviar", children: "!" });
2009
+ if (status === "sent") return /* @__PURE__ */ jsx5("span", { className: "text-emerald-100/80", children: "\u2713" });
2010
+ if (status === "delivered") return /* @__PURE__ */ jsx5("span", { className: "text-emerald-100/80", style: { letterSpacing: "-0.18em" }, children: "\u2713\u2713" });
2011
+ if (status === "read") return /* @__PURE__ */ jsx5("span", { className: "text-sky-300", style: { letterSpacing: "-0.18em" }, children: "\u2713\u2713" });
1596
2012
  return null;
1597
2013
  }
1598
- function Message({ m, searchQuery = "", isCurrentMatch = false }) {
2014
+ function Message({ m, searchQuery = "", isCurrentMatch = false, isGroup, selectable, selectMode, selected, onEnterSelect, onToggleSelect }) {
1599
2015
  if (m.side === "system") {
1600
2016
  return /* @__PURE__ */ jsx5("div", { className: "flex justify-center my-2", children: /* @__PURE__ */ jsxs3("span", { className: "text-[11px] text-slate-500 bg-slate-100 rounded-full px-2.5 py-0.5", children: [
1601
2017
  m.text,
@@ -1617,58 +2033,73 @@ function Message({ m, searchQuery = "", isCurrentMatch = false }) {
1617
2033
  ] }) });
1618
2034
  }
1619
2035
  const isOut = m.side === "out";
1620
- return /* @__PURE__ */ jsxs3("div", { className: `flex ${isOut ? "justify-end" : "justify-start"} group`, children: [
1621
- !isOut && /* @__PURE__ */ jsx5(Avatar, { name: "Samir Patel", color: "bg-violet-500", size: "sm" }),
1622
- /* @__PURE__ */ jsxs3("div", { className: `mx-2 max-w-[68%] ${isOut ? "order-first" : ""}`, children: [
1623
- /* @__PURE__ */ jsxs3("div", { className: `relative px-3.5 py-2.5 rounded-2xl shadow-soft ${isOut ? "bg-emerald-600 text-white rounded-tr-sm" : "bg-white border border-slate-200 text-slate-800 rounded-tl-sm"} ${isCurrentMatch ? "ring-2 ring-amber-400" : ""}`, children: [
1624
- m.kind === "text" && /* @__PURE__ */ jsx5("p", { className: "text-[13.5px] leading-relaxed whitespace-pre-wrap", children: highlightText(m.text, searchQuery, isCurrentMatch) }),
1625
- m.kind === "image" && /* @__PURE__ */ jsxs3("div", { className: "-mx-1 -mt-1", children: [
1626
- m.mediaUrl ? /* @__PURE__ */ jsx5("a", { href: m.mediaUrl, target: "_blank", rel: "noreferrer", children: /* @__PURE__ */ jsx5("img", { src: m.mediaUrl, alt: m.caption || "imagem", className: "w-64 max-h-72 object-cover rounded-lg" }) }) : /* @__PURE__ */ jsx5("div", { className: "w-64 h-40 rounded-lg bg-gradient-to-br from-slate-200 to-slate-300 flex items-center justify-center text-slate-500", children: I.image }),
1627
- m.caption && /* @__PURE__ */ jsx5("p", { className: "text-[13px] mt-2", children: highlightText(m.caption, searchQuery, isCurrentMatch) })
1628
- ] }),
1629
- m.kind === "video" && /* @__PURE__ */ jsxs3("div", { className: "-mx-1 -mt-1", children: [
1630
- m.mediaUrl ? /* @__PURE__ */ jsx5("video", { src: m.mediaUrl, controls: true, className: "w-64 rounded-lg" }) : /* @__PURE__ */ jsx5("div", { className: "w-64 h-40 rounded-lg bg-slate-200 flex items-center justify-center text-slate-500", children: "\u{1F3A5}" }),
1631
- m.caption && /* @__PURE__ */ jsx5("p", { className: "text-[13px] mt-2", children: highlightText(m.caption, searchQuery, isCurrentMatch) })
1632
- ] }),
1633
- m.kind === "document" && /* @__PURE__ */ jsxs3(
1634
- "a",
1635
- {
1636
- href: m.mediaUrl || "#",
1637
- target: "_blank",
1638
- rel: "noreferrer",
1639
- className: `flex items-center gap-2.5 min-w-[200px] rounded-lg px-2 py-1.5 ${isOut ? "bg-emerald-700/40" : "bg-slate-100"}`,
1640
- children: [
1641
- /* @__PURE__ */ jsx5("span", { className: isOut ? "text-emerald-100" : "text-slate-500", children: I.file }),
1642
- /* @__PURE__ */ jsx5("span", { className: "text-[13px] truncate flex-1", children: m.meta || m.caption || "arquivo" })
1643
- ]
1644
- }
1645
- ),
1646
- m.kind === "audio" && /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-3 min-w-[220px]", children: [
1647
- /* @__PURE__ */ jsx5("button", { className: `w-9 h-9 rounded-full inline-flex items-center justify-center ${isOut ? "bg-emerald-700" : "bg-slate-100 text-slate-700"}`, children: "\u25B6" }),
1648
- /* @__PURE__ */ jsxs3("div", { className: "flex-1", children: [
1649
- /* @__PURE__ */ jsx5("div", { className: `h-1 rounded-full ${isOut ? "bg-emerald-700" : "bg-slate-200"} relative overflow-hidden`, children: /* @__PURE__ */ jsx5("div", { className: `h-full w-2/5 ${isOut ? "bg-white" : "bg-emerald-500"}` }) }),
1650
- /* @__PURE__ */ jsx5("div", { className: `text-[11px] mt-1 ${isOut ? "text-emerald-100" : "text-slate-500"}`, children: m.duration })
1651
- ] }),
1652
- /* @__PURE__ */ jsx5("span", { className: isOut ? "text-emerald-100" : "text-slate-400", children: I.mic })
1653
- ] }),
1654
- /* @__PURE__ */ jsxs3("div", { className: `flex items-center gap-1 justify-end mt-1 text-[10.5px] ${isOut ? "text-emerald-100" : "text-slate-400"}`, children: [
1655
- m.author && !isOut === false && /* @__PURE__ */ jsx5("span", { className: "mr-1", children: m.author }),
1656
- /* @__PURE__ */ jsx5("span", { children: m.time }),
1657
- isOut && /* @__PURE__ */ jsx5(Ticks, { status: m.status })
2036
+ return /* @__PURE__ */ jsxs3(
2037
+ "div",
2038
+ {
2039
+ className: `flex items-center gap-2 group ${selectMode ? "cursor-pointer -mx-2 px-2 py-0.5 rounded-lg" : ""} ${selected ? "bg-emerald-50" : ""}`,
2040
+ onClick: selectMode ? onToggleSelect : void 0,
2041
+ children: [
2042
+ selectMode && /* @__PURE__ */ jsx5("span", { className: `shrink-0 w-5 h-5 rounded-full border-2 flex items-center justify-center ${selected ? "bg-emerald-500 border-emerald-500 text-white" : "border-slate-300 bg-white"}`, children: selected && /* @__PURE__ */ jsx5("svg", { width: "11", height: "11", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "3.5", strokeLinecap: "round", strokeLinejoin: "round", children: /* @__PURE__ */ jsx5("polyline", { points: "20 6 9 17 4 12" }) }) }),
2043
+ /* @__PURE__ */ jsxs3("div", { className: `flex-1 min-w-0 flex ${isOut ? "justify-end" : "justify-start"} items-center`, children: [
2044
+ /* @__PURE__ */ jsx5("div", { className: "max-w-[70%]", children: /* @__PURE__ */ jsxs3("div", { className: `relative px-3.5 py-2.5 rounded-2xl shadow-soft ${isOut ? "bg-emerald-600 text-white rounded-tr-sm" : "bg-white border border-slate-200 text-slate-800 rounded-tl-sm"} ${isCurrentMatch ? "ring-2 ring-amber-400" : ""}`, children: [
2045
+ !isOut && isGroup && m.author && /* @__PURE__ */ jsx5("div", { className: "text-[11.5px] font-semibold text-emerald-700 mb-0.5", children: m.author }),
2046
+ m.kind === "text" && /* @__PURE__ */ jsx5("p", { className: "text-[13.5px] leading-relaxed whitespace-pre-wrap", children: highlightText(m.text, searchQuery, isCurrentMatch) }),
2047
+ m.kind === "image" && /* @__PURE__ */ jsxs3("div", { className: "-mx-1 -mt-1", children: [
2048
+ m.mediaUrl ? /* @__PURE__ */ jsx5("a", { href: m.mediaUrl, target: "_blank", rel: "noreferrer", children: /* @__PURE__ */ jsx5("img", { src: m.mediaUrl, alt: m.caption || "imagem", className: "w-64 max-h-72 object-cover rounded-lg" }) }) : /* @__PURE__ */ jsx5("div", { className: "w-64 h-40 rounded-lg bg-gradient-to-br from-slate-200 to-slate-300 flex items-center justify-center text-slate-500", children: I.image }),
2049
+ m.caption && /* @__PURE__ */ jsx5("p", { className: "text-[13px] mt-2", children: highlightText(m.caption, searchQuery, isCurrentMatch) })
2050
+ ] }),
2051
+ m.kind === "video" && /* @__PURE__ */ jsxs3("div", { className: "-mx-1 -mt-1", children: [
2052
+ m.mediaUrl ? /* @__PURE__ */ jsx5("video", { src: m.mediaUrl, controls: true, className: "w-64 rounded-lg" }) : /* @__PURE__ */ jsx5("div", { className: "w-64 h-40 rounded-lg bg-slate-200 flex items-center justify-center text-slate-500", children: "\u{1F3A5}" }),
2053
+ m.caption && /* @__PURE__ */ jsx5("p", { className: "text-[13px] mt-2", children: highlightText(m.caption, searchQuery, isCurrentMatch) })
2054
+ ] }),
2055
+ m.kind === "document" && /* @__PURE__ */ jsxs3(
2056
+ "a",
2057
+ {
2058
+ href: m.mediaUrl || "#",
2059
+ target: "_blank",
2060
+ rel: "noreferrer",
2061
+ className: `flex items-center gap-2.5 min-w-[200px] rounded-lg px-2 py-1.5 ${isOut ? "bg-emerald-700/40" : "bg-slate-100"}`,
2062
+ children: [
2063
+ /* @__PURE__ */ jsx5("span", { className: isOut ? "text-emerald-100" : "text-slate-500", children: I.file }),
2064
+ /* @__PURE__ */ jsx5("span", { className: "text-[13px] truncate flex-1", children: m.filename || m.caption || "Documento" })
2065
+ ]
2066
+ }
2067
+ ),
2068
+ m.kind === "audio" && (m.mediaUrl ? (
2069
+ // player real (quando a mídia estiver armazenada — R2)
2070
+ /* @__PURE__ */ jsx5("audio", { src: m.mediaUrl, controls: true, className: "w-60 max-w-full" })
2071
+ ) : (
2072
+ // sem mídia guardada ainda: chip honesto, sem player falso
2073
+ /* @__PURE__ */ jsxs3("div", { className: `flex items-center gap-2 min-w-[140px] ${isOut ? "text-emerald-50" : "text-slate-600"}`, children: [
2074
+ /* @__PURE__ */ jsx5("span", { className: isOut ? "text-emerald-100" : "text-slate-500", children: I.mic }),
2075
+ /* @__PURE__ */ jsxs3("span", { className: "text-[13px]", children: [
2076
+ "\xC1udio",
2077
+ m.caption ? ` \xB7 ${m.caption}` : ""
2078
+ ] })
2079
+ ] })
2080
+ )),
2081
+ /* @__PURE__ */ jsxs3("div", { className: `flex items-center gap-1 justify-end mt-1 text-[10.5px] ${isOut ? "text-emerald-100" : "text-slate-400"}`, children: [
2082
+ m.author && !isOut === false && /* @__PURE__ */ jsx5("span", { className: "mr-1", children: m.author }),
2083
+ /* @__PURE__ */ jsx5("span", { children: m.time }),
2084
+ isOut && /* @__PURE__ */ jsx5(Ticks, { status: m.status })
2085
+ ] })
2086
+ ] }) }),
2087
+ !selectMode && selectable && /* @__PURE__ */ jsx5(
2088
+ "button",
2089
+ {
2090
+ onClick: (e) => {
2091
+ e.stopPropagation();
2092
+ onEnterSelect();
2093
+ },
2094
+ title: "Selecionar mensagem",
2095
+ className: "opacity-0 group-hover:opacity-100 transition-opacity self-center mx-1 p-1 rounded-md text-slate-400 hover:text-slate-700 hover:bg-slate-100 shrink-0",
2096
+ children: I.check
2097
+ }
2098
+ )
1658
2099
  ] })
1659
- ] }),
1660
- /* @__PURE__ */ jsxs3("div", { className: `flex gap-1 mt-1 opacity-0 group-hover:opacity-100 transition-opacity ${isOut ? "justify-end" : "justify-start"}`, children: [
1661
- /* @__PURE__ */ jsx5(HoverBtn, { icon: I.reply }),
1662
- /* @__PURE__ */ jsx5(HoverBtn, { icon: I.forward }),
1663
- /* @__PURE__ */ jsx5(HoverBtn, { icon: I.emoji }),
1664
- /* @__PURE__ */ jsx5(HoverBtn, { icon: I.more })
1665
- ] })
1666
- ] }),
1667
- isOut && /* @__PURE__ */ jsx5(Avatar, { name: m.author || "Maria Eduarda", color: m.author === "Alissa Mendes" ? "bg-sky-500" : "bg-amber-500", size: "sm" })
1668
- ] });
1669
- }
1670
- function HoverBtn({ icon }) {
1671
- return /* @__PURE__ */ jsx5("button", { className: "p-1 rounded-md text-slate-400 bg-white border border-slate-200 hover:text-slate-700 shadow-soft", children: icon });
2100
+ ]
2101
+ }
2102
+ );
1672
2103
  }
1673
2104
  function fmtSecs(s) {
1674
2105
  const m = Math.floor(s / 60);
@@ -1773,354 +2204,27 @@ function DetailsPanel({ c, onClose }) {
1773
2204
  ] })
1774
2205
  ] });
1775
2206
  }
1776
- function Section({ title, action, children, defaultOpen = true }) {
1777
- const [open, setOpen] = useState4(defaultOpen);
1778
- return /* @__PURE__ */ jsxs3("div", { className: "border-b border-slate-100", children: [
1779
- /* @__PURE__ */ jsxs3("button", { onClick: () => setOpen((v) => !v), className: "w-full px-4 pt-4 pb-2 flex items-center justify-between", children: [
1780
- /* @__PURE__ */ jsx5("span", { className: "text-[11px] font-semibold uppercase tracking-wider text-slate-500", children: title }),
1781
- /* @__PURE__ */ jsx5("span", { className: `text-slate-400 transition-transform ${open ? "" : "-rotate-90"}`, children: I.chevDown })
1782
- ] }),
1783
- open && /* @__PURE__ */ jsxs3("div", { className: "px-4 pb-4 space-y-2.5", children: [
1784
- children,
1785
- action
1786
- ] })
1787
- ] });
1788
- }
1789
- function Row({ label, children }) {
1790
- return /* @__PURE__ */ jsxs3("div", { className: "flex items-start gap-3 text-[12.5px]", children: [
1791
- /* @__PURE__ */ jsx5("span", { className: "w-28 shrink-0 text-slate-500 pt-0.5", children: label }),
1792
- /* @__PURE__ */ jsx5("div", { className: "flex-1 min-w-0 text-slate-800", children })
1793
- ] });
1794
- }
1795
- var PROPERTY_CATALOG = [
1796
- { id: "856235", title: "Apto 80m\xB2 \xB7 Jardins \xB7 2 dorm" },
1797
- { id: "856301", title: "Apto 65m\xB2 \xB7 Vila Mariana \xB7 2 dorm" },
1798
- { id: "857188", title: "Cobertura 120m\xB2 \xB7 Itaim \xB7 3 dorm" },
1799
- { id: "854902", title: "Casa 180m\xB2 \xB7 Alto de Pinheiros \xB7 4 dorm" },
1800
- { id: "855417", title: "Studio 35m\xB2 \xB7 Pinheiros \xB7 1 dorm" },
1801
- { id: "858033", title: "Apto 95m\xB2 \xB7 Moema \xB7 3 dorm" },
1802
- { id: "858720", title: "Apto 110m\xB2 \xB7 Perdizes \xB7 3 dorm" }
1803
- ];
1804
- function PropertyLinker() {
1805
- const [linked, setLinked] = useState4([PROPERTY_CATALOG[0], PROPERTY_CATALOG[5]]);
1806
- const [query, setQuery] = useState4("");
1807
- const [adding, setAdding] = useState4(false);
1808
- const inputRef = useRef3(null);
1809
- const wrapRef = useRef3(null);
1810
- useEffect4(() => {
1811
- var _a;
1812
- if (adding) (_a = inputRef.current) == null ? void 0 : _a.focus();
1813
- }, [adding]);
1814
- useEffect4(() => {
1815
- if (!adding) return;
1816
- const onDoc = (e) => {
1817
- if (wrapRef.current && !wrapRef.current.contains(e.target)) closeAdd();
1818
- };
1819
- document.addEventListener("mousedown", onDoc);
1820
- return () => document.removeEventListener("mousedown", onDoc);
1821
- }, [adding]);
1822
- const suggestions = useMemo2(() => {
1823
- const q = query.trim().toLowerCase();
1824
- const linkedIds = new Set(linked.map((p) => p.id));
1825
- return PROPERTY_CATALOG.filter(
1826
- (p) => !linkedIds.has(p.id) && (q === "" || p.id.toLowerCase().includes(q) || p.title.toLowerCase().includes(q))
1827
- ).slice(0, 5);
1828
- }, [query, linked]);
1829
- const closeAdd = () => {
1830
- setAdding(false);
1831
- setQuery("");
1832
- };
1833
- const addProperty = (p) => {
1834
- setLinked((l) => [...l, p]);
1835
- closeAdd();
1836
- };
1837
- const removeProperty = (id) => setLinked((l) => l.filter((p) => p.id !== id));
1838
- return /* @__PURE__ */ jsxs3("div", { ref: wrapRef, className: "w-full", children: [
1839
- /* @__PURE__ */ jsxs3("div", { className: "flex flex-wrap items-center gap-x-3 gap-y-1", children: [
1840
- linked.map((p) => /* @__PURE__ */ jsxs3("div", { className: "group inline-flex items-center gap-0.5 text-[13px] leading-tight", children: [
1841
- /* @__PURE__ */ jsxs3(
1842
- "a",
1843
- {
1844
- href: "#",
1845
- title: p.title,
1846
- className: "text-slate-700 underline underline-offset-2 hover:text-emerald-700 inline-flex items-center gap-1 tabular-nums",
1847
- children: [
1848
- p.id,
1849
- /* @__PURE__ */ jsx5("span", { className: "text-slate-400 group-hover:text-emerald-600", children: I.external })
1850
- ]
1851
- }
1852
- ),
1853
- /* @__PURE__ */ jsx5(
1854
- "button",
1855
- {
1856
- onClick: () => removeProperty(p.id),
1857
- title: "Desvincular",
1858
- className: "opacity-0 group-hover:opacity-100 p-0.5 text-slate-400 hover:text-rose-500 transition-opacity",
1859
- children: /* @__PURE__ */ jsxs3("svg", { width: "11", height: "11", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: "2.5", fill: "none", strokeLinecap: "round", children: [
1860
- /* @__PURE__ */ jsx5("line", { x1: "6", y1: "6", x2: "18", y2: "18" }),
1861
- /* @__PURE__ */ jsx5("line", { x1: "6", y1: "18", x2: "18", y2: "6" })
1862
- ] })
1863
- }
1864
- )
1865
- ] }, p.id)),
1866
- !adding && /* @__PURE__ */ jsx5(
1867
- "button",
1868
- {
1869
- onClick: () => setAdding(true),
1870
- title: "Vincular im\xF3vel",
1871
- className: "text-[11px] text-slate-500 hover:text-slate-800 inline-flex items-center gap-0.5 px-1.5 py-0.5 rounded-full border border-dashed border-slate-300 hover:border-slate-400 hover:bg-slate-50",
1872
- children: "+ im\xF3vel"
1873
- }
1874
- )
1875
- ] }),
1876
- adding && /* @__PURE__ */ jsxs3("div", { className: "relative mt-2", children: [
1877
- /* @__PURE__ */ jsx5("span", { className: "absolute left-2 top-1/2 -translate-y-1/2 text-slate-400 pointer-events-none", children: I.search }),
1878
- /* @__PURE__ */ jsx5(
1879
- "input",
1880
- {
1881
- ref: inputRef,
1882
- value: query,
1883
- onChange: (e) => setQuery(e.target.value),
1884
- onKeyDown: (e) => {
1885
- if (e.key === "Escape") closeAdd();
1886
- },
1887
- placeholder: "C\xF3digo ou bairro\u2026",
1888
- className: "w-full pl-7 pr-2 py-1.5 text-[12px] rounded-lg bg-white border border-slate-300 outline-none focus:border-slate-400 placeholder:text-slate-400"
1889
- }
1890
- ),
1891
- suggestions.length > 0 && /* @__PURE__ */ jsx5("div", { className: "absolute top-full left-0 right-0 mt-1 z-30 bg-white border border-slate-200 rounded-lg shadow-lg overflow-hidden", children: suggestions.map((p) => /* @__PURE__ */ jsxs3(
1892
- "button",
1893
- {
1894
- onClick: () => addProperty(p),
1895
- className: "w-full text-left px-2.5 py-1.5 hover:bg-slate-50 flex items-center gap-2 border-b border-slate-100 last:border-b-0",
1896
- children: [
1897
- /* @__PURE__ */ jsx5("span", { className: "text-[12.5px] font-mono font-semibold text-slate-700 tabular-nums", children: p.id }),
1898
- /* @__PURE__ */ jsx5("span", { className: "text-slate-300", children: "\xB7" }),
1899
- /* @__PURE__ */ jsx5("span", { className: "text-[11.5px] text-slate-500 truncate flex-1 min-w-0", children: p.title }),
1900
- /* @__PURE__ */ jsx5("span", { className: "text-slate-300 shrink-0", children: I.plus })
1901
- ]
1902
- },
1903
- p.id
1904
- )) }),
1905
- query.trim() && suggestions.length === 0 && /* @__PURE__ */ jsxs3("div", { className: "absolute top-full left-0 right-0 mt-1 z-30 bg-white border border-slate-200 rounded-lg shadow-lg px-3 py-2 text-[12px] text-slate-500", children: [
1906
- 'Nenhum im\xF3vel encontrado para "',
1907
- query.trim(),
1908
- '".'
1909
- ] })
1910
- ] })
1911
- ] });
1912
- }
1913
- function ActionRow({ icon, label, children, onClick }) {
1914
- const [open, setOpen] = useState4(false);
1915
- const hasSubmenu = !!children;
1916
- const handle = () => hasSubmenu ? setOpen((v) => !v) : onClick == null ? void 0 : onClick();
1917
- return /* @__PURE__ */ jsxs3("div", { children: [
1918
- /* @__PURE__ */ jsxs3(
1919
- "button",
1920
- {
1921
- onClick: handle,
1922
- className: "w-full flex items-center gap-2 text-[13px] text-slate-700 hover:bg-slate-50 -mx-1 px-1 py-1 rounded-md",
1923
- children: [
1924
- /* @__PURE__ */ jsx5("span", { className: "text-slate-500", children: icon }),
1925
- /* @__PURE__ */ jsx5("span", { className: "flex-1 text-left", children: label }),
1926
- /* @__PURE__ */ jsx5("span", { className: `text-slate-400 transition-transform ${hasSubmenu && open ? "rotate-90" : ""}`, children: hasSubmenu ? I.chevRight : I.plus })
1927
- ]
1928
- }
1929
- ),
1930
- hasSubmenu && open && /* @__PURE__ */ jsx5("div", { className: "ml-7 mt-0.5 mb-1 border-l border-slate-200 pl-3 space-y-0.5", children })
1931
- ] });
1932
- }
1933
- function SubAction({ label, onClick }) {
1934
- return /* @__PURE__ */ jsx5(
1935
- "button",
1936
- {
1937
- onClick,
1938
- className: "w-full text-left text-[12.5px] text-slate-600 hover:text-slate-900 hover:bg-slate-50 -mx-1 px-1.5 py-1 rounded-md",
1939
- children: label
1940
- }
1941
- );
1942
- }
1943
2207
  function DetailsTab({ c }) {
1944
- const palette = ["sky", "rose", "violet", "emerald", "amber", "slate"];
1945
- const [tags, setTags] = useState4([
1946
- { label: "Comprador", tone: "sky" },
1947
- { label: "Quente", tone: "rose" },
1948
- { label: "Apto 2-3 dorm", tone: "violet" },
1949
- { label: "Zona Sul", tone: "emerald" },
1950
- { label: "At\xE9 R$ 900k", tone: "amber" }
1951
- ]);
1952
- const [expanded, setExpanded] = useState4(false);
1953
- const [adding, setAdding] = useState4(false);
1954
- const [newTag, setNewTag] = useState4("");
1955
- const tagInputRef = useRef3(null);
1956
- useEffect4(() => {
1957
- var _a;
1958
- if (adding) (_a = tagInputRef.current) == null ? void 0 : _a.focus();
1959
- }, [adding]);
1960
- const VISIBLE = 3;
1961
- const shownTags = expanded ? tags : tags.slice(0, VISIBLE);
1962
- const hiddenCount = Math.max(0, tags.length - VISIBLE);
1963
- const addTag = () => {
1964
- const v = newTag.trim();
1965
- if (v) setTags((t) => [...t, { label: v, tone: palette[t.length % palette.length] }]);
1966
- setNewTag("");
1967
- setAdding(false);
1968
- };
1969
- const removeTag = (i) => setTags((t) => t.filter((_, idx) => idx !== i));
1970
- return /* @__PURE__ */ jsxs3(Fragment, { children: [
1971
- /* @__PURE__ */ jsxs3("div", { className: "px-4 pt-4 pb-3 flex items-start gap-3", children: [
1972
- /* @__PURE__ */ jsx5(Avatar, { name: c.name, color: c.avatarColor, size: "xl", online: true }),
1973
- /* @__PURE__ */ jsxs3("div", { className: "min-w-0 flex-1", children: [
1974
- /* @__PURE__ */ jsx5("div", { className: "font-semibold truncate", children: c.name }),
1975
- /* @__PURE__ */ jsx5("div", { className: "text-[12px] text-slate-500 truncate", children: c.phone }),
1976
- /* @__PURE__ */ jsxs3("div", { className: "flex flex-wrap gap-1 mt-1.5", children: [
1977
- shownTags.map((t, i) => /* @__PURE__ */ jsxs3(Pill, { tone: t.tone, children: [
1978
- t.label,
1979
- /* @__PURE__ */ jsx5(
1980
- "button",
1981
- {
1982
- onClick: () => removeTag(i),
1983
- title: "Remover",
1984
- className: "ml-0.5 -mr-0.5 opacity-60 hover:opacity-100",
1985
- children: /* @__PURE__ */ jsxs3("svg", { width: "10", height: "10", viewBox: "0 0 24 24", stroke: "currentColor", strokeWidth: "3", fill: "none", strokeLinecap: "round", children: [
1986
- /* @__PURE__ */ jsx5("line", { x1: "6", y1: "6", x2: "18", y2: "18" }),
1987
- /* @__PURE__ */ jsx5("line", { x1: "6", y1: "18", x2: "18", y2: "6" })
1988
- ] })
1989
- }
1990
- )
1991
- ] }, `${t.label}-${i}`)),
1992
- !expanded && hiddenCount > 0 && /* @__PURE__ */ jsxs3(
1993
- "button",
1994
- {
1995
- onClick: () => setExpanded(true),
1996
- className: "text-[11px] text-slate-500 hover:text-slate-800 px-1.5 py-0.5 rounded-full hover:bg-slate-100 self-center",
1997
- children: [
1998
- "+",
1999
- hiddenCount
2000
- ]
2001
- }
2002
- ),
2003
- expanded && tags.length > VISIBLE && /* @__PURE__ */ jsx5(
2004
- "button",
2005
- {
2006
- onClick: () => setExpanded(false),
2007
- className: "text-[11px] text-slate-500 hover:text-slate-800 px-1.5 py-0.5 rounded-full hover:bg-slate-100 self-center",
2008
- children: "mostrar menos"
2009
- }
2010
- ),
2011
- adding ? /* @__PURE__ */ jsx5(
2012
- "input",
2013
- {
2014
- ref: tagInputRef,
2015
- value: newTag,
2016
- onChange: (e) => setNewTag(e.target.value),
2017
- onBlur: addTag,
2018
- onKeyDown: (e) => {
2019
- if (e.key === "Enter") {
2020
- e.preventDefault();
2021
- addTag();
2022
- }
2023
- if (e.key === "Escape") {
2024
- setNewTag("");
2025
- setAdding(false);
2026
- }
2027
- },
2028
- placeholder: "nova tag\u2026",
2029
- className: "text-[11px] px-2 py-0.5 rounded-full bg-white border border-slate-300 outline-none focus:border-slate-500 w-24"
2030
- }
2031
- ) : /* @__PURE__ */ jsx5(
2032
- "button",
2033
- {
2034
- onClick: () => {
2035
- setExpanded(true);
2036
- setAdding(true);
2037
- },
2038
- title: "Adicionar tag",
2039
- className: "text-[11px] text-slate-500 hover:text-slate-800 inline-flex items-center gap-0.5 px-1.5 py-0.5 rounded-full border border-dashed border-slate-300 hover:border-slate-400 hover:bg-slate-50",
2040
- children: "+ tag"
2041
- }
2042
- )
2043
- ] })
2044
- ] })
2045
- ] }),
2046
- /* @__PURE__ */ jsxs3(Section, { title: "Atribui\xE7\xE3o", children: [
2047
- /* @__PURE__ */ jsx5(Row, { label: "Atendente", children: /* @__PURE__ */ jsxs3("span", { className: "inline-flex items-center gap-1.5", children: [
2048
- /* @__PURE__ */ jsx5(Avatar, { name: "Maria Eduarda", size: "sm", color: "bg-amber-500" }),
2049
- " Maria Eduarda"
2050
- ] }) }),
2051
- /* @__PURE__ */ jsx5(Row, { label: "Departamento", children: /* @__PURE__ */ jsxs3("span", { className: "inline-flex items-center gap-1.5", children: [
2052
- /* @__PURE__ */ jsx5("span", { className: "w-5 h-5 rounded-md bg-rose-500 text-white text-[10px] flex items-center justify-center font-bold", children: "CO" }),
2053
- "Comercial"
2054
- ] }) }),
2055
- /* @__PURE__ */ jsx5(Row, { label: "Origem", children: /* @__PURE__ */ jsxs3("span", { className: "inline-flex items-center gap-1.5 text-emerald-700", children: [
2056
- I.whatsapp,
2057
- " WhatsApp Business"
2058
- ] }) })
2059
- ] }),
2060
- /* @__PURE__ */ jsxs3(Section, { title: "Ticket", children: [
2061
- /* @__PURE__ */ jsx5(Row, { label: "Tipo", children: /* @__PURE__ */ jsx5(Pill, { tone: "rose", children: "\u{1F3E0} Interesse em compra" }) }),
2062
- /* @__PURE__ */ jsx5(Row, { label: "Status", children: /* @__PURE__ */ jsx5(Pill, { tone: statusInfo[c.status].tone, children: statusInfo[c.status].label }) }),
2063
- /* @__PURE__ */ jsx5(Row, { label: "Prioridade", children: /* @__PURE__ */ jsx5(Pill, { tone: c.priority === "urgent" ? "rose" : c.priority === "high" ? "amber" : "slate", children: "Alta" }) }),
2064
- /* @__PURE__ */ jsx5(Row, { label: "Im\xF3veis", children: /* @__PURE__ */ jsx5(PropertyLinker, {}) }),
2065
- /* @__PURE__ */ jsx5(Row, { label: "Aberto", children: "h\xE1 18 min" })
2066
- ] }),
2067
- /* @__PURE__ */ jsxs3(Section, { title: "A\xE7\xF5es", children: [
2068
- /* @__PURE__ */ jsxs3(ActionRow, { icon: I.userPlus, label: "Adicionar cliente", children: [
2069
- /* @__PURE__ */ jsx5(SubAction, { label: "Propriet\xE1rio" }),
2070
- /* @__PURE__ */ jsx5(SubAction, { label: "Cliente" })
2071
- ] }),
2072
- /* @__PURE__ */ jsx5(ActionRow, { icon: I.checkCircle, label: "Criar tarefa" }),
2073
- /* @__PURE__ */ jsx5(ActionRow, { icon: I.calendar, label: "Agendar visita" }),
2074
- /* @__PURE__ */ jsx5(ActionRow, { icon: I.file, label: "Enviar documentos" })
2208
+ return /* @__PURE__ */ jsx5(Fragment, { children: /* @__PURE__ */ jsxs3("div", { className: "px-4 pt-4 pb-3 flex items-start gap-3", children: [
2209
+ /* @__PURE__ */ jsx5(Avatar, { name: c.name, src: c.avatar, color: c.avatarColor, size: "xl" }),
2210
+ /* @__PURE__ */ jsxs3("div", { className: "min-w-0 flex-1", children: [
2211
+ /* @__PURE__ */ jsx5("div", { className: "font-semibold truncate", children: c.name }),
2212
+ c.phone && /* @__PURE__ */ jsx5("div", { className: "text-[12px] text-slate-500 truncate", children: c.phone })
2075
2213
  ] })
2214
+ ] }) });
2215
+ }
2216
+ function EmptyTab({ icon, title, hint }) {
2217
+ return /* @__PURE__ */ jsxs3("div", { className: "flex flex-col items-center justify-center text-center px-8 py-16 select-none", children: [
2218
+ /* @__PURE__ */ jsx5("div", { className: "w-12 h-12 rounded-full bg-slate-100 flex items-center justify-center text-slate-300 mb-3", children: icon }),
2219
+ /* @__PURE__ */ jsx5("p", { className: "text-[13px] font-medium text-slate-600", children: title }),
2220
+ hint && /* @__PURE__ */ jsx5("p", { className: "text-[12px] text-slate-400 mt-1 max-w-[220px]", children: hint })
2076
2221
  ] });
2077
2222
  }
2078
2223
  function NotesTab() {
2079
- return /* @__PURE__ */ jsxs3("div", { className: "p-4 space-y-3", children: [
2080
- /* @__PURE__ */ jsxs3("div", { className: "bg-yellow-50 border border-yellow-200 rounded-xl p-3", children: [
2081
- /* @__PURE__ */ jsxs3("div", { className: "flex items-center justify-between mb-1.5", children: [
2082
- /* @__PURE__ */ jsxs3("span", { className: "text-[11.5px] font-semibold text-yellow-800 inline-flex items-center gap-1", children: [
2083
- I.pin,
2084
- " Maria Eduarda"
2085
- ] }),
2086
- /* @__PURE__ */ jsx5("span", { className: "text-[11px] text-yellow-700", children: "h\xE1 11 min" })
2087
- ] }),
2088
- /* @__PURE__ */ jsx5("p", { className: "text-[13px] text-yellow-900", children: "Cliente Gold \u2014 perfil quente, decis\xE3o r\xE1pida. J\xE1 visitou conosco em fev/26 (ticket #7892), gostou da regi\xE3o mas o im\xF3vel estava acima do or\xE7amento." })
2089
- ] }),
2090
- /* @__PURE__ */ jsxs3("div", { className: "bg-yellow-50 border border-yellow-200 rounded-xl p-3", children: [
2091
- /* @__PURE__ */ jsxs3("div", { className: "flex items-center justify-between mb-1.5", children: [
2092
- /* @__PURE__ */ jsxs3("span", { className: "text-[11.5px] font-semibold text-yellow-800 inline-flex items-center gap-1", children: [
2093
- I.pin,
2094
- " Alissa Mendes"
2095
- ] }),
2096
- /* @__PURE__ */ jsx5("span", { className: "text-[11px] text-yellow-700", children: "h\xE1 4 min" })
2097
- ] }),
2098
- /* @__PURE__ */ jsx5("p", { className: "text-[13px] text-yellow-900", children: "Confirmei com os propriet\xE1rios \u2014 Vila Mariana e Moema liberados pra visita amanh\xE3 das 10h \xE0s 17h." })
2099
- ] }),
2100
- /* @__PURE__ */ jsxs3("button", { className: "w-full text-[12.5px] py-2 rounded-lg border border-dashed border-slate-300 text-slate-500 hover:bg-slate-50 inline-flex items-center justify-center gap-1", children: [
2101
- I.plus,
2102
- " Nova nota"
2103
- ] })
2104
- ] });
2224
+ return /* @__PURE__ */ jsx5(EmptyTab, { icon: I.pin, title: "Nenhuma nota ainda", hint: "Use \u201CNota interna\u201D no compositor do chat para registrar algo vis\xEDvel s\xF3 para o time." });
2105
2225
  }
2106
2226
  function HistoryTab() {
2107
- const items = [
2108
- { t: "Ticket #7892 \u2014 Visita apto Itaim", s: "Sem proposta", when: "fev/26", tone: "slate" },
2109
- { t: "Ticket #7541 \u2014 Interesse em cobertura", s: "Resolvido", when: "jan/26", tone: "emerald" },
2110
- { t: "Ticket #7233 \u2014 Pedido de matr\xEDcula", s: "Resolvido", when: "dez/25", tone: "emerald" },
2111
- { t: "Ticket #6890 \u2014 Primeira simula\xE7\xE3o", s: "Resolvido", when: "out/25", tone: "emerald" }
2112
- ];
2113
- return /* @__PURE__ */ jsx5("div", { className: "p-4 space-y-2", children: items.map((it, i) => /* @__PURE__ */ jsxs3("button", { className: "w-full text-left bg-white border border-slate-200 hover:border-slate-300 rounded-xl p-3 flex items-start gap-3", children: [
2114
- /* @__PURE__ */ jsx5("span", { className: "w-8 h-8 rounded-lg bg-slate-100 text-slate-500 flex items-center justify-center", children: I.chat }),
2115
- /* @__PURE__ */ jsxs3("div", { className: "flex-1 min-w-0", children: [
2116
- /* @__PURE__ */ jsx5("p", { className: "text-[13px] font-medium truncate", children: it.t }),
2117
- /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-2 mt-1", children: [
2118
- /* @__PURE__ */ jsx5(Pill, { tone: it.tone, children: it.s }),
2119
- /* @__PURE__ */ jsx5("span", { className: "text-[11.5px] text-slate-500", children: it.when })
2120
- ] })
2121
- ] }),
2122
- /* @__PURE__ */ jsx5("span", { className: "text-slate-400 mt-1", children: I.chevRight })
2123
- ] }, i)) });
2227
+ return /* @__PURE__ */ jsx5(EmptyTab, { icon: I.chat, title: "Sem hist\xF3rico", hint: "Atendimentos anteriores deste contato aparecer\xE3o aqui." });
2124
2228
  }
2125
2229
 
2126
2230
  // src/PloyChat.jsx
@@ -2132,11 +2236,14 @@ function PloyChat({
2132
2236
  supabase,
2133
2237
  supabaseUrl,
2134
2238
  supabaseAnonKey,
2135
- className
2239
+ className,
2240
+ onBack,
2241
+ agent,
2242
+ onUnreadChange
2136
2243
  }) {
2137
- const config = { apiBase, workspaceId, getToken, supabase, supabaseUrl, supabaseAnonKey };
2244
+ const config = { apiBase, workspaceId, getToken, supabase, supabaseUrl, supabaseAnonKey, agentName: agent == null ? void 0 : agent.name };
2138
2245
  const rootClass = ["ploychat-root", className].filter(Boolean).join(" ");
2139
- return /* @__PURE__ */ jsx6(PloyConfigProvider, { config, children: /* @__PURE__ */ jsx6("div", { className: rootClass, style: { height: "100%", width: "100%" }, children: /* @__PURE__ */ jsx6(App, {}) }) });
2246
+ return /* @__PURE__ */ jsx6(PloyConfigProvider, { config, children: /* @__PURE__ */ jsx6("div", { className: rootClass, style: { height: "100%", width: "100%" }, children: /* @__PURE__ */ jsx6(App, { onBack, agent, onUnreadChange }) }) });
2140
2247
  }
2141
2248
  var PloyChat_default = PloyChat;
2142
2249
  export {