diff --git a/backend/app/routes/leads.py b/backend/app/routes/leads.py index f1572cc..aee0390 100644 --- a/backend/app/routes/leads.py +++ b/backend/app/routes/leads.py @@ -260,12 +260,13 @@ def export_leads(): ) @leads_bp.route('/', methods=['DELETE']) -@admin_required() +@jwt_required() def delete_lead(lead_id): lead = db.session.get(Lead, lead_id) if not lead: return jsonify({'error': 'Lead não encontrado.'}), 404 + LeadInteracao.query.filter_by(lead_id=lead.id).delete() db.session.delete(lead) db.session.commit() return jsonify({'message': f'Lead {lead.nome_empresa} excluído com sucesso.'}), 200 diff --git a/frontend/api_client.py b/frontend/api_client.py index f5422e3..678a0d5 100644 --- a/frontend/api_client.py +++ b/frontend/api_client.py @@ -139,6 +139,18 @@ class APIClient: except Exception as e: return False, f"Erro: {str(e)}" + def delete_lead(self, lead_id: str) -> tuple[bool, str]: + url = f"{self.base_url}/api/v1/leads/{lead_id}" + try: + with httpx.Client(timeout=10.0) as client: + res = client.delete(url, headers=self._get_headers()) + data = res.json() + if res.status_code == 200: + return True, data.get('message', 'Lead excluído com sucesso.') + return False, data.get('error', 'Erro ao excluir lead.') + except Exception as e: + return False, f"Erro ao comunicar com o servidor: {str(e)}" + def get_users(self) -> list: url = f"{self.base_url}/api/v1/users" try: diff --git a/frontend/components/kanban.py b/frontend/components/kanban.py index 647769f..7b20432 100644 --- a/frontend/components/kanban.py +++ b/frontend/components/kanban.py @@ -63,7 +63,7 @@ def render_kanban_board(leads: list, api_client: APIClient): if maps_url: st.markdown(f"[📍 Abrir no Google Maps]({maps_url})") - c1, c2 = st.columns([2, 1]) + c1, c2, c3 = st.columns([2, 1, 1]) with c1: opts = [s[1] for s in STATUS_CONFIG] cur_idx = [s[0] for s in STATUS_CONFIG].index(status_key) @@ -80,6 +80,15 @@ def render_kanban_board(leads: list, api_client: APIClient): st.rerun() with c2: - if st.button("📝", key=f"btn_notes_{lead_id}", help="Editar notas e ver histórico"): + if st.button("📝", key=f"btn_notes_{lead_id}", help="Editar notas e ver histórico", use_container_width=True): st.session_state['selected_lead_id'] = lead_id st.rerun() + + with c3: + if st.button("🗑️", key=f"btn_del_{lead_id}", help="Deletar lead permanentemente do banco", use_container_width=True): + ok, msg = api_client.delete_lead(lead_id) + if ok: + st.toast(f"Lead '{nome}' excluído do banco!") + else: + st.error(msg) + st.rerun() diff --git a/frontend/views/analytical_table.py b/frontend/views/analytical_table.py index c877be5..7393d75 100644 --- a/frontend/views/analytical_table.py +++ b/frontend/views/analytical_table.py @@ -88,19 +88,29 @@ def render_analytical_table_view(api_client: APIClient): ) st.markdown("### ⚙️ Ações Rápidas") - col_sel, col_act = st.columns([2, 1]) + col_sel, col_opt, col_del = st.columns([2, 1, 1]) with col_sel: selected_lead_id = st.selectbox( - "Selecione um Lead para Ação LGPD:", + "Selecione um Lead para Ação:", options=[l['id'] for l in filtered], format_func=lambda x: next((f"{l['nome_empresa']} ({l['status_funil']})" for l in filtered if l['id'] == x), x) ) - with col_act: - if st.button("🚫 Marcar Opt-Out (LGPD)", type="secondary", use_container_width=True): + with col_opt: + if st.button("🚫 Opt-Out (LGPD)", type="secondary", use_container_width=True): if selected_lead_id: api_client.opt_out_lead(selected_lead_id) st.success("Lead marcado como Opt-Out (Excluído / LGPD).") st.rerun() + + with col_del: + if st.button("🗑️ Deletar do Banco", type="primary", use_container_width=True): + if selected_lead_id: + ok, msg = api_client.delete_lead(selected_lead_id) + if ok: + st.success(msg) + else: + st.error(msg) + st.rerun() else: st.info("Nenhum lead encontrado com os filtros selecionados.") diff --git a/frontend/views/crm_kanban.py b/frontend/views/crm_kanban.py index c20f249..16702e8 100644 --- a/frontend/views/crm_kanban.py +++ b/frontend/views/crm_kanban.py @@ -65,22 +65,32 @@ def render_lead_detail_modal(lead_id: str, api_client: APIClient): new_notes = st.text_area("Notas / Observações Comerciais:", value=lead_detail.get('notas', ''), height=120) - c_save, c_opt, c_close = st.columns([1, 1, 1]) + c_save, c_opt, c_del, c_close = st.columns([1, 1, 1, 1]) with c_save: - if st.button("💾 Salvar Notas"): + if st.button("💾 Salvar Notas", use_container_width=True): api_client.update_lead(lead_id, {'notas': new_notes}) st.success("Notas atualizadas!") st.rerun() with c_opt: - if st.button("🚫 Opt-Out (LGPD)", help="Marcar lead para não contatar"): + if st.button("🚫 Opt-Out (LGPD)", help="Marcar lead para não contatar", use_container_width=True): api_client.opt_out_lead(lead_id) st.warning("Lead marcado como Opt-Out LGPD.") st.session_state.pop('selected_lead_id', None) st.rerun() + with c_del: + if st.button("🗑️ Deletar Lead", type="primary", help="Excluir lead permanentemente do banco", use_container_width=True): + ok, msg = api_client.delete_lead(lead_id) + if ok: + st.success(msg) + else: + st.error(msg) + st.session_state.pop('selected_lead_id', None) + st.rerun() + with c_close: - if st.button("❌ Fechar"): + if st.button("❌ Fechar", use_container_width=True): st.session_state.pop('selected_lead_id', None) st.rerun()