"use client"; import { useState, useEffect, Suspense, useRef } from 'react'; import { useRouter, useSearchParams } from 'next/navigation'; import { useToast } from '@/components/layout/ToastContext'; import Papa from 'papaparse'; import { ArrowUpTrayIcon, DocumentTextIcon, CheckCircleIcon, XCircleIcon, ArrowPathIcon, ChevronLeftIcon, InformationCircleIcon, TableCellsIcon, CommandLineIcon, CpuChipIcon, CloudArrowUpIcon, } from '@heroicons/react/24/outline'; interface Customer { id: string; name: string; company: string; } interface Campaign { id: string; name: string; customer_id: string; } function ImportLeadsContent() { const router = useRouter(); const searchParams = useSearchParams(); const campaignIdFromUrl = searchParams.get('campaign'); const customerIdFromUrl = searchParams.get('customer'); const toast = useToast(); const [customers, setCustomers] = useState([]); const [campaigns, setCampaigns] = useState([]); const [loading, setLoading] = useState(false); const [importing, setImporting] = useState(false); const [selectedCustomer, setSelectedCustomer] = useState(customerIdFromUrl || ''); const [selectedCampaign, setSelectedCampaign] = useState(campaignIdFromUrl || ''); const [jsonContent, setJsonContent] = useState(''); const [csvFile, setCsvFile] = useState(null); const [preview, setPreview] = useState([]); const [error, setError] = useState(null); const [importType, setImportType] = useState<'json' | 'csv' | 'typebot' | 'api'>('json'); const fileInputRef = useRef(null); // Mapeamento inteligente de campos const mapLeadData = (data: any[]) => { const fieldMap: Record = { name: ['nome', 'name', 'full name', 'nome completo', 'cliente', 'contato'], email: ['email', 'e-mail', 'mail', 'correio'], phone: ['phone', 'telefone', 'celular', 'mobile', 'whatsapp', 'zap', 'tel'], source: ['source', 'origem', 'canal', 'campanha', 'midia', 'mídia', 'campaign'], status: ['status', 'fase', 'etapa', 'situação', 'situacao'], notes: ['notes', 'notas', 'observações', 'observacoes', 'obs', 'comentário', 'comentario'], }; return data.map(item => { const mapped: any = { ...item }; const itemKeys = Object.keys(item); // Tenta encontrar correspondências para cada campo principal Object.entries(fieldMap).forEach(([targetKey, aliases]) => { const foundKey = itemKeys.find(k => aliases.includes(k.toLowerCase().trim()) ); if (foundKey && !mapped[targetKey]) { mapped[targetKey] = item[foundKey]; } }); // Garante que campos básicos existam if (!mapped.name && mapped.Nome) mapped.name = mapped.Nome; if (!mapped.email && mapped.Email) mapped.email = mapped.Email; if (!mapped.phone && (mapped.Celular || mapped.Telefone)) mapped.phone = mapped.Celular || mapped.Telefone; return mapped; }); }; useEffect(() => { fetchData(); }, []); const handleFileChange = (e: React.ChangeEvent) => { const file = e.target.files?.[0]; if (!file) return; if (file.type !== 'text/csv' && !file.name.endsWith('.csv')) { toast.error('Erro', 'Por favor, selecione um arquivo CSV válido.'); return; } setCsvFile(file); setError(null); // Tenta ler o arquivo primeiro para detectar onde começam os dados const reader = new FileReader(); reader.onload = (event) => { const text = event.target?.result as string; const lines = text.split('\n'); // Procura a linha que parece ser o cabeçalho (contém Nome, Email ou Celular) let headerIndex = 0; for (let i = 0; i < Math.min(lines.length, 10); i++) { const lowerLine = lines[i].toLowerCase(); if (lowerLine.includes('nome') || lowerLine.includes('email') || lowerLine.includes('celular')) { headerIndex = i; break; } } const csvData = lines.slice(headerIndex).join('\n'); Papa.parse(csvData, { header: true, skipEmptyLines: true, complete: (results) => { if (results.errors.length > 0 && results.data.length === 0) { setError('Erro ao processar CSV. Verifique a formatação.'); setPreview([]); } else { const mappedData = mapLeadData(results.data); setPreview(mappedData.slice(0, 5)); } }, error: (err: any) => { setError('Falha ao ler o arquivo.'); setPreview([]); } }); }; reader.readAsText(file); }; const fetchData = async () => { setLoading(true); try { const [custRes, campRes] = await Promise.all([ fetch('/api/crm/customers', { headers: { 'Authorization': `Bearer ${localStorage.getItem('token')}` } }), fetch('/api/crm/lists', { headers: { 'Authorization': `Bearer ${localStorage.getItem('token')}` } }) ]); let fetchedCampaigns: Campaign[] = []; if (campRes.ok) { const data = await campRes.json(); fetchedCampaigns = data.lists || []; setCampaigns(fetchedCampaigns); } if (custRes.ok) { const data = await custRes.json(); setCustomers(data.customers || []); } // Se veio da campanha, tenta setar o cliente automaticamente if (campaignIdFromUrl && fetchedCampaigns.length > 0) { const campaign = fetchedCampaigns.find(c => c.id === campaignIdFromUrl); if (campaign && campaign.customer_id) { setSelectedCustomer(campaign.customer_id); } } } catch (err) { console.error('Error fetching data:', err); } finally { setLoading(false); } }; const handleJsonChange = (e: React.ChangeEvent) => { const content = e.target.value; setJsonContent(content); setError(null); if (!content.trim()) { setPreview([]); return; } try { const parsed = JSON.parse(content); const leads = Array.isArray(parsed) ? parsed : [parsed]; const mappedData = mapLeadData(leads); setPreview(mappedData.slice(0, 5)); } catch (err) { setError('JSON inválido. Verifique a formatação.'); setPreview([]); } }; const handleImport = async () => { let leads: any[] = []; if (importType === 'json') { if (!jsonContent.trim() || error) { toast.error('Erro', 'Por favor, insira um JSON válido.'); return; } try { const parsed = JSON.parse(jsonContent); leads = Array.isArray(parsed) ? parsed : [parsed]; } catch (err) { toast.error('Erro', 'JSON inválido.'); return; } } else if (importType === 'csv') { if (!csvFile || error) { toast.error('Erro', 'Por favor, selecione um arquivo CSV válido.'); return; } // Parse CSV again to get all data const results = await new Promise((resolve) => { const reader = new FileReader(); reader.onload = (event) => { const text = event.target?.result as string; const lines = text.split('\n'); let headerIndex = 0; for (let i = 0; i < Math.min(lines.length, 10); i++) { const lowerLine = lines[i].toLowerCase(); if (lowerLine.includes('nome') || lowerLine.includes('email') || lowerLine.includes('celular')) { headerIndex = i; break; } } const csvData = lines.slice(headerIndex).join('\n'); Papa.parse(csvData, { header: true, skipEmptyLines: true, complete: (results: any) => resolve(results.data) }); }; reader.readAsText(csvFile); }); leads = results; } if (leads.length === 0) { toast.error('Erro', 'Nenhum lead encontrado para importar.'); return; } // Aplica o mapeamento inteligente antes de enviar const mappedLeads = mapLeadData(leads); setImporting(true); try { const response = await fetch('/api/crm/leads/import', { method: 'POST', headers: { 'Authorization': `Bearer ${localStorage.getItem('token')}`, 'Content-Type': 'application/json', }, body: JSON.stringify({ customer_id: selectedCustomer, campaign_id: selectedCampaign, leads: mappedLeads }), }); if (response.ok) { const result = await response.json(); toast.success('Sucesso', `${result.count} leads importados com sucesso.`); // Se veio de uma campanha, volta para a campanha if (campaignIdFromUrl) { router.push(`/crm/campanhas/${campaignIdFromUrl}`); } else { router.push('/crm/leads'); } } else { const errData = await response.json(); toast.error('Erro na importação', errData.error || 'Ocorreu um erro ao importar os leads.'); } } catch (err) { console.error('Import error:', err); toast.error('Erro', 'Falha ao processar a importação.'); } finally { setImporting(false); } }; return (
{/* Header */}

Importar Leads

Selecione o método de importação e organize seus leads

{/* Import Methods */}
{/* Config Side */}

Destino dos Leads

{campaignIdFromUrl && (

* Campanha pré-selecionada via contexto

)}

Formato JSON Esperado

                            {`[
  {
    "name": "João Silva",
    "email": "joao@email.com",
    "phone": "11999999999",
    "source": "facebook",
    "tags": ["lead-quente"]
  }
]`}
                        
{/* Editor Side */}
{importType === 'json' ? (
Conteúdo JSON
{error && ( {error} )} {!error && preview.length > 0 && ( JSON Válido )}