Files
controles/static/js/militantes.js

1461 lines
58 KiB
JavaScript

console.log('Carregando script militantes.js...');
// Constantes para responsabilidades
const Militante = {
RESPONSAVEL_FINANCAS: 256,
RESPONSAVEL_IMPRENSA: 512,
QUADRO_ORIENTADOR: 64,
SECRETARIO: 1,
TESOUREIRO: 2,
IMPRENSA: 4,
MNS: 8,
MPS: 16,
JUVENTUDE: 32,
ASPIRANTE: 128
};
// Função para obter o token CSRF da meta tag
function getCsrfToken() {
const token = document.querySelector('meta[name="csrf-token"]')?.getAttribute('content');
if (!token) {
console.error('CSRF token não encontrado!');
return '';
}
return token;
}
// Variáveis globais para controle dos filtros e paginação
let filtroAtual = 'todos';
let filtroResponsabilidade = null;
let filtroCelula = null;
let currentPage = 1;
let rowsPerPage = 20;
let totalRows = 0;
let buscarTexto = '';
// Mapa de responsabilidades para valores numéricos
const RESPONSABILIDADES_MAP = {
'secretario': 1,
'tesoureiro': 2,
'imprensa': 4,
'mns': 8,
'mps': 16,
'juventude': 32,
'quadro-orientador': 64,
'aspirante': 128,
'responsavel-financas': 256,
'responsavel-imprensa': 512
};
// Mapa reverso para converter valores em nomes
const RESPONSABILIDADES_REVERSE_MAP = {
1: 'Secretário',
2: 'Tesoureiro',
4: 'Imprensa',
8: 'MNS',
16: 'MPS',
32: 'Juventude',
64: 'Quadro-Orientador',
128: 'Aspirante',
256: 'Responsável de Finanças',
512: 'Responsável de Imprensa'
};
// Função para validar data no formato DD/MM/YYYY
function validarData(data) {
if (!data) return true; // Campo vazio é válido
console.log('Validando data:', data);
// Verifica o formato
if (!/^\d{2}\/\d{2}\/\d{4}$/.test(data)) {
console.warn('Data com formato inválido (deve ser DD/MM/YYYY):', data);
return false;
}
// Extrai dia, mês e ano
const [dia, mes, ano] = data.split('/').map(Number);
// Verifica se os valores são números válidos
if (isNaN(dia) || isNaN(mes) || isNaN(ano)) {
console.warn('Data contém valores não numéricos:', { dia, mes, ano });
return false;
}
// Verifica se os valores estão dentro dos limites
if (mes < 1 || mes > 12) {
console.warn('Mês inválido (deve estar entre 1 e 12):', mes);
return false;
}
if (dia < 1 || dia > 31) {
console.warn('Dia inválido (deve estar entre 1 e 31):', dia);
return false;
}
if (ano < 1900 || ano > 2100) {
console.warn('Ano fora do intervalo permitido (1900-2100):', ano);
return false;
}
// Verifica meses com 30 dias
if ([4, 6, 9, 11].includes(mes) && dia > 30) {
console.warn(`Dia inválido para o mês ${mes} (máximo 30):`, dia);
return false;
}
// Verifica fevereiro e ano bissexto
if (mes === 2) {
const bissexto = (ano % 4 === 0 && ano % 100 !== 0) || (ano % 400 === 0);
const maxDias = bissexto ? 29 : 28;
if (dia > maxDias) {
console.warn(`Dia inválido para fevereiro ${bissexto ? '(bissexto)' : ''} (máximo ${maxDias}):`, dia);
return false;
}
}
// Verifica se a data não é futura
const hoje = new Date();
const dataInformada = new Date(ano, mes - 1, dia);
if (dataInformada > hoje) {
console.warn('Data futura não permitida:', data);
return false;
}
console.log('Data válida:', data);
return true;
}
// Função para formatar data do formato ISO (YYYY-MM-DD) para DD/MM/YYYY
function formatarData(data) {
if (!data) {
console.log('Data vazia, retornando string vazia');
return '';
}
console.log('Formatando data:', data);
// Se já estiver no formato DD/MM/YYYY, valida e retorna
if (/^\d{2}\/\d{2}\/\d{4}$/.test(data)) {
if (validarData(data)) {
console.log('Data já está no formato correto e é válida:', data);
return data;
} else {
console.warn('Data já está no formato DD/MM/YYYY mas é inválida:', data);
return '';
}
}
try {
// Tenta converter do formato ISO
if (/^\d{4}-\d{2}-\d{2}$/.test(data)) {
const [ano, mes, dia] = data.split('-');
const dataFormatada = `${dia}/${mes}/${ano}`;
// Valida a data antes de retornar
if (validarData(dataFormatada)) {
console.log('Data ISO convertida com sucesso:', data, '->', dataFormatada);
return dataFormatada;
} else {
console.warn('Data ISO inválida após formatação:', data, '->', dataFormatada);
return '';
}
}
console.warn('Formato de data não reconhecido (deve ser YYYY-MM-DD ou DD/MM/YYYY):', data);
return '';
} catch (error) {
console.error('Erro ao formatar data:', error, 'Data:', data);
return '';
}
}
// Função para converter data de DD/MM/YYYY para YYYY-MM-DD
function converterDataParaISO(data) {
if (!data) {
console.log('Data vazia, retornando string vazia');
return '';
}
console.log('Convertendo data para ISO:', data);
// Se já estiver no formato ISO, valida e retorna
if (/^\d{4}-\d{2}-\d{2}$/.test(data)) {
const [ano, mes, dia] = data.split('-').map(Number);
if (!isNaN(ano) && !isNaN(mes) && !isNaN(dia)) {
console.log('Data já está no formato ISO:', data);
return data;
} else {
console.warn('Data no formato ISO mas com valores inválidos:', data);
return '';
}
}
try {
// Verifica se está no formato DD/MM/YYYY
if (/^\d{2}\/\d{2}\/\d{4}$/.test(data)) {
// Valida a data antes de converter
if (!validarData(data)) {
console.warn('Data inválida antes da conversão para ISO:', data);
return '';
}
const [dia, mes, ano] = data.split('/');
const dataISO = `${ano}-${mes}-${dia}`;
console.log('Data convertida para ISO com sucesso:', data, '->', dataISO);
return dataISO;
}
console.warn('Formato de data não reconhecido (deve ser DD/MM/YYYY):', data);
return '';
} catch (error) {
console.error('Erro ao converter data para ISO:', error, 'Data:', data);
return '';
}
}
// Função para calcular o total de páginas
function calculateTotalPages() {
const allRows = document.querySelectorAll('#militantesTable tbody tr');
const visibleRows = Array.from(allRows).filter(row =>
!row.hasAttribute('data-filtered-out')
);
totalRows = visibleRows.length;
return Math.ceil(totalRows / rowsPerPage);
}
// Função para atualizar o texto de contagem
function updateCountText() {
const allRows = document.querySelectorAll('#militantesTable tbody tr');
const visibleRows = Array.from(allRows).filter(row =>
!row.hasAttribute('data-filtered-out')
);
totalRows = visibleRows.length;
const startIndex = (currentPage - 1) * rowsPerPage + 1;
const endIndex = Math.min(currentPage * rowsPerPage, totalRows);
// Atualizar texto de contagem
document.getElementById('countMilitantes').textContent =
`${startIndex}-${endIndex} de ${totalRows}`;
}
// Função para atualizar a paginação
function updatePagination() {
const totalPages = calculateTotalPages();
const paginationUl = document.querySelector('.pagination');
const prevPage = document.getElementById('prevPage');
const nextPage = document.getElementById('nextPage');
// Limpar páginas existentes (exceto prev e next)
const pageItems = paginationUl.querySelectorAll('li:not(#prevPage):not(#nextPage)');
pageItems.forEach(item => item.remove());
// Adicionar novas páginas
for (let i = 1; i <= totalPages; i++) {
const li = document.createElement('li');
li.className = `page-item${i === currentPage ? ' active' : ''}`;
li.innerHTML = `<a class="page-link" href="#">${i}</a>`;
li.addEventListener('click', (e) => {
e.preventDefault();
currentPage = i;
updateVisibleRows();
updatePagination();
});
paginationUl.insertBefore(li, nextPage);
}
// Atualizar estado dos botões prev/next
prevPage.classList.toggle('disabled', currentPage === 1);
nextPage.classList.toggle('disabled', currentPage === totalPages || totalPages === 0);
// Adicionar eventos aos botões prev/next
prevPage.onclick = (e) => {
e.preventDefault();
if (currentPage > 1) {
currentPage--;
updateVisibleRows();
updatePagination();
}
};
nextPage.onclick = (e) => {
e.preventDefault();
if (currentPage < totalPages) {
currentPage++;
updateVisibleRows();
updatePagination();
}
};
// Atualizar texto de contagem
updateCountText();
}
// Função para atualizar as linhas visíveis
function updateVisibleRows() {
const allRows = document.querySelectorAll('#militantesTable tbody tr');
const visibleRows = Array.from(allRows).filter(row =>
!row.hasAttribute('data-filtered-out')
);
const startIndex = (currentPage - 1) * rowsPerPage;
const endIndex = startIndex + rowsPerPage;
visibleRows.forEach((row, index) => {
if (index >= startIndex && index < endIndex) {
row.style.display = '';
} else {
row.style.display = 'none';
}
});
updateCountText();
}
// Função para atualizar a contagem de militantes visíveis
function atualizarContagem() {
const allRows = document.querySelectorAll('#militantesTable tbody tr');
const visibleRows = Array.from(allRows).filter(row => !row.hasAttribute('data-filtered-out'));
// Atualizar o contador
document.getElementById('countMilitantes').textContent = visibleRows.length;
// Resetar paginação
currentPage = 1;
updateVisibleRows();
updatePagination();
}
// Função para filtrar militantes
function filtrarMilitantes() {
console.log('Filtrando militantes...');
console.log('Filtro atual:', filtroAtual);
console.log('Texto busca:', buscarTexto);
console.log('Filtro responsabilidade:', filtroResponsabilidade);
console.log('Filtro célula:', filtroCelula);
const allRows = document.querySelectorAll('#militantesTable tbody tr');
allRows.forEach(row => {
const nome = row.querySelector('[data-nome]')?.getAttribute('data-nome')?.toLowerCase() || '';
const cpf = row.querySelector('[data-cpf]')?.getAttribute('data-cpf')?.toLowerCase() || '';
const email = row.querySelector('[data-email]')?.getAttribute('data-email')?.toLowerCase() || '';
const telefone = row.querySelector('[data-telefone]')?.getAttribute('data-telefone')?.toLowerCase() || '';
// Filtro de texto
const matchesSearch = buscarTexto === '' ||
nome.includes(buscarTexto) ||
cpf.includes(buscarTexto) ||
email.includes(buscarTexto) ||
telefone.includes(buscarTexto);
// Filtro de responsabilidades
let matchesResponsabilidade = true;
if (filtroResponsabilidade) {
const valorResponsabilidade = RESPONSABILIDADES_MAP[filtroResponsabilidade];
console.log('Valor da responsabilidade:', valorResponsabilidade);
if (valorResponsabilidade !== undefined) {
const responsabilidades = parseInt(row.getAttribute('data-responsabilidades') || '0');
console.log('Responsabilidades do militante:', responsabilidades);
matchesResponsabilidade = (responsabilidades & valorResponsabilidade) !== 0;
console.log('Matches responsabilidade:', matchesResponsabilidade);
}
}
// Filtro de célula
let matchesCelula = true;
if (filtroCelula) {
const celulaId = row.getAttribute('data-celula-id');
console.log('Célula do militante:', celulaId, 'Filtro:', filtroCelula);
matchesCelula = celulaId === filtroCelula;
console.log('Matches célula:', matchesCelula);
}
// Aplicar filtros
if (matchesSearch && matchesResponsabilidade && matchesCelula) {
row.removeAttribute('data-filtered-out');
row.style.display = '';
} else {
row.setAttribute('data-filtered-out', '');
row.style.display = 'none';
}
});
// Atualizar contagem e paginação
atualizarContagem();
}
// Listener para o campo de busca com debounce
let timeoutId;
document.getElementById('searchInput').addEventListener('input', function(e) {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => {
buscarTexto = this.value.toLowerCase();
filtrarMilitantes();
}, 300);
});
// Função para configurar um campo de data
function configurarCampoData(campo) {
if (!campo) return;
// Remover campo de texto anterior se existir
const campoTextoExistente = campo.previousElementSibling;
if (campoTextoExistente && campoTextoExistente.classList.contains('campo-data-texto')) {
campoTextoExistente.remove();
}
// Configurar o campo de data
campo.type = 'date';
// Se já tiver valor, garantir que está no formato ISO
if (campo.value) {
campo.value = converterDataParaISO(campo.value);
}
// Quando o valor mudar, garantir formato ISO
campo.addEventListener('change', function() {
if (this.value) {
this.value = converterDataParaISO(this.value);
}
});
}
// Função para carregar dados do militante
async function carregarDadosMilitante(militanteId) {
try {
console.log('Carregando dados do militante:', militanteId);
const response = await fetch(`/militantes/dados/${militanteId}`, {
method: 'GET',
headers: {
'X-Requested-With': 'XMLHttpRequest'
}
});
if (!response.ok) {
throw new Error(`Erro ao carregar dados do militante: ${response.status} ${response.statusText}`);
}
const data = await response.json();
console.log('Dados recebidos do servidor:', data);
if (data.status === 'error') {
throw new Error(data.message || 'Erro ao carregar dados do militante');
}
// Preencher campos do formulário com os dados do banco
document.getElementById('edit_militante_id').value = militanteId;
document.getElementById('edit_nome').value = data.nome || '';
document.getElementById('edit_cpf').value = data.cpf || '';
document.getElementById('edit_titulo_eleitoral').value = data.titulo_eleitoral || '';
// Função auxiliar para tratar campo de data
function tratarCampoData(campo, valor, nomeCampo) {
if (!campo) {
console.warn(`Campo ${nomeCampo} não encontrado no DOM`);
return;
}
// Limpar campo e remover validações
campo.value = '';
campo.classList.remove('is-invalid');
const feedback = campo.nextElementSibling;
if (feedback && feedback.classList.contains('invalid-feedback')) {
feedback.remove();
}
if (!valor) {
console.log(`${nomeCampo} não informada`);
return;
}
try {
console.log(`Formatando ${nomeCampo}:`, valor);
const dataFormatada = formatarData(valor);
if (validarData(dataFormatada)) {
campo.value = dataFormatada;
console.log(`${nomeCampo} formatada com sucesso:`, dataFormatada);
} else {
console.warn(`${nomeCampo} inválida após formatação:`, valor);
campo.classList.add('is-invalid');
const div = document.createElement('div');
div.className = 'invalid-feedback';
div.textContent = 'Data inválida';
campo.parentNode.insertBefore(div, campo.nextSibling);
}
} catch (error) {
console.error(`Erro ao formatar ${nomeCampo}:`, error);
campo.classList.add('is-invalid');
const div = document.createElement('div');
div.className = 'invalid-feedback';
div.textContent = 'Erro ao formatar data';
campo.parentNode.insertBefore(div, campo.nextSibling);
}
}
// Tratar campos de data
tratarCampoData(
document.getElementById('edit_data_nascimento'),
data.data_nascimento,
'Data de Nascimento'
);
tratarCampoData(
document.getElementById('edit_data_entrada_oci'),
data.data_entrada_oci,
'Data de Entrada na OCI'
);
tratarCampoData(
document.getElementById('edit_data_efetivacao_oci'),
data.data_efetivacao_oci,
'Data de Efetivação na OCI'
);
// Preencher outros campos
document.getElementById('edit_email').value = data.emails?.[0] || '';
document.getElementById('edit_telefone1').value = data.telefone1 || '';
// Setar célula
const selectCelula = document.getElementById('edit_celula');
if (selectCelula) {
selectCelula.value = data.celula_id || '';
console.log('Célula definida:', data.celula_id);
} else {
console.warn('Seletor de célula não encontrado');
}
// Setar responsabilidades
const responsabilidadesValue = data.responsabilidades_valor || 0;
document.getElementById('responsabilidades_values').value = responsabilidadesValue;
console.log('Responsabilidades definidas:', responsabilidadesValue);
// Atualizar badges de responsabilidades
const badges = document.querySelectorAll('.badge-clickable');
badges.forEach(badge => {
const valor = parseInt(badge.getAttribute('data-valor'));
if (responsabilidadesValue & valor) {
badge.classList.add('active');
const originalClass = badge.getAttribute('data-original-class');
if (originalClass) {
badge.className = `badge badge-clickable active ${originalClass}`;
}
console.log('Badge ativada:', badge.getAttribute('title'));
} else {
badge.classList.remove('active');
const originalClass = badge.getAttribute('data-original-class');
if (originalClass) {
badge.className = `badge badge-clickable ${originalClass}`;
}
}
});
// Setar campos de sindicato
['sindicato', 'cargo_sindical', 'central_sindical'].forEach(campo => {
const elemento = document.getElementById(campo);
if (elemento) {
elemento.value = data[campo] || '';
console.log(`Campo ${campo} definido:`, data[campo]);
}
});
// Setar checkbox de dirigente sindical
const checkDirigente = document.getElementById('dirigente_sindical');
if (checkDirigente) {
checkDirigente.checked = data.dirigente_sindical || false;
console.log('Dirigente sindical:', data.dirigente_sindical);
}
// Atualizar título do modal
const modalTitle = document.querySelector('#modalEditarMilitante .modal-title');
if (modalTitle) {
modalTitle.innerHTML = `<i class="fas fa-user-edit me-2"></i>Editar ${data.nome}`;
console.log('Título do modal atualizado');
}
console.log('Dados carregados com sucesso');
} catch (error) {
console.error('Erro ao carregar dados:', error);
mostrarAlerta('danger', `Erro ao carregar dados do militante: ${error.message}`);
}
}
// Função para mostrar alertas
function mostrarAlerta(tipo, mensagem) {
// Remover alertas existentes
document.querySelectorAll('.alert').forEach(alert => alert.remove());
const alertDiv = document.createElement('div');
alertDiv.className = `alert alert-${tipo} alert-dismissible fade show`;
alertDiv.role = 'alert';
alertDiv.style.marginBottom = '1rem';
alertDiv.innerHTML = `
${mensagem}
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Fechar"></button>
`;
const container = document.querySelector('.container-fluid');
if (container) {
container.insertBefore(alertDiv, container.firstChild);
// Remover o alerta após 5 segundos
setTimeout(() => {
if (alertDiv && alertDiv.parentNode) {
alertDiv.remove();
}
}, 5000);
}
}
// Configurar o token CSRF para todas as requisições AJAX
$.ajaxSetup({
beforeSend: function(xhr, settings) {
if (!/^(GET|HEAD|OPTIONS|TRACE)$/i.test(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", getCsrfToken());
}
}
});
// Configurar eventos quando o DOM estiver carregado
document.addEventListener('DOMContentLoaded', function() {
console.log('DOM carregado, configurando eventos...');
// Configurar seletor de linhas por página
const rowsPerPageSelect = document.getElementById('rowsPerPage');
if (rowsPerPageSelect) {
rowsPerPageSelect.addEventListener('change', function() {
rowsPerPage = parseInt(this.value);
currentPage = 1;
updateVisibleRows();
updatePagination();
});
}
// Configurar filtros
document.querySelectorAll('.dropdown-item[data-filter]').forEach(item => {
item.addEventListener('click', function(e) {
e.preventDefault();
const filter = this.getAttribute('data-filter');
const dropdownButton = document.querySelector('.dropdown-toggle');
console.log('Filtro selecionado:', filter);
if (filter === 'todos') {
filtroResponsabilidade = null;
filtroCelula = null;
} else if (filter === 'celula') {
filtroResponsabilidade = null;
filtroCelula = this.getAttribute('data-celula');
console.log('Filtro por célula:', filtroCelula);
} else {
filtroResponsabilidade = filter;
filtroCelula = null;
console.log('Filtro por responsabilidade:', filtroResponsabilidade);
}
// Atualizar filtro atual
filtroAtual = filter;
// Atualizar texto do botão
dropdownButton.innerHTML = `<i class="fas fa-filter me-2"></i>${this.textContent}`;
// Aplicar filtros
filtrarMilitantes();
});
});
console.log('Configurando modal de edição...');
// Configurar máscara para campos de data
$('.date-mask').mask('00/00/0000', {
placeholder: 'DD/MM/AAAA',
clearIfNotMatch: true,
translation: {
'placeholder': "DD/MM/AAAA"
}
});
// Validar campos de data quando perderem o foco
$('.date-mask').on('blur', function() {
const valor = $(this).val();
if (valor) {
if (!validarData(valor)) {
$(this).addClass('is-invalid');
if (!$(this).next('.invalid-feedback').length) {
$(this).after('<div class="invalid-feedback">Data inválida</div>');
}
} else {
$(this).removeClass('is-invalid');
$(this).next('.invalid-feedback').remove();
}
} else {
// Se o campo estiver vazio, remover validação
$(this).removeClass('is-invalid');
$(this).next('.invalid-feedback').remove();
}
});
// Impedir entrada de caracteres inválidos nos campos de data
$('.date-mask').on('keypress', function(e) {
const char = String.fromCharCode(e.which);
if (!/[\d\/]/.test(char)) {
e.preventDefault();
return false;
}
const valor = $(this).val();
if (valor.length === 10 && char !== '\b') {
e.preventDefault();
return false;
}
});
// Limpar campos de data quando o valor for inválido
$('.date-mask').on('input', function() {
const valor = $(this).val();
if (valor && valor.length === 10 && !validarData(valor)) {
$(this).val('');
$(this).addClass('is-invalid');
if (!$(this).next('.invalid-feedback').length) {
$(this).after('<div class="invalid-feedback">Data inválida</div>');
}
}
});
// Configurar campos de data em todos os modais
const modalNovoMilitante = document.getElementById('modalNovoMilitante');
const modalEditarMilitante = document.getElementById('modalEditarMilitante');
// Configurar modal de edição
if (modalEditarMilitante) {
modalEditarMilitante.addEventListener('show.bs.modal', async function(event) {
console.log('Modal de edição aberto');
const button = event.relatedTarget;
if (!button) {
console.error('Botão não encontrado');
mostrarAlerta('danger', 'Erro ao abrir modal: botão não encontrado');
return;
}
const militanteId = button.getAttribute('data-militante-id');
const militanteNome = button.getAttribute('data-militante-nome');
console.log('Dados do botão:', { militanteId, militanteNome });
if (!militanteId) {
console.error('ID do militante não encontrado no botão');
mostrarAlerta('danger', 'Erro ao abrir modal: ID do militante não encontrado');
return;
}
// Atualizar título do modal
const modalTitle = this.querySelector('.modal-title');
if (modalTitle) {
modalTitle.innerHTML = `<i class="fas fa-user-edit me-2"></i>Editar ${militanteNome}`;
}
try {
// Carregar dados do militante
await carregarDadosMilitante(militanteId);
} catch (error) {
console.error('Erro ao carregar dados:', error);
mostrarAlerta('danger', 'Erro ao carregar dados do militante');
}
});
// Limpar dados quando o modal for fechado
modalEditarMilitante.addEventListener('hidden.bs.modal', function() {
// Limpar formulário
const form = this.querySelector('form');
if (form) {
form.reset();
}
// Limpar campos hidden
document.getElementById('edit_militante_id').value = '';
document.getElementById('responsabilidades_values').value = '0';
// Resetar badges
this.querySelectorAll('.badge-clickable').forEach(badge => {
badge.classList.remove('active');
const originalClass = badge.getAttribute('data-original-class');
if (originalClass) {
badge.className = `badge badge-clickable ${originalClass}`;
}
});
// Limpar feedback de validação
this.querySelectorAll('.is-invalid').forEach(campo => {
campo.classList.remove('is-invalid');
});
this.querySelectorAll('.invalid-feedback').forEach(feedback => {
feedback.remove();
});
});
} else {
console.error('Modal de edição não encontrado!');
}
// Ajustar o formulário de novo militante
const formNovoMilitante = document.getElementById('formNovoMilitante');
if (formNovoMilitante) {
formNovoMilitante.addEventListener('submit', function(e) {
e.preventDefault();
// Validar todas as datas antes do envio
const camposData = this.querySelectorAll('.date-mask');
let datasValidas = true;
camposData.forEach(campo => {
const valor = campo.value;
if (valor && !validarData(valor)) {
datasValidas = false;
$(campo).addClass('is-invalid');
if (!$(campo).next('.invalid-feedback').length) {
$(campo).after('<div class="invalid-feedback">Data inválida</div>');
}
}
});
if (!datasValidas) {
return; // Não envia o formulário se houver datas inválidas
}
// Converter datas para formato ISO antes do envio
camposData.forEach(campo => {
if (campo.value) {
campo.value = converterDataParaISO(campo.value);
}
});
const formData = new FormData(this);
fetch(this.action, {
method: 'POST',
headers: {
'X-Requested-With': 'XMLHttpRequest',
'X-CSRF-Token': getCsrfToken()
},
body: formData
})
.then(response => response.json())
.then(data => {
if (data.status === 'success') {
// Fechar o modal
const modal = bootstrap.Modal.getInstance(modalNovoMilitante);
modal.hide();
// Limpar o formulário
formNovoMilitante.reset();
// Adicionar o novo militante à tabela
const tbody = document.querySelector('#militantesTable tbody');
const tr = document.createElement('tr');
tr.setAttribute('data-militante', data.militante.id);
tr.setAttribute('data-filiado', data.militante.filiado ? 'sim' : 'nao');
tr.innerHTML = `
<td data-nome="${data.militante.nome}">${data.militante.nome}</td>
<td data-cpf="${data.militante.cpf}">${data.militante.cpf}</td>
<td data-email="${data.militante.emails && data.militante.emails.length > 0 ? data.militante.emails[0].endereco_email : ''}">${data.militante.emails && data.militante.emails.length > 0 ? data.militante.emails[0].endereco_email : ''}</td>
<td data-telefone="${data.militante.telefone1}">${data.militante.telefone1}</td>
<td data-filiado="${data.militante.filiado}">
<span class="badge ${data.militante.filiado ? 'bg-success' : 'bg-secondary'}">
${data.militante.filiado ? 'Sim' : 'Não'}
</span>
</td>
<td class="text-end">
<div class="btn-group">
<button type="button"
class="btn btn-sm btn-outline-primary"
data-bs-toggle="modal"
data-bs-target="#modalEditarMilitante"
data-militante-id="${data.militante.id}"
data-militante-nome="${data.militante.nome}"
data-militante-cpf="${data.militante.cpf}"
data-militante-email="${data.militante.emails && data.militante.emails.length > 0 ? data.militante.emails[0].endereco_email : ''}"
data-militante-telefone="${data.militante.telefone1}"
data-militante-endereco="${data.militante.endereco}"
data-militante-filiado="${data.militante.filiado}"
title="Editar">
<i class="fas fa-edit"></i>
</button>
<button type="button"
class="btn btn-sm btn-outline-danger"
data-bs-toggle="modal"
data-bs-target="#deleteModal"
data-militante-id="${data.militante.id}"
data-militante-nome="${data.militante.nome}"
title="Excluir">
<i class="fas fa-trash"></i>
</button>
</div>
</td>
`;
// Inserir no início da tabela
tbody.insertBefore(tr, tbody.firstChild);
// Atualizar contador
const countElement = document.getElementById('countMilitantes');
countElement.textContent = parseInt(countElement.textContent) + 1;
// Mostrar mensagem de sucesso
const alertDiv = document.createElement('div');
alertDiv.className = 'alert alert-success alert-dismissible fade show';
alertDiv.innerHTML = `
${data.message}
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
`;
document.querySelector('.container').insertBefore(alertDiv, document.querySelector('.container').firstChild);
} else {
// Mostrar erro
const alertDiv = document.createElement('div');
alertDiv.className = 'alert alert-danger alert-dismissible fade show';
alertDiv.innerHTML = `
${data.message}
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
`;
document.querySelector('.modal-body').insertBefore(alertDiv, formNovoMilitante);
}
})
.catch(error => {
console.error('Erro:', error);
// Mostrar erro genérico
const alertDiv = document.createElement('div');
alertDiv.className = 'alert alert-danger alert-dismissible fade show';
alertDiv.innerHTML = `
Erro ao cadastrar militante. Tente novamente.
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
`;
document.querySelector('.modal-body').insertBefore(alertDiv, formNovoMilitante);
});
});
}
// Máscara para CPF
const cpfInputs = document.querySelectorAll('input[name="cpf"]');
cpfInputs.forEach(input => {
input.addEventListener('input', function(e) {
let value = e.target.value.replace(/\D/g, '');
if (value.length <= 11) {
value = value.replace(/(\d{3})(\d)/, '$1.$2');
value = value.replace(/(\d{3})(\d)/, '$1.$2');
value = value.replace(/(\d{3})(\d{1,2})$/, '$1-$2');
e.target.value = value;
}
});
});
// Máscara para telefone
const telefoneInputs = document.querySelectorAll('input[name="telefone1"], input[name="telefone2"]');
telefoneInputs.forEach(input => {
input.addEventListener('input', function(e) {
let value = e.target.value.replace(/\D/g, '');
if (value.length <= 11) {
value = value.replace(/(\d{2})(\d)/, '($1) $2');
value = value.replace(/(\d{4,5})(\d{4})$/, '$1-$2');
e.target.value = value;
}
});
});
// Configuração do modal de exclusão
const deleteModal = document.getElementById('deleteModal');
if (deleteModal) {
deleteModal.addEventListener('show.bs.modal', function(event) {
const button = event.relatedTarget;
const militanteId = button.getAttribute('data-militante-id');
const militanteNome = button.getAttribute('data-militante-nome');
document.getElementById('militanteNome').textContent = militanteNome;
document.getElementById('deleteForm').action = `/militantes/excluir/${militanteId}`;
});
}
// Ordenação
const headers = document.querySelectorAll('#militantesTable th[data-sort]');
headers.forEach(header => {
header.addEventListener('click', function() {
const column = this.getAttribute('data-sort');
const tbody = document.querySelector('#militantesTable tbody');
const rows = Array.from(tbody.querySelectorAll('tr'));
const isAsc = !this.classList.contains('sort-asc');
// Remover classes de ordenação de todos os headers
headers.forEach(h => {
h.classList.remove('sort-asc', 'sort-desc');
h.querySelector('i').className = 'fas fa-sort';
});
// Adicionar classe de ordenação ao header clicado
this.classList.add(isAsc ? 'sort-asc' : 'sort-desc');
this.querySelector('i').className = `fas fa-sort-${isAsc ? 'up' : 'down'}`;
// Ordenar linhas
rows.sort((a, b) => {
const aVal = a.querySelector(`td[data-${column}]`).getAttribute(`data-${column}`);
const bVal = b.querySelector(`td[data-${column}]`).getAttribute(`data-${column}`);
return isAsc ? aVal.localeCompare(bVal) : bVal.localeCompare(aVal);
});
// Reposicionar linhas
rows.forEach(row => tbody.appendChild(row));
});
});
// Exportar para CSV
const btnExportar = document.getElementById('btnExportar');
if (btnExportar) {
btnExportar.addEventListener('click', function() {
const rows = document.querySelectorAll('#militantesTable tbody tr:not([style*="display: none"])');
const headers = ['Nome', 'CPF', 'Email', 'Telefone', 'Filiado'];
let csv = headers.join(',') + '\n';
rows.forEach(row => {
const cols = row.querySelectorAll('td');
const values = [
cols[0].textContent,
cols[1].textContent,
cols[2].textContent,
cols[3].textContent,
cols[4].textContent.trim()
].map(val => `"${val}"`);
csv += values.join(',') + '\n';
});
const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' });
const link = document.createElement('a');
link.href = URL.createObjectURL(blob);
link.setAttribute('download', 'militantes.csv');
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
});
}
// Configurar máscaras de input
// CEP
const cepInputs = document.querySelectorAll('input[name="cep"]');
cepInputs.forEach(input => {
input.addEventListener('input', function(e) {
let value = e.target.value.replace(/\D/g, '');
if (value.length <= 8) {
value = value.replace(/(\d{5})(\d)/, '$1-$2');
e.target.value = value;
}
});
// Buscar endereço pelo CEP
input.addEventListener('blur', function(e) {
const cep = e.target.value.replace(/\D/g, '');
if (cep.length === 8) {
fetch(`https://viacep.com.br/ws/${cep}/json/`)
.then(response => response.json())
.then(data => {
if (!data.erro) {
const form = input.closest('form');
form.querySelector('input[name="logradouro"]').value = data.logradouro;
form.querySelector('input[name="bairro"]').value = data.bairro;
form.querySelector('input[name="cidade"]').value = data.localidade;
form.querySelector('select[name="estado"]').value = data.uf;
}
});
}
});
});
// Título Eleitoral
const tituloInputs = document.querySelectorAll('input[name="titulo_eleitoral"]');
tituloInputs.forEach(input => {
input.addEventListener('input', function(e) {
let value = e.target.value.replace(/\D/g, '');
if (value.length <= 12) {
value = value.replace(/(\d{4})(\d)/, '$1 $2');
value = value.replace(/(\d{4})(\d)/, '$1 $2');
e.target.value = value;
}
});
});
// Inicializar tooltips do Bootstrap
const tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'));
tooltipTriggerList.map(function (tooltipTriggerEl) {
return new bootstrap.Tooltip(tooltipTriggerEl, {
placement: 'top',
trigger: 'hover'
});
});
// Inicializar paginação
updateVisibleRows();
updatePagination();
});
// Função para atualizar o valor total das responsabilidades
function atualizarValorResponsabilidades() {
const badges = document.querySelectorAll('.badge-clickable.active');
let valorTotal = 0;
badges.forEach(badge => {
const valor = parseInt(badge.getAttribute('data-value'));
if (!isNaN(valor)) {
valorTotal |= valor;
}
});
const input = document.getElementById('responsabilidades_values');
if (input) {
input.value = valorTotal;
}
return valorTotal;
}
// Função para salvar as alterações do militante
async function salvarAlteracoesMilitante(militanteId) {
try {
const form = document.getElementById('formEditarMilitante');
const formData = new FormData(form);
// Garantir que as datas estão no formato correto (YYYY-MM-DD)
const dataNascimento = formData.get('data_nascimento');
const dataEntradaOci = formData.get('data_entrada_oci');
const dataEfetivacaoOci = formData.get('data_efetivacao_oci');
if (dataNascimento) formData.set('data_nascimento', converterDataParaISO(dataNascimento));
if (dataEntradaOci) formData.set('data_entrada_oci', converterDataParaISO(dataEntradaOci));
if (dataEfetivacaoOci) formData.set('data_efetivacao_oci', converterDataParaISO(dataEfetivacaoOci));
// Adicionar responsabilidades
let responsabilidadesValor = 0;
form.querySelectorAll('input[name="responsabilidades"]:checked').forEach(checkbox => {
responsabilidadesValor |= parseInt(checkbox.value);
});
formData.append('responsabilidades_valor', responsabilidadesValor);
const response = await fetch(`/militantes/editar/${militanteId}`, {
method: 'POST',
body: formData,
headers: {
'X-CSRFToken': getCsrfToken(),
'X-Requested-With': 'XMLHttpRequest'
}
});
const data = await response.json();
console.log('Resposta completa do servidor:', data);
if (data.status === 'success') {
mostrarAlerta('success', data.message);
// Atualizar a linha na tabela
console.log('Dados detalhados para atualizarLinhaMilitante:', JSON.stringify(data, null, 2));
atualizarLinhaMilitante(militanteId, data);
console.log('Linha da tabela atualizada');
// Fechar o modal
const modal = bootstrap.Modal.getInstance(document.getElementById('modalEditarMilitante'));
if (modal) {
modal.hide();
}
} else {
mostrarAlerta('danger', data.message || 'Erro ao salvar alterações');
}
} catch (error) {
console.error('Erro ao salvar alterações:', error);
mostrarAlerta('danger', 'Erro ao salvar alterações. Por favor, tente novamente.');
}
}
// Função para atualizar linha do militante na tabela
function atualizarLinhaMilitante(militanteId, dados) {
console.log('Atualizando linha do militante:', militanteId, dados);
const row = document.querySelector(`tr[data-militante="${militanteId}"]`);
if (row) {
// Atualizar dados básicos e seus atributos data-*
const tdNome = row.querySelector('[data-nome]');
if (tdNome) {
console.log('Atualizando nome:', dados.data.nome);
tdNome.textContent = dados.data.nome;
tdNome.setAttribute('data-nome', dados.data.nome);
}
const tdCpf = row.querySelector('[data-cpf]');
if (tdCpf) {
console.log('Atualizando CPF:', dados.data.cpf);
tdCpf.textContent = dados.data.cpf;
tdCpf.setAttribute('data-cpf', dados.data.cpf);
}
const tdEmail = row.querySelector('[data-email]');
if (tdEmail) {
const email = dados.data.emails && dados.data.emails.length > 0 ? dados.data.emails[0] : '';
console.log('Atualizando email:', email);
tdEmail.textContent = email;
tdEmail.setAttribute('data-email', email);
}
const tdTelefone = row.querySelector('[data-telefone]');
if (tdTelefone) {
const telefone = dados.data.telefone1 || '';
console.log('Atualizando telefone:', telefone);
tdTelefone.textContent = telefone;
tdTelefone.setAttribute('data-telefone', telefone);
}
// Atualizar atributos para filtros
console.log('Atualizando célula ID:', dados.data.celula_id);
row.setAttribute('data-celula-id', dados.data.celula_id || '');
console.log('Atualizando responsabilidades:', dados.data.responsabilidades_valor);
row.setAttribute('data-responsabilidades', dados.data.responsabilidades_valor || '0');
// Atualizar badges de responsabilidades
const tdResponsabilidades = row.querySelector('td:nth-child(5)');
if (tdResponsabilidades) {
console.log('Atualizando badges de responsabilidades');
tdResponsabilidades.innerHTML = gerarBadgesResponsabilidades(dados.data.responsabilidades_valor);
}
// Remover qualquer estado de filtro da linha
row.removeAttribute('data-filtered-out');
row.style.display = '';
// Verificar se a linha deve estar visível com os filtros atuais
const nome = row.querySelector('[data-nome]')?.getAttribute('data-nome')?.toLowerCase() || '';
const cpf = row.querySelector('[data-cpf]')?.getAttribute('data-cpf')?.toLowerCase() || '';
const email = row.querySelector('[data-email]')?.getAttribute('data-email')?.toLowerCase() || '';
const telefone = row.querySelector('[data-telefone]')?.getAttribute('data-telefone')?.toLowerCase() || '';
// Filtro de texto
const matchesSearch = buscarTexto === '' ||
nome.includes(buscarTexto) ||
cpf.includes(buscarTexto) ||
email.includes(buscarTexto) ||
telefone.includes(buscarTexto);
// Filtro de responsabilidades
let matchesResponsabilidade = true;
if (filtroResponsabilidade) {
const valorResponsabilidade = RESPONSABILIDADES_MAP[filtroResponsabilidade];
if (valorResponsabilidade !== undefined) {
const responsabilidades = parseInt(row.getAttribute('data-responsabilidades') || '0');
matchesResponsabilidade = (responsabilidades & valorResponsabilidade) !== 0;
}
}
// Filtro de célula
let matchesCelula = true;
if (filtroCelula) {
const celulaId = row.getAttribute('data-celula-id');
matchesCelula = celulaId === filtroCelula;
}
// Aplicar filtros mantendo a linha visível se corresponder aos critérios
if (matchesSearch && matchesResponsabilidade && matchesCelula) {
row.removeAttribute('data-filtered-out');
row.style.display = '';
} else {
row.setAttribute('data-filtered-out', '');
row.style.display = 'none';
}
// Atualizar a paginação sem recarregar a página
updateVisibleRows();
updatePagination();
}
}
// Função para gerar HTML das badges de responsabilidades
function gerarBadgesResponsabilidades(valor) {
const badges = [];
if (valor & RESPONSABILIDADES_MAP['responsavel-financas']) badges.push('<span class="badge bg-primary" title="Responsável de Finanças">RFI</span>');
if (valor & RESPONSABILIDADES_MAP['responsavel-imprensa']) badges.push('<span class="badge bg-info" title="Responsável de Imprensa">RIM</span>');
if (valor & RESPONSABILIDADES_MAP['quadro-orientador']) badges.push('<span class="badge bg-success" title="Quadro-Orientador">QOR</span>');
if (valor & RESPONSABILIDADES_MAP['secretario']) badges.push('<span class="badge bg-secondary" title="Secretário">SEC</span>');
if (valor & RESPONSABILIDADES_MAP['tesoureiro']) badges.push('<span class="badge bg-warning" title="Tesoureiro">TES</span>');
if (valor & RESPONSABILIDADES_MAP['imprensa']) badges.push('<span class="badge bg-danger" title="Imprensa">IMP</span>');
if (valor & RESPONSABILIDADES_MAP['mns']) badges.push('<span class="badge bg-purple" title="MNS">MNS</span>');
if (valor & RESPONSABILIDADES_MAP['mps']) badges.push('<span class="badge bg-teal" title="MPS">MPS</span>');
if (valor & RESPONSABILIDADES_MAP['juventude']) badges.push('<span class="badge bg-orange" title="Juventude">JUV</span>');
if (valor & RESPONSABILIDADES_MAP['aspirante']) badges.push('<span class="badge bg-dark" title="Aspirante">ASP</span>');
return badges.join(' ');
}
// Handler para o formulário de edição
document.getElementById('formEditarMilitante').addEventListener('submit', async function(e) {
e.preventDefault();
console.log('Iniciando envio do formulário de edição...');
// Função auxiliar para validar e converter data
function validarEConverterData(campo, nomeCampo) {
const valor = campo.value;
console.log(`Validando ${nomeCampo}:`, valor);
if (!valor) {
console.log(`${nomeCampo} não informada`);
return true;
}
if (!validarData(valor)) {
console.warn(`${nomeCampo} inválida:`, valor);
campo.classList.add('is-invalid');
if (!campo.nextElementSibling?.classList.contains('invalid-feedback')) {
const div = document.createElement('div');
div.className = 'invalid-feedback';
div.textContent = 'Data inválida';
campo.parentNode.insertBefore(div, campo.nextSibling);
}
return false;
}
const dataISO = converterDataParaISO(valor);
if (!dataISO) {
console.warn(`Erro ao converter ${nomeCampo} para ISO:`, valor);
campo.classList.add('is-invalid');
if (!campo.nextElementSibling?.classList.contains('invalid-feedback')) {
const div = document.createElement('div');
div.className = 'invalid-feedback';
div.textContent = 'Erro ao converter data';
campo.parentNode.insertBefore(div, campo.nextSibling);
}
return false;
}
console.log(`${nomeCampo} validada e convertida:`, valor, '->', dataISO);
return true;
}
// Validar todas as datas
const camposData = {
'Data de Nascimento': document.getElementById('edit_data_nascimento'),
'Data de Entrada na OCI': document.getElementById('edit_data_entrada_oci'),
'Data de Efetivação na OCI': document.getElementById('edit_data_efetivacao_oci')
};
let datasValidas = true;
for (const [nome, campo] of Object.entries(camposData)) {
if (campo && !validarEConverterData(campo, nome)) {
datasValidas = false;
}
}
if (!datasValidas) {
console.warn('Formulário contém datas inválidas');
mostrarAlerta('danger', 'Por favor, corrija as datas inválidas antes de salvar.');
return;
}
// Validar sequência lógica das datas
const dataNascimento = camposData['Data de Nascimento']?.value;
const dataEntrada = camposData['Data de Entrada na OCI']?.value;
const dataEfetivacao = camposData['Data de Efetivação na OCI']?.value;
if (dataNascimento && dataEntrada) {
const nascimento = new Date(converterDataParaISO(dataNascimento));
const entrada = new Date(converterDataParaISO(dataEntrada));
if (entrada < nascimento) {
console.warn('Data de entrada anterior à data de nascimento');
mostrarAlerta('danger', 'A data de entrada na OCI não pode ser anterior à data de nascimento.');
return;
}
}
if (dataEntrada && dataEfetivacao) {
const entrada = new Date(converterDataParaISO(dataEntrada));
const efetivacao = new Date(converterDataParaISO(dataEfetivacao));
if (efetivacao < entrada) {
console.warn('Data de efetivação anterior à data de entrada');
mostrarAlerta('danger', 'A data de efetivação não pode ser anterior à data de entrada na OCI.');
return;
}
}
const militanteId = document.getElementById('edit_militante_id').value;
console.log('ID do militante:', militanteId);
const formData = new FormData(this);
try {
// Converter datas para formato ISO
for (const [nome, campo] of Object.entries(camposData)) {
if (campo?.value) {
const dataISO = converterDataParaISO(campo.value);
formData.set(campo.name, dataISO);
console.log(`${nome} convertida para envio:`, campo.value, '->', dataISO);
}
}
console.log('Enviando dados para o servidor...');
const response = await fetch(`/militantes/editar/${militanteId}`, {
method: 'POST',
headers: {
'X-Requested-With': 'XMLHttpRequest',
'X-CSRF-Token': document.querySelector('meta[name="csrf-token"]')?.getAttribute('content')
},
body: formData
});
if (!response.ok) {
throw new Error(`Erro ${response.status}: ${response.statusText}`);
}
const data = await response.json();
console.log('Resposta do servidor:', data);
if (data.status === 'success') {
// Atualizar linha na tabela
console.log('Dados detalhados para atualizarLinhaMilitante:', JSON.stringify(data, null, 2));
atualizarLinhaMilitante(militanteId, data);
console.log('Linha da tabela atualizada');
// Mostrar mensagem de sucesso
mostrarAlerta('success', data.message);
// Fechar modal
const modal = bootstrap.Modal.getInstance(document.getElementById('modalEditarMilitante'));
if (modal) {
modal.hide();
console.log('Modal fechado');
}
} else {
console.warn('Erro retornado pelo servidor:', data.message);
mostrarAlerta('danger', data.message || 'Erro ao salvar alterações');
}
} catch (error) {
console.error('Erro ao salvar alterações:', error);
mostrarAlerta('danger', `Erro ao salvar alterações: ${error.message}`);
}
});
// Handler para clique nas badges de responsabilidade
document.querySelectorAll('.badge-clickable').forEach(badge => {
badge.addEventListener('click', function() {
const valor = parseInt(this.getAttribute('data-valor'));
const responsabilidadesInput = document.getElementById('responsabilidades_values');
let valorAtual = parseInt(responsabilidadesInput.value) || 0;
if (this.classList.contains('active')) {
// Remover responsabilidade
valorAtual &= ~valor; // usando operador bitwise NOT e AND
this.classList.remove('active');
const originalClass = this.getAttribute('data-original-class');
if (originalClass) {
this.className = `badge badge-clickable ${originalClass}`;
}
} else {
// Adicionar responsabilidade
valorAtual |= valor; // usando operador bitwise OR
this.classList.add('active');
const originalClass = this.getAttribute('data-original-class');
if (originalClass) {
this.className = `badge badge-clickable active ${originalClass}`;
}
}
responsabilidadesInput.value = valorAtual;
});
});