@praxisui/table 8.0.0-beta.20 → 8.0.0-beta.22

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 CHANGED
@@ -163,6 +163,15 @@ Filtro runtime:
163
163
  - The default is `false`; hosts opt in when they want settings, authoring affordances and schema-drift UX.
164
164
  - Customization mode does not change the table data mode. It only gates configuration/editorial surfaces.
165
165
 
166
+ ## Praxis Semantic Assistant
167
+
168
+ - The table assistant is part of the global Praxis semantic copilot experience, not a standalone table patch bot.
169
+ - In customization mode, opening the assistant registers a `PraxisAssistantContextSnapshot` through `PraxisAssistantSessionRegistryService`.
170
+ - The table session identity follows `table:{routeKey}:{componentInstanceId || tableId}` so multiple tables on the same page can preserve separate assistant sessions.
171
+ - Minimizing the table shell keeps a global minimized session that the App Shell can reopen; the App Shell does not interpret or execute table semantics.
172
+ - The table snapshot stores only safe context: table identity, target, context chips, manifest reference, resource path, schema field names, data/runtime digests, capability refs and governance hints.
173
+ - Prompts that indicate shared business decisions, such as rules, policies, compliance, LGPD, approval, eligibility or access control, must be routed to governed `domain-rules` handoff instead of local table config patches.
174
+
166
175
  ## Migration Note
167
176
 
168
177
  - This release treats `enableCustomization=false` as the canonical default.
@@ -274,6 +283,25 @@ rowConditionalStyles: [
274
283
  ]
275
284
  ```
276
285
 
286
+ O editor especializado tambem persiste `effects: RuleEffectDefinition[]` em
287
+ `rowConditionalStyles` e `columns[].conditionalStyles`. O runtime consome esses
288
+ efeitos diretamente e ainda preserva `cssClass`/`style` como fallback de
289
+ compatibilidade, mantendo o ciclo abrir -> aplicar/salvar -> reabrir sem exigir
290
+ que consumidores recompilem manualmente os efeitos visuais.
291
+
292
+ Para renderizacao condicional, `columns[].conditionalRenderers` tambem aceita
293
+ `effects: RuleEffectDefinition[]` como fonte canonica para renderer, tooltip e
294
+ animacao. `rowConditionalRenderers` fica tipado no contrato publico para tooltip
295
+ e animacao de linha, preservando os campos planos `tooltip`/`animation` como
296
+ fallback.
297
+
298
+ Colunas calculadas usam `columns[].computed.expression` como AST JSON Logic
299
+ canonico. Regras, estilos e renderers condicionais podem referenciar esses
300
+ valores via `computed.<field>`. Quando uma coluna calculada depende de outra
301
+ coluna calculada, o runtime resolve a ordem de avaliacao por dependencias
302
+ declaradas ou referencias `var: "computed.<field>"`, evitando que o resultado
303
+ dependa da ordem fisica de `columns[]`.
304
+
277
305
  ```scss
278
306
  .app-table .mat-mdc-row.app-row--priority-high {
279
307
  background: linear-gradient(
@@ -471,6 +499,12 @@ por `PraxisAssistantTurnOrchestratorService`; a semantica especifica da tabela c
471
499
 
472
500
  ## Agentic Authoring & Manifest
473
501
 
502
+ Para acoes globais de `toolbar`, `row` e `bulk`, o authoring agentic aceita somente `GlobalActionRef` governado:
503
+ `actionId`, `payload`, `payloadExpr` e `meta`. O schema bloqueia propriedades arbitrarias e mantem `payloadExpr`
504
+ como escape avancado explicito, nao como objeto livre. Configuracoes novas devem persistir a acao em
505
+ `effects[].kind = "global-action"` com `effects[].globalAction`; `globalAction` plano permanece apenas como fallback
506
+ de compatibilidade para documentos existentes.
507
+
474
508
  O `@praxisui/table` declara authoring agentic através de um `ComponentAuthoringManifest` canônico. O manifesto é a fonte versionada para descoberta de alvos, operações e validações de configuração da tabela.
475
509
 
476
510
  - **Component ID:** `praxis-table`
@@ -1814,6 +1848,20 @@ actions: {
1814
1848
  id: 'open-detail',
1815
1849
  label: 'Abrir detalhe',
1816
1850
  action: 'navigation.openRoute',
1851
+ effects: [
1852
+ {
1853
+ kind: 'global-action',
1854
+ globalAction: {
1855
+ actionId: 'navigation.openRoute',
1856
+ payload: {
1857
+ path: '/clientes/detalhe',
1858
+ query: {
1859
+ id: '${row.id}',
1860
+ },
1861
+ },
1862
+ },
1863
+ },
1864
+ ],
1817
1865
  globalAction: {
1818
1866
  actionId: 'navigation.openRoute',
1819
1867
  payload: {
@@ -1831,3 +1879,24 @@ actions: {
1831
1879
 
1832
1880
  O runtime da tabela resolve templates como `${row.id}` antes de executar a
1833
1881
  global action, o que permite reutilizar o mesmo contrato em `list` e `table`.
1882
+ Para configuracoes novas, `effects[].kind = 'global-action'` e o envelope
1883
+ canonico alinhado a `PraxisRuntimeGlobalActionEffect`; `globalAction` plano
1884
+ continua aceito como fallback de compatibilidade.
1885
+ O editor de acoes usa o mesmo adapter interno para toolbar, row e bulk actions:
1886
+ ao escolher novamente a mesma global action, ele preserva `payload` estruturado
1887
+ ou `payloadExpr` existente; ao trocar o `actionId`, ele descarta o payload antigo
1888
+ para evitar executar parametros de outra acao.
1889
+
1890
+ As acoes por linha tambem usam Json Logic canonico para `visibleWhen` e
1891
+ `disabledWhen`. O editor de acoes preserva esses objetos no fluxo
1892
+ abrir -> aplicar/salvar -> reabrir, por exemplo:
1893
+
1894
+ ```ts
1895
+ {
1896
+ id: 'edit-active',
1897
+ label: 'Editar',
1898
+ action: 'edit',
1899
+ visibleWhen: { '===': [{ var: 'status' }, 'Ativo'] },
1900
+ disabledWhen: { '===': [{ var: 'status' }, 'Bloqueado'] },
1901
+ }
1902
+ ```
@@ -118,6 +118,55 @@ Essas chaves são as mais importantes na prática:
118
118
  - `allowSaveTags`: habilita tags persistidas/visíveis;
119
119
  - `changeDebounceMs`: regula a cadência de emissão de `change` e `submit`.
120
120
 
121
+ #### Padronizar `materialDesign` dos campos de filtro
122
+
123
+ O `praxis-filter` normaliza a metadata efetiva de filtros com uma politica
124
+ visual consistente para toolbar compacta e formulario avancado. Quando o host
125
+ fornece metadata propria ou overrides em `alwaysVisibleFieldMetadataOverrides`,
126
+ ele deve preservar essa politica para campos com icones, prefixos, sufixos,
127
+ datepicker toggle, simbolo de moeda, seletor de cor ou clear button.
128
+
129
+ Configuracao recomendada:
130
+
131
+ ```ts
132
+ const filterFieldMaterialDesign = {
133
+ floatLabel: 'always',
134
+ subscriptSizing: 'dynamic',
135
+ } as const;
136
+ ```
137
+
138
+ Exemplo de override:
139
+
140
+ ```ts
141
+ advancedFilters: {
142
+ settings: {
143
+ alwaysVisibleFieldMetadataOverrides: {
144
+ cpf: {
145
+ prefixIcon: 'fingerprint',
146
+ materialDesign: filterFieldMaterialDesign,
147
+ },
148
+ dataNascimento: {
149
+ materialDesign: filterFieldMaterialDesign,
150
+ },
151
+ },
152
+ },
153
+ }
154
+ ```
155
+
156
+ Nao trate isso como ajuste cosmetico local. O Angular Material recomenda
157
+ `floatLabel="always"` para campos `fill`/`outline` com prefixos/sufixos porque o
158
+ label em repouso nao compartilha o mesmo alinhamento do valor do input. A regra
159
+ de plataforma e aplicar essa politica na metadata efetiva de filtro antes de
160
+ recorrer a CSS; o runtime ja faz essa normalizacao para a barra compacta e para
161
+ o formulario avancado.
162
+
163
+ Campos compostos de faixa (`priceRange`, `dateRange`, `dateTimeRange` e
164
+ `timeRange`) devem ocupar uma linha completa no formulario avancado. Eles
165
+ materializam mais de um controle interno e, quando competem lado a lado com um
166
+ campo simples, perdem alinhamento vertical e tornam o hint ambíguo. O
167
+ `praxis-filter` aplica essa classe de layout automaticamente ao montar o
168
+ `FormConfig` avancado.
169
+
121
170
  ### 3. Definir política do painel avançado
122
171
 
123
172
  Os principais knobs são:
@@ -103,6 +103,13 @@ O `PraxisFilter` converte `controlType` genérico em variante inline por padrão
103
103
 
104
104
  Para monetário, `priceRange` já aponta para a experiência compacta específica do filtro.
105
105
 
106
+ ### Formulario avancado
107
+
108
+ No formulario avancado, ranges compostos devem ocupar a linha completa. A regra
109
+ evita comparar verticalmente um controle com duas entradas internas contra um
110
+ campo simples, preserva leitura de label/hint e mantem espaco suficiente para
111
+ `Min`/`Max`, icones e sufixos.
112
+
106
113
  ### Filter settings
107
114
 
108
115
  `filter-settings.component.ts` participa da história porque ele controla:
@@ -118,6 +118,43 @@ O shape enviado não respeita o contrato ou foi interpretado de maneira diferent
118
118
 
119
119
  Compare o payload com o guia de ranges antes de tentar “corrigir no controller”.
120
120
 
121
+ ## Sintoma: label conflita com icone, prefixo ou sufixo
122
+
123
+ ### Causa provável
124
+
125
+ Campos Material `fill`/`outline` com `prefixIcon`, `suffixIcon`, `clearButton`,
126
+ datepicker toggle, simbolo de moeda, seletor de cor ou outros
127
+ `matPrefix`/`matSuffix` podem exibir o label em repouso sobreposto ou desalinhado.
128
+
129
+ Esse comportamento vem da propria geometria do `mat-form-field`: em `fill` e
130
+ `outline`, o label em repouso e o valor do input nao usam o mesmo alinhamento. O
131
+ Angular Material recomenda `floatLabel="always"` nesses casos.
132
+
133
+ ### Onde olhar
134
+
135
+ - metadata efetiva do campo no filtro avancado;
136
+ - `alwaysVisibleFieldMetadataOverrides`;
137
+ - politica global aplicada pelo `praxis-filter` para materializar campos de filtro;
138
+ - `metadata.materialDesign.floatLabel`;
139
+ - `metadata.materialDesign.subscriptSizing`.
140
+
141
+ ### Ação
142
+
143
+ Configure a metadata do campo ou a politica global do filtro:
144
+
145
+ ```json
146
+ {
147
+ "materialDesign": {
148
+ "floatLabel": "always",
149
+ "subscriptSizing": "dynamic"
150
+ }
151
+ }
152
+ ```
153
+
154
+ Nao corrija deslocando label, prefixo, sufixo ou notch por CSS local. Esse tipo
155
+ de patch acopla o host a detalhes internos do Angular Material e tende a quebrar
156
+ em upgrades.
157
+
121
158
  ## Sintoma: campo inline não aparece na barra compacta
122
159
 
123
160
  ### Causa provável
@@ -26,6 +26,9 @@ class TableAgenticAuthoringTurnFlow {
26
26
  ?.map((field) => this.toAiJsonObject(field))
27
27
  .filter((field) => Object.keys(field).length > 0);
28
28
  const contextHints = this.optionalJsonObject(this.adapter.getAuthoringContext?.());
29
+ if (this.shouldRouteToGovernedDecision(prompt, contextHints)) {
30
+ return this.toGovernedDecisionHandoff(prompt, request);
31
+ }
29
32
  const response = await firstValueFrom(this.aiApi.getPatch({
30
33
  componentId,
31
34
  componentType,
@@ -173,9 +176,26 @@ class TableAgenticAuthoringTurnFlow {
173
176
  }
174
177
  compileAdapterResponse(response) {
175
178
  const compiled = this.adapter.compileAiResponse?.(response);
179
+ if (!compiled && response.patch && Object.keys(response.patch).length > 0) {
180
+ return {
181
+ type: 'error',
182
+ message: 'A tabela exige componentEditPlan validado pelo manifesto antes de gerar patch local.',
183
+ warnings: [
184
+ 'free-table-patch-rejected',
185
+ 'Use componentEditPlan validado contra PRAXIS_TABLE_AUTHORING_MANIFEST.',
186
+ ],
187
+ };
188
+ }
176
189
  if (!compiled) {
177
190
  return response;
178
191
  }
192
+ if (compiled.type === 'error') {
193
+ return {
194
+ type: 'error',
195
+ message: compiled.message || 'O componentEditPlan da tabela nao passou na validacao de capacidades.',
196
+ warnings: compiled.warnings,
197
+ };
198
+ }
179
199
  const warnings = [
180
200
  ...(response.warnings ?? []),
181
201
  ...(compiled.warnings ?? []),
@@ -183,6 +203,7 @@ class TableAgenticAuthoringTurnFlow {
183
203
  return {
184
204
  ...response,
185
205
  ...compiled,
206
+ patch: compiled.patch,
186
207
  warnings: warnings.length ? warnings : undefined,
187
208
  };
188
209
  }
@@ -251,6 +272,85 @@ class TableAgenticAuthoringTurnFlow {
251
272
  ...(rowCount !== undefined ? { rowCount } : {}),
252
273
  };
253
274
  }
275
+ shouldRouteToGovernedDecision(prompt, contextHints) {
276
+ const normalized = prompt.normalize('NFD').replace(/[\u0300-\u036f]/g, '').toLowerCase();
277
+ const recommendedFlow = this.toRecord(contextHints?.['domainCatalog'])?.['recommendedAuthoringFlow'];
278
+ if (recommendedFlow === 'shared_rule_authoring')
279
+ return true;
280
+ return [
281
+ 'regra',
282
+ 'politica',
283
+ 'policy',
284
+ 'compliance',
285
+ 'lgpd',
286
+ 'privacidade',
287
+ 'aprovacao',
288
+ 'aprovar',
289
+ 'publicar',
290
+ 'materializar',
291
+ 'enforcement',
292
+ 'validacao de negocio',
293
+ 'validar negocio',
294
+ 'elegibilidade',
295
+ 'permissao',
296
+ 'acesso',
297
+ ].some((term) => normalized.includes(term));
298
+ }
299
+ toGovernedDecisionHandoff(prompt, request) {
300
+ const message = 'Esse pedido parece alterar uma decisao de negocio compartilhada. A tabela pode ajudar a descrever o alvo, mas a regra deve seguir pelo fluxo governado de domain-rules antes de qualquer materializacao runtime.';
301
+ return {
302
+ state: 'clarification',
303
+ phase: 'clarify',
304
+ sessionId: request.sessionId,
305
+ assistantMessage: message,
306
+ statusText: 'Handoff governado necessario.',
307
+ canApply: false,
308
+ quickReplies: [
309
+ {
310
+ id: 'shared-rule-handoff',
311
+ label: 'Continuar como regra governada',
312
+ prompt,
313
+ kind: 'shared-rule-handoff',
314
+ description: 'Criar intake de domain-rules em vez de aplicar patch local na tabela.',
315
+ icon: 'rule',
316
+ tone: 'warning',
317
+ contextHints: {
318
+ flowId: 'shared_rule_authoring',
319
+ source: 'praxis-table',
320
+ recommendedAction: 'domain-rules/intake',
321
+ },
322
+ },
323
+ ],
324
+ clarificationQuestions: [
325
+ {
326
+ id: 'table-governed-rule-confirmation',
327
+ type: 'confirm',
328
+ label: 'Deseja continuar pelo fluxo governado de regras compartilhadas?',
329
+ description: 'Esse caminho permite intake, simulacao, aprovacao/publicacao, materializacao e validacao de enforcement.',
330
+ required: true,
331
+ options: [
332
+ {
333
+ id: 'shared-rule-handoff',
334
+ label: 'Sim, continuar governado',
335
+ value: prompt,
336
+ description: 'Nao aplicar como patch local da tabela.',
337
+ contextHints: {
338
+ flowId: 'shared_rule_authoring',
339
+ source: 'praxis-table',
340
+ },
341
+ },
342
+ ],
343
+ },
344
+ ],
345
+ diagnostics: {
346
+ governedDecisionHandoff: {
347
+ flowId: 'shared_rule_authoring',
348
+ sourcePrompt: prompt,
349
+ sourceComponent: 'praxis-table',
350
+ },
351
+ },
352
+ };
353
+ }
254
354
  optionalJsonObject(value) {
255
355
  if (value === undefined || value === null) {
256
356
  return undefined;