import React, { useState, useEffect } from 'react'; import { Layers, Plus, Edit, Trash2, ChevronUp, ChevronDown, Loader2, X, Users } from 'lucide-react'; import { getFunnels, createFunnel, updateFunnel, deleteFunnel, createFunnelStage, updateFunnelStage, deleteFunnelStage, getTeams } from '../services/dataService'; import { FunnelDef, FunnelStageDef } from '../types'; export const Funnels: React.FC = () => { const [funnels, setFunnels] = useState([]); const [teams, setTeams] = useState([]); const [selectedFunnelId, setSelectedFunnelId] = useState(null); const [loading, setLoading] = useState(true); const [isModalOpen, setIsModalOpen] = useState(false); const [editingStage, setEditingStage] = useState(null); const [formData, setFormData] = useState({ name: '', color_class: '' }); const [isSaving, setIsSaving] = useState(false); // Funnel creation state const [isFunnelModalOpen, setIsFunnelModalOpen] = useState(false); const [funnelName, setFunnelName] = useState(''); const tenantId = localStorage.getItem('ctms_tenant_id') || ''; const PRESET_COLORS = [ { label: 'Cinza (Neutro)', value: 'bg-zinc-100 text-zinc-700 border-zinc-200 dark:bg-dark-input dark:text-dark-muted dark:border-dark-border' }, { label: 'Azul (Início)', value: 'bg-blue-100 text-blue-700 border-blue-200 dark:bg-blue-900/30 dark:text-blue-400 dark:border-blue-800' }, { label: 'Roxo (Meio)', value: 'bg-purple-100 text-purple-700 border-purple-200 dark:bg-purple-900/30 dark:text-purple-400 dark:border-purple-800' }, { label: 'Laranja (Atenção)', value: 'bg-orange-100 text-orange-700 border-orange-200 dark:bg-orange-900/30 dark:text-orange-400 dark:border-orange-800' }, { label: 'Verde (Sucesso)', value: 'bg-green-100 text-green-700 border-green-200 dark:bg-green-900/30 dark:text-green-400 dark:border-green-800' }, { label: 'Vermelho (Perda)', value: 'bg-red-100 text-red-700 border-red-200 dark:bg-red-900/30 dark:text-red-400 dark:border-red-800' }, ]; const loadData = async () => { setLoading(true); const [fetchedFunnels, fetchedTeams] = await Promise.all([ getFunnels(tenantId), getTeams(tenantId) ]); setFunnels(fetchedFunnels); setTeams(fetchedTeams); if (!selectedFunnelId && fetchedFunnels.length > 0) { setSelectedFunnelId(fetchedFunnels[0].id); } setLoading(false); }; useEffect(() => { loadData(); }, [tenantId]); const selectedFunnel = funnels.find(f => f.id === selectedFunnelId); // --- Funnel Handlers --- const handleCreateFunnel = async (e: React.FormEvent) => { e.preventDefault(); setIsSaving(true); try { const res = await createFunnel({ name: funnelName, tenantId }); setSelectedFunnelId(res.id); setIsFunnelModalOpen(false); setFunnelName(''); loadData(); } catch (err) { alert("Erro ao criar funil."); } finally { setIsSaving(false); } }; const handleDeleteFunnel = async (id: string) => { if (funnels.length <= 1) { alert("Você precisa ter pelo menos um funil ativo."); return; } if (confirm('Tem certeza que deseja excluir este funil e todas as suas etapas?')) { await deleteFunnel(id); setSelectedFunnelId(null); loadData(); } }; const handleToggleTeam = async (teamId: string) => { if (!selectedFunnel) return; const currentTeamIds = selectedFunnel.teamIds || []; const newTeamIds = currentTeamIds.includes(teamId) ? currentTeamIds.filter(id => id !== teamId) : [...currentTeamIds, teamId]; // Optimistic const newFunnels = [...funnels]; const idx = newFunnels.findIndex(f => f.id === selectedFunnel.id); newFunnels[idx].teamIds = newTeamIds; setFunnels(newFunnels); await updateFunnel(selectedFunnel.id, { teamIds: newTeamIds }); }; // --- Stage Handlers --- const handleSaveStage = async (e: React.FormEvent) => { e.preventDefault(); if (!selectedFunnel) return; setIsSaving(true); try { if (editingStage) { await updateFunnelStage(editingStage.id, formData); } else { await createFunnelStage(selectedFunnel.id, { ...formData, order_index: selectedFunnel.stages.length }); } setIsModalOpen(false); loadData(); } catch (err) { alert("Erro ao salvar etapa."); } finally { setIsSaving(false); } }; const handleDeleteStage = async (id: string) => { if (confirm('Tem certeza que deseja excluir esta etapa?')) { await deleteFunnelStage(id); loadData(); } }; const handleMoveStage = async (index: number, direction: 'up' | 'down') => { if (!selectedFunnel) return; const stages = selectedFunnel.stages; if (direction === 'up' && index === 0) return; if (direction === 'down' && index === stages.length - 1) return; const newIndex = direction === 'up' ? index - 1 : index + 1; const tempOrder = stages[index].order_index; stages[index].order_index = stages[newIndex].order_index; stages[newIndex].order_index = tempOrder; setFunnels([...funnels]); // trigger re-render await Promise.all([ updateFunnelStage(stages[index].id, { order_index: stages[index].order_index }), updateFunnelStage(stages[newIndex].id, { order_index: stages[newIndex].order_index }) ]); }; const openStageModal = (stage?: FunnelStageDef) => { if (stage) { setEditingStage(stage); setFormData({ name: stage.name, color_class: stage.color_class }); } else { setEditingStage(null); setFormData({ name: '', color_class: PRESET_COLORS[0].value }); } setIsModalOpen(true); }; if (loading && funnels.length === 0) return
; return (
{/* Sidebar: Funnels List */}

Gerenciar Funis

{funnels.map(f => ( ))}
{/* Main Content: Selected Funnel Details */}
{selectedFunnel ? ( <>

{selectedFunnel.name}

Gerencie as etapas deste funil e quais times o utilizam.

{/* Teams Assignment */}

Times Atribuídos

{teams.length === 0 ? (

Nenhum time cadastrado na organização.

) : (
{teams.map(t => { const isAssigned = selectedFunnel.teamIds?.includes(t.id); return ( ); })}
)}

Times não atribuídos a um funil específico usarão o Funil Padrão.

{/* Stages */}

Etapas do Funil

{selectedFunnel.stages?.sort((a,b) => a.order_index - b.order_index).map((f, index) => (
{f.name}
))} {(!selectedFunnel.stages || selectedFunnel.stages.length === 0) && (
Nenhuma etapa configurada neste funil.
)}
) : (
Selecione ou crie um funil.
)}
{/* Funnel Creation Modal */} {isFunnelModalOpen && (

Novo Funil

setFunnelName(e.target.value)} placeholder="Ex: Vendas B2B" className="w-full bg-white dark:bg-dark-input border border-zinc-200 dark:border-dark-border p-3 rounded-lg text-sm text-zinc-900 dark:text-zinc-100 focus:ring-2 focus:ring-brand-yellow/20 outline-none transition-all" required />
)} {/* Stage Modal */} {isModalOpen && (

{editingStage ? 'Editar Etapa' : 'Nova Etapa'}

setFormData({...formData, name: e.target.value})} placeholder="Ex: Qualificação" className="w-full bg-white dark:bg-dark-input border border-zinc-200 dark:border-dark-border p-3 rounded-lg text-sm text-zinc-900 dark:text-zinc-100 focus:ring-2 focus:ring-brand-yellow/20 outline-none transition-all" required />
{PRESET_COLORS.map((color, i) => ( ))}
)}
); };