8ea8faa4c2
Consolida WIP acumulado que não faz parte das features de hoje: melhorias do inbox (WhatsChatDrawer, InputBar, MessageBubble, ProtocolPanel, StickerPanel, LinkPreview, hooks, socketService, chatStore, nwClient), componentes novos (ConversationSearch, ForwardModal, LabelManagerModal, roleMeta), AgendaPresence, ComunicacaoInternaModal, Sidebar, GestaoEquipeView, SessionsView, appTime e o asset chat-bg.png. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
104 lines
4.9 KiB
TypeScript
104 lines
4.9 KiB
TypeScript
import React, { useState, useMemo } from 'react';
|
|
import { createPortal } from 'react-dom';
|
|
import { X, Search, Send, Check } from 'lucide-react';
|
|
|
|
interface ForwardModalProps {
|
|
isOpen: boolean;
|
|
onClose: () => void;
|
|
chats: any[];
|
|
onForward: (toChatIds: string[]) => Promise<void> | void;
|
|
}
|
|
|
|
// Modal para escolher um ou mais chats de destino e encaminhar a mensagem selecionada.
|
|
const ForwardModal: React.FC<ForwardModalProps> = ({ isOpen, onClose, chats, onForward }) => {
|
|
const [query, setQuery] = useState('');
|
|
const [selected, setSelected] = useState<Set<string>>(new Set());
|
|
const [sending, setSending] = useState(false);
|
|
|
|
const label = (c: any) => c.displayName || c.subject || c.name || c.phone || c.remote_jid || String(c.id);
|
|
|
|
const filtered = useMemo(() => {
|
|
const q = query.trim().toLowerCase();
|
|
if (!q) return chats;
|
|
return chats.filter((c) => label(c).toLowerCase().includes(q));
|
|
}, [chats, query]);
|
|
|
|
if (!isOpen || typeof document === 'undefined') return null;
|
|
|
|
const toggle = (id: string) => {
|
|
setSelected((prev) => {
|
|
const n = new Set(prev);
|
|
n.has(id) ? n.delete(id) : n.add(id);
|
|
return n;
|
|
});
|
|
};
|
|
|
|
const handleForward = async () => {
|
|
if (selected.size === 0 || sending) return;
|
|
setSending(true);
|
|
try { await onForward(Array.from(selected)); }
|
|
finally { setSending(false); setSelected(new Set()); setQuery(''); }
|
|
};
|
|
|
|
return createPortal(
|
|
<div className="fixed inset-0 z-[9999] flex items-center justify-center bg-black/50 backdrop-blur-sm p-4" onClick={onClose}>
|
|
<div className="w-full max-w-md bg-white rounded-2xl shadow-2xl overflow-hidden flex flex-col max-h-[80vh]" onClick={(e) => e.stopPropagation()}>
|
|
<div className="flex items-center justify-between px-4 py-3 border-b border-gray-100">
|
|
<h3 className="text-[15px] font-bold text-[#111b21]">Encaminhar para…</h3>
|
|
<button onClick={onClose} className="p-1 text-gray-400 hover:text-gray-700 rounded-full transition-colors"><X className="w-5 h-5" /></button>
|
|
</div>
|
|
|
|
<div className="px-4 py-2.5 border-b border-gray-100">
|
|
<div className="flex items-center gap-2 bg-[#f0f2f5] rounded-lg px-3 py-2">
|
|
<Search className="w-4 h-4 text-[#667781] shrink-0" />
|
|
<input
|
|
autoFocus
|
|
value={query}
|
|
onChange={(e) => setQuery(e.target.value)}
|
|
placeholder="Pesquisar conversa"
|
|
className="w-full bg-transparent text-[14px] text-[#111b21] placeholder:text-[#667781] focus:outline-none"
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="flex-1 overflow-y-auto">
|
|
{filtered.length === 0 && (
|
|
<div className="px-4 py-6 text-center text-[13px] text-[#667781]">Nenhuma conversa encontrada.</div>
|
|
)}
|
|
{filtered.map((c) => {
|
|
const id = String(c.id);
|
|
const isSel = selected.has(id);
|
|
return (
|
|
<button
|
|
key={id}
|
|
onClick={() => toggle(id)}
|
|
className="w-full flex items-center gap-3 px-4 py-2.5 hover:bg-[#f5f6f6] transition-colors text-left"
|
|
>
|
|
<div className={`w-5 h-5 rounded-full border-2 flex items-center justify-center shrink-0 transition-colors ${isSel ? 'bg-[#00a884] border-[#00a884]' : 'border-gray-300'}`}>
|
|
{isSel && <Check className="w-3 h-3 text-white" />}
|
|
</div>
|
|
<span className="text-[14px] text-[#111b21] truncate">{label(c)}</span>
|
|
</button>
|
|
);
|
|
})}
|
|
</div>
|
|
|
|
<div className="px-4 py-3 border-t border-gray-100 flex items-center justify-between">
|
|
<span className="text-[12px] text-[#667781]">{selected.size} selecionada(s)</span>
|
|
<button
|
|
onClick={handleForward}
|
|
disabled={selected.size === 0 || sending}
|
|
className="flex items-center gap-2 bg-[#00a884] text-white text-[14px] font-bold px-4 py-2 rounded-lg disabled:opacity-40 hover:bg-[#008f6f] transition-colors"
|
|
>
|
|
<Send className="w-4 h-4" />
|
|
{sending ? 'Encaminhando…' : 'Encaminhar'}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>,
|
|
document.body,
|
|
);
|
|
};
|
|
|
|
export default ForwardModal;
|