feat: melhorias na interface e estrutura do frontend
This commit is contained in:
146
static/js/forms.js
Normal file
146
static/js/forms.js
Normal file
@@ -0,0 +1,146 @@
|
||||
// Validação de CPF
|
||||
function validarCPF(cpf) {
|
||||
cpf = cpf.replace(/[^\d]/g, '');
|
||||
|
||||
if (cpf.length !== 11) return false;
|
||||
|
||||
// Verifica se todos os dígitos são iguais
|
||||
if (/^(\d)\1{10}$/.test(cpf)) return false;
|
||||
|
||||
// Validação do primeiro dígito verificador
|
||||
let soma = 0;
|
||||
for (let i = 0; i < 9; i++) {
|
||||
soma += parseInt(cpf.charAt(i)) * (10 - i);
|
||||
}
|
||||
let resto = 11 - (soma % 11);
|
||||
let dv1 = resto > 9 ? 0 : resto;
|
||||
|
||||
if (dv1 !== parseInt(cpf.charAt(9))) return false;
|
||||
|
||||
// Validação do segundo dígito verificador
|
||||
soma = 0;
|
||||
for (let i = 0; i < 10; i++) {
|
||||
soma += parseInt(cpf.charAt(i)) * (11 - i);
|
||||
}
|
||||
resto = 11 - (soma % 11);
|
||||
let dv2 = resto > 9 ? 0 : resto;
|
||||
|
||||
if (dv2 !== parseInt(cpf.charAt(10))) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Validação de email
|
||||
function validarEmail(email) {
|
||||
const re = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||
return re.test(email);
|
||||
}
|
||||
|
||||
// Validação de telefone
|
||||
function validarTelefone(telefone) {
|
||||
telefone = telefone.replace(/[^\d]/g, '');
|
||||
return telefone.length >= 10 && telefone.length <= 11;
|
||||
}
|
||||
|
||||
// Inicialização dos formulários
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Validação personalizada para CPF
|
||||
const cpfInputs = document.querySelectorAll('input[name="cpf"]');
|
||||
cpfInputs.forEach(input => {
|
||||
input.addEventListener('blur', function() {
|
||||
const cpf = this.value;
|
||||
if (!validarCPF(cpf)) {
|
||||
this.setCustomValidity('CPF inválido');
|
||||
this.classList.add('is-invalid');
|
||||
} else {
|
||||
this.setCustomValidity('');
|
||||
this.classList.remove('is-invalid');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Validação personalizada para email
|
||||
const emailInputs = document.querySelectorAll('input[type="email"]');
|
||||
emailInputs.forEach(input => {
|
||||
input.addEventListener('blur', function() {
|
||||
const email = this.value;
|
||||
if (!validarEmail(email)) {
|
||||
this.setCustomValidity('Email inválido');
|
||||
this.classList.add('is-invalid');
|
||||
} else {
|
||||
this.setCustomValidity('');
|
||||
this.classList.remove('is-invalid');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Validação personalizada para telefone
|
||||
const phoneInputs = document.querySelectorAll('input[name="telefone"]');
|
||||
phoneInputs.forEach(input => {
|
||||
input.addEventListener('blur', function() {
|
||||
const telefone = this.value;
|
||||
if (!validarTelefone(telefone)) {
|
||||
this.setCustomValidity('Telefone inválido');
|
||||
this.classList.add('is-invalid');
|
||||
} else {
|
||||
this.setCustomValidity('');
|
||||
this.classList.remove('is-invalid');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Validação de campos monetários
|
||||
const moneyInputs = document.querySelectorAll('input[type="number"][step="0.01"]');
|
||||
moneyInputs.forEach(input => {
|
||||
input.addEventListener('blur', function() {
|
||||
const value = parseFloat(this.value);
|
||||
if (isNaN(value) || value < 0) {
|
||||
this.setCustomValidity('Valor inválido');
|
||||
this.classList.add('is-invalid');
|
||||
} else {
|
||||
this.setCustomValidity('');
|
||||
this.classList.remove('is-invalid');
|
||||
this.value = value.toFixed(2);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Validação de datas
|
||||
const dateInputs = document.querySelectorAll('input[type="date"]');
|
||||
dateInputs.forEach(input => {
|
||||
input.addEventListener('change', function() {
|
||||
const date = new Date(this.value);
|
||||
const today = new Date();
|
||||
|
||||
if (this.hasAttribute('min')) {
|
||||
const minDate = new Date(this.getAttribute('min'));
|
||||
if (date < minDate) {
|
||||
this.setCustomValidity(`A data não pode ser anterior a ${minDate.toLocaleDateString()}`);
|
||||
this.classList.add('is-invalid');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (this.hasAttribute('max')) {
|
||||
const maxDate = new Date(this.getAttribute('max'));
|
||||
if (date > maxDate) {
|
||||
this.setCustomValidity(`A data não pode ser posterior a ${maxDate.toLocaleDateString()}`);
|
||||
this.classList.add('is-invalid');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
this.setCustomValidity('');
|
||||
this.classList.remove('is-invalid');
|
||||
});
|
||||
});
|
||||
|
||||
// Feedback visual para campos obrigatórios
|
||||
const requiredInputs = document.querySelectorAll('input[required], select[required], textarea[required]');
|
||||
requiredInputs.forEach(input => {
|
||||
const label = input.previousElementSibling;
|
||||
if (label && label.tagName === 'LABEL') {
|
||||
label.innerHTML += ' <span class="text-danger">*</span>';
|
||||
}
|
||||
});
|
||||
});
|
||||
145
static/js/main.js
Normal file
145
static/js/main.js
Normal file
@@ -0,0 +1,145 @@
|
||||
// Máscaras para campos de formulário
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// 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{3})(\d{3})(\d{2})/, "$1.$2.$3-$4");
|
||||
e.target.value = value;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Máscara para telefone
|
||||
const phoneInputs = document.querySelectorAll('input[name="telefone"]');
|
||||
phoneInputs.forEach(input => {
|
||||
input.addEventListener('input', function(e) {
|
||||
let value = e.target.value.replace(/\D/g, '');
|
||||
if (value.length <= 11) {
|
||||
if (value.length === 11) {
|
||||
value = value.replace(/(\d{2})(\d{5})(\d{4})/, "($1) $2-$3");
|
||||
} else {
|
||||
value = value.replace(/(\d{2})(\d{4})(\d{4})/, "($1) $2-$3");
|
||||
}
|
||||
e.target.value = value;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Formatação de valores monetários
|
||||
const moneyInputs = document.querySelectorAll('input[type="number"][step="0.01"]');
|
||||
moneyInputs.forEach(input => {
|
||||
input.addEventListener('blur', function(e) {
|
||||
const value = parseFloat(e.target.value);
|
||||
if (!isNaN(value)) {
|
||||
e.target.value = value.toFixed(2);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Funções para tabelas
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const tables = document.querySelectorAll('.table');
|
||||
tables.forEach(table => {
|
||||
// Ordenação
|
||||
const headers = table.querySelectorAll('th[data-sort]');
|
||||
headers.forEach(header => {
|
||||
header.addEventListener('click', function() {
|
||||
const column = this.dataset.sort;
|
||||
const asc = this.classList.toggle('sort-asc');
|
||||
const tbody = table.querySelector('tbody');
|
||||
const rows = Array.from(tbody.querySelectorAll('tr'));
|
||||
|
||||
rows.sort((a, b) => {
|
||||
const aVal = a.querySelector(`td[data-${column}]`).dataset[column];
|
||||
const bVal = b.querySelector(`td[data-${column}]`).dataset[column];
|
||||
return asc ? aVal.localeCompare(bVal) : bVal.localeCompare(aVal);
|
||||
});
|
||||
|
||||
rows.forEach(row => tbody.appendChild(row));
|
||||
});
|
||||
});
|
||||
|
||||
// Filtro
|
||||
const filterInput = document.querySelector(`#filter-${table.id}`);
|
||||
if (filterInput) {
|
||||
filterInput.addEventListener('input', function() {
|
||||
const searchTerm = this.value.toLowerCase();
|
||||
const rows = table.querySelectorAll('tbody tr');
|
||||
|
||||
rows.forEach(row => {
|
||||
const text = row.textContent.toLowerCase();
|
||||
row.style.display = text.includes(searchTerm) ? '' : 'none';
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Validação de formulários
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const forms = document.querySelectorAll('form');
|
||||
forms.forEach(form => {
|
||||
form.addEventListener('submit', function(e) {
|
||||
if (!form.checkValidity()) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
// Destacar campos inválidos
|
||||
const invalidInputs = form.querySelectorAll(':invalid');
|
||||
invalidInputs.forEach(input => {
|
||||
input.classList.add('is-invalid');
|
||||
|
||||
// Adicionar mensagem de erro
|
||||
const feedback = document.createElement('div');
|
||||
feedback.className = 'invalid-feedback';
|
||||
feedback.textContent = input.validationMessage;
|
||||
input.parentNode.appendChild(feedback);
|
||||
});
|
||||
}
|
||||
|
||||
form.classList.add('was-validated');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Animações e feedback visual
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// Animar cards ao carregar
|
||||
const cards = document.querySelectorAll('.card');
|
||||
cards.forEach((card, index) => {
|
||||
card.style.opacity = '0';
|
||||
card.style.transform = 'translateY(20px)';
|
||||
setTimeout(() => {
|
||||
card.style.transition = 'all 0.3s ease';
|
||||
card.style.opacity = '1';
|
||||
card.style.transform = 'translateY(0)';
|
||||
}, index * 100);
|
||||
});
|
||||
|
||||
// Feedback visual para ações
|
||||
const actionButtons = document.querySelectorAll('[data-action]');
|
||||
actionButtons.forEach(button => {
|
||||
button.addEventListener('click', function() {
|
||||
button.classList.add('animate__animated', 'animate__pulse');
|
||||
setTimeout(() => {
|
||||
button.classList.remove('animate__animated', 'animate__pulse');
|
||||
}, 1000);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// Confirmações de ações
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const deleteButtons = document.querySelectorAll('[data-confirm]');
|
||||
deleteButtons.forEach(button => {
|
||||
button.addEventListener('click', function(e) {
|
||||
if (!confirm(this.dataset.confirm)) {
|
||||
e.preventDefault();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user