Files
clube67_newwhats.local/clube67/newwhats.local/frontend/components/ContactProfile.tsx
T
VPS 4 Deploy Agent 2f8c04a0a7
continuous-integration/webhook Falha no deploy de clube67_newwhats.local (VPS 4)
chore(ops): restore all source files in newwhats.clube67.com
2026-05-18 03:28:29 +02:00

174 lines
8.8 KiB
TypeScript

'use client';
import React, { useMemo } from 'react';
import {
X,
ChevronRight,
Image as ImageIcon,
Video as VideoIcon,
FileText,
Mic,
Bell,
Star,
Clock,
Ban,
ThumbsDown,
Trash2
} from 'lucide-react';
import { motion, AnimatePresence } from 'framer-motion';
import {
formatPhone,
getMediaUrl
} from '../utils/inboxUtils';
import Avatar from './ui/Avatar';
import { Message } from '../types/inboxTypes';
interface ContactProfileProps {
isOpen: boolean;
chat: any;
messages: Message[];
onClose: () => void;
}
const ContactProfile: React.FC<ContactProfileProps> = ({
isOpen,
chat,
messages,
onClose,
}) => {
// Filter media messages
const mediaMessages = useMemo(() => {
return messages.filter(m => m.type === 'image' || m.type === 'video' || m.type === 'document' || m.type === 'audio');
}, [messages]);
const photoVideos = mediaMessages.filter(m => m.type === 'image' || m.type === 'video');
const documents = mediaMessages.filter(m => m.type === 'document');
const audios = mediaMessages.filter(m => m.type === 'audio');
if (!chat) return null;
return (
<AnimatePresence>
{isOpen && (
<motion.div
initial={{ x: '100%' }}
animate={{ x: 0 }}
exit={{ x: '100%' }}
transition={{ type: 'spring', damping: 25, stiffness: 200 }}
className="w-[400px] h-full bg-[#f0f2f5] border-l border-[#d1d7db] flex flex-col z-40 relative flex-shrink-0"
>
{/* Header */}
<div className="h-[60px] px-4 bg-white flex items-center gap-6 border-b border-[#d1d7db] shrink-0">
<button
onClick={onClose}
className="p-2 hover:bg-black/5 rounded-full transition-colors"
>
<X className="w-6 h-6 text-[#54656f]" />
</button>
<h2 className="text-[16px] font-normal text-[#111b21]">Dados do contato</h2>
</div>
{/* Content */}
<div className="flex-1 overflow-y-auto overflow-x-hidden custom-scrollbar pb-10">
{/* Profile Section */}
<div className="bg-white px-8 py-8 flex flex-col items-center mb-2 shadow-sm">
{/* Profile Photo */}
<Avatar chat={chat} size={200} className="mb-5 shadow-lg" />
<h1 className="text-[24px] font-normal text-[#111b21] mb-1 text-center">
{chat.lead_name || chat.subject || formatPhone(chat.phone, chat.remote_jid)}
</h1>
<p className="text-[16px] text-[#667781] font-normal mb-1">
{formatPhone(chat.phone, chat.remote_jid)}
</p>
{chat.bio && (
<p className="text-[14px] text-[#667781] font-normal mt-2 text-center max-w-xs">
{chat.bio}
</p>
)}
</div>
{/* Status (Bio) */}
<div className="bg-white px-8 py-4 mb-2 shadow-sm">
<h3 className="text-[14px] text-[#667781] font-normal mb-3">Recado</h3>
<p className="text-[16px] text-[#111b21] font-normal leading-relaxed">
{chat.bio || 'Sem recado disponível'}
</p>
</div>
{/* Media Section */}
<div className="bg-white mb-2 shadow-sm">
<button className="w-full px-8 py-4 flex items-center justify-between hover:bg-[#f5f6f6] transition-colors group">
<span className="text-[14px] text-[#667781] font-normal">Mídia, links e documentos</span>
<div className="flex items-center gap-1">
<span className="text-[14px] text-[#667781] font-normal">{mediaMessages.length}</span>
<ChevronRight className="w-5 h-5 text-[#8696a0] group-hover:translate-x-0.5 transition-transform" />
</div>
</button>
{mediaMessages.length > 0 ? (
<div className="px-8 pb-4 grid grid-cols-3 gap-1">
{photoVideos.slice(0, 3).map((m, i) => (
<div
key={m.id || i}
className="aspect-square bg-gray-100 rounded-sm overflow-hidden cursor-pointer hover:opacity-90 transition-opacity"
onClick={() => {
const url = getMediaUrl(m.media_url);
if (url) window.open(url, '_blank');
}}
>
<div className="w-full h-full flex items-center justify-center bg-black/10">
{m.type === 'video' ? <VideoIcon className="w-6 h-6 text-white" /> : <ImageIcon className="w-6 h-6 text-gray-400" />}
</div>
</div>
))}
{mediaMessages.length > 3 && (
<div className="aspect-square flex items-center justify-center bg-gray-50 border border-dashed border-gray-200 text-gray-400 text-xs rounded-sm">
+{mediaMessages.length - 3}
</div>
)}
</div>
) : (
<div className="px-8 pb-6 flex flex-col items-center justify-center text-center">
<p className="text-[14px] text-[#667781] italic">Nenhuma mídia compartilhada</p>
</div>
)}
</div>
{/* Actions Section */}
<div className="bg-white mb-2 shadow-sm py-2">
{[
{ icon: Bell, label: 'Silenciar notificações', color: '#111b21' },
{ icon: Star, label: 'Mensagens favoritas', color: '#111b21' },
{ icon: Clock, label: 'Mensagens temporárias', sub: 'Desativadas', color: '#111b21' },
].map((item, idx) => (
<button key={idx} className="w-full px-8 py-3.5 flex items-center gap-6 hover:bg-[#f5f6f6] transition-colors text-left">
<item.icon className="w-5 h-5 text-[#8696a0]" />
<div className="flex flex-col">
<span className="text-[16px] text-[#111b21] font-normal leading-none">{item.label}</span>
{item.sub && <span className="text-[12px] text-[#667781] mt-1">{item.sub}</span>}
</div>
</button>
))}
</div>
<div className="bg-white mb-2 shadow-sm py-2">
{[
{ icon: Ban, label: `Bloquear ${chat.lead_name || 'Contato'}`, color: '#ea0038' },
{ icon: ThumbsDown, label: `Denunciar ${chat.lead_name || 'Contato'}`, color: '#ea0038' },
{ icon: Trash2, label: 'Apagar conversa', color: '#ea0038' }
].map((item, idx) => (
<button key={idx} className="w-full px-8 py-3.5 flex items-center gap-6 hover:bg-[#f5f6f6] transition-colors text-left">
<item.icon className="w-5 h-5" style={{ color: item.color }} />
<span className="text-[16px] font-normal" style={{ color: item.color }}>{item.label}</span>
</button>
))}
</div>
</div>
</motion.div>
)}
</AnimatePresence>
);
};
export default ContactProfile;