# IDT App - Documentação

## Visão Geral
O IDT App é uma aplicação Angular para gerenciamento de veículos, desenvolvida como parte do sistema Prafrota. A aplicação permite o gerenciamento completo de frota de veículos, incluindo cadastro, edição, localização e simulação de financiamento.

## Estrutura do Projeto

### Componentes Principais
- `VehiclesComponent`: Componente principal para gerenciamento de veículos
- `VehicleMapComponent`: Componente para visualização de veículos em mapa
- `DataTableComponent`: Componente reutilizável para exibição de dados em tabela

### Funcionalidades Principais
1. **Gerenciamento de Veículos**
   - Cadastro de novos veículos
   - Edição de veículos existentes
   - Visualização em tabela com filtros e ordenação
   - Paginação de resultados

2. **Localização**
   - Visualização de veículos em mapa
   - Localização específica por placa

3. **Financiamento**
   - Simulação de financiamento para veículos
   - Cálculo de valores e parcelas

### Campos do Veículo
- Placa
- Chassi (VIN)
- Tipo de Carroceria
- Marca
- Cor
- Descrição
- Combustível
- Grupo
- Ano de Fabricação
- Marca Registrada
- Número de Portas
- RENAVAM
- Número de Assentos
- Status
- Transmissão
- Tipo de Veículo

### Recursos Técnicos
- Angular Material para componentes UI
- Componentes standalone
- Gerenciamento de estado
- Serviços para comunicação com backend
- Sistema de formulários dinâmicos
- Upload de imagens
- Integração com mapas

## API Backend - PraFrota

### Endpoint Base
- **URL**: `https://prafrota-be-bff-tenant-api.grupopra.tech`
- **Versão**: v1
- **Protocolo**: HTTPS
- **Formato**: JSON

### Autenticação
- **Tipo**: Bearer Token (JWT)
- **Header**: `Authorization: Bearer <token>`
- **Renovação**: Automática via refresh token
- **Expiração**: Configurável por tenant

### Estrutura de Rotas da API

#### Veículos
- `GET /api/v1/vehicles` - Listar veículos
- `POST /api/v1/vehicles` - Criar veículo
- `PUT /api/v1/vehicles/{id}` - Atualizar veículo
- `DELETE /api/v1/vehicles/{id}` - Excluir veículo
- `GET /api/v1/vehicles/{id}` - Obter veículo específico

#### Motoristas
- `GET /api/v1/drivers` - Listar motoristas
- `POST /api/v1/drivers` - Criar motorista
- `PUT /api/v1/drivers/{id}` - Atualizar motorista
- `DELETE /api/v1/drivers/{id}` - Excluir motorista

#### Finanças
- `GET /api/v1/finances/accounts-payable` - Contas a pagar
- `POST /api/v1/finances/accounts-payable` - Criar conta a pagar
- `PUT /api/v1/finances/accounts-payable/{id}` - Atualizar conta
- `DELETE /api/v1/finances/accounts-payable/{id}` - Excluir conta

#### Rotas
- `GET /api/v1/routes/mercado-live` - Rotas Mercado Live
- `GET /api/v1/routes/shopee` - Rotas Shopee
- `POST /api/v1/routes/sync` - Sincronizar rotas
- `POST /api/v1/routes/optimize` - Otimizar rotas

### Padrões de Resposta

#### Sucesso (200/201)
```json
{
  "success": true,
  "data": [...],
  "totalCount": 100,
  "pageCount": 10,
  "currentPage": 1,
  "timestamp": "2024-12-13T10:00:00Z"
}
```

#### Erro (400/401/500)
```json
{
  "success": false,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Dados inválidos",
    "details": ["Campo obrigatório: placa"]
  },
  "timestamp": "2024-12-13T10:00:00Z"
}
```

### Paginação
- **Parâmetros**: `page`, `limit`, `sort`, `order`
- **Limite máximo**: 100 itens por página
- **Padrão**: 10 itens por página

### Filtros
- **Formato**: Query parameters
- **Exemplo**: `?status=active&brand=toyota&year=2023`
- **Operadores**: `eq`, `like`, `gt`, `lt`, `between`

### Headers Obrigatórios
```http
Content-Type: application/json
Authorization: Bearer <token>
X-Tenant-ID: <tenant-id>
X-Client-Version: 1.0.0
```

### Códigos de Status
- `200` - Sucesso
- `201` - Criado com sucesso
- `400` - Dados inválidos
- `401` - Não autorizado
### Pré-requisitos
- Node.js
- Angular CLI
- Git

### Instalação
1. Clone o repositório
2. Instale as dependências:
   ```bash
   npm install
   ```
3. Execute o projeto:
   ```bash
   ng serve
   ```

## Convenções de Código
- Uso de TypeScript
- Componentes standalone
- Estilo de código seguindo as diretrizes do Angular
- Documentação de código em inglês

## 🎯 PADRÃO OBRIGATÓRIO - Template HTML para Componentes de Domínio

### Template Padrão BaseDomainComponent
TODOS os componentes que estendem BaseDomainComponent DEVEM usar exatamente este template HTML:

```html
<div class="domain-container">
  <div class="main-content">
    <app-tab-system
      #tabSystem
      [config]="tabConfig"
      [events]="tabEvents"
      [showDebugInfo]="false"
      (tabSelected)="onTabSelected($event)"
      (tabClosed)="onTabClosed($event)"
      (tabAdded)="onTabAdded($event)"
      (tableEvent)="onTableEvent($event)">
    </app-tab-system>
  </div>
</div>
```

### ❌ NUNCA FAZER:
- `<app-tab-system></app-tab-system>` (sem bindings)
- Templates customizados para domínios ERP
- Estruturas HTML diferentes

### ✅ SEMPRE FAZER:
- Usar exatamente o template acima
- Incluir todos os bindings de eventos
- Manter a estrutura domain-container > main-content
- Referenciar #tabSystem para controle programático

### Componentes que DEVEM seguir este padrão:
- VehiclesComponent ✅
- DriversComponent ✅  
- RoutesComponent ✅
- FinancialCategoriesComponent ✅
- AccountPayableComponent ✅
- Qualquer novo componente de domínio ERP

## 🚫 PADRÃO OBRIGATÓRIO - Services com ApiClientService

### ❌ NUNCA FAZER:
```typescript
// ❌ ERRADO - Usar HttpClient diretamente
import { HttpClient } from '@angular/common/http';

constructor(private http: HttpClient) {}

this.http.get('api/endpoint')  // NUNCA FAZER ISSO!
```

### ✅ SEMPRE FAZER:
```typescript
// ✅ CORRETO - Usar ApiClientService
import { ApiClientService } from '../../shared/services/api/api-client.service';

constructor(private apiClient: ApiClientService) {}

this.apiClient.get('endpoint')  // SEMPRE ASSIM!
```

### Template Completo de Service:
```typescript
import { Injectable } from '@angular/core';
import { Observable, map } from 'rxjs';
import { ApiClientService } from '../../shared/services/api/api-client.service';
import { DomainService } from '../../shared/components/base-domain/base-domain.component';
import { PaginatedResponse } from '../../shared/interfaces/paginate.interface';

@Injectable({
  providedIn: 'root'
})
export class ExampleService implements DomainService<Entity> {
  
  constructor(
    private apiClient: ApiClientService
  ) {}

  getEntities(page: number, pageSize: number, filters: any): Observable<{
    data: Entity[];
    totalCount: number;
    pageCount: number;
    currentPage: number;
  }> {
    return this.getExamples(page, pageSize, filters).pipe(
      map(response => ({
        data: response.data,
        totalCount: response.totalCount,
        pageCount: response.pageCount,
        currentPage: response.currentPage
      }))
    );
  }

  create(data: any): Observable<Entity> {
    return this.apiClient.post<Entity>('examples', data);
  }

  update(id: any, data: any): Observable<Entity> {
    return this.apiClient.patch<Entity>(`examples/${id}`, data);
  }

  getExamples(page = 1, limit = 10, filters?: any): Observable<PaginatedResponse<Entity>> {
    let url = `examples?page=${page}&limit=${limit}`;
    
    if (filters) {
      const params = new URLSearchParams();
      for (const [key, value] of Object.entries(filters)) {
        if (value) {
          params.append(key, value.toString());
        }
      }
      if (params.toString()) {
        url += `&${params.toString()}`;
      }
    }

    return this.apiClient.get<PaginatedResponse<Entity>>(url);
  }

  getById(id: string): Observable<Entity> {
    return this.apiClient.get<Entity>(`examples/${id}`);
  }

  delete(id: string): Observable<void> {
    return this.apiClient.delete<void>(`examples/${id}`);
  }
}
```

## 🚫 PADRÕES DE NOMENCLATURA CRÍTICOS - Services

### ⚠️ REGRAS OBRIGATÓRIAS DE NOMENCLATURA

**NUNCA QUEBRAR ESTES PADRÕES!** A inconsistência na nomenclatura quebra toda a arquitetura do projeto.

#### ✅ MÉTODOS OBRIGATÓRIOS (SEMPRE seguir):
```typescript
// Interface DomainService - OBRIGATÓRIOS
getEntities(page: number, pageSize: number, filters: any): Observable<any>
create(data: any): Observable<Entity>
update(id: any, data: any): Observable<Entity>

// Métodos específicos - PADRÃO ESTABELECIDO
getById(id: string): Observable<Entity>
delete(id: string): Observable<void>
get[Domain]s(page: number, limit: number, filters?: any): Observable<PaginatedResponse<Entity>>
```

#### ❌ MÉTODOS PROIBIDOS (NUNCA usar):
```typescript
// 🚫 SUFIXOS ESPECÍFICOS - PROIBIDO
createRoute(), createVehicle(), createDriver()     // ❌ Usar: create()
updateRoute(), updateVehicle(), updateDriver()     // ❌ Usar: update()
deleteRoute(), deleteVehicle(), deleteDriver()     // ❌ Usar: delete()
getRoute(), getVehicle(), getDriver()              // ❌ Usar: getById()

// 🚫 NOMENCLATURA ALTERNATIVA - PROIBIDO
addEntity(), editEntity(), removeEntity()          // ❌ Usar: create(), update(), delete()
findById(), searchById(), retrieveById()           // ❌ Usar: getById()
saveEntity(), persistEntity()                      // ❌ Usar: create() ou update()
```

#### 📋 AUDITORIA DE CONFORMIDADE:
| Service | ✅ create | ✅ update | ✅ delete | ✅ getById | ✅ get[Domain]s |
|---------|-----------|-----------|-----------|-----------|----------------|
| VehiclesService | ✅ | ✅ | ✅ | ✅ | getVehicles ✅ |
| DriversService | ✅ | ✅ | ✅ | ✅ | getDrivers ✅ |
| RoutesService | ✅ | ✅ | ✅ | ✅ | getRoutes ✅ |
| FinancialCategoriesService | ✅ | ✅ | ✅ | ✅ | getCategories ✅ |
| AccountPayableService | ✅ | ✅ | ✅ | ✅ | getAccounts ✅ |

### 🎯 MOTIVOS TÉCNICOS:
1. **BaseDomainComponent** espera métodos `create`, `update`, `delete`
2. **DomainService interface** define contratos específicos
3. **Consistency** com todo o ecosistema Angular do projeto
4. **Maintainability** - padrões claros facilitam manutenção
5. **Team Standards** - evita confusão entre desenvolvedores

## Contribuição
1. Crie uma branch para sua feature
2. Faça commit das alterações
3. Envie um pull request

## Padrão de Branches

### Estrutura de Branches
- `main`: Branch principal de produção
- `develop`: Branch de desenvolvimento
- `release/*`: Branches para preparação de releases
- `feature/*`: Branches para novas funcionalidades
- `bugfix/*`: Branches para correções de bugs
- `hotfix/*`: Branches para correções urgentes em produção

### Convenções de Nomenclatura
1. **Feature Branches**
   - Formato: `feature/nome-da-feature`
   - Exemplo: `feature/vehicle-location-map`

2. **Bugfix Branches**
   - Formato: `bugfix/descricao-do-bug`
   - Exemplo: `bugfix/fix-vehicle-form-validation`

3. **Hotfix Branches**
   - Formato: `hotfix/descricao-do-problema`
   - Exemplo: `hotfix/fix-critical-security-issue`

4. **Release Branches**
   - Formato: `release/versao`
   - Exemplo: `release/v1.2.0`

### Fluxo de Trabalho
1. **Desenvolvimento de Features**
   - Criar branch a partir de `develop`
   - Desenvolver feature
   - Criar PR para `develop`
   - Após aprovação, merge em `develop`

2. **Correção de Bugs**
   - Criar branch a partir de `develop`
   - Corrigir bug
   - Criar PR para `develop`
   - Após aprovação, merge em `develop`

3. **Preparação de Release**
   - Criar branch a partir de `develop`
   - Realizar testes e ajustes finais
   - Criar PR para `main` e `develop`
   - Após aprovação, merge em ambas as branches

4. **Hotfixes**
   - Criar branch a partir de `main`
   - Corrigir problema
   - Criar PR para `main` e `develop`
   - Após aprovação, merge em ambas as branches

### Regras Importantes
- Manter branches atualizadas com a branch base
- Realizar rebase antes de criar PR
- Seguir padrão de commits convencionais
- Manter histórico de commits limpo e organizado
- Deletar branches após merge bem-sucedido

## 📊 Padrões de Paginação e Listagem

### Problema Comum: Paginação com Múltiplas APIs
Quando há necessidade de consultar múltiplas APIs e consolidar os dados, a paginação deve ser implementada localmente.

#### Estrutura Recomendada
```typescript
loadData(currentPage = this.currentPage, itemsPerPage = this.itemsPerPage) {
  this.isLoading = true;
  this.data = [];
  this.totalItems = 0;
  
  const sources = ['source1', 'source2', 'source3'];
  let completedRequests = 0;
  let allData: any[] = [];

  // Buscar todos os dados primeiro
  for (const source of sources) {
    this.service.getData(1, 1000, source, this.currentFilters)
      .subscribe({
        next: (response) => {
          if (response.data[0]?.data_string) {
            const datapack = JSON.parse(response.data[0].data_string);
            allData = [...allData, ...datapack];
          }
          completedRequests++;
          
          // Aplicar paginação local quando todas as requisições terminarem
          if (completedRequests === sources.length) {
            // Ordenação opcional
            allData.sort((a, b) => /* critério de ordenação */);
            
            // Aplicar paginação
            this.totalItems = allData.length;
            this.totalPages = Math.ceil(this.totalItems / itemsPerPage);
            
            const startIndex = (currentPage - 1) * itemsPerPage;
            const endIndex = startIndex + itemsPerPage;
            this.data = allData.slice(startIndex, endIndex);
            
            this.isLoading = false;
          }
        },
        error: (error) => {
          completedRequests++;
          if (completedRequests === sources.length) {
            this.isLoading = false;
          }
        }
      });
  }
}
```

#### Controle de Mudança de Página
```typescript
onPageChange(event: { page: number; pageSize: number }) {
  const maxPage = Math.ceil(this.totalItems / event.pageSize);
  const validPage = Math.min(event.page, maxPage);

  if (this.currentPage !== validPage || this.itemsPerPage !== event.pageSize) {
    this.currentPage = validPage;
    this.itemsPerPage = event.pageSize;
    this.loadData(this.currentPage, this.itemsPerPage);
  }
}
```

### DataTable Component Integration
- **Componente**: `DataTableComponent` localizado em `shared/components/data-table/`
- **Configuração**: Usar `TableConfig` para definir colunas, ações e comportamentos
- **Paginação**: Sempre implementar `totalDataItems`, `currentPage` e `pageChange`

#### Exemplo de Configuração de Tabela
```typescript
tableConfig: TableConfig = {
  columns: [
    {
      field: "campo",
      header: "Cabeçalho",
      sortable: true,
      filterable: true,
      label: (data: any) => {
        // Transformação de dados para exibição
        return data;
      }
    }
  ],
  pageSize: this.itemsPerPage,
  pageSizeOptions: [5, 10, 25, 50],
  showFirstLastButtons: true,
  actions: [
    {
      icon: "fas fa-edit",
      label: "Editar",
      action: "edit",
    }
  ]
};
```

## 📝 Logger Service

### Implementação Baseada no Backend
O Logger service foi criado seguindo o mesmo padrão da interface do backend, mantendo compatibilidade de API.

#### Estrutura do Logger
- **Interface**: `LoggerService` com métodos `log`, `error`, `warn`, `debug`, `verbose`, `fatal`
- **Tipos**: `LogLevel` com todos os níveis de log suportados
- **Classe**: `Logger` que implementa `LoggerService`

#### Uso Básico
```typescript
import { Logger } from '../../../../shared/services/logger';

// Método estático (uso global)
Logger.log('Mensagem de log');
Logger.error('Erro ocorreu', 'MeuComponente');
Logger.warn('Aviso importante');
Logger.debug('Info de debug');
Logger.verbose('Log detalhado');
Logger.fatal('Erro crítico');

// Instância com contexto (recomendado para componentes)
export class MeuComponent {
  private logger = new Logger('MeuComponent');
  
  ngOnInit() {
    this.logger.log('Componente inicializado');
    this.logger.debug('Dados carregados:', dados);
  }
  
  onError(error: any) {
    this.logger.error('Erro no processamento:', error);
  }
}
```

#### Funcionalidades Avançadas
```typescript
// Configuração personalizada
const logger = new Logger('DataService', {
  timestamp: true,
  environment: 'production',
  enableConsole: true,
  enableRemoteLogging: false
});

// Controle de níveis de log
Logger.overrideLogger(['error', 'warn']); // Só mostra erros e avisos
Logger.overrideLogger(false); // Desabilita todos os logs
Logger.overrideLogger(true);  // Habilita todos os logs

// Buffer para logs de inicialização
Logger.attachBuffer();
Logger.log('Log armazenado no buffer');
Logger.detachBuffer(); // Flush todos os logs

// Verificação condicional
if (Logger.isLevelEnabled('debug')) {
  const expensiveData = calculateDebugData();
  Logger.debug('Debug data:', expensiveData);
}
```

#### Cores e Formatação
O Logger inclui formatação colorida no console:
- **LOG**: Azul (#2196F3)
- **ERROR/FATAL**: Vermelho (#f44336/#d32f2f)  
- **WARN**: Laranja (#ff9800)
- **DEBUG**: Verde (#4caf50)
- **VERBOSE**: Roxo (#9c27b0)

#### Localização
- **Arquivos**: `shared/services/logger/`
- **Interface**: `logger.interface.ts`
- **Implementação**: `logger.service.ts`
- **Exemplos**: `logger.example.ts`
- **Export**: `index.ts`

## 🔄 Padrões de Mapeamento de Dados

### Mapeamento de APIs Heterogêneas para Interface Unificada
Quando trabalhando com múltiplas APIs que retornam estruturas diferentes, use o padrão de mapeamento específico por tipo.

#### Estrutura de Mapeamento
```typescript
// Método principal de mapeamento
private mapRoutesToInterface(datapack: any[], type: string): InterfacePadrao[] {
  return datapack.map(item => {
    switch (type) {
      case 'tipo1':
        return this.mapTipo1(item);
      case 'tipo2':
        return this.mapTipo2(item);
      default:
        return this.mapGenerico(item, type);
    }
  });
}

// Mapeamentos específicos por tipo
private mapTipo1(data: any): InterfacePadrao {
  return {
    id: data.id?.toString() || '',
    campo1: data.campo_api || 'valor_padrao',
    status: this.mapStatus(data.status),
    // ... outros campos
  };
}
```

#### Exemplo: Rotas Mercado Livre
```typescript
// Aplicação no recebimento de dados
const datapack = JSON.parse(response.data[0].data_string);
const mappedRoutes = this.mapRoutesToInterface(datapack, type);
allRoutes = [...allRoutes, ...mappedRoutes];

// Mapeamento específico por tipo de rota
private mapFirstMileRoute(route: any): MercadoLiveRoute {
  return {
    id: route.id?.toString() || '',
    customerName: route.carrierName || 'N/A',
    estimatedPackages: route.estimatedPackages || 0,
    priority: route.warnings?.length > 0 ? 'high' : 'medium'
  };
}
```

#### Padrões de Status Normalization
```typescript
private mapStatus(status: string): StatusPadrao {
  const statusMap: { [key: string]: StatusPadrao } = {
    'active': 'in_transit',
    'pending': 'pending',
    'finished': 'delivered',
    'cancelled': 'cancelled'
  };
  
  return statusMap[status?.toLowerCase()] || 'pending';
}
```

#### Tratamento de Dados Opcionais
```typescript
// Safe navigation e fallbacks
campo: data.nivel1?.nivel2?.campo || 'valor_padrao',
data: data.timestamp ? new Date(data.timestamp * 1000) : new Date(),
numero: data.valor || 0,
array: data.lista?.length ? data.lista : []
```

### Interfaces Específicas por Tipo
Criar interfaces específicas para cada estrutura de dados diferente:

```typescript
// Interfaces específicas por tipo de fonte
export interface FirstMileRoute {
  id: number;
  routeType: string;
  facilityName: string;
  estimatedPackages: number;
  vehicleName: string;
  driverName: string;
  warnings: FirstMileWarning[];
}

export interface LineHaulRoute {
  carrier_id: number;
  carrier: string;
  drivers: LineHaulDriver[];
  vehicles: LineHaulVehicle[];
  steps: LineHaulStep[];
  stops: LineHaulStop[];
}

export interface LastMileRoute {
  id: string;
  cluster: string;
  driver: LastMileDriver;
  counters: LastMileCounters;
  timingData: LastMileTimingData;
}

// Union type para tipagem de entrada
export type MercadoLiveRouteRaw = FirstMileRoute | LineHaulRoute | LastMileRoute;
```

#### Mapeamento com Type Safety
```typescript
private mapRoutesToInterface(datapack: MercadoLiveRouteRaw[], type: string): MercadoLiveRoute[] {
  return datapack.map(route => {
    switch (type) {
      case 'first_mile':
        return this.mapFirstMileRoute(route as FirstMileRoute);
      case 'line_haul':
        return this.mapLineHaulRoute(route as LineHaulRoute);
      case 'last_mile':
        return this.mapLastMileRoute(route as LastMileRoute);
    }
  });
}

private mapFirstMileRoute(route: FirstMileRoute): MercadoLiveRoute {
  return {
    id: route.id.toString(),
    customerName: route.carrierName,
    estimatedPackages: route.estimatedPackages,
    vehicleType: route.vehicleType, // FirstMileRoute.vehicleType
    locationName: route.facilityName, // FirstMileRoute.facilityName
    driverName: route.driverName, // FirstMileRoute.driverName
    DepartureDate: new Date(route.initDate * 1000), // timestamp → Date
    priority: route.warnings.length > 0 ? 'high' : 'medium'
  };
}

private mapLineHaulRoute(route: LineHaulRoute): MercadoLiveRoute {
  return {
    // ...
    vehicleType: route.vehicle_type, // LineHaulRoute.vehicle_type
    locationName: route.site_id, // LineHaulRoute.site_id
    driverName: route.drivers[0]?.name, // LineHaulRoute.drivers[0].name
    DepartureDate: new Date(route.departure_date), // datetime → Date
    // ...
  };
}

private mapLastMileRoute(route: LastMileRoute): MercadoLiveRoute {
  return {
    // ...
    vehicleType: route.vehicle.description, // LastMileRoute.vehicle.description
    locationName: route.facilityId, // LastMileRoute.facilityId
    driverName: route.driver.driverName, // LastMileRoute.driver.driverName
    DepartureDate: new Date(route.initDate * 1000), // timestamp → Date
    // ...
  };
}
```

### Conversão de Formatos de Data
Quando diferentes APIs retornam datas em formatos distintos, implemente conversões específicas:

```typescript
// Função concisa para conversão automática de datas
private convertToDate(dateValue: any): Date {
  if (!dateValue) return new Date();
  
  if (typeof dateValue === 'number') {
    // Se menor que 10^12, está em segundos; senão, em milissegundos
    return new Date(dateValue < 1000000000000 ? dateValue * 1000 : dateValue);
  }
  
  if (typeof dateValue === 'string') {
    return new Date(dateValue);
  }
  
  return new Date();
}

// Exemplos de conversão por tipo usando a função auxiliar
DepartureDate: this.convertToDate(route.initDate),        // auto-detecta formato
DepartureDate: this.convertToDate(route.departure_date),  // auto-detecta formato
estimatedDelivery: this.convertToDate(route.finalDate),   // auto-detecta formato

// Exemplos de dados reais:
// "initDate": 1748351864 (timestamp segundos) → 25/01/2025
// "departure_date": "2025-05-25T16:48:47Z" (ISO string) → 25/05/2025
```

### Vantagens do Padrão
✅ **Consistência**: Dados uniformizados independente da origem  
✅ **Manutenibilidade**: Fácil adição de novos tipos  
✅ **Legibilidade**: Mapeamentos específicos e organizados  
✅ **Robustez**: Tratamento de dados faltantes  
✅ **Type Safety**: Garantia de tipos através de interfaces específicas  
✅ **IntelliSense**: Autocompletar e validação em tempo de desenvolvimento  
✅ **Detecção de Erros**: Erros de tipagem detectados em build time  
✅ **Conversão de Formatos**: Normalização automática de timestamps e datetime strings  

## 🎨 Componentes de Interface Avançados

### Color Input Component
Componente especializado para seleção de cores com interface visual intuitiva.

#### Funcionalidades
- **Dropdown Visual**: Grid de círculos coloridos com nomes
- **Preview Seleção**: Mostra cor selecionada no botão principal
- **Botão Limpar**: Opção para remover seleção
- **Overlay Inteligente**: Fecha ao clicar fora
- **Responsive**: Layout adaptado para mobile
- **Tema Escuro**: Suporte completo a temas

#### Implementação
```typescript
{
  key: 'color',
  label: 'Cor',
  type: 'color-input',
  required: false,
  options: [
    { value: { name: 'Branco', code: '#ffffff' }, label: 'Branco' },
    { value: { name: 'Preto', code: '#000000' }, label: 'Preto' },
    // ... outras cores
  ]
}
```

#### Integração com Data Table
- **Renderização HTML**: Círculos de cor nas células da tabela
- **DomSanitizer**: HTML seguro com `bypassSecurityTrustHtml()`
- **Fallback Inteligente**: Mapa de cores para objetos sem código hex
- **Configuração**: `allowHtml: true` na configuração da coluna

### Indicadores de Campos Obrigatórios
Sistema unificado de sinalização visual para campos obrigatórios em formulários.

#### Componentes Atualizados
- ✅ **custom-input**: Asterisco vermelho nos labels
- ✅ **color-input**: Suporte nativo no template inline
- ✅ **kilometer-input**: Asterisco + interface TypeScript atualizada
- ✅ **generic-tab-form**: Labels dos selects nativos com asterisco
- ✅ **remote-select**: Sistema `required-asterisk` já implementado
- ✅ **multi-select**: Sistema `required-asterisk` já implementado

#### CSS Unificado
```scss
.required-indicator {
  color: var(--idt-danger, #dc3545);
  margin-left: 4px;
  font-weight: 700;
  font-size: 16px;
  line-height: 1;
}
```

#### Vantagens
- ✅ **UX Melhorada**: Usuários sabem quais campos são obrigatórios
- ✅ **Consistência Visual**: Mesmo padrão em todos os componentes
- ✅ **Acessibilidade**: Indicação clara de campos obrigatórios
- ✅ **Prevenção de Erros**: Reduz tentativas de submit com dados incompletos

### Data Table HTML Rendering
Sistema seguro para renderização de HTML personalizado em células de tabela.

#### Configuração de Coluna
```typescript
{
  field: "color",
  header: "Cor",
  allowHtml: true,
  label: (value: any) => {
    const colorCode = value.code || '#999999';
    return `<span style="display: inline-flex; align-items: center; gap: 6px;">
              <span style="width: 12px; height: 12px; border-radius: 50%; 
                           background-color: ${colorCode}; border: 1px solid #ddd;"></span>
              <span>${value.name}</span>
            </span>`;
  }
}
```

#### Segurança
- **DomSanitizer**: Uso de `bypassSecurityTrustHtml()` para HTML seguro
- **Validação**: Verificação de `allowHtml` antes da renderização
- **Fallback**: Renderização de texto simples quando HTML não é permitido

## 🔧 Padrões de Validação e Formulários

### Validação Condicional
Sistema inteligente que aplica validação apenas quando necessário.

#### Implementação
```typescript
// Validação aplicada apenas para campos required: true
createOptionValidator(field: TabFormField): ValidatorFn | null {
  if (!field.required) return null;
  
  return (control: AbstractControl): ValidationErrors | null => {
    if (!control.value) return { required: true };
    
    if (field.returnObjectSelected) {
      return this.isValidObjectSelection(control.value, field) ? null : { invalidOption: true };
    }
    
    return this.isValidPrimitiveSelection(control.value, field) ? null : { invalidOption: true };
  };
}
```

#### Vantagens
- ✅ **Performance**: Validação apenas quando necessário
- ✅ **Flexibilidade**: Campos opcionais não geram erros
- ✅ **Robustez**: Suporte a objetos complexos e valores primitivos

### Serialização de Objetos em Formulários
Tratamento correto de campos que retornam objetos complexos.

#### Problema Comum
```html
<!-- ❌ INCORRETO: Serializa como "[object Object]" -->
<option [value]="option.value">{{ option.label }}</option>

<!-- ✅ CORRETO: Preserva objeto completo -->
<option [ngValue]="option.value">{{ option.label }}</option>
```

#### Processamento no Submit
```typescript
onSubmit(): void {
  const formData = { ...this.form.value };
  
  // Processar campos com returnObjectSelected
  this.config.fields
    .filter(field => field.returnObjectSelected)
    .forEach(field => {
      if (formData[field.key] && typeof formData[field.key] === 'object') {
        // Objeto já está correto, não precisa processar
        console.log(`✅ Campo ${field.key} já é objeto:`, formData[field.key]);
      }
    });
  
  this.submitData.emit(formData);
}
```

## Suporte
Para suporte ou dúvidas, entre em contato com a equipe de desenvolvimento. 