import React, { useState, useRef } from 'react'; import { Building2, Phone, Mail, MapPin, Globe, Calendar, FileText, Users, Plus, ArrowLeft, MoreHorizontal, Download, Search, Filter, CheckCircle, Briefcase, ExternalLink, X, Save, UploadCloud, DollarSign, MessageCircle, AlertTriangle, Pencil, Camera, Trash2, ChevronDown } from 'lucide-react'; import { Company, ContactPerson, CompanyDocument, Service } from '../types'; import { useToast } from '../contexts/ToastContext'; interface CRMViewProps { companies: Company[]; setCompanies: (companies: Company[]) => void; availableServices: Service[]; } // Simple Modal (Reused) const Modal: React.FC<{isOpen: boolean; onClose: () => void; title: string; children: React.ReactNode; maxWidth?: string}> = ({ isOpen, onClose, title, children, maxWidth = 'max-w-lg' }) => { if (!isOpen) return null; return (

{title}

{children}
); }; export const CRMView: React.FC = ({ companies, setCompanies, availableServices }) => { const { addToast } = useToast(); const [viewMode, setViewMode] = useState<'list' | 'detail'>('list'); const [selectedCompanyId, setSelectedCompanyId] = useState(null); const [activeTab, setActiveTab] = useState<'overview' | 'contacts' | 'documents' | 'services'>('overview'); // Modal States const [isCompanyModalOpen, setIsCompanyModalOpen] = useState(false); const [isContactModalOpen, setIsContactModalOpen] = useState(false); const [isServiceModalOpen, setIsServiceModalOpen] = useState(false); // Editing States const [isEditingOverview, setIsEditingOverview] = useState(false); const [editingContactId, setEditingContactId] = useState(null); // Forms State const [newCompany, setNewCompany] = useState>({ status: 'active', industry: 'Outro', logo: '' }); const [overviewForm, setOverviewForm] = useState>({}); const [contactForm, setContactForm] = useState>({}); const [selectedServiceId, setSelectedServiceId] = useState(''); // Refs for File Uploads const logoInputRef = useRef(null); const avatarInputRef = useRef(null); const docInputRef = useRef(null); const selectedCompany = companies.find(c => c.id === selectedCompanyId); const clientRevenue = selectedCompany ? selectedCompany.activeServices.reduce((acc, s) => acc + s.price, 0) : 0; // --- HELPER: FILE TO BASE64 --- const handleFileChange = (e: React.ChangeEvent, callback: (base64: string) => void) => { const file = e.target.files?.[0]; if (file) { const reader = new FileReader(); reader.onloadend = () => { callback(reader.result as string); }; reader.readAsDataURL(file); } }; // --- ACTIONS --- const handleSaveCompany = () => { if (!newCompany.name) { addToast({ type: 'warning', title: 'Campos Obrigatórios', message: 'Por favor, informe ao menos a Razão Social.' }); return; } const company: Company = { ...newCompany, id: Math.random().toString(36).substr(2, 9), logo: newCompany.logo || `https://ui-avatars.com/api/?name=${newCompany.name}&background=random`, contacts: [], documents: [], activeServices: [] } as Company; setCompanies([...companies, company]); setIsCompanyModalOpen(false); setNewCompany({ status: 'active', industry: 'Outro', logo: '' }); addToast({ type: 'success', title: 'Empresa Cadastrada', message: `${company.name} foi adicionada com sucesso.` }); }; const handleUpdateOverview = () => { if (!selectedCompany || !overviewForm.name) return; const updatedCompanies = companies.map(c => { if (c.id === selectedCompany.id) { return { ...c, ...overviewForm }; } return c; }); setCompanies(updatedCompanies); setIsEditingOverview(false); addToast({ type: 'success', title: 'Dados Atualizados', message: 'As informações da empresa foram salvas.' }); }; const openContactModal = (contact?: ContactPerson) => { if (contact) { setEditingContactId(contact.id); setContactForm(contact); } else { setEditingContactId(null); setContactForm({ avatar: '' }); } setIsContactModalOpen(true); }; const handleSaveContact = () => { if (!selectedCompany || !contactForm.name) { addToast({ type: 'warning', title: 'Nome Obrigatório', message: 'Informe o nome do responsável.' }); return; } let updatedContacts = [...selectedCompany.contacts]; if (editingContactId) { // Edit existing updatedContacts = updatedContacts.map(c => c.id === editingContactId ? { ...c, ...contactForm } as ContactPerson : c); } else { // Create new const newContact: ContactPerson = { ...contactForm, id: Math.random().toString(36).substr(2, 9), avatar: contactForm.avatar || `https://ui-avatars.com/api/?name=${contactForm.name}&background=random` } as ContactPerson; updatedContacts.push(newContact); } const updatedCompanies = companies.map(c => { if (c.id === selectedCompany.id) { return { ...c, contacts: updatedContacts }; } return c; }); setCompanies(updatedCompanies); setIsContactModalOpen(false); setContactForm({}); setEditingContactId(null); addToast({ type: 'success', title: 'Responsável Salvo', message: 'Lista de contatos atualizada.' }); }; const handleDeleteContact = (contactId: string) => { if (!selectedCompany || !window.confirm("Excluir responsável?")) return; const updatedCompanies = companies.map(c => { if (c.id === selectedCompany.id) { return { ...c, contacts: c.contacts.filter(ct => ct.id !== contactId) }; } return c; }); setCompanies(updatedCompanies); addToast({ type: 'info', title: 'Responsável Removido' }); }; const handleUploadDocument = (e: React.ChangeEvent) => { const file = e.target.files?.[0]; if (!file || !selectedCompany) return; const newDoc: CompanyDocument = { id: Math.random().toString(36).substr(2, 9), title: file.name, type: file.name.endsWith('.pdf') ? 'briefing' : 'other', date: new Date().toLocaleDateString('pt-BR'), size: `${(file.size / 1024 / 1024).toFixed(2)} MB` }; const updatedCompanies = companies.map(c => { if (c.id === selectedCompany.id) { return { ...c, documents: [...c.documents, newDoc] }; } return c; }); setCompanies(updatedCompanies); addToast({ type: 'success', title: 'Upload Concluído', message: 'Arquivo anexado com sucesso.' }); }; const handleAddService = () => { if (!selectedCompany || !selectedServiceId) return; const serviceToAdd = availableServices.find(s => s.id === selectedServiceId); if (!serviceToAdd) return; const updatedCompanies = companies.map(c => { if (c.id === selectedCompany.id) { return { ...c, activeServices: [...c.activeServices, serviceToAdd] }; } return c; }); setCompanies(updatedCompanies); setIsServiceModalOpen(false); setSelectedServiceId(''); addToast({ type: 'success', title: 'Serviço Adicionado', message: 'Contrato atualizado.' }); }; const handleRemoveService = (index: number) => { if (!selectedCompany) return; const updatedCompanies = companies.map(c => { if (c.id === selectedCompany.id) { const newServices = [...c.activeServices]; newServices.splice(index, 1); return { ...c, activeServices: newServices }; } return c; }); setCompanies(updatedCompanies); addToast({ type: 'info', title: 'Serviço Removido' }); }; const openWhatsApp = (phone: string) => { const cleanPhone = phone.replace(/\D/g, ''); window.open(`https://wa.me/55${cleanPhone}`, '_blank'); }; // Styles const inputClass = "w-full p-3 bg-white border border-slate-200 rounded-xl focus:ring-2 focus:ring-primary-200 outline-none text-slate-800 text-sm"; const selectClass = "w-full bg-white border border-slate-200 rounded-xl px-4 py-3 pr-10 text-sm font-medium text-slate-700 outline-none focus:ring-2 focus:ring-primary-500 focus:border-primary-500 appearance-none transition-all cursor-pointer shadow-sm"; const labelClass = "block text-xs font-bold text-slate-700 mb-1 uppercase tracking-wide"; // --- VIEWS --- if (viewMode === 'list') { return (
setIsCompanyModalOpen(false)} title="Nova Empresa" maxWidth="max-w-2xl">
{/* Logo Upload */}
logoInputRef.current?.click()} > {newCompany.logo ? ( Logo ) : ( )}
Alterar
Clique para adicionar logo handleFileChange(e, (base64) => setNewCompany({...newCompany, logo: base64}))} />
setNewCompany({...newCompany, name: e.target.value})} placeholder="Ex: Tech Solutions LTDA" />
setNewCompany({...newCompany, fantasyName: e.target.value})} placeholder="Ex: Tech Sol" />
setNewCompany({...newCompany, cnpj: e.target.value})} placeholder="00.000.000/0001-00" />
setNewCompany({...newCompany, ie: e.target.value})} />
setNewCompany({...newCompany, city: e.target.value})} />
setNewCompany({...newCompany, phone: e.target.value})} />
setNewCompany({...newCompany, email: e.target.value})} />
setNewCompany({...newCompany, address: e.target.value})} />

Clientes & Empresas

Gerencie contratos, responsáveis e documentos.

{companies.map((company) => { const totalValue = company.activeServices.reduce((acc, s) => acc + s.price, 0); return (
{ setSelectedCompanyId(company.id); setViewMode('detail'); }} className={`group bg-white p-6 rounded-[2rem] shadow-sm hover:shadow-lg hover:-translate-y-1 transition-all border cursor-pointer relative overflow-hidden ${company.status === 'overdue' ? 'border-red-200 ring-1 ring-red-100' : 'border-slate-50'}`} > {company.status === 'overdue' && (
EM ATRASO
)}
{company.logo ? : company.name.substring(0,2)}

{company.fantasyName || company.name}

{company.city || 'Local não informado'}

{company.status === 'active' && Ativo} {company.status === 'pending' && Pendente} {company.status === 'overdue' && Financeiro Pendente}
{company.contacts.length} Resp. R$ {totalValue.toLocaleString('pt-BR')} /mês
); })}
); } // DETALHE DA EMPRESA if (selectedCompany) { return (
{/* Modal Contato (Novo/Edição) */} setIsContactModalOpen(false)} title={editingContactId ? "Editar Responsável" : "Novo Responsável"}>
avatarInputRef.current?.click()} > {contactForm.avatar ? ( Avatar ) : ( )}
Foto
handleFileChange(e, (base64) => setContactForm({...contactForm, avatar: base64}))} />
setContactForm({...contactForm, name: e.target.value})} />
setContactForm({...contactForm, role: e.target.value})} />
setContactForm({...contactForm, email: e.target.value})} />
setContactForm({...contactForm, phone: e.target.value})} placeholder="(00) 00000-0000" />
{/* Modal Adicionar Serviço */} setIsServiceModalOpen(false)} title="Adicionar Serviço">
{/* Header Detalhe */}

{selectedCompany.fantasyName || selectedCompany.name}

{selectedCompany.name} - {selectedCompany.cnpj}

{selectedCompany.status === 'overdue' && (
CLIENTE EM ATRASO
)}
{/* Tabs */}
{['overview', 'contacts', 'documents', 'services'].map(tab => ( ))}
{/* Tab Content */}
{activeTab === 'overview' && (
{!isEditingOverview ? ( ) : (
)}

Dados Cadastrais

CNPJ {isEditingOverview ? setOverviewForm({...overviewForm, cnpj: e.target.value})} /> : {selectedCompany.cnpj || '-'}}
Inscrição Estadual {isEditingOverview ? setOverviewForm({...overviewForm, ie: e.target.value})} /> : {selectedCompany.ie || 'Isento'}}
Razão Social {isEditingOverview ? setOverviewForm({...overviewForm, name: e.target.value})} /> : {selectedCompany.name}}
Endereço {isEditingOverview ? (
setOverviewForm({...overviewForm, address: e.target.value})} placeholder="Rua..." /> setOverviewForm({...overviewForm, city: e.target.value})} placeholder="Cidade..." />
) : ( {selectedCompany.address}, {selectedCompany.city} )}

Contato Geral

{isEditingOverview ? setOverviewForm({...overviewForm, phone: e.target.value})} /> : {selectedCompany.phone}}
{isEditingOverview ? setOverviewForm({...overviewForm, email: e.target.value})} /> : {selectedCompany.email}}
{isEditingOverview ? setOverviewForm({...overviewForm, website: e.target.value})} placeholder="Website" /> : {selectedCompany.website || 'Sem site'}}
)} {activeTab === 'contacts' && (
{selectedCompany.contacts.length === 0 ? (
Nenhum responsável cadastrado.
) : (
{selectedCompany.contacts.map(contact => (
{contact.avatar ? : contact.name.charAt(0)}

{contact.name}

{contact.role}

{contact.email}

))}
)}
)} {activeTab === 'documents' && (
docInputRef.current?.click()} className="bg-slate-50 border-2 border-dashed border-slate-200 rounded-2xl p-8 text-center hover:bg-slate-100 transition-colors cursor-pointer group" >

Arraste arquivos aqui ou clique para anexar

PDFs, Contratos (DOCX), Briefings.

{selectedCompany.documents.map(doc => (

{doc.title}

{doc.date} • {doc.size}

))}
)} {activeTab === 'services' && (

Faturamento Total

R$ {clientRevenue.toLocaleString('pt-BR')} /mês

{selectedCompany.activeServices.map((service, idx) => (

{service.name}

{service.billingType === 'recurring' ? 'Recorrente' : 'Pontual'}
R$ {service.price.toLocaleString('pt-BR')}
))}
)}
); } return null; };