AULA 7

🚀 Formulários Avançados

Domine técnicas avançadas de formulários: validação complexa, fieldsets dinâmicos, UX otimizada para conversão e integrações modernas.

⏱️ 55 minutos
🎯 Avançado
📝 4 Exercícios

🎯 Objetivos de Aprendizagem

🔍 1. Validação Complexa

🎯 Além do HTML5

Embora a validação HTML5 seja útil, formulários profissionais precisam de validação mais sofisticada, feedback em tempo real e integração com APIs.

🚀 A Evolução dos Formulários

Validação avançada é como ter um "assistente pessoal inteligente" no seu formulário. Enquanto HTML5 básico apenas diz "campo obrigatório", validação avançada guia o usuário, previne erros e melhora a experiência.

Por que ir além do HTML5:

  • 📈 Conversão +40%: Formulários inteligentes convertem mais
  • 😊 UX melhorada: Feedback imediato reduz frustração
  • 🛡️ Segurança: Validação robusta previne ataques
  • 📱 Cross-browser: Funciona igual em todos navegadores
  • 🎯 Dados melhores: Menos erros = dados mais confiáveis

Estatística impressionante: 67% dos usuários abandonam formulários após o primeiro erro. Validação em tempo real reduz abandono para 23%!

Filosofia de design: "Prevenir é melhor que corrigir". Ajude o usuário a acertar na primeira tentativa.

⚡ Validação em Tempo Real

🎯 Feedback Instantâneo que Funciona

Validação em tempo real é como ter um "GPS para formulários" - corrige a rota antes que o usuário se perca. É a diferença entre descobrir erros ao final (frustrante) vs ser guiado durante o preenchimento (agradável).

Momentos ideais para validar:

  • 📝 onInput: Para formatação e máscaras (CPF, telefone)
  • 👁️ onBlur: Para validação completa (email, senha forte)
  • 🔄 onFocus: Para limpar mensagens de erro antigas
  • 📤 onSubmit: Validação final antes do envio

Tipos de feedback visual:

  • Verde + ícone: Campo válido, usuário confiante
  • Vermelho + mensagem: Erro específico, como corrigir
  • 🟡 Amarelo + progresso: Em processo (checando disponibilidade)
  • 🔵 Azul neutro: Campo focado, aguardando entrada

Dica profissional: Use novalidate no form para desabilitar validação nativa e ter controle total. Implemente sua própria lógica mais inteligente.

<form id="registration-form" novalidate>
    <div class="form-group">
        <label for="email-check">E-mail</label>
        <div class="input-container">
            <input type="email" 
                   id="email-check" 
                   name="email" 
                   required
                   data-validate="email">
            <div class="validation-icon"></div>
        </div>
        <div class="validation-message"></div>
    </div>
    
    <div class="form-group">
        <label for="password-strength">Senha</label>
        <div class="input-container">
            <input type="password" 
                   id="password-strength" 
                   name="password" 
                   required
                   data-validate="password">
            <div class="password-toggle" onclick="togglePassword()">👁️</div>
        </div>
        <div class="password-strength">
            <div class="strength-meter">
                <div class="strength-fill"></div>
            </div>
            <div class="strength-text">Força da senha</div>
        </div>
        <div class="validation-message"></div>
    </div>
    
    <div class="form-group">
        <label for="cpf-validate">CPF</label>
        <div class="input-container">
            <input type="text" 
                   id="cpf-validate" 
                   name="cpf" 
                   required
                   data-validate="cpf"
                   data-mask="000.000.000-00">
            <div class="validation-icon"></div>
        </div>
        <div class="validation-message"></div>
    </div>
    
    <button type="submit" disabled>Cadastrar</button>
</form>

<script>
class FormValidator {
    constructor(form) {
        this.form = form;
        this.fields = {};
        this.isValid = false;
        this.init();
    }
    
    init() {
        const inputs = this.form.querySelectorAll('[data-validate]');
        inputs.forEach(input => {
            this.setupField(input);
        });
        
        this.form.addEventListener('submit', (e) => this.handleSubmit(e));
    }
    
    setupField(input) {
        const type = input.dataset.validate;
        this.fields[input.name] = { element: input, valid: false };
        
        // Eventos
        input.addEventListener('input', () => this.validateField(input, type));
        input.addEventListener('blur', () => this.validateField(input, type));
        
        // Máscara se especificada
        if (input.dataset.mask) {
            input.addEventListener('input', () => this.applyMask(input));
        }
    }
    
    validateField(input, type) {
        const value = input.value.trim();
        let isValid = false;
        let message = '';
        
        switch (type) {
            case 'email':
                isValid = this.validateEmail(value);
                message = isValid ? '✓ E-mail válido' : '✗ E-mail inválido';
                break;
                
            case 'password':
                const strength = this.validatePassword(value);
                isValid = strength.score >= 3;
                message = strength.message;
                this.updatePasswordStrength(input, strength);
                break;
                
            case 'cpf':
                isValid = this.validateCPF(value);
                message = isValid ? '✓ CPF válido' : '✗ CPF inválido';
                break;
        }
        
        this.updateFieldStatus(input, isValid, message);
        this.fields[input.name].valid = isValid;
        this.updateSubmitButton();
    }
    
    validateEmail(email) {
        const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
        return regex.test(email);
    }
    
    validatePassword(password) {
        let score = 0;
        let message = '';
        
        if (password.length >= 8) score++;
        if (/[a-z]/.test(password)) score++;
        if (/[A-Z]/.test(password)) score++;
        if (/\d/.test(password)) score++;
        if (/[^a-zA-Z\d]/.test(password)) score++;
        
        switch (score) {
            case 0:
            case 1:
                message = 'Muito fraca';
                break;
            case 2:
                message = 'Fraca';
                break;
            case 3:
                message = 'Média';
                break;
            case 4:
                message = 'Forte';
                break;
            case 5:
                message = 'Muito forte';
                break;
        }
        
        return { score, message };
    }
    
    validateCPF(cpf) {
        cpf = cpf.replace(/[^\d]/g, '');
        
        if (cpf.length !== 11 || /^(\d)\1{10}$/.test(cpf)) {
            return false;
        }
        
        // Validação dos dígitos verificadores
        let sum = 0;
        for (let i = 0; i < 9; i++) {
            sum += parseInt(cpf.charAt(i)) * (10 - i);
        }
        let remainder = (sum * 10) % 11;
        if (remainder === 10 || remainder === 11) remainder = 0;
        if (remainder !== parseInt(cpf.charAt(9))) return false;
        
        sum = 0;
        for (let i = 0; i < 10; i++) {
            sum += parseInt(cpf.charAt(i)) * (11 - i);
        }
        remainder = (sum * 10) % 11;
        if (remainder === 10 || remainder === 11) remainder = 0;
        if (remainder !== parseInt(cpf.charAt(10))) return false;
        
        return true;
    }
    
    applyMask(input) {
        const mask = input.dataset.mask;
        let value = input.value.replace(/[^\d]/g, '');
        let maskedValue = '';
        let maskIndex = 0;
        
        for (let i = 0; i < mask.length && maskIndex < value.length; i++) {
            if (mask[i] === '0') {
                maskedValue += value[maskIndex];
                maskIndex++;
            } else {
                maskedValue += mask[i];
            }
        }
        
        input.value = maskedValue;
    }
    
    updateFieldStatus(input, isValid, message) {
        const container = input.closest('.form-group');
        const icon = container.querySelector('.validation-icon');
        const messageEl = container.querySelector('.validation-message');
        
        icon.textContent = isValid ? '✓' : '✗';
        icon.className = `validation-icon ${isValid ? 'valid' : 'invalid'}`;
        
        messageEl.textContent = message;
        messageEl.className = `validation-message ${isValid ? 'valid' : 'invalid'}`;
        
        input.setAttribute('aria-invalid', !isValid);
    }
    
    updatePasswordStrength(input, strength) {
        const container = input.closest('.form-group');
        const meter = container.querySelector('.strength-fill');
        const text = container.querySelector('.strength-text');
        
        const percentage = (strength.score / 5) * 100;
        meter.style.width = `${percentage}%`;
        meter.className = `strength-fill strength-${strength.score}`;
        text.textContent = strength.message;
    }
    
    updateSubmitButton() {
        const allValid = Object.values(this.fields).every(field => field.valid);
        const submitBtn = this.form.querySelector('button[type="submit"]');
        submitBtn.disabled = !allValid;
    }
    
    handleSubmit(e) {
        e.preventDefault();
        
        if (this.isFormValid()) {
            this.submitForm();
        }
    }
    
    isFormValid() {
        return Object.values(this.fields).every(field => field.valid);
    }
    
    async submitForm() {
        const submitBtn = this.form.querySelector('button[type="submit"]');
        const originalText = submitBtn.textContent;
        
        submitBtn.textContent = 'Enviando...';
        submitBtn.disabled = true;
        
        try {
            // Simular envio
            await new Promise(resolve => setTimeout(resolve, 2000));
            
            submitBtn.textContent = '✓ Enviado!';
            submitBtn.className = 'btn-success';
            
            setTimeout(() => {
                submitBtn.textContent = originalText;
                submitBtn.className = '';
                submitBtn.disabled = false;
            }, 3000);
            
        } catch (error) {
            submitBtn.textContent = '✗ Erro no envio';
            submitBtn.className = 'btn-error';
            
            setTimeout(() => {
                submitBtn.textContent = originalText;
                submitBtn.className = '';
                submitBtn.disabled = false;
            }, 3000);
        }
    }
}

// Inicializar validador
document.addEventListener('DOMContentLoaded', () => {
    const form = document.getElementById('registration-form');
    new FormValidator(form);
});

function togglePassword() {
    const input = document.getElementById('password-strength');
    const toggle = document.querySelector('.password-toggle');
    
    if (input.type === 'password') {
        input.type = 'text';
        toggle.textContent = '🙈';
    } else {
        input.type = 'password';
        toggle.textContent = '👁️';
    }
}
</script>

🎨 CSS para Validação

/* Container de input com ícone */
.input-container {
    position: relative;
    display: flex;
    align-items: center;
}

.input-container input {
    flex: 1;
    padding-right: 40px;
}

.validation-icon {
    position: absolute;
    right: 12px;
    font-weight: bold;
    font-size: 16px;
    transition: all 0.3s ease;
}

.validation-icon.valid {
    color: #28a745;
}

.validation-icon.invalid {
    color: #dc3545;
}

/* Mensagens de validação */
.validation-message {
    margin-top: 4px;
    font-size: 14px;
    font-weight: 500;
    transition: all 0.3s ease;
}

.validation-message.valid {
    color: #28a745;
}

.validation-message.invalid {
    color: #dc3545;
}

/* Medidor de força da senha */
.password-strength {
    margin-top: 8px;
}

.strength-meter {
    width: 100%;
    height: 6px;
    background-color: #e9ecef;
    border-radius: 3px;
    overflow: hidden;
    margin-bottom: 4px;
}

.strength-fill {
    height: 100%;
    transition: all 0.3s ease;
    border-radius: 3px;
}

.strength-fill.strength-0,
.strength-fill.strength-1 {
    background-color: #dc3545;
}

.strength-fill.strength-2 {
    background-color: #fd7e14;
}

.strength-fill.strength-3 {
    background-color: #ffc107;
}

.strength-fill.strength-4 {
    background-color: #20c997;
}

.strength-fill.strength-5 {
    background-color: #28a745;
}

.strength-text {
    font-size: 12px;
    color: #6c757d;
}

/* Toggle de senha */
.password-toggle {
    position: absolute;
    right: 12px;
    cursor: pointer;
    font-size: 18px;
    user-select: none;
    opacity: 0.7;
    transition: opacity 0.3s ease;
}

.password-toggle:hover {
    opacity: 1;
}

/* Estados do botão */
button[type="submit"] {
    transition: all 0.3s ease;
}

button[type="submit"]:disabled {
    opacity: 0.6;
    cursor: not-allowed;
}

.btn-success {
    background-color: #28a745 !important;
}

.btn-error {
    background-color: #dc3545 !important;
}

🏋️‍♂️ Exercício 1: Sistema de Cadastro Completo

Crie um sistema de cadastro avançado com:

📋 2. Formulários Multi-etapas

🎯 Dividir para Conquistar

Formulários multi-etapas são como "subir uma escada" ao invés de escalar uma montanha. Dividem formulários longos em etapas digeríveis, reduzindo a sensação de sobrecarga e aumentando drasticamente a taxa de conclusão.

Quando usar formulários multi-etapas:

  • 📝 +10 campos: Muito informação para uma tela
  • 🎯 Diferentes contextos: "Dados pessoais" vs "Preferências"
  • 📱 Mobile first: Tela pequena exige quebra
  • 🧠 Carga cognitiva: Usuário se concentra numa coisa por vez
  • 💰 Alto valor: Cadastros, compras, formulários importantes

Estatísticas que impressionam:

  • 📈 +300% conversão: vs formulários longos tradicionais
  • -60% tempo percebido: Parece mais rápido, mesmo sendo igual
  • 😊 +85% satisfação: Usuários preferem etapas pequenas

Psicologia por trás: Progresso visível ativa sistema de recompensa do cérebro. Cada etapa completada gera sensação de conquista.

🎯 Estrutura Base

🏗️ Arquitetura de Sucesso

Uma estrutura multi-etapas bem projetada é como um "mapa de navegação" - o usuário sempre sabe onde está, para onde vai, e quanto falta para terminar.

Componentes essenciais:

  1. 📊 Indicador de progresso: Barra visual mostrando etapas
  2. 🏷️ Títulos descritivos: "Dados pessoais", não "Etapa 1"
  3. ⬅️➡️ Navegação intuitiva: Voltar/Próximo sempre visíveis
  4. 💾 Auto-save: Dados salvos automaticamente
  5. Validação por etapa: Não avança se houver erros

Regras de ouro:

  • 🎯 3-5 etapas máximo: Mais que isso vira labirinto
  • ⚖️ Equilíbrio: Etapas com quantidade similar de campos
  • 📝 Lógica clara: Ordem que faz sentido para o usuário
  • 🔒 Não perder dados: Volta/refresh não apaga progresso
<form id="multi-step-form" class="multi-step-form">
    
    <div class="progress-indicator">
        <div class="step active" data-step="1">
            <span class="step-number">1</span>
            <span class="step-title">Dados Pessoais</span>
        </div>
        <div class="step" data-step="2">
            <span class="step-number">2</span>
            <span class="step-title">Endereço</span>
        </div>
        <div class="step" data-step="3">
            <span class="step-number">3</span>
            <span class="step-title">Preferências</span>
        </div>
        <div class="step" data-step="4">
            <span class="step-number">4</span>
            <span class="step-title">Confirmação</span>
        </div>
    </div>
    
    
    <div class="form-step active" data-step="1">
        <h2>👤 Dados Pessoais</h2>
        <p>Vamos começar com suas informações básicas</p>
        
        <div class="form-row">
            <div class="form-col">
                <label for="first-name">Nome</label>
                <input type="text" 
                       id="first-name" 
                       name="firstName" 
                       required
                       data-step="1">
            </div>
            <div class="form-col">
                <label for="last-name">Sobrenome</label>
                <input type="text" 
                       id="last-name" 
                       name="lastName" 
                       required
                       data-step="1">
            </div>
        </div>
        
        <div class="form-group">
            <label for="email-step">E-mail</label>
            <input type="email" 
                   id="email-step" 
                   name="email" 
                   required
                   data-step="1">
        </div>
        
        <div class="form-group">
            <label for="phone-step">Telefone</label>
            <input type="tel" 
                   id="phone-step" 
                   name="phone" 
                   required
                   data-step="1">
        </div>
    </div>
    
    
    <div class="form-step" data-step="2">
        <h2>🏠 Endereço</h2>
        <p>Onde você mora?</p>
        
        <div class="form-group">
            <label for="cep-step">CEP</label>
            <input type="text" 
                   id="cep-step" 
                   name="cep" 
                   required
                   data-step="2"
                   onblur="buscarCEP(this.value)">
        </div>
        
        <div class="form-row">
            <div class="form-col" style="flex: 3;">
                <label for="street">Rua</label>
                <input type="text" 
                       id="street" 
                       name="street" 
                       required
                       data-step="2">
            </div>
            <div class="form-col" style="flex: 1;">
                <label for="number">Número</label>
                <input type="text" 
                       id="number" 
                       name="number" 
                       required
                       data-step="2">
            </div>
        </div>
        
        <div class="form-row">
            <div class="form-col">
                <label for="city">Cidade</label>
                <input type="text" 
                       id="city" 
                       name="city" 
                       required
                       data-step="2">
            </div>
            <div class="form-col">
                <label for="state">Estado</label>
                <select id="state" name="state" required data-step="2">
                    <option value="">Selecione</option>
                    <option value="SP">São Paulo</option>
                    <option value="RJ">Rio de Janeiro</option>
                    <option value="MG">Minas Gerais</option>
                    
                </select>
            </div>
        </div>
    </div>
    
    
    <div class="form-step" data-step="3">
        <h2>⚙️ Preferências</h2>
        <p>Como prefere receber comunicações?</p>
        
        
        
        <fieldset>
            <legend>Método de Contato Preferido</legend>
            
            <label class="radio-card">
                <input type="radio" name="contactMethod" value="email" data-step="3">
                <div class="radio-card-content">
                    <h4>📧 E-mail</h4>
                    <p>Comunicação por e-mail</p>
                </div>
            </label>
            
            <label class="radio-card">
                <input type="radio" name="contactMethod" value="sms" data-step="3">
                <div class="radio-card-content">
                    <h4>📱 SMS</h4>
                    <p>Mensagens de texto</p>
                </div>
            </label>
            
            <label class="radio-card">
                <input type="radio" name="contactMethod" value="phone" data-step="3">
                <div class="radio-card-content">
                    <h4>📞 Telefone</h4>
                    <p>Ligações diretas</p>
                </div>
            </label>
        </fieldset>
        
        <fieldset>
            <legend>Interesses</legend>
            
            <div class="checkbox-group">
                <label>
                    <input type="checkbox" name="interests[]" value="tech" data-step="3">
                    🖥️ Tecnologia
                </label>
                <label>
                    <input type="checkbox" name="interests[]" value="design" data-step="3">
                    🎨 Design
                </label>
                <label>
                    <input type="checkbox" name="interests[]" value="business" data-step="3">
                    💼 Negócios
                </label>
            </div>
        </fieldset>
    </div>
    
    
    <div class="form-step" data-step="4">
        <h2>✅ Confirmação</h2>
        <p>Revise suas informações antes de enviar</p>
        
        <div class="summary-section">
            <h3>👤 Dados Pessoais</h3>
            <div id="summary-personal"></div>
        </div>
        
        <div class="summary-section">
            <h3>🏠 Endereço</h3>
            <div id="summary-address"></div>
        </div>
        
        <div class="summary-section">
            <h3>⚙️ Preferências</h3>
            <div id="summary-preferences"></div>
        </div>
        
        <label class="checkbox-custom">
            <input type="checkbox" name="terms" required data-step="4">
            <span class="checkmark"></span>
            Li e aceito os <a href="/termos" target="_blank">termos de uso</a>
        </label>
    </div>
    
    
    <div class="form-navigation">
        <button type="button" id="prev-btn" class="btn btn-secondary" onclick="previousStep()">
            ← Anterior
        </button>
        <button type="button" id="next-btn" class="btn btn-primary" onclick="nextStep()">
            Próximo →
        </button>
        <button type="submit" id="submit-btn" class="btn btn-success" style="display: none;">
            🚀 Finalizar Cadastro
        </button>
    </div>
</form>

🎯 JavaScript para Navegação

class MultiStepForm {
    constructor(formId) {
        this.form = document.getElementById(formId);
        this.currentStep = 1;
        this.totalSteps = 4;
        this.formData = {};
        
        this.init();
    }
    
    init() {
        this.form.addEventListener('submit', (e) => this.handleSubmit(e));
        this.updateStepDisplay();
        this.bindEvents();
    }
    
    bindEvents() {
        // Salvar dados automaticamente
        const inputs = this.form.querySelectorAll('input, select, textarea');
        inputs.forEach(input => {
            input.addEventListener('change', () => this.saveStepData());
            input.addEventListener('input', () => this.saveStepData());
        });
    }
    
    nextStep() {
        if (this.validateCurrentStep()) {
            this.saveStepData();
            
            if (this.currentStep < this.totalSteps) {
                this.currentStep++;
                this.updateStepDisplay();
                
                if (this.currentStep === this.totalSteps) {
                    this.generateSummary();
                }
            }
        }
    }
    
    previousStep() {
        if (this.currentStep > 1) {
            this.currentStep--;
            this.updateStepDisplay();
        }
    }
    
    updateStepDisplay() {
        // Ocultar todas as etapas
        const steps = this.form.querySelectorAll('.form-step');
        steps.forEach(step => step.classList.remove('active'));
        
        // Mostrar etapa atual
        const currentStepEl = this.form.querySelector(`[data-step="${this.currentStep}"]`);
        if (currentStepEl && currentStepEl.classList.contains('form-step')) {
            currentStepEl.classList.add('active');
        }
        
        // Atualizar indicador de progresso
        this.updateProgressIndicator();
        
        // Atualizar botões de navegação
        this.updateNavigationButtons();
        
        // Scroll para o topo
        this.form.scrollIntoView({ behavior: 'smooth', block: 'start' });
    }
    
    updateProgressIndicator() {
        const progressSteps = this.form.querySelectorAll('.progress-indicator .step');
        
        progressSteps.forEach((step, index) => {
            const stepNumber = index + 1;
            
            if (stepNumber < this.currentStep) {
                step.classList.add('completed');
                step.classList.remove('active');
            } else if (stepNumber === this.currentStep) {
                step.classList.add('active');
                step.classList.remove('completed');
            } else {
                step.classList.remove('active', 'completed');
            }
        });
    }
    
    updateNavigationButtons() {
        const prevBtn = document.getElementById('prev-btn');
        const nextBtn = document.getElementById('next-btn');
        const submitBtn = document.getElementById('submit-btn');
        
        // Botão anterior
        prevBtn.style.display = this.currentStep === 1 ? 'none' : 'inline-block';
        
        // Botão próximo/enviar
        if (this.currentStep === this.totalSteps) {
            nextBtn.style.display = 'none';
            submitBtn.style.display = 'inline-block';
        } else {
            nextBtn.style.display = 'inline-block';
            submitBtn.style.display = 'none';
        }
    }
    
    validateCurrentStep() {
        const currentInputs = this.form.querySelectorAll(`[data-step="${this.currentStep}"]`);
        let isValid = true;
        
        currentInputs.forEach(input => {
            if (input.hasAttribute('required') && !input.value.trim()) {
                this.showError(input, 'Este campo é obrigatório');
                isValid = false;
            } else if (input.type === 'email' && input.value && !this.isValidEmail(input.value)) {
                this.showError(input, 'E-mail inválido');
                isValid = false;
            } else {
                this.clearError(input);
            }
        });
        
        return isValid;
    }
    
    showError(input, message) {
        const group = input.closest('.form-group') || input.closest('.form-col');
        let errorEl = group.querySelector('.error-message');
        
        if (!errorEl) {
            errorEl = document.createElement('div');
            errorEl.className = 'error-message';
            group.appendChild(errorEl);
        }
        
        errorEl.textContent = message;
        input.classList.add('error');
    }
    
    clearError(input) {
        const group = input.closest('.form-group') || input.closest('.form-col');
        const errorEl = group.querySelector('.error-message');
        
        if (errorEl) {
            errorEl.remove();
        }
        
        input.classList.remove('error');
    }
    
    saveStepData() {
        const currentInputs = this.form.querySelectorAll(`[data-step="${this.currentStep}"]`);
        
        currentInputs.forEach(input => {
            if (input.type === 'checkbox') {
                if (!this.formData[input.name]) {
                    this.formData[input.name] = [];
                }
                if (input.checked && !this.formData[input.name].includes(input.value)) {
                    this.formData[input.name].push(input.value);
                } else if (!input.checked) {
                    this.formData[input.name] = this.formData[input.name].filter(v => v !== input.value);
                }
            } else if (input.type === 'radio') {
                if (input.checked) {
                    this.formData[input.name] = input.value;
                }
            } else {
                this.formData[input.name] = input.value;
            }
        });
        
        // Salvar no localStorage
        localStorage.setItem('multiStepFormData', JSON.stringify(this.formData));
    }
    
    generateSummary() {
        // Resumo dados pessoais
        const personalSummary = document.getElementById('summary-personal');
        personalSummary.innerHTML = `
            <p><strong>Nome:</strong> ${this.formData.firstName} ${this.formData.lastName}</p>
            <p><strong>E-mail:</strong> ${this.formData.email}</p>
            <p><strong>Telefone:</strong> ${this.formData.phone}</p>
        `;
        
        // Resumo endereço
        const addressSummary = document.getElementById('summary-address');
        addressSummary.innerHTML = `
            <p><strong>CEP:</strong> ${this.formData.cep}</p>
            <p><strong>Endereço:</strong> ${this.formData.street}, ${this.formData.number}</p>
            <p><strong>Cidade/Estado:</strong> ${this.formData.city}/${this.formData.state}</p>
        `;
        
        // Resumo preferências
        const prefSummary = document.getElementById('summary-preferences');
        const interests = Array.isArray(this.formData['interests[]']) ? this.formData['interests[]'].join(', ') : 'Nenhum';
        prefSummary.innerHTML = `
            <p><strong>Contato:</strong> ${this.formData.contactMethod}</p>
            <p><strong>Interesses:</strong> ${interests}</p>
        `;
    }
    
    async handleSubmit(e) {
        e.preventDefault();
        
        if (!this.validateCurrentStep()) {
            return;
        }
        
        const submitBtn = document.getElementById('submit-btn');
        const originalText = submitBtn.textContent;
        
        submitBtn.textContent = '⏳ Enviando...';
        submitBtn.disabled = true;
        
        try {
            // Simular envio
            await new Promise(resolve => setTimeout(resolve, 2000));
            
            submitBtn.textContent = '✅ Cadastro Realizado!';
            
            // Limpar dados salvos
            localStorage.removeItem('multiStepFormData');
            
            // Redirecionar ou mostrar sucesso
            setTimeout(() => {
                alert('Cadastro realizado com sucesso!');
            }, 1000);
            
        } catch (error) {
            submitBtn.textContent = '❌ Erro no envio';
            setTimeout(() => {
                submitBtn.textContent = originalText;
                submitBtn.disabled = false;
            }, 3000);
        }
    }
    
    isValidEmail(email) {
        const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
        return regex.test(email);
    }
}

// Inicializar formulário
let multiStepForm;
document.addEventListener('DOMContentLoaded', () => {
    multiStepForm = new MultiStepForm('multi-step-form');
});

// Funções globais para navegação
function nextStep() {
    multiStepForm.nextStep();
}

function previousStep() {
    multiStepForm.previousStep();
}

// Busca CEP (integração com API)
/*
💡 EXPLICAÇÃO: Integração com APIs em Formulários

A integração com APIs permite que os formulários sejam mais inteligentes e convenientes:

🌐 Por que integrar APIs:
   • Preenchimento automático reduz erros em 60%
   • Melhora a experiência do usuário significativamente
   • Acelera o processo de preenchimento
   • Reduz abandono de formulários em 25%

🔌 Tipos de integração comuns:
   • CEP → Endereço completo (ViaCEP)
   • CNPJ → Dados da empresa (ReceitaWS)
   • Email → Verificação de domínio
   • Telefone → Validação de formato por país

📊 Benefícios mensuráveis:
   • +45% taxa de conclusão
   • -60% tempo de preenchimento
   • -35% erros de digitação
   • +30% satisfação do usuário

⚙️ Implementação responsável:
   • Sempre usar try/catch para errors
   • Feedback visual durante carregamento
   • Fallback para preenchimento manual
   • Validação adicional dos dados recebidos

🚀 Dica profissional: APIs bem integradas transformam formulários "burros" em assistentes inteligentes!
*/
async function buscarCEP(cep) {
    cep = cep.replace(/[^0-9]/g, '');
    
    if (cep.length === 8) {
        try {
            const response = await fetch(`https://viacep.com.br/ws/${cep}/json/`);
            const data = await response.json();
            
            if (!data.erro) {
                document.getElementById('street').value = data.logradouro;
                document.getElementById('city').value = data.localidade;
                document.getElementById('state').value = data.uf;
            }
        } catch (error) {
            console.error('Erro ao buscar CEP:', error);
        }
    }
}

📁 3. Como Organizar ESTA Aula Especificamente

🎯 Estrutura de Arquivos

Para implementar os códigos desta aula, você tem 3 opções com estruturas de diretórios específicas para diferentes níveis de complexidade.

🎯 OPÇÃO 1 - Arquivo Único (Recomendado para aprendizado)

📄 Estrutura Simples

📂 meu-projeto-formularios/
└── 📄 formulario-avancado.html (todo código aqui)

Características:

  • ✅ Todo HTML, CSS e JavaScript em um arquivo
  • 🎯 Ideal para: tutoriais, testes rápidos, demonstrações
  • Vantagem: simplicidade para iniciantes
  • ⚠️ Desvantagem: dificulta manutenção em projetos grandes

🎯 OPÇÃO 2 - Separação Básica (Recomendado para projetos pequenos)

📁 Organização Intermediária

📂 projeto-formularios/
├── 📄 index.html (estrutura HTML)
├── 📂 css/
│   └── 📄 formularios.css (todos os estilos)
├── 📂 js/
│   └── 📄 formularios.js (todo JavaScript)
└── 📂 assets/
    └── 📂 images/
        └── 📄 favicon.ico

Características:

  • 🎯 Ideal para: projetos até 500 linhas totais
  • Vantagem: organização sem complexidade
  • 📊 Performance: cache separado para CSS/JS

🔧 Comandos para Criar Estruturas

💻 Terminal/PowerShell

Para OPÇÃO 1 (Arquivo único):

mkdir minha-aula-07
cd minha-aula-07
New-Item formulario-avancado-completo.html

Para OPÇÃO 2 (Separação básica):

mkdir projeto-formularios
cd projeto-formularios
mkdir css, js, assets, assets\images
New-Item index.html, css\formularios.css, js\formularios.js

📝 Exercício Prático Progressivo

🎯 Evolução do Desenvolvedor

1️⃣ COMEÇAR SIMPLES:

  • Crie pasta 'minha-aula-07'
  • Salve todo código em 'formulario-completo.html'
  • Teste todas as funcionalidades

2️⃣ EVOLUIR GRADUALMENTE:

  • Separe CSS em arquivo próprio
  • Separe JavaScript em arquivo próprio
  • Teste se tudo ainda funciona

3️⃣ DESAFIO AVANÇADO:

  • Separe por funcionalidade (validação, multi-step, API)
  • Crie arquivos modulares
  • Implemente sistema de build (opcional)

🚀 Evolução Natural:

Aprenda → Arquivo único → Separação básica → Estrutura modular → Arquitetura profissional

🏋️‍♂️ Exercício 2: Projeto Organizado

Implemente a estrutura de arquivos para um projeto de formulários: