Exemplo de Filter DTO com Metadados x-ui

Exemplo de Filter DTO com Metadados x-ui

Este exemplo mostra como declarar um DTO de filtro que:

  1. Usa @Filterable para mapear campos para Specifications Spring Data.
  2. Expõe metadados @UISchema para que o frontend renderize os componentes corretos.
  3. Demonstra a integração automática com o endpoint /schemas/filtered.
package com.example.hr.employee.filter;

import org.praxisplatform.uischema.FieldControlType;
import org.praxisplatform.uischema.NumericFormat;
import org.praxisplatform.uischema.extension.annotation.UISchema;
import org.praxisplatform.uischema.filter.annotation.Filterable;
import org.praxisplatform.uischema.filter.dto.GenericFilterDTO;

import java.math.BigDecimal;
import java.time.LocalDate;

public class EmployeeFilterDTO implements GenericFilterDTO {

    @Filterable(operation = Filterable.FilterOperation.LIKE, relation = "person.name")
    @UISchema(
        label = "Nome",
        placeholder = "Busque por nome ou parte",
        controlType = FieldControlType.INPUT,
        order = 10
    )
    private String name;

    @Filterable(operation = Filterable.FilterOperation.EQUAL, relation = "department.id")
    @UISchema(
        label = "Departamento",
        controlType = FieldControlType.SEARCHABLE_SELECT,
        endpoint = "/api/human-resources/departamentos/options/filter",
        valueField = "id",
        displayField = "label",
        order = 20
    )
    private Long departmentId;

    @Filterable(operation = Filterable.FilterOperation.IN, relation = "communicationChannels.id")
    @UISchema(
        label = "Canais",
        controlType = FieldControlType.SELECTION_LIST,
        endpoint = "/api/human-resources/communication-channels/options/filter",
        valueField = "id",
        displayField = "label",
        multiple = true,
        order = 25
    )
    private java.util.List<Long> channelIds;

    @UISchema(
        label = "Visualização",
        controlType = FieldControlType.BUTTON_TOGGLE,
        options = "[{\"label\":\"Ativos\",\"value\":\"ACTIVE\"},{\"label\":\"Todos\",\"value\":\"ALL\"}]",
        order = 27
    )
    private String viewMode;

    @Filterable(operation = Filterable.FilterOperation.GREATER_OR_EQUAL, relation = "admissionDate")
    @UISchema(
        label = "Admitido a partir de",
        controlType = FieldControlType.DATE_PICKER,
        order = 30
    )
    private LocalDate admissionDateFrom;

    @Filterable(operation = Filterable.FilterOperation.GREATER_OR_EQUAL, relation = "salary")
    @UISchema(
        label = "Salário mínimo",
        controlType = FieldControlType.CURRENCY_INPUT,
        numericFormat = NumericFormat.CURRENCY,
        numericStep = "0.01",
        order = 40
    )
    private BigDecimal salaryMin;

    @UISchema(
        label = "Cor da etiqueta",
        controlType = FieldControlType.COLOR_INPUT,
        order = 45
    )
    private String tagColor;

    // getters e setters omitidos
}

Como isso aparece no OpenAPI filtrado

Quando o CustomOpenApiResolver processa esse DTO:

No Angular, o GenericCrudService normaliza esse contrato para a forma canônica de runtime:

Ou seja: no backend Java o contrato anotado continua sendo endpoint/displayField/valueField, mas a UI Praxis passa a operar internamente com resourcePath e chaves option*Key, revalidando o schema com If-None-Match e consumindo ETag e X-Schema-Hash.

Observações de maturidade do cenário atual:

Boas práticas

Padrão recomendado para faixa monetária (enterprise)

Para filtros monetários, prefira um objeto com nomes explícitos em vez de lista posicional:

{
  "salaryRange": {
    "minPrice": 6500,
    "maxPrice": 15000,
    "currency": "BRL"
  }
}

Contrato OpenAPI publicado para ranges:

Regras recomendadas:

Compatibilidade legada controlada por configuração: