Pular para o conteúdo principal

Produtos do tipo Kit

Resumo executivo

Um produto do tipo Kit é um produto composto por outros produtos vinculados, vendido como uma unidade única. Esta funcionalidade detecta automaticamente esses produtos, destaca-os visualmente em todas as telas relevantes e permite ao usuário visualizar os itens inclusos sem sair do contexto atual.

Visão geral

A partir desta versão, produtos podem possuir uma propriedade COMPONENTE contendo uma lista de itens vinculados. Quando essa propriedade está populada, o produto é tratado como um Kit: um produto que, quando comprado, entrega múltiplos produtos para o cliente.

Exemplo prático:

O produto CANETA CROWN AMSTERDAN ESF PRATA DI11451S possui como componente o item ESTOJO SONETO DI11451S. Ao adicionar a caneta no pedido, o estojo acompanha automaticamente.

Disponibilidade

A feature pode ser habilitada por cliente de duas formas (descritas em detalhe na seção Habilitando para novos clientes):

MecanismoComo funciona
UTILKIT no SankhyaParâmetro configurado diretamente no ERP, sem deploy de código.
hasKitFeature() no códigoMétodo no permission.ts que identifica clientes por lógica de negócio (ex: isCanetasCrown()).

Clientes ativos atualmente:

ClienteMecanismo ativo
Canetas CrownhasKitFeature()
Outros clientesConfigurar UTILKIT = S no Sankhya

Onde a feature aparece

A identidade de Kit é apresentada de forma consistente em quatro contextos distintos do sistema:

1. Catálogo — modo Lista

Na visualização em tabela do catálogo, produtos do tipo Kit exibem um badge "Kit" dentro da célula de descrição, ao lado do nome do produto.

Ao clicar no badge, uma linha de expansão abre logo abaixo, mostrando todos os itens inclusos no kit com suas respectivas quantidades.

┌──────┬────────────────────────────────────────────┬───────────┐
│ Cód │ Descrição │ Preço │
├──────┼────────────────────────────────────────────┼───────────┤
│ 35907│ CANETA CROWN AMSTERDAN [▦ Kit · 2 itens ▾] │ R$ 49,90 │
│ │ ⤷ Itens inclusos neste kit: │ │
│ │ ┌─────────────────────────────────────┐ │ │
│ │ │ 📷 ESTOJO SONETO DI11451S │ │ │
│ │ │ 1 UN · cód. 35908 [👁] │ │ │
│ │ └─────────────────────────────────────┘ │ │
└──────┴────────────────────────────────────────────┴───────────┘

Screenshot: ![Catálogo lista](../assets/produtos-kit/catalogo-lista.png)

Substitua o placeholder acima por screenshots reais ao publicar a doc.

2. Catálogo — modo Grid

No modo cards do catálogo, o badge "Kit" aparece no canto superior do card, ao lado do indicador de "Destaque". Ao clicar no badge, um painel expansível mostra os itens inclusos diretamente no card, sem abrir nenhum modal.

Screenshot: ![Catálogo grid](../assets/produtos-kit/catalogo-grid.png)

3. Modal de Detalhes do Produto

Ao abrir o modal de detalhes de um produto Kit, duas coisas mudam:

  1. Badge "Kit · N itens" aparece no cabeçalho do modal, junto com badges existentes como "Em promoção" e "Destaque".
  2. Uma seção promocional dedicada (card com banner gradient azul) é renderizada listando todos os componentes do kit em formato visual rico, com imagens dos produtos.
┌─────────────────────────────────────────────────────────────────┐
│ ╔═══════════════════════════════════════════════════════════╗ │
│ ║ ◯ Este produto é um KIT ║ │ ← Banner gradient
│ ║ ▣ 3 produtos inclusos ║ │
│ ╚═══════════════════════════════════════════════════════════╝ │
│ │
│ COMPOSIÇÃO │
│ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ [📷] ESTOJO SONETO DI11451S [👁] │ │ ← Card por item
│ │ 1 UN · cód. 35908 │ │ (com imagem e
│ └─────────────────────────────────────────────────────┘ │ botão detalhes)
│ ┌─────────────────────────────────────────────────────┐ │
│ │ [📷] CAIXA DE PRESENTE BRANCA [👁] │ │
│ │ 1 UN · cód. 35910 │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘

Screenshot: ![Modal de detalhes](../assets/produtos-kit/modal-detalhes.png)

4. Nova Venda — Tabela de Produtos

Na tela de criação de venda, uma coluna dedicada (entre o código do produto e a descrição) exibe o badge "Kit" apenas para os produtos que se qualificam. A coluna é automaticamente ocultada para clientes que não têm a feature ativa.

Ao posicionar o mouse sobre o badge, um popover aparece mostrando todos os itens inclusos no kit — útil para conferência rápida durante a montagem do pedido.

┌──────┬───────┬─────────────────────────────────────┬──────┐
│ Cód │ Kit │ Descrição │ Qtd │
├──────┼───────┼─────────────────────────────────────┼──────┤
│ 35907│ [▣Kit]│ CANETA CROWN AMSTERDAN ESF PRATA │ 1 │
│ 35351│ — │ CARGA ESF CS C/1 BLISTADA CROWN │ 5 │
└──────┴───────┴─────────────────────────────────────┴──────┘

↓ hover no badge
╔═════════════════════════════════════╗
║ Itens inclusos neste kit ║
║ ║
║ [📷] ESTOJO SONETO DI11451S ║
║ 1 UN · cód. 35908 ║
╚═════════════════════════════════════╝

Screenshot: ![Nova Venda](../assets/produtos-kit/nova-venda.png)


Interações disponíveis

Em qualquer contexto onde os componentes do kit são listados, o usuário pode:

Ampliar imagem do componente

Cada componente exibe a foto do produto (48×48px). Ao clicar na imagem, o visualizador de imagens do sistema (ImageViewer) abre em tela maior. Útil quando o usuário quer verificar visualmente o que está incluso.

Imagem indisponível

Quando o produto incluso não possui imagem cadastrada, o sistema exibe automaticamente um ícone de "pacote" (📦) como fallback visual, em vez da imagem padrão de "sem foto".

Abrir detalhes do produto incluso

À direita de cada item do kit há um botão com ícone de olho (👁️). Ao clicar:

  • Abre o Modal de Detalhes do Produto para aquele item específico do kit.
  • Útil para conferir descrição completa, preços, estoque, histórico de vendas do componente individualmente.
  • O botão dispara a ação Redux setProductModal(CODPROD) que é capturada pelo listener global ViewModal.
Comportamento dentro do Modal de Detalhes

Quando o usuário já está no Modal de Detalhes de um Kit e clica em "Ver detalhes" de um componente, o modal atualmente substitui o produto exibido (não empilha modais). Para retornar ao produto Kit original, o usuário precisa fechar e reabri-lo a partir da listagem.


Estrutura de dados

A entidade Product recebe a propriedade opcional COMPONENTE:

interface ProductKitComponent {
CODPROD: number; // Código do produto incluso
DESCRPROD: string; // Descrição do produto incluso
CODVOL?: string; // Unidade de venda (ex: "UN", "PC")
SEQUENCIA: number; // Ordem de exibição (sort ascendente)
QUANTIDADE: number; // Quantidade do componente no kit
}

interface Product {
// ... campos existentes
COMPONENTE?: ProductKitComponent[];
}

Regras:

  • COMPONENTE é opcional. Se ausente ou array vazio, o produto é tratado como produto comum.
  • Componentes são sempre renderizados ordenados por SEQUENCIA ascendente.
  • QUANTIDADE é um number direto (anteriormente um objeto { source, parsedValue } — alterado para refletir o contrato real da API).
  • Quando CODVOL está ausente, a unidade é omitida na exibição (mostra apenas a quantidade).

Exemplo de payload

{
"CODPROD": 35907,
"DESCRPROD": "CANETA CROWN AMSTERDAN ESF PRATA DI11451S",
"COMPONENTE": [
{
"CODPROD": 35908,
"DESCRPROD": "ESTOJO SONETO DI11451S",
"CODVOL": "UN",
"QUANTIDADE": 1,
"SEQUENCIA": 1
}
]
}

Arquitetura técnica

A feature foi construída em três camadas para garantir reutilização e manutenção fácil:

Camada 1 — Componentes UI (puros e agnósticos de cliente)

Componentes em src/components/Kit/:

ArquivoResponsabilidade
KitBadge.tsxRenderiza o badge "Kit" com ícone Boxes e contagem opcional.
KitComponentsList.tsxLista visual dos componentes (imagem, descrição, quantidade, ações).
index.tsBarrel export para consumo via @/components/Kit.

Nenhum desses componentes conhece o conceito de "cliente Canetas Crown". Eles recebem dados como props e renderizam. Isso garante que a UI seja reutilizável em qualquer contexto.

Camada 2 — Feature gate (única fonte de verdade)

O helper hasKitFeatureEnabled resolve a feature como true se qualquer uma das duas condições abaixo for satisfeita:

Mecanismo 1 — hasKitFeature() no código

Localização: src/utils/permission.ts:144.

hasKitFeature = () => this.isCanetasCrown();

Ativado por lógica de negócio embutida no front-end. Requer deploy para adicionar ou remover clientes.

Mecanismo 2 — parâmetro UTILKIT no Sankhya

O campo UTILKIT é retornado pela API junto com os dados de permissão do usuário. Quando seu valor é 'S', a feature é habilitada sem necessidade de alteração de código ou deploy.

// src/utils/kit.utils.ts
export function hasKitFeatureEnabled(permission: any): boolean {
return (
permission?.permissionByCompany?.hasKitFeature?.() ||
permission?.UTILKIT === "S"
);
}

Helpers em src/utils/kit.utils.ts:

// Verifica se um produto específico deve ser exibido como Kit
// (cliente tem feature + produto tem componentes)
isKitProduct(product, permission): boolean

// Conta quantos componentes o produto tem
getKitComponentCount(product): number

// Verifica apenas se a feature está ativa para o cliente
// (sem considerar produtos)
hasKitFeatureEnabled(permission): boolean

Camada 3 — Integração nas telas

Cada tela importa os componentes UI e o gate, e renderiza condicionalmente:

import { KitBadge, KitComponentsList } from "@/components/Kit";
import { isKitProduct, getKitComponentCount } from "@/utils/kit.utils";

// Em qualquer tela:
{
isKitProduct(product, permission) && (
<KitBadge componentCount={getKitComponentCount(product)} />
);
}

Fluxo de dados

Renderização do badge

Interação: ver detalhes de um componente

Carregamento da imagem do componente


Habilitando para novos clientes

A feature pode ser ativada de duas formas, dependendo se você prefere uma configuração no ERP sem deploy ou uma alteração de código.


Opção 1 — Parâmetro UTILKIT no Sankhya (recomendado)

Esta é a forma mais rápida e não requer deploy de código. Basta configurar o parâmetro diretamente no Sankhya:

  1. Acesse as configurações de parâmetros do cliente no Sankhya.
  2. Localize o parâmetro UTILKIT.
  3. Defina o valor como S.

Após a configuração, na próxima autenticação do usuário daquele cliente, a feature já estará ativa em todas as quatro telas automaticamente.

Pré-requisito

A API precisa estar enviando a propriedade COMPONENTE populada nos produtos do cliente. Sem dados, a feature gate retorna true mas nenhum produto será detectado como Kit.

Desabilitando

Para desativar, basta alterar o valor de UTILKIT para qualquer valor diferente de 'S' (ex: 'N').


Opção 2 — Método hasKitFeature() no código

Use esta opção quando a regra de ativação for baseada em lógica de negócio (ex: identificar o cliente por tipo de empresa).

Passos:

  1. Abrir src/utils/permission.ts.
  2. Localizar o método hasKitFeature (linha 144 aproximadamente).
  3. Adicionar o cliente ao OR:

Antes:

hasKitFeature = () => this.isCanetasCrown();

Depois (exemplo habilitando "Vinicola Garibaldi"):

hasKitFeature = () => this.isCanetasCrown() || this.isVinicolaGaribaldi();

Após o deploy:

  • Todas as 4 telas passam a exibir Kits automaticamente para o novo cliente.
  • A coluna KIT_FLAG da Nova Venda passa a aparecer automaticamente.
  • Nenhuma outra mudança de código é necessária.

Verificação após habilitar (ambas as opções)

  1. Fazer login com um usuário do cliente habilitado.
  2. Abrir o catálogo e verificar que produtos com COMPONENTE exibem o badge "Kit".
  3. Abrir o modal de detalhes e confirmar que a seção promocional "Itens do Kit" aparece.
  4. Ir para Nova Venda e confirmar que a coluna "Kit" foi adicionada à tabela.

Configuração visual

A identidade visual do Kit usa a cor institucional #1C73C3 (azul) e o ícone Boxes da biblioteca Lucide. A tabela abaixo documenta os tokens visuais:

ElementoValor
Cor primária#1C73C3
Cor secundária (gradient)#0d5ea0
Background da tagbg-blue-50 · #EFF6FF
Borda da tagborder-blue-100 · #DBEAFE
Ícone do badgeBoxes (lucide-react)
Ícone fallbackPackage (lucide-react)
Ícone "ver detalhes"Eye (lucide-react)
Variantes da listainline (padrão) · cards (modal promocional)

Acessibilidade

A feature segue boas práticas de a11y:

  • O KitBadge interativo é renderizado como <button type="button"> com aria-expanded e aria-label="Ver itens do kit".
  • Imagens dos componentes têm alt={DESCRPROD} descritivo.
  • Quando a imagem está em fallback, o container do ícone tem aria-label="Imagem de [Produto] indisponível".
  • O botão "Ver detalhes" tem aria-label com o nome do produto.
  • Lista de componentes usa estrutura semântica <ul><li>...</li></ul>.
  • Tooltips do popover são acessíveis via teclado (foco) e leitores de tela.

Testes

A feature possui 32 testes unitários cobrindo:

ArquivoTestesFoco
src/utils/kit.utils.test.ts16Gates isKitProduct, getKitComponentCount, hasKitFeatureEnabled (incluindo cenários com UTILKIT)
src/components/Kit/KitBadge.test.tsx8Renderização do badge, variantes, acessibilidade
src/components/Kit/KitComponentsList.test.tsx8Renderização da lista, variantes, fallback de imagem

Executar:

npm test

Cobertura específica:

npm run test:coverage

Limitações conhecidas

Navegação Modal-em-Modal

Ao clicar em "Ver detalhes" de um componente quando já está dentro do modal de detalhes, o modal substitui o produto exibido em vez de empilhar. O usuário perde o contexto do produto Kit original.

Workaround atual: fechar o modal e reabrir o Kit a partir da listagem.

Solução futura: transformar state.modal.product em uma pilha (array) para permitir "voltar" para o produto anterior. Requer mudança arquitetural no slice modal e no ViewModal.

Flash do ícone fallback

Para produtos com imagem cadastrada, durante a janela de fetch assíncrono pode haver um breve flash do ícone Package antes da imagem real aparecer. Em conexões rápidas é imperceptível.

Solução futura: pré-carregar imagens via Image() ou aplicar uma transição de opacidade no swap.

Sem suporte a kits aninhados

Atualmente o sistema não suporta um componente do kit ser também um kit. Se um produto COMPONENTE[N] também tiver sua própria propriedade COMPONENTE, essa estrutura não é renderizada recursivamente.

Sem suporte a edição

Os componentes do kit são exibidos somente para leitura. Não é possível editar quantidades de componentes individuais nem remover componentes do kit pelo front-end — essas operações devem ser feitas pelo ERP.


FAQ

Por que o badge "Kit" não aparece para meu produto?

Verifique:

  1. O usuário está logado em um cliente onde a feature está ativa? Pode ser via UTILKIT = S no Sankhya ou via hasKitFeature() no código (atualmente: Canetas Crown).
  2. O produto possui a propriedade COMPONENTE populada? Cheque a resposta da API ou o estado Redux.
  3. O array COMPONENTE tem pelo menos 1 item? Arrays vazios não disparam o badge.
Qual a diferença entre ativar pelo Sankhya e pelo código?

Ativar pelo parâmetro UTILKIT = S no Sankhya não requer deploy — a mudança é imediata na próxima autenticação do usuário. Já a ativação via hasKitFeature() no código exige alteração no permission.ts, build e deploy. Use o Sankhya quando possível; use o código quando a regra de ativação for baseada em lógica de negócio que não existe como parâmetro no ERP.

Como o sistema decide entre mostrar a imagem ou o ícone fallback?

A lógica está em KitItemRow dentro de KitComponentsList.tsx:

  1. O hook useProductImage é chamado com fallbackSrc: '' (string vazia).
  2. Se a URL retornada é vazia OU onError da <AppImage> é disparado, o estado hasLoadError vira true.
  3. Quando hasLoadError === true ou imageUrl === '', renderiza o ícone Package.
  4. Caso contrário, renderiza <AppImage> com a URL.
Posso desabilitar a coluna "Kit" da tabela de Nova Venda?

A coluna é automaticamente ocultada quando hasKitFeatureEnabled(permission) retorna false. Ou seja, ela só aparece para clientes que têm a feature ativada (via código ou via UTILKIT). Não é necessária ação manual.

O popover do badge funciona em mobile?

Sim. O <Tooltip> do MUI suporta long-press em dispositivos touch (enterTouchDelay={300}).

O que acontece se um cliente tiver COMPONENTE mas não tiver feature ativa?

A feature gate isKitProduct(product, permission) retorna false, então nenhum tratamento visual de Kit é aplicado. O produto aparece como um produto comum em todas as telas.


Referências internas

  • Ticket: FORCEWEB-1754
  • Branch: customization/FORCEWEB-1754-exibir-componentes-do-kit-no-call

Arquivos-chave

CamadaArquivo
Tiposrc/types/product/product.types.ts
Gatesrc/utils/permission.ts
Helperssrc/utils/kit.utils.ts
UIsrc/components/Kit/KitBadge.tsx
UIsrc/components/Kit/KitComponentsList.tsx
Integração 1src/screens/catalogo/components/TableRow/index.tsx
Integração 2src/screens/catalogo/components/ProductsGrid/components/ProductCard/index.tsx
Integração 3src/screens/nova-venda/.../KitFlag/NewSaleProductsKitFlag.tsx
Integração 4src/screens/catalogo/components/ProductModal/index.tsx

Changelog

VersãoDataMudança
1.1.02026-05-22Suporte ao parâmetro UTILKIT do Sankhya como segundo mecanismo de ativação da feature, sem necessidade de deploy.
Initial2026-05-22Feature inicial — badge, expansão, popover, modal promocional, ícone Package fallback, click-to-zoom, botão "Ver detalhes".