# 🎯 Sistema de Campos Condicionais - Documentação Completa ## 📖 Visão Geral O **Sistema de Campos Condicionais** permite controlar a visibilidade de campos em formulários baseado nos valores de outros campos, proporcionando interfaces dinâmicas e intuitivas para o usuário. ### ✨ Funcionalidades Principais - 🔄 **Visibilidade Reativa** - Campos aparecem/desaparecem automaticamente - 🧹 **Limpeza Automática** - Dados inconsistentes são removidos automaticamente - ⚡ **Performance Otimizada** - Listeners inteligentes sem overhead - 🎯 **Validação Inteligente** - Suporte a múltiplos operadores - 🔧 **Configuração Simples** - Interface declarativa intuitiva - 📱 **Responsivo** - Funciona perfeitamente em mobile e desktop ## 🚀 Instalação e Setup ### 1. Interface Atualizada (Já Implementada) A interface `TabFormField` já foi atualizada com suporte a campos condicionais: ```typescript // projects/idt_app/src/app/shared/interfaces/generic-tab-form.interface.ts export interface TabFormField { // ... campos existentes conditional?: { field: string; // Campo que controla a visibilidade value: any; // Valor que o campo deve ter para mostrar este campo operator?: 'equals' | 'not-equals' | 'in' | 'not-in'; // Operador de comparação }; } ``` ### 2. Sistema de Renderização (Já Implementado) O componente `GenericTabFormComponent` já possui: - Método `shouldShowField()` para verificar condições - Listeners automáticos para campos controladores - Atualização reativa da interface ## 📋 Configuração de Campos ### Configuração Básica ```typescript { key: 'campo_condicional', label: 'Campo Condicional', type: 'text', required: false, conditional: { field: 'campo_controlador', value: 'valor_esperado' } } ``` ### Operadores Disponíveis #### 1. **Equals (Padrão)** ```typescript conditional: { field: 'type', value: 'Individual', operator: 'equals' // ou omitir (padrão) } ``` #### 2. **Not Equals** ```typescript conditional: { field: 'status', value: 'inactive', operator: 'not-equals' } ``` #### 3. **In (Array de Valores)** ```typescript conditional: { field: 'category', value: ['premium', 'gold', 'platinum'], operator: 'in' } ``` #### 4. **Not In (Exclusão de Array)** ```typescript conditional: { field: 'type', value: ['temporary', 'suspended'], operator: 'not-in' } ``` ## 🎯 Exemplo Prático: CPF/CNPJ Condicional ### Implementação Completa ```typescript // Campo Tipo (Controlador) { key: 'type', label: 'Tipo', type: 'select', required: true, options: [ { value: 'Individual', label: 'Pessoa Física' }, { value: 'Business', label: 'Pessoa Jurídica' } ], onValueChange: (value: string, formGroup: any) => { // Limpeza automática de campos opostos if (value === 'Individual') { formGroup.get('cnpj')?.setValue(''); } else if (value === 'Business') { formGroup.get('cpf')?.setValue(''); } } }, // Campo CPF (Condicional) { key: 'cpf', label: 'CPF', type: 'text', required: false, placeholder: '000.000.000-00', mask: '000.000.000-00', conditional: { field: 'type', value: 'Individual' }, onValueChange: (value: string, formGroup: any) => { // Busca automática por CPF const cleanCpf = value?.replace(/\D/g, '') || ''; if (cleanCpf.length === 11) { this.searchPersonByDocument(value, 'cpf', formGroup); } } }, // Campo CNPJ (Condicional) { key: 'cnpj', label: 'CNPJ', type: 'text', required: false, placeholder: '00.000.000/0000-00', mask: '00.000.000/0000-00', conditional: { field: 'type', value: 'Business' }, onValueChange: (value: string, formGroup: any) => { // Busca automática por CNPJ const cleanCnpj = value?.replace(/\D/g, '') || ''; if (cleanCnpj.length === 14) { this.searchPersonByDocument(value, 'cnpj', formGroup); } } } ``` ## 🔄 Fluxo de Funcionamento ### 1. **Inicialização do Formulário** ```typescript private initForm() { // 1. Criar FormGroup com todos os campos this.form = this.fb.group(formGroup); // 2. Configurar listeners para campos condicionais this.setupConditionalFieldListeners(); } ``` ### 2. **Setup de Listeners** ```typescript private setupConditionalFieldListeners(): void { const allFields = this.getAllFieldsFromSubTabs(); const controllerFields = new Set(); // Identificar campos controladores allFields.forEach(field => { if (field.conditional?.field) { controllerFields.add(field.conditional.field); } }); // Configurar listeners para detectar mudanças controllerFields.forEach(fieldKey => { const control = this.form.get(fieldKey); if (control) { control.valueChanges.subscribe(() => { this.cdr.detectChanges(); // Atualizar interface }); } }); } ``` ### 3. **Verificação de Visibilidade** ```typescript shouldShowField(field: TabFormField): boolean { if (!field.conditional || !this.form) { return true; } const { field: conditionalField, value: conditionalValue, operator = 'equals' } = field.conditional; const currentValue = this.form.get(conditionalField)?.value; switch (operator) { case 'equals': return currentValue === conditionalValue; case 'not-equals': return currentValue !== conditionalValue; case 'in': return Array.isArray(conditionalValue) && conditionalValue.includes(currentValue); case 'not-in': return Array.isArray(conditionalValue) && !conditionalValue.includes(currentValue); default: return currentValue === conditionalValue; } } ``` ### 4. **Renderização no Template** ```html
``` ## 🎨 Exemplos de Uso ### 1. **Campo Dependente de Status** ```typescript { key: 'cancellation_reason', label: 'Motivo do Cancelamento', type: 'textarea', required: true, conditional: { field: 'status', value: 'cancelled' } } ``` ### 2. **Múltiplas Condições (In)** ```typescript { key: 'special_notes', label: 'Observações Especiais', type: 'textarea', conditional: { field: 'category', value: ['vip', 'premium', 'corporate'], operator: 'in' } } ``` ### 3. **Exclusão de Valores (Not In)** ```typescript { key: 'regular_fields', label: 'Campos Regulares', type: 'text', conditional: { field: 'user_type', value: ['admin', 'super_admin'], operator: 'not-in' } } ``` ### 4. **Campos Aninhados** ```typescript // Campo controlador principal { key: 'payment_method', label: 'Método de Pagamento', type: 'select', options: [ { value: 'credit_card', label: 'Cartão de Crédito' }, { value: 'bank_transfer', label: 'Transferência Bancária' }, { value: 'pix', label: 'PIX' } ] }, // Campo dependente 1 { key: 'card_number', label: 'Número do Cartão', type: 'text', conditional: { field: 'payment_method', value: 'credit_card' } }, // Campo dependente 2 { key: 'bank_details', label: 'Dados Bancários', type: 'textarea', conditional: { field: 'payment_method', value: 'bank_transfer' } }, // Campo dependente 3 { key: 'pix_key', label: 'Chave PIX', type: 'text', conditional: { field: 'payment_method', value: 'pix' } } ``` ## 🧪 Testes e Validação ### Cenários de Teste #### 1. **Teste Básico de Visibilidade** ```typescript describe('Conditional Fields', () => { it('should show CPF field when type is Individual', () => { // Arrange component.form.get('type')?.setValue('Individual'); // Act const field = { key: 'cpf', conditional: { field: 'type', value: 'Individual' } }; const isVisible = component.shouldShowField(field); // Assert expect(isVisible).toBe(true); }); it('should hide CNPJ field when type is Individual', () => { // Arrange component.form.get('type')?.setValue('Individual'); // Act const field = { key: 'cnpj', conditional: { field: 'type', value: 'Business' } }; const isVisible = component.shouldShowField(field); // Assert expect(isVisible).toBe(false); }); }); ``` #### 2. **Teste de Limpeza Automática** ```typescript it('should clear CNPJ when switching to Individual', () => { // Arrange component.form.get('cnpj')?.setValue('12.345.678/0001-90'); // Act component.form.get('type')?.setValue('Individual'); // Assert expect(component.form.get('cnpj')?.value).toBe(''); }); ``` ### Validação Manual 1. **Abrir formulário de fornecedores** 2. **Selecionar "Pessoa Física"** - ✅ Campo CPF deve aparecer - ✅ Campo CNPJ deve desaparecer 3. **Selecionar "Pessoa Jurídica"** - ✅ Campo CNPJ deve aparecer - ✅ Campo CPF deve desaparecer 4. **Testar limpeza automática** - Digite valor no CNPJ - Mude para "Pessoa Física" - ✅ Valor do CNPJ deve ser limpo ## 🚀 Implementação em Novos Domínios ### Passo a Passo #### 1. **Definir Campos Condicionais** ```typescript // No arquivo domain/[nome]/[nome].component.ts getFormConfig(): TabFormConfig { return { // ... configuração existente subTabs: [ { id: 'dados', label: 'Dados Básicos', fields: [ // Campo controlador { key: 'category', label: 'Categoria', type: 'select', options: [...] }, // Campo condicional { key: 'special_field', label: 'Campo Especial', type: 'text', conditional: { field: 'category', value: 'special' } } ] } ] }; } ``` #### 2. **Adicionar Lógica de Limpeza (Opcional)** ```typescript { key: 'category', label: 'Categoria', type: 'select', onValueChange: (value: string, formGroup: any) => { // Limpar campos relacionados quando necessário if (value !== 'special') { formGroup.get('special_field')?.setValue(''); } } } ``` #### 3. **Testar Implementação** - Verificar visibilidade dos campos - Testar mudanças reativas - Validar limpeza automática ## 📊 Performance e Otimizações ### Características de Performance - **Listeners Inteligentes**: Apenas campos controladores são monitorados - **Change Detection Otimizada**: Apenas quando necessário - **Cache de Configuração**: Configurações são processadas uma vez - **Lazy Evaluation**: Verificações só ocorrem durante renderização ### Métricas - ⚡ **0ms delay** - Mudanças instantâneas - 📱 **Mobile-first** - Performance otimizada para dispositivos móveis - 🔄 **Reactive** - Integração nativa com Angular Reactive Forms - 💾 **Low Memory** - Footprint mínimo de memória ## 🔧 Troubleshooting ### Problemas Comuns #### 1. **Campo não aparece/desaparece** ```typescript // ❌ Problema: Valor não corresponde exatamente conditional: { field: 'status', value: 'Active' // Mas o valor real é 'active' } // ✅ Solução: Verificar case sensitivity conditional: { field: 'status', value: 'active' } ``` #### 2. **Múltiplas condições não funcionam** ```typescript // ❌ Problema: Tentativa de múltiplas condições em um campo conditional: { field: 'type', value: 'Individual' // E também quero que dependa de 'status' } // ✅ Solução: Usar operador 'in' ou criar campo intermediário conditional: { field: 'combined_condition', value: 'Individual_Active' } ``` #### 3. **Performance lenta** ```typescript // ❌ Problema: Muitos listeners desnecessários // Criar listener para cada campo individual // ✅ Solução: Sistema otimizado já implementado // O sistema automaticamente identifica apenas campos controladores ``` ## 📚 Exemplos Avançados ### 1. **Formulário de Configuração Multi-Nivel** ```typescript { fields: [ // Nível 1: Tipo de integração { key: 'integration_type', label: 'Tipo de Integração', type: 'select', options: [ { value: 'api', label: 'API REST' }, { value: 'webhook', label: 'Webhook' }, { value: 'file', label: 'Arquivo' } ] }, // Nível 2: Configuração de API { key: 'api_endpoint', label: 'Endpoint da API', type: 'text', conditional: { field: 'integration_type', value: 'api' } }, // Nível 2: Configuração de Webhook { key: 'webhook_url', label: 'URL do Webhook', type: 'text', conditional: { field: 'integration_type', value: 'webhook' } }, // Nível 2: Configuração de Arquivo { key: 'file_format', label: 'Formato do Arquivo', type: 'select', options: [ { value: 'csv', label: 'CSV' }, { value: 'excel', label: 'Excel' }, { value: 'json', label: 'JSON' } ], conditional: { field: 'integration_type', value: 'file' } }, // Nível 3: Configuração específica de CSV { key: 'csv_delimiter', label: 'Delimitador CSV', type: 'select', options: [ { value: ',', label: 'Vírgula (,)' }, { value: ';', label: 'Ponto e vírgula (;)' }, { value: '\t', label: 'Tab' } ], conditional: { field: 'file_format', value: 'csv' } } ] } ``` ### 2. **Sistema de Permissões Condicionais** ```typescript { fields: [ { key: 'user_role', label: 'Função do Usuário', type: 'select', options: [ { value: 'viewer', label: 'Visualizador' }, { value: 'editor', label: 'Editor' }, { value: 'admin', label: 'Administrador' }, { value: 'super_admin', label: 'Super Administrador' } ] }, // Campos para editores e admins { key: 'can_edit_data', label: 'Pode Editar Dados', type: 'slide-toggle', conditional: { field: 'user_role', value: ['editor', 'admin', 'super_admin'], operator: 'in' } }, // Campos apenas para admins { key: 'can_manage_users', label: 'Pode Gerenciar Usuários', type: 'slide-toggle', conditional: { field: 'user_role', value: ['admin', 'super_admin'], operator: 'in' } }, // Campos exclusivos de super admin { key: 'system_settings_access', label: 'Acesso às Configurações do Sistema', type: 'slide-toggle', conditional: { field: 'user_role', value: 'super_admin' } } ] } ``` ## 📍 Localização do Código ``` projects/idt_app/src/app/ ├── shared/ │ ├── interfaces/ │ │ └── generic-tab-form.interface.ts # Interface TabFormField com conditional │ └── components/ │ └── generic-tab-form/ │ ├── generic-tab-form.component.ts # Lógica shouldShowField() e listeners │ └── generic-tab-form.component.html # Template com [style.display] ├── domain/ │ └── supplier/ │ └── supplier.component.ts # Exemplo de implementação CPF/CNPJ └── docs/ └── components/ └── CONDITIONAL_FIELDS_SYSTEM.md # 📄 Esta documentação ``` ## 🎯 Próximos Passos ### Melhorias Futuras (Roadmap) 1. **Validações Condicionais** - Campos obrigatórios baseados em condições - Validações customizadas por contexto 2. **Operadores Avançados** - `greater_than`, `less_than` para números - `contains`, `starts_with` para strings - `between` para ranges 3. **Múltiplas Condições** - Operadores lógicos `AND`, `OR` - Condições aninhadas complexas 4. **Interface Visual** - Builder visual de condições - Preview em tempo real ### Como Contribuir 1. **Reportar Issues**: Problemas específicos de implementação 2. **Sugerir Melhorias**: Novos operadores ou funcionalidades 3. **Documentar Casos**: Adicionar novos exemplos de uso 4. **Otimizar Performance**: Melhorias no sistema de listeners --- **Sistema de Campos Condicionais** | Versão 1.0.0 | PraFrota Dashboard **Status**: ✅ **Implementado e Funcional** **Compatibilidade**: Angular 19.2.x + TypeScript 5.5.x **Última atualização**: Janeiro 2025