// Função global para validar CPF
function validarCPF(campo) {
// Máscara do CPF
function mascaraCPF(valor) {
return valor
.replace(/\D/g, "")
.replace(/(\d{3})(\d)/, "$1.$2")
.replace(/(\d{3})(\d)/, "$1.$2")
.replace(/(\d{3})(\d{1,2})$/, "$1-$2");
}
// Aplica máscara a cada digito
let valor = campo.value.replace(/\D/g, "");
campo.value = mascaraCPF(valor);
const cpf = valor;
// Remove tooltips antigas
let oldTooltip = document.getElementById('cpf-tooltip');
if (oldTooltip) oldTooltip.remove();
// Ícone de status
let statusIcon = document.getElementById('cpf-status-icon');
if (!statusIcon) {
statusIcon = document.createElement('span');
statusIcon.id = 'cpf-status-icon';
statusIcon.style.display = 'none';
statusIcon.style.cursor = 'pointer';
statusIcon.style.position = 'absolute';
statusIcon.style.top = '70%';
statusIcon.style.right = '-2px';
statusIcon.style.transform = 'translateY(-50%)';
statusIcon.style.fontSize = '1.2em';
statusIcon.style.zIndex = '2';
statusIcon.innerHTML = '⚠️'; // Ícone padrão
let wrapper = campo.closest('.cpf-wrapper');
if (wrapper) {
wrapper.style.position = 'relative';
wrapper.appendChild(statusIcon);
} else if (campo.parentNode) {
campo.parentNode.style.position = 'relative';
campo.parentNode.appendChild(statusIcon);
}
}
// Se o campo estiver vazio, limpa tudo e retorna
if (!campo.value.trim()) {
campo.style.color = '';
statusIcon.style.display = 'none';
campo.setCustomValidity("");
return;
}
// Validação do CPF
let invalido = false;
if (cpf.length !== 11 || /^(\d)\1+$/.test(cpf)) {
invalido = true;
} else {
let soma = 0;
for (let i = 0; i < 9; i++) soma += parseInt(cpf.charAt(i)) * (10 - i);
let digito1 = 11 - (soma % 11);
if (digito1 > 9) digito1 = 0;
if (parseInt(cpf.charAt(9)) !== digito1) invalido = true;
soma = 0;
for (let i = 0; i < 10; i++) soma += parseInt(cpf.charAt(i)) * (11 - i);
let digito2 = 11 - (soma % 11);
if (digito2 > 9) digito2 = 0;
if (parseInt(cpf.charAt(10)) !== digito2) invalido = true;
}
// Função para mostrar tooltip customizada
function showTooltip(text, color, link) {
let tooltip = document.createElement('div');
tooltip.id = 'cpf-tooltip';
tooltip.style.position = 'absolute';
tooltip.style.top = '100%';
tooltip.style.right = '0';
tooltip.style.background = color;
tooltip.style.color = '#fff';
tooltip.style.padding = '8px 14px';
tooltip.style.borderRadius = '6px';
tooltip.style.fontSize = '0.95em';
tooltip.style.boxShadow = '0 2px 8px rgba(0,0,0,0.15)';
tooltip.style.whiteSpace = 'nowrap';
tooltip.style.zIndex = '10';
tooltip.style.transition = 'opacity 0.2s';
tooltip.style.opacity = '0';
// Permite interação
tooltip.style.pointerEvents = 'auto';
if (link) {
tooltip.innerHTML = `${text} clique aqui`;
} else {
tooltip.innerHTML = `${text}`;
}
let wrapper = campo.closest('.cpf-wrapper');
if (wrapper) wrapper.appendChild(tooltip);
else campo.parentNode.appendChild(tooltip);
setTimeout(() => { tooltip.style.opacity = '1'; }, 10);
// Mantém tooltip enquanto mouse está sobre ícone ou tooltip
function hideTooltip(e) {
// Só remove se mouse não está sobre o ícone nem sobre a tooltip
if (!statusIcon.matches(':hover') && !tooltip.matches(':hover')) {
tooltip.remove();
}
}
statusIcon.onmouseleave = () => setTimeout(hideTooltip, 100);
tooltip.onmouseleave = () => setTimeout(hideTooltip, 100);
}
// Atualiza ícone e cor
if (invalido) {
campo.style.color = 'red';
statusIcon.style.display = 'inline-block';
statusIcon.style.color = 'red';
statusIcon.innerHTML = '❌';
statusIcon.onmouseenter = () => showTooltip('CPF inválido', '#c00');
} else {
campo.style.color = 'green';
statusIcon.style.display = 'inline-block';
statusIcon.style.color = 'green';
statusIcon.innerHTML = '✔️';
statusIcon.onmouseenter = () => showTooltip('CPF válido', '#2e7d32');
}
// Verifica duplicidade no backend
fetch(`/cidadaos/buscar-codcid/?cpf=${cpf}`)
.then(response => response.json())
.then(data => {
// Verifica se está editando o próprio cadastro
let codcidAtual = window.location.pathname.split('/')[2];
if (data.existe && data.codcid && data.codcid != codcidAtual) {
campo.setCustomValidity("");
statusIcon.style.display = 'inline-block';
statusIcon.style.color = '#FFD700'; // amarelo
statusIcon.innerHTML = '⚠️';
statusIcon.onmouseenter = () => showTooltip('CPF já cadastrado, ', '#FFD700', `/cidadaos/${data.codcid}/`);
}
})
.catch(() => {
campo.setCustomValidity("Erro ao verificar CPF.");
statusIcon.style.display = 'inline-block';
statusIcon.style.color = 'red';
statusIcon.innerHTML = '❌';
statusIcon.onmouseenter = () => showTooltip('Erro ao verificar CPF', '#c00');
});
}
// Função do botão de fechar a sidebar
const closeSidebarListener = () => {
const sidebarCloseBtn = document.querySelector(".sidebar-close");
const sidebar = document.querySelector(".sidebar");
sidebarCloseBtn.addEventListener("click", () => {
if (sidebar.offsetWidth > 100) {
// Só quando o sidebar estiver aberto, > 60px
sidebar.classList.add("closed");
sidebar.classList.add("forceclose"); // Força o fechamento quando está em telas menores
setTimeout(() => sidebar.classList.remove("forceclose"), 250);
return;
}
return sidebar.classList.remove("closed");
});
};
closeSidebarListener();
// Seleciona o item de menu correspondente ao link atual
const menuItemListener = () => {
const activeUrl = window.location.pathname.split("/")[2]; // Obtém o caminho do URL atual. O primeiro depois do '/'
const menu = document.querySelector(".nav-items"); // Seleciona todos os itens de menu
const menuItems = menu.querySelectorAll("li a"); // Seleciona todos os itens de menu
menuItems.forEach((menuItem) => {
const menuUrl = new URL(menuItem.href).pathname.split("/")[2]; // Obtém o caminho do URL do item de menu
// Se for link simbólico "#", ignora
if (menuItem.href.includes("/#") || menuItem.href.substr(-1) === "#") return false;
if (menuUrl === activeUrl) {
menuItem.parentElement.classList.add("active"); // Adiciona classe 'active' ao item de menu correspondente
// Seleciona o item pai correspondente
const parentMenu = menuItem.closest("li.submenu");
if (parentMenu) {
parentMenu.classList.add("active");
parentMenu.querySelector("input[type=checkbox]").checked = true;
}
}
});
// Adiciona a animação ao menu, somente depois de carregar
// Isso evita que o menu fique animando o movimento de abrir toda vez que a página carrega
setTimeout(() => menu.classList.add("animated"), 100);
};
menuItemListener();
// Seleciona todos os checkboxes
const selectAllCheckboxes = (e) => {
const checkAll = document.querySelector("input[type=checkbox]#selectAll");
checkAll?.addEventListener("click", (e) => {
const checkboxes = checkAll.closest(".table-section").querySelectorAll("input[type=checkbox]");
checkboxes.forEach((checkbox) => (checkbox.checked = checkAll.checked));
});
};
selectAllCheckboxes();
// Initialize when DOM is loaded
document.addEventListener("DOMContentLoaded", () => {
const textareaField = document.querySelectorAll(".textarea-input");
// Função que ajusta a altura de um campo ao conteúdo
function ajustarAltura(elem) {
elem.style.height = "auto";
elem.style.height = elem.scrollHeight + "px";
}
textareaField.forEach(field => {
// Se houver valor pré-carregado, já ajusta a altura agora
ajustarAltura(field);
// Sempre que o usuário digitar (ou o valor mudar), ajusta novamente
field.addEventListener("input", function() {
ajustarAltura(this);
});
});
});