feat: melhorias na interface e estrutura do frontend

This commit is contained in:
andersonid
2025-04-02 14:14:37 -03:00
parent 9d17c66c46
commit 54261e455c
9 changed files with 1009 additions and 274 deletions

146
static/js/forms.js Normal file
View 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
View 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();
}
});
});
});