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 = `${i}`;
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 = `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}
`;
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 = `${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('
Data inválida
');
}
} 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('
Data inválida
');
}
}
});
// 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 = `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('
Data inválida
');
}
}
});
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 = `