// CRM Module function CRM() { const [clients, setClients] = React.useState([]); const [loading, setLoading] = React.useState(true); const [selected, setSelected] = React.useState(null); const [detailNotes, setDetailNotes] = React.useState([]); const [note, setNote] = React.useState(''); const [addModal, setAddModal] = React.useState(false); const [editModal, setEditModal] = React.useState(false); const [delConfirm, setDelConfirm] = React.useState(false); const [saving, setSaving] = React.useState(false); const emptyForm = { name: '', contact: '', email: '', phone: '', city: '', segment: 'SMB', status: 'Prospect' }; const [form, setForm] = React.useState(emptyForm); const [editForm, setEditForm] = React.useState(emptyForm); const [toast, setToast] = React.useState(null); const showToast = (msg, type = 'success') => { setToast({ msg, type }); setTimeout(() => setToast(null), 3000); }; React.useEffect(() => { sb.from('clients').select('*').order('name').then(({ data }) => { if (data) setClients(data); setLoading(false); }); }, []); const openClient = (c) => { setSelected(c); setDetailNotes(c.notes || []); setNote(''); }; const closeClient = () => { setSelected(null); setDetailNotes([]); setNote(''); }; const saveNote = async () => { if (!note.trim() || !selected) return; const updated = [...detailNotes, note]; setDetailNotes(updated); setNote(''); await sb.from('clients').update({ notes: updated }).eq('id', selected.id); setClients(prev => prev.map(cl => cl.id === selected.id ? { ...cl, notes: updated } : cl)); showToast('Anotação salva!'); }; const openEdit = () => { setEditForm({ name: selected.name, contact: selected.contact || '', email: selected.email || '', phone: selected.phone || '', city: selected.city || '', segment: selected.segment || 'SMB', status: selected.status || 'Prospect' }); setEditModal(true); }; const handleEditSave = async () => { if (!editForm.name.trim()) { showToast('Preencha o nome.', 'danger'); return; } setSaving(true); const { data, error } = await sb.from('clients').update({ name: editForm.name, contact: editForm.contact, email: editForm.email, phone: editForm.phone, city: editForm.city, segment: editForm.segment, status: editForm.status, }).eq('id', selected.id).select().single(); setSaving(false); if (error) { showToast('Erro ao salvar.', 'danger'); return; } setClients(prev => prev.map(c => c.id === data.id ? data : c).sort((a, b) => a.name.localeCompare(b.name))); setSelected(data); setDetailNotes(data.notes || []); setEditModal(false); showToast('Cliente atualizado!'); }; const handleDelete = async () => { const id = selected.id; const nome = selected.name; await sb.from('clients').delete().eq('id', id); setClients(prev => prev.filter(c => c.id !== id)); setDelConfirm(false); closeClient(); showToast(`${nome} excluído.`); }; const handleCreate = async () => { if (!form.name.trim()) { showToast('Preencha o nome do cliente.', 'danger'); return; } setSaving(true); const { data, error } = await sb.from('clients').insert({ name: form.name, contact: form.contact, email: form.email, phone: form.phone, city: form.city, segment: form.segment, status: form.status, events_count: 0, total_spent: 0, notes: [], }).select().single(); setSaving(false); if (error) { showToast('Erro ao adicionar cliente.', 'danger'); return; } setClients(prev => [...prev, data].sort((a, b) => a.name.localeCompare(b.name))); setAddModal(false); setForm(emptyForm); showToast(`${data.name} adicionado com sucesso!`); }; const statusColor = { 'Ativo': 'success', 'Prospect': 'warning', 'Inativo': 'default' }; const segmentColor = { 'Enterprise': 'primary', 'SMB': 'default', 'PF': 'purple' }; const totalAtivos = clients.filter(c => c.status === 'Ativo').length; const receitaTotal = clients.reduce((s, c) => s + (c.total_spent || 0), 0); const ticketMedio = clients.length > 0 ? receitaTotal / clients.length : 0; const timeline = [ { icon: 'calendar', text: 'Evento confirmado', date: '22 Abr 2026', color: DS.colors.primary }, { icon: 'budget', text: 'Orçamento enviado', date: '18 Abr 2026', color: DS.colors.warning }, { icon: 'message', text: 'Reunião de briefing realizada', date: '10 Abr 2026', color: DS.colors.success }, ]; return (
{/* Header */}

Clientes (CRM)

Gerencie relacionamentos e histórico de clientes

setAddModal(true)}>Novo cliente
{/* Stats */}
{[ { label: 'Clientes ativos', value: totalAtivos, color: DS.colors.primary }, { label: 'Receita total', value: 'R$ ' + (receitaTotal / 1000000).toFixed(1) + 'M', color: DS.colors.success }, { label: 'Ticket médio', value: 'R$ ' + Math.round(ticketMedio / 1000) + 'k', color: DS.colors.warning }, ].map(s => (
{s.label}
{s.value}
))}
{/* Table */} {loading ? : ( <>
EmpresaContatoÚltimo eventoEventosSegmentoStatusTotal
{clients.length === 0 ? : clients.map((c, i) => (
openClient(c)} style={{ display: 'grid', gridTemplateColumns: '2fr 1.2fr 1fr 0.8fr 0.8fr 0.8fr 0.8fr', padding: '14px 24px', borderBottom: i < clients.length - 1 ? `1px solid ${DS.colors.border}` : 'none', alignItems: 'center', cursor: 'pointer', transition: 'background 0.1s' }} onMouseEnter={e => e.currentTarget.style.background = '#F8FAFC'} onMouseLeave={e => e.currentTarget.style.background = 'transparent'} >
{c.name}
{c.contact} {(c.last_event || '').substring(0, 18)}{(c.last_event || '').length > 18 ? '…' : ''} {c.events_count || 0} {c.segment} {c.status} R$ {((c.total_spent || 0) / 1000).toFixed(0)}k
)) } )}
{/* ── Popup detalhe do cliente ── */} Editar setDelConfirm(true)}>Excluir
)}> {selected && (
{/* Coluna esquerda */}
{/* Stats */}
{[ { label: 'Total investido', value: 'R$ ' + (selected.total_spent || 0).toLocaleString('pt-BR'), color: DS.colors.primary }, { label: 'Eventos realizados', value: selected.events_count || 0, color: DS.colors.success }, { label: 'Ticket médio', value: selected.events_count > 0 ? 'R$ ' + Math.round((selected.total_spent || 0) / selected.events_count).toLocaleString('pt-BR') : '—', color: DS.colors.warning }, ].map(s => (
{s.label}
{s.value}
))}
{/* Timeline */}
Linha do tempo
{timeline.map((t, i) => (
{i < timeline.length - 1 &&
}
{t.text}
{t.date}
))}
{/* Notas */}
Anotações & Follow-up
{detailNotes.length === 0 ?
Nenhuma anotação ainda.
: detailNotes.map((n, i) => (
{n}
)) }
setNote(e.target.value)} onKeyDown={e => { if (e.key === 'Enter') saveNote(); }} placeholder="Adicionar anotação..." style={{ flex: 1, border: `1px solid ${DS.colors.border}`, borderRadius: DS.radius.sm, padding: '7px 11px', fontSize: 13, outline: 'none', fontFamily: 'inherit' }} onFocus={e => e.target.style.borderColor = DS.colors.primary} onBlur={e => e.target.style.borderColor = DS.colors.border} /> Salvar
{/* Coluna direita — informações */}
{/* Avatar + badges */}
{selected.name}
{selected.contact}
{selected.segment} {selected.status}
selected.email ? window.open(`mailto:${selected.email}`) : showToast('E-mail não cadastrado.', 'danger')}>E-mail selected.phone ? window.open(`tel:${selected.phone.replace(/\D/g, '')}`) : showToast('Telefone não cadastrado.', 'danger')}>Ligar
{/* Dados */}
Contato
{[ { icon: 'mail', label: selected.email || '—' }, { icon: 'phone', label: selected.phone || '—' }, { icon: 'map', label: selected.city || '—' }, ].map(r => (
{r.label}
))}
{/* Último evento */} {selected.last_event && (
Último evento
{selected.last_event}
)}
)} {/* Modal editar cliente */} setEditModal(false)} title="Editar Cliente" width={560}>
setEditForm(f => ({ ...f, name: v }))} placeholder="Ex: TechCorp Brasil" />
setEditForm(f => ({ ...f, contact: v }))} placeholder="Ex: Marcelo Andrade" /> setEditForm(f => ({ ...f, city: v }))} placeholder="Ex: São Paulo" />
setEditForm(f => ({ ...f, email: v }))} placeholder="Ex: contato@empresa.com" /> setEditForm(f => ({ ...f, phone: v }))} placeholder="Ex: (11) 3291-4400" />
setEditForm(f => ({ ...f, status: v }))} options={[{ value: 'Prospect', label: 'Prospect' }, { value: 'Ativo', label: 'Ativo' }, { value: 'Inativo', label: 'Inativo' }]} />
setEditModal(false)}>Cancelar {saving ? 'Salvando…' : 'Salvar alterações'}
{/* Confirmação de exclusão */} setDelConfirm(false)} title="Excluir cliente" width={400}>

Tem certeza que deseja excluir {selected?.name}? Esta ação não pode ser desfeita.

setDelConfirm(false)}>Cancelar Excluir
{/* Modal novo cliente */} { setAddModal(false); setForm(emptyForm); }} title="Novo Cliente" width={560}>
setForm(f => ({ ...f, name: v }))} placeholder="Ex: TechCorp Brasil" />
setForm(f => ({ ...f, contact: v }))} placeholder="Ex: Marcelo Andrade" /> setForm(f => ({ ...f, city: v }))} placeholder="Ex: São Paulo" />
setForm(f => ({ ...f, email: v }))} placeholder="Ex: contato@empresa.com" /> setForm(f => ({ ...f, phone: v }))} placeholder="Ex: (11) 3291-4400" />
setForm(f => ({ ...f, status: v }))} options={[ { value: 'Prospect', label: 'Prospect' }, { value: 'Ativo', label: 'Ativo' }, { value: 'Inativo', label: 'Inativo' }, ]} />
{ setAddModal(false); setForm(emptyForm); }}>Cancelar {saving ? 'Salvando…' : 'Adicionar cliente'}
{toast && setToast(null)} />}
); } Object.assign(window, { CRM });