112 lines
5.0 KiB
Python
112 lines
5.0 KiB
Python
import streamlit as st
|
|
from api_client import APIClient
|
|
from components.kanban import render_kanban_board
|
|
from components.metrics import render_metrics_summary
|
|
|
|
def render_crm_kanban_view(api_client: APIClient):
|
|
st.title("📌 Funil Commercial CRM (Kanban)")
|
|
|
|
# Carregar leads da API
|
|
leads = api_client.get_leads()
|
|
|
|
# Barra de Filtros
|
|
fc1, fc2, fc3 = st.columns([2, 1, 1])
|
|
with fc1:
|
|
search_query = st.text_input("🔍 Buscar por nome, telefone ou nota:", value="", key="kanban_search")
|
|
with fc2:
|
|
ramos = sorted(list(set(l.get('ramo_atividade', '') for l in leads if l.get('ramo_atividade'))))
|
|
selected_ramo = st.selectbox("Filtrar Ramo:", ["Todos"] + ramos, key="kanban_ramo")
|
|
with fc3:
|
|
cidades = sorted(list(set(l.get('cidade', '') for l in leads if l.get('cidade'))))
|
|
selected_cidade = st.selectbox("Filtrar Cidade:", ["Todas"] + cidades, key="kanban_cidade")
|
|
|
|
# Aplicar filtros locais
|
|
filtered_leads = leads
|
|
if search_query:
|
|
sq = search_query.lower()
|
|
filtered_leads = [
|
|
l for l in filtered_leads if
|
|
sq in l.get('nome_empresa', '').lower() or
|
|
sq in l.get('telefone', '').lower() or
|
|
sq in l.get('notas', '').lower()
|
|
]
|
|
if selected_ramo != "Todos":
|
|
filtered_leads = [l for l in filtered_leads if l.get('ramo_atividade') == selected_ramo]
|
|
if selected_cidade != "Todas":
|
|
filtered_leads = [l for l in filtered_leads if l.get('cidade') == selected_cidade]
|
|
|
|
# Exibir resumo de métricas KPI
|
|
render_metrics_summary(filtered_leads)
|
|
st.markdown("---")
|
|
|
|
# Exibir Modal de Detalhes se selecionado
|
|
selected_lead_id = st.session_state.get('selected_lead_id')
|
|
if selected_lead_id:
|
|
render_lead_detail_modal(selected_lead_id, api_client)
|
|
|
|
# Renderizar o Quadro Kanban
|
|
render_kanban_board(filtered_leads, api_client)
|
|
|
|
def render_lead_detail_modal(lead_id: str, api_client: APIClient):
|
|
lead_detail = api_client.get_lead_details(lead_id)
|
|
if not lead_detail:
|
|
st.session_state.pop('selected_lead_id', None)
|
|
return
|
|
|
|
with st.expander(f"📝 Detalhes e Histórico de Auditoria: {lead_detail.get('nome_empresa')}", expanded=True):
|
|
col1, col2 = st.columns([1, 1])
|
|
|
|
with col1:
|
|
st.subheader("Informações do Lead")
|
|
st.write(f"**Ramo:** {lead_detail.get('ramo_atividade')}")
|
|
st.write(f"**Telefone:** {lead_detail.get('telefone', 'N/A')}")
|
|
st.write(f"**Endereço:** {lead_detail.get('logradouro', '')}, {lead_detail.get('bairro', '')} - {lead_detail.get('cidade', '')}/{lead_detail.get('uf', '')}")
|
|
st.write(f"**Status Atual:** `{lead_detail.get('status_funil')}`")
|
|
|
|
new_notes = st.text_area("Notas / Observações Comerciais:", value=lead_detail.get('notas', ''), height=120)
|
|
|
|
c_save, c_opt, c_del, c_close = st.columns([1, 1, 1, 1])
|
|
with c_save:
|
|
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", 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", use_container_width=True):
|
|
st.session_state.pop('selected_lead_id', None)
|
|
st.rerun()
|
|
|
|
with col2:
|
|
st.subheader("📜 Histórico de Interações (Auditoria)")
|
|
interacoes = lead_detail.get('interacoes', [])
|
|
if not interacoes:
|
|
st.info("Nenhuma interação registrada ainda.")
|
|
else:
|
|
for idx in interacoes:
|
|
st.markdown(
|
|
f"""
|
|
<div style="background: #21262D; border: 1px solid #30363D; border-radius: 6px; padding: 8px; margin-bottom: 6px; font-size: 0.85rem;">
|
|
<strong>[{idx.get('tipo')}]</strong> {idx.get('descricao')}<br>
|
|
<small style="color: #8B949E;">Por: {idx.get('usuario_nome')} em {idx.get('timestamp')[:19] if idx.get('timestamp') else ''}</small>
|
|
</div>
|
|
""", unsafe_allow_html=True
|
|
)
|