/* ============================================================
pipeline.jsx — Pipeline comercial (Kanban arrastável)
============================================================ */
function DealCard({ deal, onDragStart, onOpen, dragging }) {
const svcNames = deal.items.map(i => i.nome);
const stage = PIPELINE_STAGES.find(s => s.id === deal.stage);
return (
onDragStart(e, deal.id)} onClick={() => onOpen(deal)}>
{deal.cliente.nome || 'Sem nome'}
{deal.cliente.empresa &&
{deal.cliente.empresa}
}
{svcNames.slice(0, 2).map((n, i) => {n})}
{svcNames.length > 2 && +{svcNames.length - 2}}
{fmtBRL(deal.total)}
{new Date(deal.createdAt).toLocaleDateString('pt-BR', { day: '2-digit', month: 'short' })}
);
}
function DealDrawer({ deal, onClose, onMove, onDelete, onResume }) {
if (!deal) return null;
const stage = PIPELINE_STAGES.find(s => s.id === deal.stage);
const idx = PIPELINE_STAGES.findIndex(s => s.id === deal.stage);
return (
);
}
function Pipeline({ deals, onMove, onDelete, onNew, onResume }) {
const [dragId, setDragId] = React.useState(null);
const [overCol, setOverCol] = React.useState(null);
const [open, setOpen] = React.useState(null);
const [query, setQuery] = React.useState('');
const filtered = deals.filter(d =>
!query || (d.cliente.nome + d.cliente.empresa).toLowerCase().includes(query.toLowerCase()));
const onDragStart = (e, id) => { setDragId(id); e.dataTransfer.effectAllowed = 'move'; };
const onDrop = (stageId) => { if (dragId) onMove(dragId, stageId); setDragId(null); setOverCol(null); };
const totalGeral = deals.reduce((s, d) => s + d.total, 0);
const ganho = deals.filter(d => ['pagamento', 'contrato', 'projeto'].includes(d.stage)).reduce((s, d) => s + d.total, 0);
const openDeal = open ? deals.find(d => d.id === open) : null;
return (
{deals.length === 0 ? (
{window.FAVICON
?

:
}
Nenhum negócio ainda
Crie a primeira proposta. Ao registrar o aceite, o negócio aparece aqui automaticamente.
) : (
{PIPELINE_STAGES.map(stage => {
const cards = filtered.filter(d => d.stage === stage.id);
const colTotal = cards.reduce((s, d) => s + d.total, 0);
return (
{ e.preventDefault(); setOverCol(stage.id); }}
onDragLeave={(e) => { if (e.currentTarget === e.target) setOverCol(null); }}
onDrop={() => onDrop(stage.id)}>
{stage.nome}
{cards.length}
{fmtBRL(colTotal)}
{cards.map(d => (
setOpen(dd.id)} />
))}
{cards.length === 0 && Solte aqui
}
);
})}
)}
setOpen(null)} onMove={onMove} onDelete={onDelete} onResume={onResume} />
);
}
Object.assign(window, { Pipeline });