feat: versão 1.5 - CRM Beta com leads, funis, campanhas e portal do cliente
This commit is contained in:
624
front-end-agency/app/(agency)/crm/campanhas/[id]/page.tsx
Normal file
624
front-end-agency/app/(agency)/crm/campanhas/[id]/page.tsx
Normal file
@@ -0,0 +1,624 @@
|
||||
"use client";
|
||||
|
||||
import { Fragment, useEffect, useState, use } from 'react';
|
||||
import { Tab, Menu, Transition } from '@headlessui/react';
|
||||
import {
|
||||
UserGroupIcon,
|
||||
InformationCircleIcon,
|
||||
CreditCardIcon,
|
||||
ArrowLeftIcon,
|
||||
PlusIcon,
|
||||
MagnifyingGlassIcon,
|
||||
FunnelIcon,
|
||||
EllipsisVerticalIcon,
|
||||
PencilIcon,
|
||||
TrashIcon,
|
||||
EnvelopeIcon,
|
||||
PhoneIcon,
|
||||
TagIcon,
|
||||
CalendarIcon,
|
||||
UserIcon,
|
||||
ArrowDownTrayIcon,
|
||||
BriefcaseIcon,
|
||||
} from '@heroicons/react/24/outline';
|
||||
import Link from 'next/link';
|
||||
import { useToast } from '@/components/layout/ToastContext';
|
||||
import KanbanBoard from '@/components/crm/KanbanBoard';
|
||||
|
||||
interface Lead {
|
||||
id: string;
|
||||
name: string;
|
||||
email: string;
|
||||
phone: string;
|
||||
status: string;
|
||||
created_at: string;
|
||||
tags: string[];
|
||||
}
|
||||
|
||||
interface Campaign {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
color: string;
|
||||
customer_id: string;
|
||||
customer_name: string;
|
||||
lead_count: number;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
const STATUS_OPTIONS = [
|
||||
{ value: 'novo', label: 'Novo', color: 'bg-blue-100 text-blue-800 dark:bg-blue-900 dark:text-blue-200' },
|
||||
{ value: 'qualificado', label: 'Qualificado', color: 'bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-200' },
|
||||
{ value: 'negociacao', label: 'Em Negociação', color: 'bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-200' },
|
||||
{ value: 'convertido', label: 'Convertido', color: 'bg-purple-100 text-purple-800 dark:bg-purple-900 dark:text-purple-200' },
|
||||
{ value: 'perdido', label: 'Perdido', color: 'bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-200' },
|
||||
];
|
||||
|
||||
function classNames(...classes: string[]) {
|
||||
return classes.filter(Boolean).join(' ');
|
||||
}
|
||||
|
||||
export default function CampaignDetailPage({ params }: { params: Promise<{ id: string }> }) {
|
||||
const { id } = use(params);
|
||||
const toast = useToast();
|
||||
const [campaign, setCampaign] = useState<Campaign | null>(null);
|
||||
const [leads, setLeads] = useState<Lead[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const [searchTerm, setSearchTerm] = useState('');
|
||||
const [funnels, setFunnels] = useState<any[]>([]);
|
||||
const [selectedFunnelId, setSelectedFunnelId] = useState<string>('');
|
||||
|
||||
useEffect(() => {
|
||||
fetchCampaignDetails();
|
||||
fetchCampaignLeads();
|
||||
fetchFunnels();
|
||||
}, [id]);
|
||||
|
||||
const fetchFunnels = async () => {
|
||||
try {
|
||||
const response = await fetch('/api/crm/funnels', {
|
||||
headers: { 'Authorization': `Bearer ${localStorage.getItem('token')}` }
|
||||
});
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
setFunnels(data.funnels || []);
|
||||
if (data.funnels?.length > 0) {
|
||||
setSelectedFunnelId(data.funnels[0].id);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error fetching funnels:', error);
|
||||
}
|
||||
};
|
||||
|
||||
const fetchCampaignDetails = async () => {
|
||||
try {
|
||||
const response = await fetch(`/api/crm/lists`, {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${localStorage.getItem('token')}`,
|
||||
},
|
||||
});
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
const found = data.lists?.find((l: Campaign) => l.id === id);
|
||||
if (found) {
|
||||
setCampaign(found);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error fetching campaign details:', error);
|
||||
}
|
||||
};
|
||||
|
||||
const fetchCampaignLeads = async () => {
|
||||
try {
|
||||
const response = await fetch(`/api/crm/lists/${id}/leads`, {
|
||||
headers: {
|
||||
'Authorization': `Bearer ${localStorage.getItem('token')}`,
|
||||
},
|
||||
});
|
||||
if (response.ok) {
|
||||
const data = await response.json();
|
||||
setLeads(data.leads || []);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error fetching leads:', error);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const filteredLeads = leads.filter(lead =>
|
||||
(lead.name?.toLowerCase() || '').includes(searchTerm.toLowerCase()) ||
|
||||
(lead.email?.toLowerCase() || '').includes(searchTerm.toLowerCase())
|
||||
);
|
||||
|
||||
const handleExport = async (format: 'csv' | 'xlsx' | 'json') => {
|
||||
try {
|
||||
const token = localStorage.getItem('token');
|
||||
const response = await fetch(`/api/crm/leads/export?format=${format}&campaign_id=${id}`, {
|
||||
headers: { 'Authorization': `Bearer ${token}` }
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
const blob = await response.blob();
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = `leads-${campaign?.name || 'campaign'}.${format === 'xlsx' ? 'xlsx' : format}`;
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
window.URL.revokeObjectURL(url);
|
||||
document.body.removeChild(a);
|
||||
toast.success('Exportado com sucesso!');
|
||||
} else {
|
||||
toast.error('Erro ao exportar leads');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Export error:', error);
|
||||
toast.error('Erro ao exportar');
|
||||
}
|
||||
};
|
||||
|
||||
if (loading && !campaign) {
|
||||
return (
|
||||
<div className="flex items-center justify-center min-h-screen">
|
||||
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-brand-500"></div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
if (!campaign) {
|
||||
return (
|
||||
<div className="p-8 text-center">
|
||||
<h2 className="text-2xl font-bold text-zinc-900 dark:text-white">Campanha não encontrada</h2>
|
||||
<Link href="/crm/campanhas" className="mt-4 inline-flex items-center text-brand-500 hover:underline">
|
||||
<ArrowLeftIcon className="w-4 h-4 mr-2" />
|
||||
Voltar para Campanhas
|
||||
</Link>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="p-6 max-w-[1600px] mx-auto space-y-6">
|
||||
{/* Header */}
|
||||
<div className="flex flex-col gap-4">
|
||||
<Link
|
||||
href="/crm/campanhas"
|
||||
className="inline-flex items-center text-sm text-zinc-500 hover:text-zinc-900 dark:hover:text-zinc-300 transition-colors"
|
||||
>
|
||||
<ArrowLeftIcon className="w-4 h-4 mr-2" />
|
||||
Voltar para Campanhas
|
||||
</Link>
|
||||
|
||||
<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-4">
|
||||
<div className="flex items-center gap-4">
|
||||
<div
|
||||
className="w-14 h-14 rounded-2xl flex items-center justify-center text-white shadow-lg"
|
||||
style={{ backgroundColor: campaign.color }}
|
||||
>
|
||||
<UserGroupIcon className="w-8 h-8" />
|
||||
</div>
|
||||
<div>
|
||||
<h1 className="text-2xl font-bold text-zinc-900 dark:text-white tracking-tight">
|
||||
{campaign.name}
|
||||
</h1>
|
||||
<div className="flex items-center gap-2 mt-1">
|
||||
{campaign.customer_name ? (
|
||||
<span className="inline-flex items-center px-2 py-0.5 rounded text-[10px] font-bold uppercase tracking-wider bg-brand-50 text-brand-700 dark:bg-brand-900/20 dark:text-brand-400 border border-brand-100 dark:border-brand-800/50">
|
||||
{campaign.customer_name}
|
||||
</span>
|
||||
) : (
|
||||
<span className="inline-flex items-center px-2 py-0.5 rounded text-[10px] font-bold uppercase tracking-wider bg-zinc-100 text-zinc-600 dark:bg-zinc-800 dark:text-zinc-400 border border-zinc-200 dark:border-zinc-700">
|
||||
Geral
|
||||
</span>
|
||||
)}
|
||||
<span className="text-zinc-400 text-xs">•</span>
|
||||
<span className="text-xs text-zinc-500 dark:text-zinc-400">
|
||||
{leads.length} leads vinculados
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="relative inline-block text-left">
|
||||
<Menu>
|
||||
<Menu.Button className="inline-flex items-center justify-center gap-2 px-4 py-2 text-sm font-medium text-zinc-700 dark:text-zinc-300 bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-800 rounded-lg hover:bg-zinc-50 dark:hover:bg-zinc-800 transition-colors">
|
||||
<ArrowDownTrayIcon className="w-4 h-4" />
|
||||
Exportar
|
||||
</Menu.Button>
|
||||
<Transition
|
||||
as={Fragment}
|
||||
enter="transition ease-out duration-100"
|
||||
enterFrom="transform opacity-0 scale-95"
|
||||
enterTo="transform opacity-100 scale-100"
|
||||
leave="transition ease-in duration-75"
|
||||
leaveFrom="transform opacity-100 scale-100"
|
||||
leaveTo="transform opacity-0 scale-95"
|
||||
>
|
||||
<Menu.Items className="absolute right-0 mt-2 w-48 origin-top-right divide-y divide-zinc-100 dark:divide-zinc-800 rounded-xl bg-white dark:bg-zinc-900 shadow-lg ring-1 ring-black ring-opacity-5 focus:outline-none z-50 border border-zinc-200 dark:border-zinc-800">
|
||||
<div className="px-1 py-1">
|
||||
<Menu.Item>
|
||||
{({ active }) => (
|
||||
<button
|
||||
onClick={() => handleExport('csv')}
|
||||
className={`${active ? 'bg-zinc-50 dark:bg-zinc-800' : ''} group flex w-full items-center rounded-lg px-3 py-2 text-sm text-zinc-700 dark:text-zinc-300`}
|
||||
>
|
||||
Exportar como CSV
|
||||
</button>
|
||||
)}
|
||||
</Menu.Item>
|
||||
<Menu.Item>
|
||||
{({ active }) => (
|
||||
<button
|
||||
onClick={() => handleExport('xlsx')}
|
||||
className={`${active ? 'bg-zinc-50 dark:bg-zinc-800' : ''} group flex w-full items-center rounded-lg px-3 py-2 text-sm text-zinc-700 dark:text-zinc-300`}
|
||||
>
|
||||
Exportar como Excel
|
||||
</button>
|
||||
)}
|
||||
</Menu.Item>
|
||||
<Menu.Item>
|
||||
{({ active }) => (
|
||||
<button
|
||||
onClick={() => handleExport('json')}
|
||||
className={`${active ? 'bg-zinc-50 dark:bg-zinc-800' : ''} group flex w-full items-center rounded-lg px-3 py-2 text-sm text-zinc-700 dark:text-zinc-300`}
|
||||
>
|
||||
Exportar como JSON
|
||||
</button>
|
||||
)}
|
||||
</Menu.Item>
|
||||
</div>
|
||||
</Menu.Items>
|
||||
</Transition>
|
||||
</Menu>
|
||||
</div>
|
||||
<button className="px-4 py-2 text-sm font-medium text-zinc-700 dark:text-zinc-300 bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-800 rounded-lg hover:bg-zinc-50 dark:hover:bg-zinc-800 transition-colors">
|
||||
Editar Campanha
|
||||
</button>
|
||||
<Link
|
||||
href={`/crm/leads/importar?campaign=${campaign.id}`}
|
||||
className="inline-flex items-center justify-center gap-2 px-4 py-2 text-sm font-medium text-white rounded-lg hover:opacity-90 transition-opacity"
|
||||
style={{ background: 'var(--gradient)' }}
|
||||
>
|
||||
<PlusIcon className="w-4 h-4" />
|
||||
Importar Leads
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Tabs */}
|
||||
<Tab.Group>
|
||||
<Tab.List className="flex space-x-1 rounded-xl bg-zinc-100 dark:bg-zinc-800/50 p-1 max-w-lg">
|
||||
<Tab className={({ selected }) =>
|
||||
classNames(
|
||||
'w-full rounded-lg py-2.5 text-sm font-medium leading-5 transition-all duration-200',
|
||||
'ring-white ring-opacity-60 ring-offset-2 ring-offset-brand-400 focus:outline-none',
|
||||
selected
|
||||
? 'bg-white dark:bg-zinc-900 text-brand-600 dark:text-brand-400 shadow-sm'
|
||||
: 'text-zinc-500 hover:text-zinc-700 dark:text-zinc-400 dark:hover:text-zinc-200 hover:bg-white/[0.12]'
|
||||
)
|
||||
}>
|
||||
<div className="flex items-center justify-center gap-2">
|
||||
<FunnelIcon className="w-4 h-4" />
|
||||
Monitoramento
|
||||
</div>
|
||||
</Tab>
|
||||
<Tab className={({ selected }) =>
|
||||
classNames(
|
||||
'w-full rounded-lg py-2.5 text-sm font-medium leading-5 transition-all duration-200',
|
||||
'ring-white ring-opacity-60 ring-offset-2 ring-offset-brand-400 focus:outline-none',
|
||||
selected
|
||||
? 'bg-white dark:bg-zinc-900 text-brand-600 dark:text-brand-400 shadow-sm'
|
||||
: 'text-zinc-500 hover:text-zinc-700 dark:text-zinc-400 dark:hover:text-zinc-200 hover:bg-white/[0.12]'
|
||||
)
|
||||
}>
|
||||
<div className="flex items-center justify-center gap-2">
|
||||
<UserGroupIcon className="w-4 h-4" />
|
||||
Leads
|
||||
</div>
|
||||
</Tab>
|
||||
<Tab className={({ selected }) =>
|
||||
classNames(
|
||||
'w-full rounded-lg py-2.5 text-sm font-medium leading-5 transition-all duration-200',
|
||||
'ring-white ring-opacity-60 ring-offset-2 ring-offset-brand-400 focus:outline-none',
|
||||
selected
|
||||
? 'bg-white dark:bg-zinc-900 text-brand-600 dark:text-brand-400 shadow-sm'
|
||||
: 'text-zinc-500 hover:text-zinc-700 dark:text-zinc-400 dark:hover:text-zinc-200 hover:bg-white/[0.12]'
|
||||
)
|
||||
}>
|
||||
<div className="flex items-center justify-center gap-2">
|
||||
<InformationCircleIcon className="w-4 h-4" />
|
||||
Informações
|
||||
</div>
|
||||
</Tab>
|
||||
<Tab className={({ selected }) =>
|
||||
classNames(
|
||||
'w-full rounded-lg py-2.5 text-sm font-medium leading-5 transition-all duration-200',
|
||||
'ring-white ring-opacity-60 ring-offset-2 ring-offset-brand-400 focus:outline-none',
|
||||
selected
|
||||
? 'bg-white dark:bg-zinc-900 text-brand-600 dark:text-brand-400 shadow-sm'
|
||||
: 'text-zinc-500 hover:text-zinc-700 dark:text-zinc-400 dark:hover:text-zinc-200 hover:bg-white/[0.12]'
|
||||
)
|
||||
}>
|
||||
<div className="flex items-center justify-center gap-2">
|
||||
<CreditCardIcon className="w-4 h-4" />
|
||||
Pagamentos
|
||||
</div>
|
||||
</Tab>
|
||||
</Tab.List>
|
||||
|
||||
<Tab.Panels className="mt-6">
|
||||
{/* Monitoramento Panel */}
|
||||
<Tab.Panel className="space-y-6">
|
||||
{funnels.length > 0 ? (
|
||||
<div className="flex flex-col h-full">
|
||||
<div className="flex items-center justify-between mb-6">
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="p-2 bg-brand-50 dark:bg-brand-900/20 rounded-lg">
|
||||
<FunnelIcon className="h-5 w-5 text-brand-600 dark:text-brand-400" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-sm font-bold text-zinc-900 dark:text-white uppercase tracking-wider">Monitoramento de Leads</h3>
|
||||
<p className="text-xs text-zinc-500 dark:text-zinc-400">Acompanhe o progresso dos leads desta campanha no funil.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-3">
|
||||
<label className="text-xs font-bold text-zinc-500 uppercase">Funil:</label>
|
||||
<select
|
||||
value={selectedFunnelId}
|
||||
onChange={(e) => setSelectedFunnelId(e.target.value)}
|
||||
className="bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-800 rounded-lg px-3 py-1.5 text-sm font-medium focus:ring-2 focus:ring-brand-500/20 outline-none"
|
||||
>
|
||||
{funnels.map(f => (
|
||||
<option key={f.id} value={f.id}>{f.name}</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex-1 min-h-[600px]">
|
||||
<KanbanBoard funnelId={selectedFunnelId} campaignId={id} />
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex flex-col items-center justify-center h-64 bg-white dark:bg-zinc-900 rounded-xl border border-zinc-200 dark:border-zinc-800 text-center p-8">
|
||||
<div className="w-16 h-16 bg-zinc-50 dark:bg-zinc-800 rounded-full flex items-center justify-center mb-4">
|
||||
<FunnelIcon className="w-8 h-8 text-zinc-400" />
|
||||
</div>
|
||||
<h3 className="text-lg font-medium text-zinc-900 dark:text-white mb-1">
|
||||
Nenhum funil configurado
|
||||
</h3>
|
||||
<p className="text-zinc-500 dark:text-zinc-400 max-w-sm mx-auto">
|
||||
Configure um funil de vendas para começar a monitorar os leads desta campanha.
|
||||
</p>
|
||||
<Link href="/crm/funis" className="mt-4 text-brand-600 font-medium hover:underline">
|
||||
Configurar Funis
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
</Tab.Panel>
|
||||
|
||||
{/* Leads Panel */}
|
||||
<Tab.Panel className="space-y-6">
|
||||
<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-4">
|
||||
<div className="relative w-full lg:w-96">
|
||||
<div className="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
|
||||
<MagnifyingGlassIcon className="h-5 w-5 text-zinc-400" aria-hidden="true" />
|
||||
</div>
|
||||
<input
|
||||
type="text"
|
||||
className="block w-full pl-10 pr-3 py-2 border border-zinc-200 dark:border-zinc-700 rounded-lg leading-5 bg-white dark:bg-zinc-900 text-zinc-900 dark:text-zinc-100 placeholder-zinc-400 focus:outline-none focus:ring-1 focus:ring-[var(--brand-color)] focus:border-[var(--brand-color)] sm:text-sm transition duration-150 ease-in-out"
|
||||
placeholder="Buscar leads nesta campanha..."
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<button className="inline-flex items-center gap-2 px-3 py-2 text-sm font-medium text-zinc-700 dark:text-zinc-300 bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-800 rounded-lg hover:bg-zinc-50 dark:hover:bg-zinc-800 transition-colors">
|
||||
<FunnelIcon className="w-4 h-4" />
|
||||
Filtros
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{filteredLeads.length === 0 ? (
|
||||
<div className="flex flex-col items-center justify-center h-64 bg-white dark:bg-zinc-900 rounded-xl border border-zinc-200 dark:border-zinc-800 text-center p-8">
|
||||
<div className="w-16 h-16 bg-zinc-50 dark:bg-zinc-800 rounded-full flex items-center justify-center mb-4">
|
||||
<UserGroupIcon className="w-8 h-8 text-zinc-400" />
|
||||
</div>
|
||||
<h3 className="text-lg font-medium text-zinc-900 dark:text-white mb-1">
|
||||
Nenhum lead encontrado
|
||||
</h3>
|
||||
<p className="text-zinc-500 dark:text-zinc-400 max-w-sm mx-auto">
|
||||
{searchTerm ? 'Nenhum lead corresponde à sua busca.' : 'Esta campanha ainda não possui leads vinculados.'}
|
||||
</p>
|
||||
</div>
|
||||
) : (
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||
{filteredLeads.map((lead) => (
|
||||
<div key={lead.id} className="bg-white dark:bg-zinc-900 rounded-xl border border-zinc-200 dark:border-zinc-800 p-4 hover:shadow-md transition-shadow">
|
||||
<div className="flex items-start justify-between mb-3">
|
||||
<div className="flex-1 min-w-0">
|
||||
<h3 className="font-semibold text-zinc-900 dark:text-white truncate">
|
||||
{lead.name || 'Sem nome'}
|
||||
</h3>
|
||||
<span className={classNames(
|
||||
'inline-block px-2 py-0.5 text-[10px] font-bold uppercase tracking-wider rounded-full mt-1',
|
||||
STATUS_OPTIONS.find(s => s.value === lead.status)?.color || 'bg-zinc-100 text-zinc-800'
|
||||
)}>
|
||||
{STATUS_OPTIONS.find(s => s.value === lead.status)?.label || lead.status}
|
||||
</span>
|
||||
</div>
|
||||
<button className="p-1 hover:bg-zinc-100 dark:hover:bg-zinc-800 rounded text-zinc-400">
|
||||
<EllipsisVerticalIcon className="w-5 h-5" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2 text-sm">
|
||||
{lead.email && (
|
||||
<div className="flex items-center gap-2 text-zinc-600 dark:text-zinc-400">
|
||||
<EnvelopeIcon className="w-4 h-4 flex-shrink-0" />
|
||||
<span className="truncate">{lead.email}</span>
|
||||
</div>
|
||||
)}
|
||||
{lead.phone && (
|
||||
<div className="flex items-center gap-2 text-zinc-600 dark:text-zinc-400">
|
||||
<PhoneIcon className="w-4 h-4 flex-shrink-0" />
|
||||
<span>{lead.phone}</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="mt-4 pt-4 border-t border-zinc-100 dark:border-zinc-800 flex items-center justify-between">
|
||||
<div className="flex items-center gap-1 text-[10px] text-zinc-400 uppercase font-bold tracking-widest">
|
||||
<CalendarIcon className="w-3 h-3" />
|
||||
{new Date(lead.created_at).toLocaleDateString('pt-BR')}
|
||||
</div>
|
||||
<button className="text-xs font-semibold text-brand-600 dark:text-brand-400 hover:underline">
|
||||
Ver Detalhes
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</Tab.Panel>
|
||||
|
||||
{/* Info Panel */}
|
||||
<Tab.Panel>
|
||||
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
||||
<div className="lg:col-span-2 space-y-6">
|
||||
<div className="bg-white dark:bg-zinc-900 rounded-2xl border border-zinc-200 dark:border-zinc-800 overflow-hidden">
|
||||
<div className="p-6 border-b border-zinc-100 dark:border-zinc-800">
|
||||
<h3 className="text-lg font-bold text-zinc-900 dark:text-white">Detalhes da Campanha</h3>
|
||||
</div>
|
||||
<div className="p-6 space-y-6">
|
||||
<div>
|
||||
<label className="block text-xs font-bold text-zinc-400 uppercase tracking-widest mb-2">Descrição</label>
|
||||
<p className="text-zinc-600 dark:text-zinc-400">
|
||||
{campaign.description || 'Nenhuma descrição fornecida para esta campanha.'}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-6">
|
||||
<div>
|
||||
<label className="block text-xs font-bold text-zinc-400 uppercase tracking-widest mb-2">Data de Criação</label>
|
||||
<div className="flex items-center gap-2 text-zinc-900 dark:text-white">
|
||||
<CalendarIcon className="w-5 h-5 text-zinc-400" />
|
||||
{new Date(campaign.created_at).toLocaleDateString('pt-BR', { day: '2-digit', month: 'long', year: 'numeric' })}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-xs font-bold text-zinc-400 uppercase tracking-widest mb-2">Cor de Identificação</label>
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="w-6 h-6 rounded-full shadow-sm" style={{ backgroundColor: campaign.color }}></div>
|
||||
<span className="text-zinc-900 dark:text-white font-medium">{campaign.color}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-white dark:bg-zinc-900 rounded-2xl border border-zinc-200 dark:border-zinc-800 overflow-hidden">
|
||||
<div className="p-6 border-b border-zinc-100 dark:border-zinc-800">
|
||||
<h3 className="text-lg font-bold text-zinc-900 dark:text-white">Configurações de Integração</h3>
|
||||
</div>
|
||||
<div className="p-6">
|
||||
<div className="bg-zinc-50 dark:bg-zinc-800/50 rounded-xl p-4 border border-zinc-200 dark:border-zinc-700">
|
||||
<div className="flex items-start gap-3">
|
||||
<InformationCircleIcon className="w-5 h-5 text-brand-500 mt-0.5" />
|
||||
<div>
|
||||
<h4 className="text-sm font-bold text-zinc-900 dark:text-white">Webhook de Entrada</h4>
|
||||
<p className="text-xs text-zinc-500 dark:text-zinc-400 mt-1">
|
||||
Use este endpoint para enviar leads automaticamente de outras plataformas (Typeform, Elementor, etc).
|
||||
</p>
|
||||
<div className="mt-3 flex items-center gap-2">
|
||||
<code className="flex-1 block p-2 bg-white dark:bg-zinc-900 border border-zinc-200 dark:border-zinc-700 rounded text-[10px] text-zinc-600 dark:text-zinc-400 overflow-x-auto">
|
||||
https://api.aggios.app/v1/webhooks/leads/{campaign.id}
|
||||
</code>
|
||||
<button className="p-2 text-zinc-400 hover:text-brand-500 transition-colors">
|
||||
<TagIcon className="w-4 h-4" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-6">
|
||||
<div className="bg-white dark:bg-zinc-900 rounded-2xl border border-zinc-200 dark:border-zinc-800 p-6">
|
||||
<h3 className="text-lg font-bold text-zinc-900 dark:text-white mb-4">Cliente Responsável</h3>
|
||||
{campaign.customer_id ? (
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="w-10 h-10 rounded-full bg-brand-100 dark:bg-brand-900/30 flex items-center justify-center text-brand-600 dark:text-brand-400">
|
||||
<UserIcon className="w-6 h-6" />
|
||||
</div>
|
||||
<div>
|
||||
<p className="text-sm font-bold text-zinc-900 dark:text-white">{campaign.customer_name}</p>
|
||||
<p className="text-xs text-zinc-500">Cliente Ativo</p>
|
||||
</div>
|
||||
</div>
|
||||
<Link
|
||||
href={`/crm/clientes?id=${campaign.customer_id}`}
|
||||
className="w-full flex items-center justify-center gap-2 px-4 py-2 bg-zinc-50 dark:bg-zinc-800 text-zinc-700 dark:text-zinc-300 text-xs font-bold rounded-lg hover:bg-zinc-100 dark:hover:bg-zinc-700 transition-colors"
|
||||
>
|
||||
<BriefcaseIcon className="w-4 h-4" />
|
||||
Ver Perfil do Cliente
|
||||
</Link>
|
||||
</div>
|
||||
) : (
|
||||
<div className="text-center py-4">
|
||||
<p className="text-sm text-zinc-500">Esta é uma campanha geral da agência.</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="bg-gradient-to-br from-brand-500 to-brand-600 rounded-2xl p-6 text-white shadow-lg">
|
||||
<h3 className="text-lg font-bold mb-2">Resumo de Performance</h3>
|
||||
<div className="space-y-4 mt-4">
|
||||
<div className="flex justify-between items-end">
|
||||
<span className="text-xs text-brand-100">Total de Leads</span>
|
||||
<span className="text-2xl font-bold">{leads.length}</span>
|
||||
</div>
|
||||
<div className="w-full bg-white/20 rounded-full h-1.5">
|
||||
<div className="bg-white h-1.5 rounded-full" style={{ width: '65%' }}></div>
|
||||
</div>
|
||||
<p className="text-[10px] text-brand-100">
|
||||
+12% em relação ao mês passado
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Tab.Panel>
|
||||
|
||||
{/* Payments Panel */}
|
||||
<Tab.Panel>
|
||||
<div className="bg-white dark:bg-zinc-900 rounded-2xl border border-zinc-200 dark:border-zinc-800 overflow-hidden">
|
||||
<div className="p-12 text-center">
|
||||
<div className="w-20 h-20 bg-zinc-50 dark:bg-zinc-800 rounded-full flex items-center justify-center mx-auto mb-6">
|
||||
<CreditCardIcon className="w-10 h-10 text-zinc-400" />
|
||||
</div>
|
||||
<h3 className="text-xl font-bold text-zinc-900 dark:text-white mb-2">Módulo de Pagamentos</h3>
|
||||
<p className="text-zinc-500 dark:text-zinc-400 max-w-md mx-auto mb-8">
|
||||
Em breve você poderá gerenciar orçamentos, faturas e pagamentos vinculados diretamente a esta campanha.
|
||||
</p>
|
||||
<button className="px-6 py-3 bg-zinc-900 dark:bg-white text-white dark:text-zinc-900 font-bold rounded-xl hover:opacity-90 transition-opacity">
|
||||
Solicitar Acesso Antecipado
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</Tab.Panel>
|
||||
</Tab.Panels>
|
||||
</Tab.Group>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user