# 🎯 Sistema de Data Binding para Campos de Imagem

## 📖 **Visão Geral**

O sistema de upload de imagens agora suporta **duas abordagens** para obter dados iniciais:

1. **Abordagem Atual**: Referência direta ao campo do formulário
2. **Nova Abordagem**: Data Binding dinâmico com Observable

## 🚀 **Implementação**

### **1. Extensão da Interface SubTabConfig**

```typescript
export interface SubTabConfig {
  // ... campos existentes ...
  
  // 🎯 NOVA: Data Binding para campos de imagem
  dataBinding?: {
    getInitialData?: () => Observable<any> | any; // Para buscar dados iniciais
    onDataChange?: (data: any) => void; // Para mudanças
  };
}
```

### **2. Lógica de Prioridade**

O sistema segue esta ordem de prioridade:

1. **Data Binding** (nova abordagem)
2. **Required Fields** (abordagem atual)  
3. **Campo direto** do initialData
4. **Fallback** (array vazio)

## 📋 **Abordagem 1: Data Binding Dinâmico (NOVA)**

### **Configuração**

```typescript
{
  id: 'photos',
  label: 'Fotos',
  icon: 'fa-camera',
  templateType: 'fields',
  dataBinding: {
    getInitialData: () => this.getAttachmentsIds() // Observable<number[]>
  },
  fields: [
    {
      key: 'attachmentsIds',
      label: 'Fotos do Documento',
      type: 'send-image',
      imageConfiguration: {
        maxImages: 10,
        maxSizeMb: 5,
        allowedTypes: ['image/jpeg', 'image/png', 'image/webp'],
        existingImages: [] // Preenchido via dataBinding
      }
    }
  ]
}
```

### **Método no Componente**

```typescript
getAttachmentsIds(): Observable<number[]> {
  const currentTab = this.tabSystem?.getSelectedTab();
  return this.serviceAccountPayable.getAttachmentsById(currentTab?.data?.id).pipe(
    map((response: { data: Attachments[] }) => response.data.map((attachment: Attachments) => attachment.id))
  );
}
```

### **Service Ajustado**

```typescript
// No AccountPayableService
getAttachmentsById(id: string): Observable<{ data: Attachments[] }> {
  return this.apiClient.get<{ data: Attachments[] }>(`account-payable/${id}/attachments`);
}
```

### **Estrutura da Resposta da API**

```json
{
  "data": [
    {
      "id": 95,
      "acl": "private",
      "name": "image.jpg",
      "storage_key": "tenant/...",
      "uuid": "95484f4d-140d-41ee-8251-d96addf80788",
      "confirmed": true,
      "createdAt": "2025-07-09T00:09:18.565Z",
      "updatedAt": "2025-07-09T00:09:19.188Z"
    }
  ]
}
```

**Importante**: A API retorna um objeto com a propriedade `data` contendo o array de attachments.

### **Vantagens**

✅ **Flexibilidade**: Busca dados de qualquer fonte  
✅ **Assíncrono**: Suporta Observable para dados dinâmicos  
✅ **Performance**: Carregamento sob demanda  
✅ **Reutilização**: Método pode ser usado em outros contextos  

## 📋 **Abordagem 2: Required Fields (ATUAL)**

### **Configuração**

```typescript
{
  id: 'photos',
  label: 'Fotos',
  icon: 'fa-camera',
  templateType: 'fields',
  requiredFields: ['salesPhotoIds'], // Referencia campo do formulário
  fields: [
    {
      key: 'salesPhotoIds',
      label: 'Fotos do Veículo',
      type: 'send-image',
      imageConfiguration: {
        maxImages: 3,
        maxSizeMb: 2,
        allowedTypes: ['image/jpeg', 'image/png', 'image/webp'],
        existingImages: [] // Preenchido do initialData[salesPhotoIds]
      }
    }
  ]
}
```

### **Dados Esperados**

```typescript
// initialData deve conter:
{
  salesPhotoIds: [1, 2, 3], // Array de IDs das imagens
  // ... outros campos
}
```

### **Vantagens**

✅ **Simplicidade**: Configuração direta  
✅ **Compatibilidade**: Funciona com dados estáticos  
✅ **Performance**: Dados já disponíveis no formulário  

## 🔧 **Implementação Interna**

### **Método getExistingImagesForField**

```typescript
private getExistingImagesForField(field: TabFormField): number[] {
  const subTab = this.getSubTabForField(field);
  
  // 🎯 PRIORIDADE 1: Data Binding
  if (subTab?.dataBinding?.getInitialData) {
    const initialData = subTab.dataBinding.getInitialData();
    if (initialData instanceof Observable) {
      return []; // Tratado assincronamente
    } else {
      return Array.isArray(initialData) ? initialData : [];
    }
  }
  
  // 🎯 PRIORIDADE 2: Required Fields
  if (subTab?.requiredFields && subTab.requiredFields.length > 0 && this.initialData) {
    const fieldKey = subTab.requiredFields[0];
    const formValue = this.initialData[fieldKey];
    return Array.isArray(formValue) ? formValue : [];
  }
  
  // 🎯 PRIORIDADE 3: Campo direto
  if (this.initialData && this.initialData[field.key]) {
    const fieldValue = this.initialData[field.key];
    return Array.isArray(fieldValue) ? fieldValue : [];
  }
  
  return [];
}
```

### **Método initializeImagePreviews**

```typescript
private initializeImagePreviews() {
  allFields
    .filter((field) => field.type === 'send-image')
    .forEach((field) => {
      const subTab = this.getSubTabForField(field);
      
      // 🎯 Data Binding com Observable
      if (subTab?.dataBinding?.getInitialData) {
        const initialData = subTab.dataBinding.getInitialData();
        if (initialData instanceof Observable) {
          this.imagePreviewsMap[field.key] = initialData.pipe(
            switchMap((imageIds: number[]) => this.getImagePreviewsWithBase64(imageIds)),
            catchError((error) => of([])),
            tap((images) => this.initializeImageData(field.key, images))
          );
          return;
        }
      }
      
      // 🎯 Dados estáticos
      const existingIds = field.imageConfiguration?.existingImages || [];
      this.imagePreviewsMap[field.key] = this.getImagePreviewsWithBase64(existingIds);
    });
}
```

## 🎯 **Casos de Uso**

### **Data Binding (Recomendado para)**

- Dados que precisam ser buscados dinamicamente
- APIs externas ou serviços complexos
- Dados que dependem do contexto atual
- Reutilização de métodos em múltiplos contextos

### **Required Fields (Recomendado para)**

- Dados já disponíveis no formulário
- Configurações simples e diretas
- Compatibilidade com sistemas existentes
- Performance crítica com dados estáticos

## 🔄 **Migração**

### **De Required Fields para Data Binding**

```typescript
// ANTES
{
  requiredFields: ['photoIds'],
  fields: [...]
}

// DEPOIS
{
  dataBinding: {
    getInitialData: () => this.getPhotoIds()
  },
  fields: [...]
}
```

### **Método de Migração**

```typescript
// Adicionar método no componente
getPhotoIds(): Observable<number[]> {
  return this.service.getPhotoIds(this.currentId);
}
```

## ✅ **Testes**

### **Verificar Funcionamento**

1. **Data Binding**: Verificar se Observable retorna dados
2. **Required Fields**: Verificar se initialData contém campo
3. **Fallback**: Verificar se array vazio é retornado
4. **Performance**: Verificar carregamento assíncrono

### **Logs de Debug**

```typescript
console.log('🔍 [DEBUG] Campo ${field.key} - usando dataBinding Observable');
console.log('🔍 [DEBUG] Campo ${field.key} - existingIds estáticos:', existingIds);
console.log('✅ [DEBUG] Campo ${field.key} - imagens carregadas:', images.length);
```

---

## 🚀 **Próximos Passos**

1. **Migrar domínios existentes** para data binding quando necessário
2. **Criar novos domínios** usando data binding por padrão
3. **Documentar casos de uso** específicos por domínio
4. **Otimizar performance** para carregamentos assíncronos 