// 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); }); }); });