# 🚀 Domain Data Display Enricher - Guia Frontend

> Motor de enriquecimento para transformar dados brutos em objetos enriquecidos para display na UI

## 📋 Índice

1. [Como Usar no Frontend](#-como-usar-no-frontend)
2. [Formato dos EnrichMappers](#-formato-dos-enrichmappers)
3. [Estrutura de Saída](#-estrutura-de-saída)
4. [Schemas SSOT](#-schemas-ssot)
5. [Exemplos Completos](#-exemplos-completos)
6. [Troubleshooting](#-troubleshooting)

---

## 🎯 Como Usar no Frontend

### **1. Instalação**
```typescript
import { DomainDataDisplayEnricher } from '@horizon-apps/domain-schema-core'
```

### **2. Criar Registry de Domínios**
```typescript
const registry = {
  property: {
    key: "property",
    enrichMapper: propertyEnrichMapper
  },
  broker: {
    key: "broker", 
    enrichMapper: brokerEnrichMapper
  }
}
```

### **3. Instanciar Motor**
```typescript
const enricher = new DomainDataDisplayEnricher(registry, {
  locale: "pt-BR",
  currency: "BRL",
  includeMetadata: false,  // true = copia COMPLETA do FieldMetadata (todos os campos)
  getIcon: (iconName: string) => `<icon-${iconName} />`
})
```

### **4. Processar Dados**
```typescript
const rawData = {
  id: 1001,
  title: "Casa teste",
  valor: 1500000,
  broker: { name: "João Silva", creci: "12345" }
}

const enrichedData = enricher.enrich(rawData, "property")
```

---

## 🏗️ Formato dos EnrichMappers

**FORMATO OBRIGATÓRIO** que cada domínio deve implementar:

```typescript
type EnrichMapper = (rawData: Record<string, any>) => EnrichMapperResult

interface EnrichMapperResult {
  data: Record<string, any>           // Dados originais
  schema: FieldMetadata[]             // Schema SSOT - SÓ ARRAY!
  computedSchema: FieldMetadata[]     // Schema dos campos computados - SÓ ARRAY!
  computedData: Record<string, any>   // Dados computados
}
```

### **Exemplo Property EnrichMapper:**
```typescript
const propertyEnrichMapper = (rawData: Record<string, any>): EnrichMapperResult => {
  // Cálculos complexos
  const area_total = (rawData.area_privativa || 0) + (rawData.area_comum || 0)
  const valor_m2 = area_total > 0 ? Math.round(rawData.valor / area_total) : 0
  
  // Categorizar preço
  let categoria_preco = "economico"
  if (valor_m2 > 15000) categoria_preco = "luxo"
  else if (valor_m2 > 10000) categoria_preco = "alto"
  else if (valor_m2 > 6000) categoria_preco = "medio"

  return {
    data: rawData,                    // ← Dados originais
    schema: propertySchemaArray,      // ← Schema principal ARRAY!
    computedSchema: propertyComputedSchemaArray, // ← Schema computados ARRAY!
    computedData: {                   // ← Valores computados
      area_total,
      valor_m2,
      categoria_preco
    }
  }
}
```

### **Exemplo Broker EnrichMapper:**
```typescript
const brokerEnrichMapper = (rawData: Record<string, any>): EnrichMapperResult => {
  const display_name = `${rawData.name} (CRECI: ${rawData.creci})`
  const contact_preference = rawData.phone ? "WhatsApp/Telefone" : "E-mail"

  return {
    data: rawData,
    schema: brokerSchemaArray,           // ARRAY DIRETO!
    computedSchema: brokerComputedSchemaArray, // ARRAY DIRETO!
    computedData: {
      display_name,
      contact_preference
    }
  }
}
```

---

## 📤 Estrutura de Saída

### **EnrichedField (Campo Individual):**
```typescript
interface EnrichedField {
  // SEMPRE PRIMEIRO
  key: string                       // Chave original do campo (sempre presente, primeiro)

  // VALORES CORE (sempre presente)
  value: any                        // RAW para números/medidas, RESOLVIDO para enums
  label: string                     // ui.label do schema

  // VALORES PROCESSADOS (quando aplicável)
  valueLabel?: string | string[]    // Formatado: moeda, data, enum resolvido
  displayLabel?: string             // Template processado com emojis

  // VISUAL
  iconName?: string                 // Nome do ícone (nunca resolvido)
  icon?: string                     // Ícone resolvido pela função getIcon (só quando configurada)

  // METADADOS BÁSICOS
  categories?: string[]             // Categorias para agrupamento/filtros

  // METADADOS COMPLETOS (só quando includeMetadata: true)
  // Cópia EXATA e COMPLETA do FieldMetadata original (incluindo campos undefined)
  metadata?: FieldMetadata;
}
```

### **Lógica de Values:**

| Tipo Campo | Value | ValueLabel | Exemplo |
|------------|-------|------------|---------|
| **Número/Medida** | RAW | Formatado | `value: 1500000` → `valueLabel: "R$ 1.500.000,00"` |
| **Enum String** | RAW | Resolvido | `value: "economico"` → `valueLabel: "Econômico"` |
| **Enum Array** | RAW | Resolvido | `value: ["wifi", "piscina"]` → `valueLabel: ["WiFi", "Piscina"]` |
| **Boolean** | RAW | Traduzido | `value: true` → `valueLabel: "Sim"` |

### **Exemplo de Saída Completa:**
```typescript
{
  // Campo simples
  valor: {
    key: "valor",                   // Key sempre primeiro
    value: 1500000,                 // RAW para conversões
    label: "Valor de Venda",
    valueLabel: "R$ 1.500.000,00",  // Formatado para display
    displayLabel: "💰 R$ 1.500.000,00", // Com template
    
    // METADADOS BÁSICOS (sempre presentes quando existem)
    categories: ["valores"],
    type: "Number",                 // Tipo do campo
    format: "currency",             // Formato do campo
    unit: "BRL",                    // Unidade do campo
    
    // METADADOS COMPLETOS (só com includeMetadata: true)
    // CÓPIA COMPLETA do FieldMetadata - TODO campo que vier no schema
    metadata: {
      key: "valor",
      type: "Number",
      format: "currency",
      unit: "BRL",
      categories: ["valores"],
      validation: undefined,
      ui: { label: "Valor de Venda", displayTemplate: "💰 {{valueLabel}}", filterable: true, sortable: true },
      audit: undefined,
      enum: undefined,
      rules: undefined,
      db: undefined
    }
  },
  
  // Campo com enum
  tags: {
    key: "tags",                         // Key sempre primeiro
    value: ["wifi", "piscina"],          // RAW (chaves)
    label: "Tags",
    valueLabel: ["WiFi", "Piscina"]      // RESOLVIDO (labels)
  },
  
  // Relacionamento nested
  broker: {
    name: {
      key: "name",
      value: "João Silva",
      label: "Nome"
    },
    // ... outros campos do broker
    computed: {
      display_name: {
        key: "display_name",
        value: "João Silva (CRECI: 12345)",
        label: "Nome Completo"
      }
    }
  },
  
  // Campos computados na raiz
  computed: {
    area_total: {
      key: "area_total",             // Key sempre primeiro
      value: 225,                    // RAW para conversões
      label: "Área Total",
      valueLabel: "225 m²",          // Formatado
      displayLabel: "📏 225 m² total"
    }
  }
}
```

---

## 📋 Schemas SSOT

### **Estrutura Completa (Formato Array):**
```typescript
interface FieldMetadata {
  // RAIZ - Identificação básica
  key: string
  type: "String" | "Number" | "Boolean" | "String[]" | "Json" | "Json[]"
  enum?: Record<string, string>     // Para resolução de chaves → labels
  format?: "currency" | "date" | "area" | "distance" | "percent" | "count" | "year"
  unit?: string                     // BRL, m2, etc
  categories?: string[]

  // CONTEXTOS
  rules?: { parent?: string, conditions?: string[] }
  validation?: { required?: boolean, min?: number, max?: number }
  db?: { type?: string, unique?: boolean, index?: boolean }
  ui?: UiContext
  audit?: { origin?: string, modifiedBy?: string[] }
}

interface UiContext {
  label?: string                    // Label para display
  description?: string
  placeholder?: string
  iconName?: string                 // Nome do ícone (ui.iconName!)
  displayTemplate?: string          // Template com {{value}}, {{valueLabel}}
  mask?: string
  searchable?: boolean
  filterable?: boolean
  sortable?: boolean
}
```

### **Exemplo Schema Property (Array Format):**
```typescript
const propertySchemaArray: FieldMetadata[] = [
  {
    key: "valor",
    type: "Number",
    format: "currency",
    unit: "BRL",
    ui: {
      label: "Valor de Venda",
      displayTemplate: "💰 {{valueLabel}}",
      filterable: true,
      sortable: true
    }
  },
  
  {
    key: "tags",
    type: "String[]",
    enum: {
      "wifi": "WiFi gratuito",
      "piscina": "Piscina",
      "garagem": "Garagem"
    },
    ui: {
      label: "Tags",
      displayTemplate: "🏷️ {{valueLabel}}",
      filterable: true
    }
  },
  
  {
    key: "quartos",
    type: "Number",
    format: "count",
    ui: {
      label: "Quartos",
      displayTemplate: "🛏️ {{value}} quarto{{p:s}}", // Pluralização
      iconName: "bedroom"
    }
  }
]
```

---

## 🎨 Templates

### **Sintaxe Suportada:**
- `{{value}}` - Valor raw
- `{{valueLabel}}` - Valor formatado
- `{{p:texto}}` - Texto apenas no plural (valor ≠ 1)
- `{{s:texto}}` - Texto apenas no singular (valor = 1)

### **Exemplos:**
```typescript
// Pluralização
"{{value}} quarto{{p:s}}"          // 1 quarto | 3 quartos

// Com emoji
"💰 {{valueLabel}}"                // 💰 R$ 1.500.000,00

// Condicional
"{{s:⭐ }}{{valueLabel}}"          // ⭐ Sim | Não
```

---

## 🔗 Relacionamentos Nested

O motor detecta automaticamente relacionamentos por:
1. **Nome do campo** (broker, images, user, etc)
2. **Estrutura** (objeto ou array de objetos)

### **Suportados:**
```typescript
// Relacionamento único
broker: { name: "João", creci: "123" }

// Array de relacionamentos  
images: [
  { url: "img1.jpg", caption: "Fachada" },
  { url: "img2.jpg", caption: "Sala" }
]
```

---

## 🧮 Campos Computados

### **Como Funcionam:**
1. **EnrichMapper** calcula valores derivados
2. **Motor** aplica schema dos computados
3. **Resultado** fica em `computed: {}`

### **Exemplo:**
```typescript
// No EnrichMapper
computedData: {
  area_total: 180 + 45,          // = 225
  valor_m2: 1500000 / 225        // = 6666
}

// Na saída
computed: {
  area_total: {
    value: 225,                  // RAW para conversões
    label: "Área Total", 
    valueLabel: "225 m²",        // Formatado
    displayLabel: "📏 225 m² total"
  }
}
```

---

## 🛠️ Troubleshooting

### **Problema: Campo não aparece enriquecido**
- ✅ Verificar se o campo existe no schema array
- ✅ Verificar se o EnrichMapper retorna os dados corretos

### **Problema: ValueLabel não resolve enum**
- ✅ Verificar se `enum` está definido no schema
- ✅ Verificar se os valores raw correspondem às chaves do enum

### **Problema: DisplayTemplate não funciona**
- ✅ Verificar sintaxe: `{{value}}`, `{{valueLabel}}`
- ✅ Arrays não processam templates (retorna `undefined`)

### **Problema: IconName não aparece**
- ✅ Verificar se `ui.iconName` está definido no schema (não mais `ui.icon`!)
- ✅ IconName sempre como string, nunca objeto resolvido

### **Problema: Schema não funciona**
- ✅ Verificar se está retornando `FieldMetadata[]` (array) e não objeto
- ✅ EnrichMapper deve retornar `schema` e `computedSchema` como arrays

### **Problema: Campos null sendo enriquecidos desnecessariamente**
- ✅ **Comportamento correto**: Campos `null` e `undefined` são **automaticamente pulados**
- ✅ **Resultado**: Campos null não aparecem no resultado (economiza payload)
- ✅ **Arrays vazios `[]`**: São enriquecidos normalmente (comportamento esperado)
- ✅ **Semântica**: `null` = "não aplicável", `[]` = "lista vazia"

---

## 📝 Checklist de Implementação Frontend

### **1. Criar EnrichMappers:**
- [ ] Implementar função `(rawData) => EnrichMapperResult`
- [ ] Definir schemas principal e computado como **arrays**
- [ ] Calcular campos derivados
- [ ] Usar `ui.iconName` (não `ui.icon`)

### **2. Configurar Registry:**
- [ ] Mapear domínios para EnrichMappers
- [ ] Configurar opções (locale, currency, getIcon)

### **3. Usar no Componente:**
- [ ] Instanciar `DomainDataDisplayEnricher`
- [ ] Chamar `enricher.enrich(data, domain)`
- [ ] Renderizar campos enriquecidos

### **4. Tratar Saída:**
- [ ] `value` para conversões/formulários
- [ ] `valueLabel` para display simples  
- [ ] `displayLabel` para display com template
- [ ] `iconName` para resolução de ícones

---

## 🎣 React Hooks

### **useEnrichList() - Para Listas**
```typescript
// Grid de resultados de busca
export function GridResultado({ data }) {
  const { enrichedData, loading, error } = useEnrichList(
    data,           // Array de dados brutos
    'property',     // Domínio
    registry,       // Registry de domínios
    { locale: 'pt-BR' }  // Opções (opcional)
  );

  if (loading) return <div>Enriquecendo dados...</div>;
  if (error) return <div>Erro: {error.message}</div>;

  return (
    <div className="grid">
      {enrichedData.map((item, i) => (
        <PropertyCard key={i} {...item} />
      ))}
    </div>
  );
}
```

### **useEnrich() - Para Item Único**
```typescript
// Página single de imóvel
export function PropertySingle({ propertyId }) {
  const { data: rawProperty } = useQuery(`/property/${propertyId}`);
  
  const { enrichedData, loading, error } = useEnrich(
    rawProperty,    // Dados brutos do imóvel
    'property',     // Domínio
    registry        // Registry de domínios
  );

  if (loading) return <div>Carregando imóvel...</div>;
  if (error) return <div>Erro: {error.message}</div>;
  if (!enrichedData) return null;

  return (
    <div className="property-single">
      <h1>{enrichedData.title.displayLabel}</h1>
      <div className="price">{enrichedData.valor.displayLabel}</div>
      <div className="details">
        <span>{enrichedData.quartos.displayLabel}</span>
        <span>{enrichedData.area_privativa.displayLabel}</span>
      </div>
      
      {/* Campos computados */}
      {enrichedData.computed && (
        <div className="computed">
          <span>{enrichedData.computed.area_total.displayLabel}</span>
          <span>{enrichedData.computed.valor_m2.displayLabel}</span>
        </div>
      )}

      {/* Relacionamento broker */}
      {enrichedData.broker && (
        <div className="broker">
          <h3>{enrichedData.broker.name.displayLabel}</h3>
          <p>{enrichedData.broker.phone.displayLabel}</p>
        </div>
      )}
    </div>
  );
}
```

### **useEnricher() - Para Controle Manual**
```typescript
// Quando quiser controle total
export function CustomComponent({ data }) {
  const enricher = useEnricher(registry, { locale: 'pt-BR' });
  
  const handleEnrich = () => {
    const enriched = enricher.enrich(data, 'property');
    // fazer algo com enriched...
  };

  const handleEnrichList = () => {
    const enrichedList = enricher.enrichList(dataArray, 'property');
    // fazer algo com enrichedList...
  };

  return <button onClick={handleEnrich}>Enriquecer</button>;
}
```