@praxisui/table 8.0.0-beta.2 → 8.0.0-beta.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +150 -8
- package/docs/DSL-Extensions-Guide.md +23 -0
- package/docs/adr/2026-03-dynamic-filter-cross-lib-coupling.md +107 -0
- package/docs/adr/2026-03-filter-drawer-adapter-light-entrypoint.md +105 -0
- package/docs/adr/2026-03-table-editor-idfield-decision.md +85 -0
- package/docs/column-resize-reorder-implementation-plan.md +338 -0
- package/docs/column-resize-reorder-review-prompt.md +34 -0
- package/docs/dynamic-filter-architecture-overview.md +207 -0
- package/docs/dynamic-filter-backend-contract-cheatsheet.md +167 -0
- package/docs/dynamic-filter-editor-settings-guide.md +229 -0
- package/docs/dynamic-filter-host-integration-guide.md +217 -0
- package/docs/dynamic-filter-payload-contract.md +331 -0
- package/docs/dynamic-filter-range-filters-guide.md +289 -0
- package/docs/dynamic-filter-troubleshooting-guide.md +220 -0
- package/docs/dynamic-inline-filter-catalog.md +147 -0
- package/docs/e2e-column-drag-playwright.md +62 -0
- package/docs/expandable-rows-enterprise-big-leagues-plan.md +1080 -0
- package/docs/json-logic-operators-and-helpers.md +57 -0
- package/docs/local-data-mode-precedence.md +12 -0
- package/docs/local-data-pre-implementation-baseline.md +22 -0
- package/docs/local-data-preimplementation-go-no-go.md +39 -0
- package/docs/local-data-support-implementation-plan.md +524 -0
- package/docs/local-data-support-pr-package.md +66 -0
- package/docs/localization-persistence-merge.md +22 -0
- package/docs/performance-hardening-v2-implementation-plan.md +479 -0
- package/docs/playground-scenario-curation-plan.md +482 -0
- package/docs/playground-scenario-second-opinion-prompt.md +121 -0
- package/docs/playground-scenario-second-opinion-review.md +234 -0
- package/docs/release-notes-p1-hardening.md +76 -0
- package/docs/table-authoring-document-completeness-checklist.md +120 -0
- package/docs/table-editor-capability-review-prompt.md +349 -0
- package/docs/visual-rules-editor-transition.md +29 -0
- package/fesm2022/{praxisui-table-table-agentic-authoring-turn-flow-tu7jtTwV.mjs → praxisui-table-table-agentic-authoring-turn-flow-DRuE55Mi.mjs} +100 -0
- package/fesm2022/{praxisui-table-table-ai.adapter-DxjDaQqy.mjs → praxisui-table-table-ai.adapter-fS74fZ7o.mjs} +14 -5
- package/fesm2022/praxisui-table.mjs +5317 -736
- package/index.d.ts +325 -106
- package/package.json +15 -9
- package/src/lib/praxis-table.json-api.md +1325 -0
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Dynamic Filter Payload Contract"
|
|
3
|
+
slug: "dynamic-filter-payload-contract"
|
|
4
|
+
description: "Contrato operacional do payload do filtro dinamico, cobrindo eventos do PraxisFilter, envio ao backend e normalizacao aplicada pelo metadata-starter."
|
|
5
|
+
doc_type: "reference"
|
|
6
|
+
document_kind: "host-guide"
|
|
7
|
+
component: "table"
|
|
8
|
+
category: "integration"
|
|
9
|
+
audience:
|
|
10
|
+
- "host"
|
|
11
|
+
- "frontend"
|
|
12
|
+
- "backend"
|
|
13
|
+
- "architect"
|
|
14
|
+
level: "advanced"
|
|
15
|
+
status: "active"
|
|
16
|
+
owner: "praxis-ui"
|
|
17
|
+
tags:
|
|
18
|
+
- "dynamic-filter"
|
|
19
|
+
- "payload"
|
|
20
|
+
- "GenericFilterDTO"
|
|
21
|
+
- "metadata-starter"
|
|
22
|
+
- "http"
|
|
23
|
+
order: 27
|
|
24
|
+
icon: "send"
|
|
25
|
+
toc: true
|
|
26
|
+
sidebar: true
|
|
27
|
+
search_boost: 1.2
|
|
28
|
+
reading_time: 18
|
|
29
|
+
estimated_setup_time: 40
|
|
30
|
+
version: "1.0"
|
|
31
|
+
related_docs:
|
|
32
|
+
- "dynamic-filter-architecture-overview"
|
|
33
|
+
- "dynamic-filter-range-filters-guide"
|
|
34
|
+
- "dynamic-fields-inline-components-guide"
|
|
35
|
+
- "dynamic-fields-inline-filter-runtime-contract"
|
|
36
|
+
- "table-overview"
|
|
37
|
+
keywords:
|
|
38
|
+
- "submit"
|
|
39
|
+
- "change"
|
|
40
|
+
- "GenericFilterDTO"
|
|
41
|
+
- "POST /filter"
|
|
42
|
+
source_of_truth:
|
|
43
|
+
- "projects/praxis-table/src/lib/components/praxis-filter/praxis-filter.component.ts"
|
|
44
|
+
- "projects/praxis-table/src/lib/components/praxis-filter/praxis-filter.component.spec.ts"
|
|
45
|
+
- "../praxis-metadata-starter/src/main/java/org/praxisplatform/uischema/controller/base/AbstractResourceQueryController.java"
|
|
46
|
+
- "../praxis-metadata-starter/src/main/java/org/praxisplatform/uischema/filter/web/FilterRequestBodyAdvice.java"
|
|
47
|
+
- "../praxis-metadata-starter/docs/examples/filter-dto.md"
|
|
48
|
+
- "../praxis-metadata-starter/docs/guides/FILTROS-E-PAGINACAO.md"
|
|
49
|
+
last_updated: "2026-03-07"
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
# Dynamic Filter Payload Contract
|
|
53
|
+
|
|
54
|
+
## Objetivo
|
|
55
|
+
|
|
56
|
+
Formalizar como o payload do filtro dinâmico nasce na UI, como deve ser enviado pelo host e como o `praxis-metadata-starter` o trata antes de construir a consulta.
|
|
57
|
+
|
|
58
|
+
Nota de trilha:
|
|
59
|
+
|
|
60
|
+
- `table` documenta o payload da feature
|
|
61
|
+
- `dynamic-fields` documenta o shape que cada inline coloca no formulario antes do envio
|
|
62
|
+
|
|
63
|
+
## Pré-requisitos
|
|
64
|
+
|
|
65
|
+
- Conhecimento básico do `PraxisFilter` e dos endpoints resource-oriented de filtro.
|
|
66
|
+
- DTO de filtro backend implementando `GenericFilterDTO`.
|
|
67
|
+
- Entendimento mínimo de `controlType`, `@Filterable` e operações de range.
|
|
68
|
+
|
|
69
|
+
## Quando usar
|
|
70
|
+
|
|
71
|
+
Use este documento para:
|
|
72
|
+
|
|
73
|
+
- implementar integração host -> backend sem adivinhação;
|
|
74
|
+
- revisar shape do payload emitido por `change` e `submit`;
|
|
75
|
+
- alinhar frontend e backend sobre o que é aceito como filtro válido;
|
|
76
|
+
- depurar `400 FILTER_PAYLOAD_INVALID`.
|
|
77
|
+
|
|
78
|
+
## Contrato funcional
|
|
79
|
+
|
|
80
|
+
O runtime do filtro expõe dois eventos principais:
|
|
81
|
+
|
|
82
|
+
- `change`: mudanças incrementais do formulário;
|
|
83
|
+
- `submit`: snapshot que normalmente deve ser enviado ao backend.
|
|
84
|
+
|
|
85
|
+
Além disso, existem eventos auxiliares como `clear`, `tagsChange`, `selectedFieldIdsChange`, `metaChanged` e `schemaStatusChange`, mas eles não substituem o payload principal do filtro.
|
|
86
|
+
|
|
87
|
+
O fluxo oficial do payload é o seguinte.
|
|
88
|
+
|
|
89
|
+
```mermaid
|
|
90
|
+
sequenceDiagram
|
|
91
|
+
participant UI as PraxisFilter
|
|
92
|
+
participant Host
|
|
93
|
+
participant Controller as AbstractResourceQueryController
|
|
94
|
+
participant Advice as FilterRequestBodyAdvice
|
|
95
|
+
participant Normalizer as RangePayloadNormalizer
|
|
96
|
+
participant DTO as GenericFilterDTO
|
|
97
|
+
participant Builder as GenericSpecificationsBuilder
|
|
98
|
+
|
|
99
|
+
UI->>Host: submit or change payload
|
|
100
|
+
Host->>Controller: POST /filter
|
|
101
|
+
Controller->>Advice: intercept request body
|
|
102
|
+
Advice->>Normalizer: normalize ranges and aliases
|
|
103
|
+
Normalizer->>DTO: canonical payload
|
|
104
|
+
DTO->>Builder: typed filter ready for specification
|
|
105
|
+
Builder-->>Controller: query/specification pronta ou erro
|
|
106
|
+
Controller-->>Host: result payload or 400 FILTER_PAYLOAD_INVALID
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## O que o host realmente envia
|
|
110
|
+
|
|
111
|
+
O host deve serializar o DTO produzido pela UI como JSON e enviá-lo para o controller correspondente.
|
|
112
|
+
|
|
113
|
+
Superfícies padrão do starter:
|
|
114
|
+
|
|
115
|
+
- `POST /filter`
|
|
116
|
+
- `POST /filter/cursor`
|
|
117
|
+
- `POST /locate`
|
|
118
|
+
- `POST /options/filter`
|
|
119
|
+
|
|
120
|
+
O shape exato depende do seu `FilterDTO`, mas o princípio é o mesmo: cada propriedade enviada deve corresponder a um campo filtrável do backend.
|
|
121
|
+
|
|
122
|
+
## Pipeline do payload
|
|
123
|
+
|
|
124
|
+
### 1. Runtime Angular
|
|
125
|
+
|
|
126
|
+
O `PraxisFilter` agrega o estado do formulário e emite objetos do tipo `Record<string, any>`.
|
|
127
|
+
|
|
128
|
+
Aspectos relevantes do runtime:
|
|
129
|
+
|
|
130
|
+
- ranges vazios não devem sobreviver à serialização;
|
|
131
|
+
- estado avançado e preferências visuais podem ser persistidos localmente;
|
|
132
|
+
- aliases inline e metadata overrides influenciam a UI, mas não devem gerar um contrato HTTP arbitrário.
|
|
133
|
+
|
|
134
|
+
### 2. Envio HTTP
|
|
135
|
+
|
|
136
|
+
O host é responsável por:
|
|
137
|
+
|
|
138
|
+
- observar `submit` ou `change`;
|
|
139
|
+
- aplicar debounce/estratégia de busca adequada;
|
|
140
|
+
- enviar o JSON ao endpoint correto;
|
|
141
|
+
- não inventar transformações fora do contrato do backend.
|
|
142
|
+
|
|
143
|
+
### 3. Interceptação no backend
|
|
144
|
+
|
|
145
|
+
No `praxis-metadata-starter`, o request body passa por `FilterRequestBodyAdvice` antes da desserialização final.
|
|
146
|
+
|
|
147
|
+
Esse ponto é central para a doc:
|
|
148
|
+
|
|
149
|
+
- o payload não é consumido “como veio”;
|
|
150
|
+
- ranges podem ser canonicalizados;
|
|
151
|
+
- payload inválido já pode falhar nessa etapa.
|
|
152
|
+
|
|
153
|
+
### 4. Normalização de ranges
|
|
154
|
+
|
|
155
|
+
`RangePayloadNormalizer` converte payloads canônicos para o formato de lista esperado pelos DTOs tipados.
|
|
156
|
+
|
|
157
|
+
Isso inclui:
|
|
158
|
+
|
|
159
|
+
- arrays canônicos;
|
|
160
|
+
- objetos canônicos;
|
|
161
|
+
- nomes de bound reconhecidos pela plataforma para domínios como data, numérico e monetário.
|
|
162
|
+
|
|
163
|
+
### 5. Construção da consulta
|
|
164
|
+
|
|
165
|
+
Depois da normalização, `GenericSpecificationsBuilder` monta a predicate com base em `@Filterable`.
|
|
166
|
+
|
|
167
|
+
Para o frontend, a consequência prática é simples:
|
|
168
|
+
|
|
169
|
+
- a UI pode ser rica;
|
|
170
|
+
- o contrato final precisa continuar mapeável para `GenericFilterDTO`.
|
|
171
|
+
|
|
172
|
+
## Exemplo de payload enviado pela UI
|
|
173
|
+
|
|
174
|
+
```json
|
|
175
|
+
{
|
|
176
|
+
"name": "Alice",
|
|
177
|
+
"departmentId": 10,
|
|
178
|
+
"salaryRange": {
|
|
179
|
+
"minPrice": 6500,
|
|
180
|
+
"maxPrice": 15000,
|
|
181
|
+
"currency": "BRL"
|
|
182
|
+
},
|
|
183
|
+
"admissionPeriod": {
|
|
184
|
+
"startDate": "2026-03-01",
|
|
185
|
+
"endDate": "2026-03-31"
|
|
186
|
+
},
|
|
187
|
+
"status": [
|
|
188
|
+
"ACTIVE",
|
|
189
|
+
"PENDING"
|
|
190
|
+
]
|
|
191
|
+
}
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
## Entity Lookup em coleções
|
|
195
|
+
|
|
196
|
+
Quando um filtro usa `entityLookup` com `multiple=true`, o `PraxisFilter` não deve persistir nem emitir a coleção rica do runtime como contrato HTTP final. A tabela agora reaproveita o helper canônico de `@praxisui/core` e respeita o mesmo `payloadMode` já usado pelo `dynamic-form`.
|
|
197
|
+
|
|
198
|
+
Regras:
|
|
199
|
+
|
|
200
|
+
- `multiple=true` sem `payloadMode` explícito envia `ids`
|
|
201
|
+
- `multiple=true` com `payloadMode: "entityRefs"` envia `[{ id, type }]`
|
|
202
|
+
- o `entityType` padrão vem de `optionSource.entityKey` quando a linha ainda não trouxer `type`
|
|
203
|
+
|
|
204
|
+
Exemplo com `ids`:
|
|
205
|
+
|
|
206
|
+
```json
|
|
207
|
+
{
|
|
208
|
+
"supplierIds": ["sup_1", "sup_2"]
|
|
209
|
+
}
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
Exemplo com `entityRefs`:
|
|
213
|
+
|
|
214
|
+
```json
|
|
215
|
+
{
|
|
216
|
+
"suppliers": [
|
|
217
|
+
{ "id": "sup_1", "type": "supplier" },
|
|
218
|
+
{ "id": "sup_2", "type": "legacy-supplier" }
|
|
219
|
+
]
|
|
220
|
+
}
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
Diretriz:
|
|
224
|
+
|
|
225
|
+
- hosts não devem criar um serializer paralelo para filtros de entidade em tabela;
|
|
226
|
+
- `PraxisFilter` já deve emitir o payload canônico pronto para envio ao backend.
|
|
227
|
+
|
|
228
|
+
## Como o starter trata esse payload
|
|
229
|
+
|
|
230
|
+
Para campos simples:
|
|
231
|
+
|
|
232
|
+
- valores seguem diretamente para o `FilterDTO`.
|
|
233
|
+
|
|
234
|
+
Para ranges:
|
|
235
|
+
|
|
236
|
+
- o backend pode converter o objeto em lista canônica;
|
|
237
|
+
- chaves auxiliares consumidas pelo normalizador são removidas do payload final desserializado;
|
|
238
|
+
- listas com limites inválidos ou conflitantes retornam erro.
|
|
239
|
+
|
|
240
|
+
Exemplo de canonicalização:
|
|
241
|
+
|
|
242
|
+
```json
|
|
243
|
+
{
|
|
244
|
+
"salaryRange": [6500, 15000],
|
|
245
|
+
"admissionPeriod": ["2026-03-01", "2026-03-31"]
|
|
246
|
+
}
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
## Regras práticas para o frontend
|
|
250
|
+
|
|
251
|
+
1. Envie apenas propriedades semanticamente preenchidas.
|
|
252
|
+
Não envie objetos vazios, listas com três posições ou placeholders neutros.
|
|
253
|
+
|
|
254
|
+
2. Prefira objeto canônico ou lista canônica para ranges.
|
|
255
|
+
Não envie payload escalar para operações Java de range.
|
|
256
|
+
|
|
257
|
+
3. Não serialize metadata de UI junto com o DTO.
|
|
258
|
+
`label`, `tooltip`, `clearButton` e `inlineAutoSize` são contrato de renderização, não de busca.
|
|
259
|
+
|
|
260
|
+
4. Use nomes canônicos nos contratos novos.
|
|
261
|
+
Para numérico genérico, prefira `{ min, max }`; para monetário, `{ minPrice, maxPrice }`; para datas, `{ startDate, endDate }`.
|
|
262
|
+
|
|
263
|
+
5. Considere `change` e `submit` como contrato de integração, não como detalhe interno.
|
|
264
|
+
|
|
265
|
+
## Query Context na Orquestração de Página
|
|
266
|
+
|
|
267
|
+
Quando o filtro dinâmico participar de um `praxis-dynamic-page`, o contrato recomendado entre widgets não é mais gravar diretamente `filterCriteria` em cada consumidor.
|
|
268
|
+
|
|
269
|
+
O caminho canônico passa a ser:
|
|
270
|
+
|
|
271
|
+
- `praxis-filter` emite `requestSearch` com o DTO filtrado
|
|
272
|
+
- a página escreve esse payload em `page.state`
|
|
273
|
+
- a página ou o builder propagam um `queryContext` para widgets de dados
|
|
274
|
+
|
|
275
|
+
Exemplo recomendado:
|
|
276
|
+
|
|
277
|
+
```json
|
|
278
|
+
{
|
|
279
|
+
"queryContext": {
|
|
280
|
+
"filters": {
|
|
281
|
+
"departmentId": 10,
|
|
282
|
+
"status": "ACTIVE"
|
|
283
|
+
},
|
|
284
|
+
"sort": ["nome,asc"],
|
|
285
|
+
"page": {
|
|
286
|
+
"index": 0,
|
|
287
|
+
"size": 25
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
Diretriz de compatibilidade:
|
|
294
|
+
|
|
295
|
+
- `filterCriteria` continua válido em componentes que ainda publicam esse contrato
|
|
296
|
+
- para novo authoring, recipes e bindings, prefira `queryContext`
|
|
297
|
+
- a semântica mínima garantida hoje é `queryContext.filters`; `sort`, `limit` e `page` devem ser consumidos apenas por widgets que já declararam suporte a esses campos
|
|
298
|
+
|
|
299
|
+
## Payloads que devem ser evitados
|
|
300
|
+
|
|
301
|
+
Exemplos inválidos ou indesejáveis:
|
|
302
|
+
|
|
303
|
+
```json
|
|
304
|
+
{ "salaryRange": 1500 }
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
```json
|
|
308
|
+
{ "salaryRange": [10, 20, 30] }
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
```json
|
|
312
|
+
{ "admissionPeriod": { "startDate": null, "endDate": null } }
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
```json
|
|
316
|
+
{ "salaryRange": { "from": 10, "minPrice": 20 } }
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
O último caso é especialmente perigoso porque pode configurar fonte conflitante para o mesmo bound.
|
|
320
|
+
|
|
321
|
+
## Como documentar esta feature daqui para frente
|
|
322
|
+
|
|
323
|
+
Toda documentação de filtro dinâmico precisa citar explicitamente:
|
|
324
|
+
|
|
325
|
+
- qual evento gera o payload;
|
|
326
|
+
- qual endpoint o recebe;
|
|
327
|
+
- como o `FilterRequestBodyAdvice` o intercepta;
|
|
328
|
+
- como `RangePayloadNormalizer` o canonicaliza;
|
|
329
|
+
- quais falhas devolvem `400`.
|
|
330
|
+
|
|
331
|
+
Sem isso, a doc tende a ficar “bonita na UI” e incompleta na integração real.
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: "Dynamic Filter Range Filters Guide"
|
|
3
|
+
slug: "dynamic-filter-range-filters-guide"
|
|
4
|
+
description: "Guia avancado de ranges no filtro dinamico, cobrindo variantes inline, shapes aceitos no payload, normalizacao backend e semantica das operacoes."
|
|
5
|
+
doc_type: "reference"
|
|
6
|
+
document_kind: "host-guide"
|
|
7
|
+
component: "table"
|
|
8
|
+
category: "data-crud"
|
|
9
|
+
audience:
|
|
10
|
+
- "host"
|
|
11
|
+
- "frontend"
|
|
12
|
+
- "backend"
|
|
13
|
+
- "architect"
|
|
14
|
+
level: "enterprise"
|
|
15
|
+
status: "active"
|
|
16
|
+
owner: "praxis-ui"
|
|
17
|
+
tags:
|
|
18
|
+
- "range"
|
|
19
|
+
- "date-range"
|
|
20
|
+
- "time-range"
|
|
21
|
+
- "price-range"
|
|
22
|
+
- "dynamic-filter"
|
|
23
|
+
order: 28
|
|
24
|
+
icon: "swap_horiz"
|
|
25
|
+
toc: true
|
|
26
|
+
sidebar: true
|
|
27
|
+
search_boost: 1.25
|
|
28
|
+
reading_time: 20
|
|
29
|
+
estimated_setup_time: 45
|
|
30
|
+
version: "1.0"
|
|
31
|
+
related_docs:
|
|
32
|
+
- "dynamic-filter-architecture-overview"
|
|
33
|
+
- "dynamic-filter-payload-contract"
|
|
34
|
+
- "dynamic-fields-inline-components-guide"
|
|
35
|
+
- "dynamic-fields-inline-filter-runtime-contract"
|
|
36
|
+
- "dynamic-fields-inline-filter-catalog"
|
|
37
|
+
- "table-overview"
|
|
38
|
+
keywords:
|
|
39
|
+
- "BETWEEN"
|
|
40
|
+
- "BETWEEN_EXCLUSIVE"
|
|
41
|
+
- "OUTSIDE_RANGE"
|
|
42
|
+
- "minPrice"
|
|
43
|
+
- "startDate"
|
|
44
|
+
source_of_truth:
|
|
45
|
+
- "projects/praxis-table/src/lib/components/praxis-filter/praxis-filter.component.ts"
|
|
46
|
+
- "projects/praxis-table/src/lib/components/praxis-filter/praxis-filter.component.spec.ts"
|
|
47
|
+
- "projects/praxis-table/src/lib/filter-settings/filter-settings.component.ts"
|
|
48
|
+
- "projects/praxis-dynamic-fields/docs/dynamic-fields-inline-components-guide.md"
|
|
49
|
+
- "../praxis-metadata-starter/src/main/java/org/praxisplatform/uischema/extension/CustomOpenApiResolver.java"
|
|
50
|
+
- "../praxis-metadata-starter/src/main/java/org/praxisplatform/uischema/filter/range/RangePayloadNormalizer.java"
|
|
51
|
+
- "../praxis-metadata-starter/src/main/java/org/praxisplatform/uischema/filter/range/RangeBoundAliasRegistry.java"
|
|
52
|
+
- "../praxis-metadata-starter/src/main/java/org/praxisplatform/uischema/filter/specification/GenericSpecificationsBuilder.java"
|
|
53
|
+
- "../praxis-metadata-starter/docs/examples/filter-dto.md"
|
|
54
|
+
last_updated: "2026-03-07"
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
# Dynamic Filter Range Filters Guide
|
|
58
|
+
|
|
59
|
+
## Objetivo
|
|
60
|
+
|
|
61
|
+
Documentar com precisão enterprise como funcionam os filtros de faixa no ecossistema Praxis, da escolha do `controlType` até a predicate final aplicada no backend.
|
|
62
|
+
|
|
63
|
+
Nota de trilha:
|
|
64
|
+
|
|
65
|
+
- este guia cobre a semantica de range da feature
|
|
66
|
+
- a implementacao dos inline range components fica na suite especializada de `praxis-dynamic-fields`
|
|
67
|
+
|
|
68
|
+
## Pré-requisitos
|
|
69
|
+
|
|
70
|
+
- Familiaridade com `PraxisFilter`, `dynamic-fields` e `GenericFilterDTO`.
|
|
71
|
+
- Conhecimento básico de `priceRange`, `rangeSlider`, `dateRange` e `timeRange`.
|
|
72
|
+
- Backend configurado com `@Filterable` para operações de range.
|
|
73
|
+
|
|
74
|
+
## Quando usar
|
|
75
|
+
|
|
76
|
+
Use este guia quando precisar:
|
|
77
|
+
|
|
78
|
+
- modelar filtros monetários, numéricos, de data ou de hora;
|
|
79
|
+
- entender como a UI envia ranges;
|
|
80
|
+
- decidir entre formatos canônicos de payload;
|
|
81
|
+
- explicar por que `BETWEEN_EXCLUSIVE` e `OUTSIDE_RANGE` não se comportam como `BETWEEN`.
|
|
82
|
+
|
|
83
|
+
## Tipos de range cobertos
|
|
84
|
+
|
|
85
|
+
No runtime Angular, os cenários principais são:
|
|
86
|
+
|
|
87
|
+
- `rangeSlider` / `inlineRange`
|
|
88
|
+
- `priceRange` / `inlineCurrencyRange`
|
|
89
|
+
- `dateRange` / `inlineDateRange`
|
|
90
|
+
- `timeRange` / `inlineTimeRange`
|
|
91
|
+
|
|
92
|
+
Além disso, controles especializados como `inlineRating`, `inlineDistanceRadius` e `inlineScorePriority` também reutilizam semântica de faixa.
|
|
93
|
+
|
|
94
|
+
## Como a UI resolve ranges
|
|
95
|
+
|
|
96
|
+
### Toolbar compacta
|
|
97
|
+
|
|
98
|
+
O `PraxisFilter` converte `controlType` genérico em variante inline por padrão quando existe equivalente dedicado. As flags abaixo funcionam como opt-out:
|
|
99
|
+
|
|
100
|
+
- `useInlineRangeVariant`
|
|
101
|
+
- `useInlineDateRangeVariant`
|
|
102
|
+
- `useInlineTimeRangeVariant`
|
|
103
|
+
|
|
104
|
+
Para monetário, `priceRange` já aponta para a experiência compacta específica do filtro.
|
|
105
|
+
|
|
106
|
+
### Filter settings
|
|
107
|
+
|
|
108
|
+
`filter-settings.component.ts` participa da história porque ele controla:
|
|
109
|
+
|
|
110
|
+
- quais tipos aparecem como opção no editor;
|
|
111
|
+
- como o host promove um campo para a toolbar compacta.
|
|
112
|
+
|
|
113
|
+
### Regra de UX importante
|
|
114
|
+
|
|
115
|
+
Range vazio não deve virar critério ativo.
|
|
116
|
+
|
|
117
|
+
Esse comportamento precisa aparecer na doc porque já existe proteção no runtime e em teste para ignorar objeto vazio no `advanced change payload`.
|
|
118
|
+
|
|
119
|
+
## Shapes aceitos no payload
|
|
120
|
+
|
|
121
|
+
### Lista canônica
|
|
122
|
+
|
|
123
|
+
Para operações não exclusivas, o backend aceita:
|
|
124
|
+
|
|
125
|
+
- `[min]`
|
|
126
|
+
- `[min, max]`
|
|
127
|
+
- `[null, max]`
|
|
128
|
+
|
|
129
|
+
Exemplo:
|
|
130
|
+
|
|
131
|
+
```json
|
|
132
|
+
{
|
|
133
|
+
"salaryRange": [6500, 15000]
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### Objeto canônico
|
|
138
|
+
|
|
139
|
+
Também são aceitos objetos descritivos, especialmente úteis para clareza na UI e no host:
|
|
140
|
+
|
|
141
|
+
```json
|
|
142
|
+
{
|
|
143
|
+
"salaryRange": {
|
|
144
|
+
"minPrice": 6500,
|
|
145
|
+
"maxPrice": 15000,
|
|
146
|
+
"currency": "BRL"
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
```json
|
|
152
|
+
{
|
|
153
|
+
"scoreRange": {
|
|
154
|
+
"min": 10,
|
|
155
|
+
"max": 20
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
```json
|
|
161
|
+
{
|
|
162
|
+
"admissionPeriod": {
|
|
163
|
+
"startDate": "2026-03-01",
|
|
164
|
+
"endDate": "2026-03-31"
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
## Bounds aceitos pelo backend
|
|
170
|
+
|
|
171
|
+
O `RangeBoundAliasRegistry` reconhece nomes de bounds publicados pela plataforma para clareza por domínio.
|
|
172
|
+
|
|
173
|
+
Exemplos relevantes:
|
|
174
|
+
|
|
175
|
+
- datas: `startDate`, `fromDate`, `start`, `from`, `endDate`, `toDate`, `end`, `to`
|
|
176
|
+
- monetário: `minPrice`, `valorMin`, `min`, `from`, `start`, `maxPrice`, `valorMax`, `max`, `to`, `end`
|
|
177
|
+
|
|
178
|
+
Regra editorial:
|
|
179
|
+
|
|
180
|
+
- use nomes canônicos em novos contratos: `{ min, max }`, `{ minPrice, maxPrice }` ou `{ startDate, endDate }`.
|
|
181
|
+
|
|
182
|
+
## Operações suportadas
|
|
183
|
+
|
|
184
|
+
### `BETWEEN`
|
|
185
|
+
|
|
186
|
+
Semântica:
|
|
187
|
+
|
|
188
|
+
- com dois limites: intervalo inclusivo;
|
|
189
|
+
- com um limite inferior: `>=`;
|
|
190
|
+
- com um limite superior: `<=`.
|
|
191
|
+
|
|
192
|
+
### `BETWEEN_EXCLUSIVE`
|
|
193
|
+
|
|
194
|
+
Semântica:
|
|
195
|
+
|
|
196
|
+
- exige exatamente dois limites não nulos;
|
|
197
|
+
- aplica `>` no lower bound e `<` no upper bound.
|
|
198
|
+
|
|
199
|
+
Consequência prática:
|
|
200
|
+
|
|
201
|
+
- não aceite payload parcial;
|
|
202
|
+
- não documente `[null, max]` nem `[min]` para essa operação.
|
|
203
|
+
|
|
204
|
+
### `NOT_BETWEEN`
|
|
205
|
+
|
|
206
|
+
Semântica:
|
|
207
|
+
|
|
208
|
+
- com dois limites: negação do intervalo;
|
|
209
|
+
- com limite único: transforma a operação em comparação simples oposta.
|
|
210
|
+
|
|
211
|
+
### `OUTSIDE_RANGE`
|
|
212
|
+
|
|
213
|
+
Semântica:
|
|
214
|
+
|
|
215
|
+
- com dois limites: `< lower OR > upper`;
|
|
216
|
+
- com limite único: segue comparação unilateral fora da faixa.
|
|
217
|
+
|
|
218
|
+
## Regras de normalização que precisam estar na doc
|
|
219
|
+
|
|
220
|
+
1. Payload escalar é inválido.
|
|
221
|
+
|
|
222
|
+
```json
|
|
223
|
+
{ "salaryRange": 1500 }
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
Não há flag canônica no starter para preservar payload escalar em operações de range.
|
|
227
|
+
|
|
228
|
+
2. Bounds invertidos podem ser normalizados.
|
|
229
|
+
|
|
230
|
+
Se o usuário enviar `min > max`, o normalizador pode fazer swap para preservar semântica consistente.
|
|
231
|
+
|
|
232
|
+
3. Upper-only deve preservar nulidade explícita do lower.
|
|
233
|
+
|
|
234
|
+
```json
|
|
235
|
+
{ "salaryRange": [null, 15000] }
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
4. Objeto sem bound efetivo é inválido.
|
|
239
|
+
|
|
240
|
+
```json
|
|
241
|
+
{ "salaryRange": { "minPrice": null, "maxPrice": null } }
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
5. Fontes conflitantes para o mesmo range devem falhar.
|
|
245
|
+
|
|
246
|
+
Não misture duas origens diferentes para o mesmo campo.
|
|
247
|
+
|
|
248
|
+
## Regras por domínio
|
|
249
|
+
|
|
250
|
+
### Numérico genérico
|
|
251
|
+
|
|
252
|
+
Use `rangeSlider` quando o intervalo for numérico puro e houver benefício em slider/preset.
|
|
253
|
+
|
|
254
|
+
### Monetário
|
|
255
|
+
|
|
256
|
+
Use `priceRange` ou `inlineCurrencyRange` quando precisar:
|
|
257
|
+
|
|
258
|
+
- máscara monetária;
|
|
259
|
+
- formatação por locale;
|
|
260
|
+
- resumo de faixa monetária;
|
|
261
|
+
- combinação de slider e inputs numéricos.
|
|
262
|
+
|
|
263
|
+
### Data
|
|
264
|
+
|
|
265
|
+
Use `dateRange` ou `inlineDateRange` quando o campo representar período calendárico.
|
|
266
|
+
|
|
267
|
+
No backend, `LocalDate` exige atenção especial: algumas predicates trabalham com início do dia e início do próximo dia para manter semântica correta.
|
|
268
|
+
|
|
269
|
+
### Hora
|
|
270
|
+
|
|
271
|
+
Use `timeRange` ou `inlineTimeRange` quando a busca depender de faixa horária pura, sem necessidade de data.
|
|
272
|
+
|
|
273
|
+
## O que documentar para hosts
|
|
274
|
+
|
|
275
|
+
Em qualquer doc host-facing de range, explique sempre:
|
|
276
|
+
|
|
277
|
+
- qual `controlType` foi adotado;
|
|
278
|
+
- se o host manteve o padrão inline ou aplicou opt-out explícito;
|
|
279
|
+
- qual payload o host deve enviar;
|
|
280
|
+
- qual operação backend está associada;
|
|
281
|
+
- quais formatos inválidos geram `400`.
|
|
282
|
+
|
|
283
|
+
## Boas práticas finais
|
|
284
|
+
|
|
285
|
+
1. Prefira objeto canônico na documentação funcional e lista canônica na explicação de normalização.
|
|
286
|
+
2. Não publique payload escalar como exemplo de operação de range.
|
|
287
|
+
3. Relacione sempre a UI de range com a operação `@Filterable` correspondente.
|
|
288
|
+
4. Documente claramente diferença entre `BETWEEN`, `BETWEEN_EXCLUSIVE`, `NOT_BETWEEN` e `OUTSIDE_RANGE`.
|
|
289
|
+
5. Em casos críticos, cite explicitamente que o `praxis-metadata-starter` é quem canonicaliza o range antes da specification.
|