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 (
);
};
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 ? (

) : (
)}
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"}>
{/* Modal Adicionar Serviço */}
setIsServiceModalOpen(false)} title="Adicionar Serviço">
{/* Header Detalhe */}
{selectedCompany.fantasyName || selectedCompany.name}
{selectedCompany.name} - {selectedCompany.cnpj}
{selectedCompany.status === 'overdue' && (
)}
{/* Tabs */}
{['overview', 'contacts', 'documents', 'services'].map(tab => (
))}
{/* Tab Content */}
{activeTab === 'overview' && (
{!isEditingOverview ? (
) : (
)}
)}
{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;
};