Pular para o conteúdo principal

Layout Service

Gerencia operações relacionadas aos layouts de formulários, permitindo buscar configurações de layout por entidade.

Visão Geral​

O LayoutService é responsável por:

  • Buscar layouts de formulários da API remota
  • Filtrar layouts por entidade (Cliente, Produto, Cabeçalho, Contato)
  • Fornecer estruturas de layout para renderização dinâmica de formulários

Os layouts definem quais campos devem ser exibidos em formulários para diferentes entidades do sistema, permitindo customização por organização.

Localização: services/layout/layout.service.ts

Endpoints Utilizados​

Base URL​

Todos os endpoints são relativos à base URL configurada em services/api.service.ts

Métodos​

fetchRemote(params)​

Busca layouts remotamente com opções de filtro e paginação.

Endpoint: GET /layout

Parâmetros:

type FetchLayoutParams = {
page?: number; // Número da página (padrão: 0)
size?: number; // Quantidade de itens por página (padrão: 999)
dataHora?: string;
entity?: LayoutEntity; // Filtro por entidade
};

type LayoutEntity = "Cliente" | "Produto" | "Cabeçalho" | "Contato";

Query Params enviados:

  • page: número da página
  • size: tamanho da página
  • dataHora: timestamp (opcional)
  • entity: entidade para filtrar (opcional)

Retorno:

Promise<Layout[]>;

Exemplo de uso:

import { layoutService } from "@/services/layout/layout.instance";

// Buscar todos os layouts
const allLayouts = await layoutService.fetchRemote();

// Buscar layouts apenas para Cliente
const clientLayouts = await layoutService.fetchRemote({
entity: "Cliente",
});

// Buscar layouts com paginação
const layoutsPage = await layoutService.fetchRemote({
page: 0,
size: 50,
entity: "Produto",
});

Tratamento de erros:

  • Lança Error se o status da resposta não for 2xx
  • Mensagem: LayoutService.fetchRemote failed: {status}

Estruturas de Dados​

Interface Layout​

type Layout = {
id: string;
idOrganization: string;
nome: string;
entidade: LayoutEntity;
organization: string;
layout: FieldLayout[];
};

Type LayoutEntity​

type LayoutEntity = "Cliente" | "Produto" | "Cabeçalho" | "Contato";

Define as entidades suportadas pelo sistema de layouts.

Interface FieldLayout​

Os layouts contêm um array de FieldLayout que define cada campo:

type FieldLayout = {
fieldLayoutId: string;
requerido: "S" | "N";
editavel: "S" | "N";
visivel: "S" | "N";
valorPadrao: string;
aba: string;
field: FieldBasic;
layout: LayoutBasic;
Opcoes: Option[];
};

Ver Field Layout Service para detalhes completos.


Componentes Relacionados​

filterLayoutByEntity Helper​

Função utilitária que filtra layouts por entidade.

Localização: helpers/fiterLayoutByEntity.ts

function filterLayoutByEntity(layout: Layout[], entity: string): Layout[];

Nota: Essa função é usada internamente porque a API atualmente não filtra corretamente por entidade. Quando o backend for corrigido, essa filtragem pode ser removida.

buildQueryParams Helper​

Função utilitária que converte objeto de parâmetros em URLSearchParams.

Localização: helpers/buildQueryParams.ts

PageResponse Interface​

Interface compartilhada para respostas paginadas.

Localização: services/client/client.service.ts

interface PageResponse<T> {
content: T[]; // Array de itens da página
page?: number; // Número da página atual
size?: number; // Tamanho da página
totalPages?: number; // Total de páginas
totalElements?: number; // Total de elementos
}

Fluxo de Integração​

Fluxo de Busca de Layouts​

1. App precisa renderizar formulário de Cliente
2. App chama layoutService.fetchRemote({ entity: 'Cliente' })
3. GET /layout é enviado com query params
4. API retorna layouts disponíveis
5. Filtro por entidade é aplicado (workaround temporário)
6. Layouts filtrados são retornados
7. App usa layout.layout (array de FieldLayout) para renderizar formulário

Fluxo de Renderização Dinâmica​

1. Obter layout para entidade específica
2. Para cada FieldLayout no array layout.layout:
a. Verificar se visivel === "S"
b. Renderizar campo com base em field.tipo
c. Aplicar validação se requerido === "S"
d. Desabilitar campo se editavel === "N"
e. Aplicar valorPadrao se definido
f. Renderizar options se disponíveis
3. Agrupar campos por aba

Casos de Uso​

Buscar Layout de Cliente​

// Buscar layouts para formulário de cadastro de cliente
const clientLayouts = await layoutService.fetchRemote({
entity: "Cliente",
});

if (clientLayouts.length > 0) {
const mainLayout = clientLayouts[0];
console.log("Layout name:", mainLayout.nome);
console.log("Total fields:", mainLayout.layout.length);

// Agrupar campos por aba
const fieldsByTab = mainLayout.layout.reduce(
(acc, field) => {
if (!acc[field.aba]) acc[field.aba] = [];
acc[field.aba].push(field);
return acc;
},
{} as Record<string, FieldLayout[]>,
);

console.log("Tabs:", Object.keys(fieldsByTab));
}

Testes​

Localização​

services/layout/layout.service.test.ts

Executar Testes​

# Teste específico do serviço
npx jest services/layout/layout.service.test.ts

# Modo watch
npx jest services/layout/layout.service.test.ts --watch

# Com coverage
npx jest services/layout/layout.service.test.ts --coverage

Casos de Teste​

  • ✅ fetchRemote: busca de layouts sem filtros
  • ✅ fetchRemote: busca de layouts filtrados por entidade
  • ✅ fetchRemote: busca com paginação
  • ✅ Tratamento de erros para status não-2xx
  • ✅ Filtro por entidade aplicado corretamente

Mocks​

Localização​

__mocks__/layout.mock.ts

Dados Disponíveis​

Array de layouts de exemplo para diferentes entidades:

  • Layout de Cliente (L-001): Layout simples com campos básicos
  • Layout de Produto (L-002): Layout com campos de produto
  • Layout de Cabeçalho (L-003): Layout para documentos
  • Layout de Contato (L-004): Layout para contatos

Exemplo de uso em testes:

import { layouts } from "@/__mocks__/layout.mock";

// Mock da API
const apiMock = {
get: jest.fn(async () => ({
status: 200,
data: { content: layouts },
})),
};

const service = new LayoutService({ apiWithoutAccessToken: apiMock });
const result = await service.fetchRemote({ entity: "Cliente" });

expect(result).toHaveLength(1);
expect(result[0].entidade).toBe("Cliente");