1
2
Implementando Validações
Exemplos práticos de diferentes tipos de validação
Implementando Validações
Validações Básicas
Campo Obrigatório
function validarCampoObrigatorio(valor, nomeCampo) {
if (!valor || valor.trim() === '') {
return `${nomeCampo} é obrigatório`;
}
return '';
}
function validarEmail(email) {
const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
if (!email) {
return 'Email é obrigatório';
}
if (!regex.test(email)) {
return 'Email inválido';
}
return '';
}
Telefone
function validarTelefone(telefone) {
const regex = /^\(?[1-9]{2}\)? ?(?:[2-8]|9[1-9])[0-9]{3}\-?[0-9]{4}$/;
if (!telefone) {
return ''; // Campo opcional
}
const numeroLimpo = telefone.replace(/\D/g, '');
if (!regex.test(numeroLimpo)) {
return 'Telefone inválido';
}
return '';
}
CPF
function validarCPF(cpf) {
const cpfLimpo = cpf.replace(/\D/g, '');
if (cpfLimpo.length !== 11) {
return 'CPF deve ter 11 dígitos';
}
// Validação de CPF (algoritmo completo)
if (/^(\d)\1{10}$/.test(cpfLimpo)) {
return 'CPF inválido';
}
let soma = 0;
for (let i = 0; i < 9; i++) {
soma += parseInt(cpfLimpo.charAt(i)) * (10 - i);
}
let resto = 11 - (soma % 11);
if (resto === 10 || resto === 11) resto = 0;
if (resto !== parseInt(cpfLimpo.charAt(9))) {
return 'CPF inválido';
}
soma = 0;
for (let i = 0; i < 10; i++) {
soma += parseInt(cpfLimpo.charAt(i)) * (11 - i);
}
resto = 11 - (soma % 11);
if (resto === 10 || resto === 11) resto = 0;
if (resto !== parseInt(cpfLimpo.charAt(10))) {
return 'CPF inválido';
}
return '';
}
Sistema de Validação Completo
function FormularioValidado() {
const [formData, setFormData] = useState({
nome: '',
email: '',
cpf: '',
telefone: '',
senha: '',
confirmarSenha: ''
});
const [erros, setErros] = useState({});
const [tocados, setTocados] = useState({});
// Regras de validação
const validacoes = {
nome: (valor) => {
if (!valor) return 'Nome é obrigatório';
if (valor.length < 3) return 'Nome deve ter pelo menos 3 caracteres';
if (!/^[a-zA-Zà-ú\s]+$/.test(valor)) return 'Nome deve conter apenas letras';
return '';
},
email: (valor) => {
if (!valor) return 'Email é obrigatório';
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(valor)) return 'Email inválido';
return '';
},
cpf: (valor) => validarCPF(valor),
telefone: (valor) => validarTelefone(valor),
senha: (valor) => {
if (!valor) return 'Senha é obrigatória';
if (valor.length < 8) return 'Senha deve ter pelo menos 8 caracteres';
if (!/(?=.*[a-z])/.test(valor)) return 'Senha deve ter pelo menos uma letra minúscula';
if (!/(?=.*[A-Z])/.test(valor)) return 'Senha deve ter pelo menos uma letra maiúscula';
if (!/(?=.*\d)/.test(valor)) return 'Senha deve ter pelo menos um número';
if (!/(?=.*[@$!%*?&])/.test(valor)) return 'Senha deve ter pelo menos um caractere especial';
return '';
},
confirmarSenha: (valor, todosValores) => {
if (!valor) return 'Confirmação de senha é obrigatória';
if (valor !== todosValores.senha) return 'Senhas não coincidem';
return '';
}
};
// Validar campo individual
const validarCampo = (nome, valor) => {
const validacao = validacoes[nome];
if (validacao) {
return validacao(valor, formData);
}
return '';
};
// Validar todos os campos
const validarFormulario = () => {
const novosErros = {};
Object.keys(formData).forEach(campo => {
const erro = validarCampo(campo, formData[campo]);
if (erro) {
novosErros[campo] = erro;
}
});
setErros(novosErros);
return Object.keys(novosErros).length === 0;
};
// Handler de mudança com validação
const handleChange = (e) => {
const { name, value } = e.target;
setFormData(prev => ({
...prev,
[name]: value
}));
// Validação em tempo real apenas se o campo já foi tocado
if (tocados[name]) {
const erro = validarCampo(name, value);
setErros(prev => ({
...prev,
[name]: erro
}));
}
};
// Handler de blur
const handleBlur = (e) => {
const { name, value } = e.target;
setTocados(prev => ({
...prev,
[name]: true
}));
const erro = validarCampo(name, value);
setErros(prev => ({
...prev,
[name]: erro
}));
};
// Submit do formulário
const handleSubmit = (e) => {
e.preventDefault();
// Marca todos os campos como tocados
const todosTocados = {};
Object.keys(formData).forEach(campo => {
todosTocados[campo] = true;
});
setTocados(todosTocados);
if (validarFormulario()) {
console.log('Formulário válido:', formData);
// Enviar dados
}
};
return (
<form onSubmit={handleSubmit}>
<div>
<input
type="text"
name="nome"
value={formData.nome}
onChange={handleChange}
onBlur={handleBlur}
placeholder="Nome completo"
className={erros.nome && tocados.nome ? 'erro' : ''}
/>
{erros.nome && tocados.nome && (
<span className="mensagem-erro">{erros.nome}</span>
)}
</div>
<div>
<input
type="email"
name="email"
value={formData.email}
onChange={handleChange}
onBlur={handleBlur}
placeholder="Email"
className={erros.email && tocados.email ? 'erro' : ''}
/>
{erros.email && tocados.email && (
<span className="mensagem-erro">{erros.email}</span>
)}
</div>
<div>
<input
type="text"
name="cpf"
value={formData.cpf}
onChange={handleChange}
onBlur={handleBlur}
placeholder="CPF"
className={erros.cpf && tocados.cpf ? 'erro' : ''}
/>
{erros.cpf && tocados.cpf && (
<span className="mensagem-erro">{erros.cpf}</span>
)}
</div>
<div>
<input
type="password"
name="senha"
value={formData.senha}
onChange={handleChange}
onBlur={handleBlur}
placeholder="Senha"
className={erros.senha && tocados.senha ? 'erro' : ''}
/>
{erros.senha && tocados.senha && (
<span className="mensagem-erro">{erros.senha}</span>
)}
</div>
<button type="submit">Enviar</button>
</form>
);
}
Hook Customizado para Validação
function useFormValidation(valoresIniciais, validacoes) {
const [valores, setValores] = useState(valoresIniciais);
const [erros, setErros] = useState({});
const [tocados, setTocados] = useState({});
const [enviando, setEnviando] = useState(false);
const validarCampo = (nome, valor) => {
const validacao = validacoes[nome];
if (validacao) {
return validacao(valor, valores);
}
return '';
};
const handleChange = (e) => {
const { name, value } = e.target;
setValores(prev => ({
...prev,
[name]: value
}));
if (tocados[name]) {
const erro = validarCampo(name, value);
setErros(prev => ({
...prev,
[name]: erro
}));
}
};
const handleBlur = (e) => {
const { name } = e.target;
setTocados(prev => ({
...prev,
[name]: true
}));
const erro = validarCampo(name, valores[name]);
setErros(prev => ({
...prev,
[name]: erro
}));
};
const validarTudo = () => {
const novosErros = {};
Object.keys(valores).forEach(campo => {
const erro = validarCampo(campo, valores[campo]);
if (erro) {
novosErros[campo] = erro;
}
});
setErros(novosErros);
return Object.keys(novosErros).length === 0;
};
const handleSubmit = (callback) => (e) => {
e.preventDefault();
const todosTocados = {};
Object.keys(valores).forEach(campo => {
todosTocados[campo] = true;
});
setTocados(todosTocados);
if (validarTudo()) {
setEnviando(true);
callback(valores);
}
};
return {
valores,
erros,
tocados,
enviando,
handleChange,
handleBlur,
handleSubmit,
setEnviando
};
}
// Usando o hook
function MeuFormulario() {
const validacoes = {
email: (valor) => {
if (!valor) return 'Email obrigatório';
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(valor)) return 'Email inválido';
return '';
},
senha: (valor) => {
if (!valor) return 'Senha obrigatória';
if (valor.length < 6) return 'Senha muito curta';
return '';
}
};
const {
valores,
erros,
tocados,
enviando,
handleChange,
handleBlur,
handleSubmit
} = useFormValidation(
{ email: '', senha: '' },
validacoes
);
const enviarFormulario = async (dados) => {
// Simular envio
await new Promise(resolve => setTimeout(resolve, 2000));
console.log('Dados enviados:', dados);
};
return (
<form onSubmit={handleSubmit(enviarFormulario)}>
<input
type="email"
name="email"
value={valores.email}
onChange={handleChange}
onBlur={handleBlur}
/>
{erros.email && tocados.email && <span>{erros.email}</span>}
<input
type="password"
name="senha"
value={valores.senha}
onChange={handleChange}
onBlur={handleBlur}
/>
{erros.senha && tocados.senha && <span>{erros.senha}</span>}
<button type="submit" disabled={enviando}>
{enviando ? 'Enviando...' : 'Enviar'}
</button>
</form>
);
}
Indicadores Visuais de Validação
function CampoComFeedback({ tipo, nome, valor, erro, tocado, ...props }) {
const getStatusClass = () => {
if (!tocado) return '';
return erro ? 'campo-erro' : 'campo-valido';
};
const getStatusIcon = () => {
if (!tocado) return null;
return erro ? '❌' : '✅';
};
return (
<div className="campo-wrapper">
<div className="campo-input">
<input
type={tipo}
name={nome}
value={valor}
className={getStatusClass()}
{...props}
/>
<span className="status-icon">{getStatusIcon()}</span>
</div>
{erro && tocado && (
<span className="mensagem-erro">{erro}</span>
)}
</div>
);
}
CSS para Feedback Visual
.campo-erro {
border: 2px solid #f44336;
background-color: #ffebee;
}
.campo-valido {
border: 2px solid #4caf50;
background-color: #e8f5e9;
}
.mensagem-erro {
color: #f44336;
font-size: 12px;
margin-top: 4px;
display: block;
}
.status-icon {
position: absolute;
right: 10px;
top: 50%;
transform: translateY(-50%);
}
.campo-wrapper {
margin-bottom: 16px;
}
.campo-input {
position: relative;
}
3
Exercício: Sistema de Validação Completo
Crie um formulário com validação avançada e feedback visual
Activity
Exercício: Sistema de Validação Completo
Objetivo
Criar um formulário de pagamento com validação completa, incluindo validação de cartão de crédito e feedback visual.
Requisitos
-
Campos do Formulário
- Nome do titular (texto)
- Número do cartão (formatado)
- Data de validade (MM/AA)
- CVV (3 ou 4 dígitos)
- CEP (busca automática de endereço)
- Endereço completo
- Valor do pagamento
-
Validações
- Nome: mínimo 3 caracteres, apenas letras
- Cartão: algoritmo de Luhn
- Data: não expirada
- CVV: 3 dígitos (4 para Amex)
- CEP: formato válido e busca API
- Valor: maior que zero
-
Recursos Visuais
- Identificação da bandeira do cartão
- Máscara de formatação em tempo real
- Indicadores de campo válido/inválido
- Animações de transição
- Resumo do pagamento
Template Inicial
import React, { useState, useEffect } from 'react';
function FormularioPagamento() {
const [formData, setFormData] = useState({
nomeCartao: '',
numeroCartao: '',
dataValidade: '',
cvv: '',
cep: '',
endereco: '',
numero: '',
complemento: '',
cidade: '',
estado: '',
valor: ''
});
const [erros, setErros] = useState({});
const [tocados, setTocados] = useState({});
const [bandeira, setBandeira] = useState('');
const [processando, setProcessando] = useState(false);
// Seu código aqui
return (
<div>
{/* Implemente o formulário aqui */}
</div>
);
}
export default FormularioPagamento;
Funções Úteis
Algoritmo de Luhn (Validação de Cartão)
function validarLuhn(numero) {
const digitos = numero.replace(/\D/g, '');
let soma = 0;
let isSegundo = false;
for (let i = digitos.length - 1; i >= 0; i--) {
let digito = parseInt(digitos[i]);
if (isSegundo) {
digito *= 2;
if (digito > 9) {
digito -= 9;
}
}
soma += digito;
isSegundo = !isSegundo;
}
return soma % 10 === 0;
}
Identificar Bandeira
function identificarBandeira(numero) {
const padroes = {
visa: /^4/,
mastercard: /^5[1-5]/,
amex: /^3[47]/,
discover: /^6(?:011|5)/,
diners: /^3(?:0[0-5]|[68])/,
jcb: /^(?:2131|1800|35)/
};
for (const [bandeira, padrao] of Object.entries(padroes)) {
if (padrao.test(numero)) {
return bandeira;
}
}
return '';
}
Solução
Clique para ver a solução completa
import React, { useState, useEffect } from 'react';
function FormularioPagamento() {
const [formData, setFormData] = useState({
nomeCartao: '',
numeroCartao: '',
dataValidade: '',
cvv: '',
cep: '',
endereco: '',
numero: '',
complemento: '',
cidade: '',
estado: '',
valor: ''
});
const [erros, setErros] = useState({});
const [tocados, setTocados] = useState({});
const [bandeira, setBandeira] = useState('');
const [processando, setProcessando] = useState(false);
const [buscandoCep, setBuscandoCep] = useState(false);
const [pagamentoSucesso, setPagamentoSucesso] = useState(false);
// Validação Luhn
const validarLuhn = (numero) => {
const digitos = numero.replace(/\D/g, '');
let soma = 0;
let isSegundo = false;
for (let i = digitos.length - 1; i >= 0; i--) {
let digito = parseInt(digitos[i]);
if (isSegundo) {
digito *= 2;
if (digito > 9) {
digito -= 9;
}
}
soma += digito;
isSegundo = !isSegundo;
}
return soma % 10 === 0;
};
// Identificar bandeira
const identificarBandeira = (numero) => {
const padroes = {
visa: /^4/,
mastercard: /^5[1-5]/,
amex: /^3[47]/,
discover: /^6(?:011|5)/,
diners: /^3(?:0[0-5]|[68])/,
jcb: /^(?:2131|1800|35)/
};
for (const [bandeira, padrao] of Object.entries(padroes)) {
if (padrao.test(numero)) {
return bandeira;
}
}
return '';
};
// Formatar número do cartão
const formatarNumeroCartao = (valor) => {
const numero = valor.replace(/\D/g, '');
const partes = [];
if (bandeira === 'amex') {
// Amex: 4-6-5
if (numero.length > 0) partes.push(numero.substring(0, 4));
if (numero.length > 4) partes.push(numero.substring(4, 10));
if (numero.length > 10) partes.push(numero.substring(10, 15));
} else {
// Outros: 4-4-4-4
for (let i = 0; i < numero.length; i += 4) {
partes.push(numero.substring(i, i + 4));
}
}
return partes.join(' ').substring(0, 23);
};
// Formatar data de validade
const formatarDataValidade = (valor) => {
const numero = valor.replace(/\D/g, '');
if (numero.length >= 2) {
return numero.substring(0, 2) + '/' + numero.substring(2, 4);
}
return numero;
};
// Formatar CEP
const formatarCEP = (valor) => {
const numero = valor.replace(/\D/g, '');
if (numero.length > 5) {
return numero.substring(0, 5) + '-' + numero.substring(5, 8);
}
return numero;
};
// Formatar valor
const formatarValor = (valor) => {
const numero = valor.replace(/\D/g, '');
const valorFormatado = (parseInt(numero) / 100).toFixed(2);
return `R$ ${valorFormatado.replace('.', ',')}`;
};
// Buscar CEP
const buscarCEP = async (cep) => {
const cepLimpo = cep.replace(/\D/g, '');
if (cepLimpo.length === 8) {
setBuscandoCep(true);
try {
const response = await fetch(`https://viacep.com.br/ws/${cepLimpo}/json/`);
const data = await response.json();
if (!data.erro) {
setFormData(prev => ({
...prev,
endereco: data.logradouro,
cidade: data.localidade,
estado: data.uf
}));
} else {
setErros(prev => ({ ...prev, cep: 'CEP não encontrado' }));
}
} catch (error) {
setErros(prev => ({ ...prev, cep: 'Erro ao buscar CEP' }));
}
setBuscandoCep(false);
}
};
// Validações
const validacoes = {
nomeCartao: (valor) => {
if (!valor) return 'Nome do titular obrigatório';
if (valor.length < 3) return 'Nome muito curto';
if (!/^[a-zA-Zà-ú\s]+$/.test(valor)) return 'Nome inválido';
return '';
},
numeroCartao: (valor) => {
const numero = valor.replace(/\D/g, '');
if (!numero) return 'Número do cartão obrigatório';
if (numero.length < 13) return 'Número incompleto';
if (!validarLuhn(numero)) return 'Número de cartão inválido';
return '';
},
dataValidade: (valor) => {
if (!valor) return 'Data de validade obrigatória';
if (valor.length !== 5) return 'Data incompleta';
const [mes, ano] = valor.split('/');
const mesNum = parseInt(mes);
const anoNum = parseInt('20' + ano);
const hoje = new Date();
const dataCartao = new Date(anoNum, mesNum - 1);
if (mesNum < 1 || mesNum > 12) return 'Mês inválido';
if (dataCartao < hoje) return 'Cartão expirado';
return '';
},
cvv: (valor) => {
if (!valor) return 'CVV obrigatório';
const tamanhoEsperado = bandeira === 'amex' ? 4 : 3;
if (valor.length !== tamanhoEsperado) {
return `CVV deve ter ${tamanhoEsperado} dígitos`;
}
return '';
},
cep: (valor) => {
const cepLimpo = valor.replace(/\D/g, '');
if (!cepLimpo) return 'CEP obrigatório';
if (cepLimpo.length !== 8) return 'CEP incompleto';
return '';
},
numero: (valor) => {
if (!valor) return 'Número obrigatório';
return '';
},
valor: (valor) => {
const numero = parseFloat(valor.replace(/[^\d,]/g, '').replace(',', '.'));
if (!numero || numero <= 0) return 'Valor deve ser maior que zero';
if (numero > 10000) return 'Valor máximo: R$ 10.000,00';
return '';
}
};
// Handlers
const handleChange = (e) => {
const { name, value } = e.target;
let valorFormatado = value;
// Aplicar formatações
switch (name) {
case 'numeroCartao':
valorFormatado = formatarNumeroCartao(value);
const novaBandeira = identificarBandeira(value);
setBandeira(novaBandeira);
break;
case 'dataValidade':
valorFormatado = formatarDataValidade(value);
break;
case 'cep':
valorFormatado = formatarCEP(value);
if (value.replace(/\D/g, '').length === 8) {
buscarCEP(value);
}
break;
case 'cvv':
valorFormatado = value.replace(/\D/g, '').substring(0, 4);
break;
case 'valor':
if (value.length > formData.valor.length) {
valorFormatado = formatarValor(value);
}
break;
}
setFormData(prev => ({
...prev,
[name]: valorFormatado
}));
// Validar se já foi tocado
if (tocados[name]) {
const erro = validacoes[name] ? validacoes[name](valorFormatado) : '';
setErros(prev => ({
...prev,
[name]: erro
}));
}
};
const handleBlur = (e) => {
const { name, value } = e.target;
setTocados(prev => ({
...prev,
[name]: true
}));
const erro = validacoes[name] ? validacoes[name](value) : '';
setErros(prev => ({
...prev,
[name]: erro
}));
};
const validarFormulario = () => {
const novosErros = {};
Object.keys(validacoes).forEach(campo => {
const erro = validacoes[campo](formData[campo]);
if (erro) {
novosErros[campo] = erro;
}
});
setErros(novosErros);
return Object.keys(novosErros).length === 0;
};
const handleSubmit = async (e) => {
e.preventDefault();
// Marcar todos como tocados
const todosTocados = {};
Object.keys(validacoes).forEach(campo => {
todosTocados[campo] = true;
});
setTocados(todosTocados);
if (validarFormulario()) {
setProcessando(true);
// Simular processamento
await new Promise(resolve => setTimeout(resolve, 2000));
setPagamentoSucesso(true);
setProcessando(false);
}
};
const getInputClass = (campo) => {
if (!tocados[campo]) return '';
return erros[campo] ? 'campo-erro' : 'campo-valido';
};
if (pagamentoSucesso) {
return (
<div style={{
textAlign: 'center',
padding: '40px',
backgroundColor: '#e8f5e9',
borderRadius: '8px'
}}>
<h2 style={{ color: '#4caf50' }}>✅ Pagamento Realizado com Sucesso!</h2>
<p>Valor: {formData.valor}</p>
<p>Cartão: **** **** **** {formData.numeroCartao.slice(-4)}</p>
<button onClick={() => {
setPagamentoSucesso(false);
setFormData({
nomeCartao: '',
numeroCartao: '',
dataValidade: '',
cvv: '',
cep: '',
endereco: '',
numero: '',
complemento: '',
cidade: '',
estado: '',
valor: ''
});
setTocados({});
setErros({});
}}>
Novo Pagamento
</button>
</div>
);
}
return (
<form onSubmit={handleSubmit} style={{ maxWidth: '500px', margin: '0 auto' }}>
<h1>Pagamento</h1>
<fieldset>
<legend>Dados do Cartão</legend>
<div className="campo-grupo">
<label>Nome do Titular *</label>
<input
type="text"
name="nomeCartao"
value={formData.nomeCartao}
onChange={handleChange}
onBlur={handleBlur}
className={getInputClass('nomeCartao')}
placeholder="NOME COMO NO CARTÃO"
style={{ textTransform: 'uppercase' }}
/>
{erros.nomeCartao && tocados.nomeCartao && (
<span className="erro">{erros.nomeCartao}</span>
)}
</div>
<div className="campo-grupo">
<label>
Número do Cartão *
{bandeira && <span className="bandeira">{bandeira.toUpperCase()}</span>}
</label>
<input
type="text"
name="numeroCartao"
value={formData.numeroCartao}
onChange={handleChange}
onBlur={handleBlur}
className={getInputClass('numeroCartao')}
placeholder="0000 0000 0000 0000"
maxLength="23"
/>
{erros.numeroCartao && tocados.numeroCartao && (
<span className="erro">{erros.numeroCartao}</span>
)}
</div>
<div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '10px' }}>
<div className="campo-grupo">
<label>Validade *</label>
<input
type="text"
name="dataValidade"
value={formData.dataValidade}
onChange={handleChange}
onBlur={handleBlur}
className={getInputClass('dataValidade')}
placeholder="MM/AA"
maxLength="5"
/>
{erros.dataValidade && tocados.dataValidade && (
<span className="erro">{erros.dataValidade}</span>
)}
</div>
<div className="campo-grupo">
<label>CVV *</label>
<input
type="text"
name="cvv"
value={formData.cvv}
onChange={handleChange}
onBlur={handleBlur}
className={getInputClass('cvv')}
placeholder={bandeira === 'amex' ? '0000' : '000'}
maxLength={bandeira === 'amex' ? '4' : '3'}
/>
{erros.cvv && tocados.cvv && (
<span className="erro">{erros.cvv}</span>
)}
</div>
</div>
</fieldset>
<fieldset>
<legend>Endereço de Cobrança</legend>
<div className="campo-grupo">
<label>CEP *</label>
<input
type="text"
name="cep"
value={formData.cep}
onChange={handleChange}
onBlur={handleBlur}
className={getInputClass('cep')}
placeholder="00000-000"
maxLength="9"
disabled={buscandoCep}
/>
{buscandoCep && <span>Buscando endereço...</span>}
{erros.cep && tocados.cep && (
<span className="erro">{erros.cep}</span>
)}
</div>
<div className="campo-grupo">
<label>Endereço</label>
<input
type="text"
name="endereco"
value={formData.endereco}
onChange={handleChange}
readOnly={buscandoCep}
/>
</div>
<div style={{ display: 'grid', gridTemplateColumns: '1fr 2fr', gap: '10px' }}>
<div className="campo-grupo">
<label>Número *</label>
<input
type="text"
name="numero"
value={formData.numero}
onChange={handleChange}
onBlur={handleBlur}
className={getInputClass('numero')}
/>
{erros.numero && tocados.numero && (
<span className="erro">{erros.numero}</span>
)}
</div>
<div className="campo-grupo">
<label>Complemento</label>
<input
type="text"
name="complemento"
value={formData.complemento}
onChange={handleChange}
/>
</div>
</div>
<div style={{ display: 'grid', gridTemplateColumns: '2fr 1fr', gap: '10px' }}>
<div className="campo-grupo">
<label>Cidade</label>
<input
type="text"
name="cidade"
value={formData.cidade}
onChange={handleChange}
readOnly={buscandoCep}
/>
</div>
<div className="campo-grupo">
<label>Estado</label>
<input
type="text"
name="estado"
value={formData.estado}
onChange={handleChange}
readOnly={buscandoCep}
maxLength="2"
/>
</div>
</div>
</fieldset>
<fieldset>
<legend>Valor do Pagamento</legend>
<div className="campo-grupo">
<label>Valor *</label>
<input
type="text"
name="valor"
value={formData.valor}
onChange={handleChange}
onBlur={handleBlur}
className={getInputClass('valor')}
placeholder="R$ 0,00"
style={{ fontSize: '24px', fontWeight: 'bold' }}
/>
{erros.valor && tocados.valor && (
<span className="erro">{erros.valor}</span>
)}
</div>
</fieldset>
<button
type="submit"
disabled={processando}
style={{
width: '100%',
padding: '15px',
fontSize: '18px',
backgroundColor: processando ? '#ccc' : '#4caf50',
color: 'white',
border: 'none',
borderRadius: '4px',
cursor: processando ? 'not-allowed' : 'pointer',
marginTop: '20px'
}}
>
{processando ? 'Processando...' : `Pagar ${formData.valor || 'R$ 0,00'}`}
</button>
<style jsx>{`
fieldset {
border: 1px solid #ddd;
border-radius: 4px;
padding: 20px;
margin-bottom: 20px;
}
legend {
font-weight: bold;
padding: 0 10px;
}
.campo-grupo {
margin-bottom: 15px;
}
label {
display: block;
margin-bottom: 5px;
font-weight: 500;
}
input {
width: 100%;
padding: 10px;
border: 2px solid #ddd;
border-radius: 4px;
font-size: 16px;
transition: all 0.3s;
}
input:focus {
outline: none;
border-color: #2196f3;
}
.campo-erro {
border-color: #f44336;
background-color: #ffebee;
}
.campo-valido {
border-color: #4caf50;
background-color: #e8f5e9;
}
.erro {
color: #f44336;
font-size: 12px;
margin-top: 5px;
display: block;
}
.bandeira {
background: #2196f3;
color: white;
padding: 2px 8px;
border-radius: 4px;
font-size: 12px;
margin-left: 10px;
}
`}</style>
</form>
);
}
export default FormularioPagamento;
Desafios Extras
- Parcelamento: Adicione opção de parcelamento com cálculo de juros
- Múltiplos Cartões: Permitir dividir pagamento em vários cartões
- Cartões Salvos: Simular cartões salvos para seleção rápida
- Análise de Fraude: Adicionar simulação de verificação antifraude
- Cupons de Desconto: Sistema de cupons com validação
3 content items