Todos os artigos
FT-MAPPING-CARDS-HIERARCHY-V1· 1.6.0Integrações

Cards e filtros do mapping — hierarquia de confiança

Os 6 cards do mapping editor refletem dimensões granulares (não mutuamente exclusivas): total de campos com origem, com destino, mapeados por IA, mapeados manualmente, sem destino, sem origem. Click no card filtra.

Atualizado em 5/17/2026

Cards e filtros do mapping editor



A página de detalhe do mapping (\/edi-hub/[id]\) mostra 6 cards no topo + 7 filtros 1:1 com os cards.

Os 6 cards



CardCorO que conta
Campos OrigemAzul infoLinhas com \source_field !== null\
Campos DestinoRoxoLinhas com \destination_field !== null\
Mapeados por IACianoLinhas com \ai_suggested = true\ (aguarda review humana)
Mapeados ManualVerdesource+destination definidos por humano (!ai_suggested && !ignore)
Origem sem DestinoAmarelosource && !destination (precisa decisão)
Destino sem OrigemVermelho!source && destination (destino solto)


Importante: essas categorias não são mutuamente exclusivas. Uma linha mapeada manualmente é contada em "Origem", "Destino" E "Manual" simultaneamente. A soma dos 6 cards é maior que o total de linhas — isso é desenho.

Antes (v1.0): 4 cards mutuamente exclusivos (Confirmado/IA Pendente/Sem Mapping/Total). Feedback Bruno: genérico demais. v1.1 entrega visibilidade granular por dimensão.

Filtros



7 botões alinhados com os cards: \Todos / Com Origem / Com Destino / IA / Manual / Sem Destino / Sem Origem\. Click no card também filtra (atalho visual).

Banner OData (collapsible)



Quando source é OData/SAP e usa navigation properties, aparece banner discreto "N navigation properties OData no \$expand" — collapsed por default. Expande para troubleshoot 404.

Tickets



  • #205 v1.0 implementação original (4 cards hierárquicos).
  • v1.1 PR #54 — refator pra 6 dimensões granulares + lint clean + dead code.

Histórico de versões

  • 1.6.0

    Constante COM valor conta como "mapeado no destino" e particiona por ai_suggested — "Mapeados no Destino" = "Mapeados por IA" quando tudo veio da IA; fixedValue vira subconjunto informacional.

    6/9/2026 · updated

  • 1.5.0

    Obrigatórios pelo PAI IMEDIATO (não pelo segmento de topo) + cobertura por forma CANÔNICA do path (rule [*] ↔ catálogo) + gate conta obrigatórios pelo MESMO modelo do card (required-coverage.ts helper único).

    6/8/2026 · updated

  • 1.4.2

    Contadores/filtro do editor respeitam is_ignored (Bruno 30/05). O PR #387 fez o engine pular is_ignored (duplicata do dedup), mas a camada derivada (computeStats + matchesFilter + cobertura + emitido) só checava transform=ignore — então o card "Mapeados por IA" e o filtro ainda contavam a duplicata (linha que não executa). Helper isIgnoredRule(f) = transform===ignore || is_ignored===true, espelhando o runtime, aplicado em matchesFilter (ai/manual/destCovered/mapped/conflicts/destReqTotal), computeStats, computeEmittedSegments, conflictDests, cardinalityConflict, virtualRequiredRows, destCoverage, odataNavProperties. is_ignored adicionado ao tipo FieldMapping. Regra do Bruno consistente em toda a stack: 1 linha visível que produz valor = 1 mapeado; rejeitada e duplicata não contam. +2 tests.

    5/30/2026 · updated

  • 1.4.1

    Fix follow-up do modelo dinâmico (Bruno 30/05): o modelo "obrigatório = segmento emitido" sub-contava destinos OData — campos escalares no topo da entidade (sem ".", ex: BillingDocumentDate, 88 no BillingDocument V4) viravam o próprio segmento e só contavam se mapeados, então required não-mapeado no topo não aparecia. Helper isRequiredEmitted espelha o validate-required (parentSegmentEmitted: campo sem pai sempre cobrado): top-level (sem ".") sempre conta; segmentado (EDIFACT/X12/IDoc ou nav OData to_X.Y) gateia por segmento emitido. Usado em computeStats + requiredDestPaths + virtualRequiredRows. Destinos EDIFACT por segmento emitido; OData cobra todos required de topo. +2 tests.

    5/29/2026 · updated

  • 1.4.0

    Modelo DINÂMICO de "obrigatório" no editor (Bruno 30/05): "obrigatório" não é o M/C teórico do spec EDIFACT (que marca quase tudo required), e sim o conjunto MÍNIMO de campos que precisam estar preenchidos pra mensagem validar/ser aceita. O validador de runtime (validate-required.ts) já fazia isso via parentSegmentEmitted (required só é cobrado se o segmento pai for emitido); o editor estava desalinhado, contava required estático do catálogo e trazia free text de segmentos que o operador nem está montando. Agora o editor usa a MESMA lógica: computeStats destReqTotal/destReqUnmapped contam required só de segmento EMITIDO (top-segment de alguma rule ativa); destReqTotalAll continua = spec (referência). Filtro (requiredDestPaths + virtualRequiredRows) só required de segmento emitido → clicar "Obrigatórios" mostra exatamente o que a validação cobraria. Árvore + banner: segmento não-emitido não fica vermelho. Helpers puros topSegment + computeEmittedSegments. Removido o split oper·spec do PR-2 (superado). Substitui a heurística business (PR #384 fechado). Reclassificação de dados partner_profile→conditional mantida (inócua no modelo dinâmico). 2357 tests.

    5/29/2026 · updated

  • 1.3.0

    Reconciliação dos cards de mapeamento (Bruno 29/05): "Mapeados no Destino"=88, "por IA"=68, "Manual"=0 não somavam. Investigação no mapping real A_BillingDocument→INVOIC_D01B revelou: (1) o "88" contava LINHAS de catálogo cobertas, não destinos — 2 rules da IA apontavam pra nome cru de componente (C51904/C55304) que existe em 12 segmentos LOC do INVOIC, e o destCovered casava por name → cada uma batia 12 linhas (64 normais + 2×12 = 88); (2) o "68 por IA" incluía 2 sugestões rejeitadas (transform=ignore). Real: 66 destinos mapeados, todos por IA. Fix (decisão Bruno opção B): destCovered passa a contar mapeamento COMPLETO ativo (origem→destino, não-ignore), particionado por ai_suggested → destCovered = ai + manual POR CONSTRUÇÃO. ai exclui ignore (68→66). Dest sem origem fica em noSource (não vira manual). matchesFilter alinhado (clicar o card mostra exatamente o que conta). Resultado: 66 = 66 IA + 0 Manual; cobertura de catálogo (X/645) fica no banner de cobertura estrutural. +1 test do invariante.

    5/29/2026 · updated

  • 1.2.0

    Consistência dos obrigatórios no destino (Bruno 29/05): o card "Obrigatórios no Destino" mostrava 18 (subconjunto do operador — required_kind business/partner_profile) mas a árvore estruturada e o banner de cobertura embaixo mostravam ~89 (todos os required do spec, incl. technical/conditional). Parecia bug ("clico e aparecem mais campos"). Não era contagem errada — eram predicados diferentes sob o mesmo rótulo. Decisão do Bruno: mostrar os DOIS números sempre. Agora a árvore (mapping-structured-view) e o banner (mapping-coverage-banner) mostram "(X/Y oper · A/B spec)" por segmento quando diferem, e "(A/B req)" quando coincidem (OData/NFe/FHIR). Predicado único isOperatorRequired (lib/catalog/required-kind) reusado em computeStats, destCoverage e na árvore — não podem mais divergir. Card 18 oper · 89 spec ↔ soma da árvore 18 oper · 89 spec. Helper puro requiredDisplay + 5 tests.

    5/29/2026 · updated

  • 1.1.0

    Cards/filtros refeitos em 6 dimensões granulares (Bruno feedback). Substitui 4 cards mutuamente exclusivos do v1.0.0 por: Campos Origem, Campos Destino, Mapeados IA, Mapeados Manual, Origem sem Destino, Destino sem Origem. NÃO mutuamente exclusivos — cada métrica isolada faz sentido pro operador. 7 filtros 1:1 + click no card filtra. Layout grid-cols-3 lg:grid-cols-6 (mobile-friendly). Inclui no mesmo PR: lint full repo limpo (0 errors/warnings — corrigiu 4 errors + 3 warnings que existiam: setState síncrono em useEffect, JSX unescaped entities, unused-vars, let→const). Dead code: removida getFieldSource() unused + 13 chaves i18n órfãs (mappedFields/noDestinationStat/aiSuggested/totalFields/statConfirmed/Pending/NoMapping/filterMapped/Unmapped/DestOnly/Confirmed/AiPending/NoMapping). PR #54.

    5/17/2026 · updated

  • 1.0.0

    Mapping editor com cards/filtros consistentes (Confirmados/IA Pendente/Sem Mapping/Total — soma = total). Cards viraram botões click-to-filter. Banner OData collapsible. 5 chaves i18n novas. PR #45.

    5/17/2026 · new