@oondemand/create-central-oon 0.3.11 → 0.3.14

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.
@@ -2,7 +2,12 @@
2
2
 
3
3
  Este documento é a referência operacional para o Codex criar ou alterar manifestos do frontend OonCore.
4
4
 
5
- Use este arquivo junto com `FRONTEND_PATTERNS.md`. O arquivo de padrões explica como pensar a UI; este arquivo lista as opções do contrato atual.
5
+ Use este arquivo junto com `FRONTEND_PATTERNS.md`. O arquivo de padrões explica como pensar a UI; este arquivo lista as opções do contrato atual e das extensões planejadas para UX avançada.
6
+
7
+ Para modal com abas, grids relacionados editáveis e ações por linha, leia também:
8
+
9
+ - `ADVANCED_UX_PATTERNS.md`
10
+ - `DETAIL_MODAL_AND_RELATED_GRIDS.md`
6
11
 
7
12
  ## Arquivos envolvidos
8
13
 
@@ -40,8 +45,9 @@ Para Codex, a ordem recomendada é:
40
45
 
41
46
  1. Começar pelo `central.ui.json`.
42
47
  2. Usar `pages` e `blocks` quando precisar de UI v2.
43
- 3. Usar registry/overrides em TypeScript quando precisar de componentes customizados.
44
- 4. Evitar recriar shell, rotas, menu, grid, cards ou formulários manualmente.
48
+ 3. Usar `collections[].list`, `collections[].detailModal`, `form.groups`, `relations` e `relatedGrid` para UX operacional avançada.
49
+ 4. Usar registry/overrides em TypeScript quando precisar de componentes customizados.
50
+ 5. Evitar recriar shell, rotas, menu, grid, cards ou formulários manualmente.
45
51
 
46
52
  ## CentralUiManifest
47
53
 
@@ -57,13 +63,7 @@ interface CentralUiManifest {
57
63
  registry?: OonComponentRegistry;
58
64
  navigation?: OonNavigationConfig;
59
65
  pages?: OonPageDef[];
60
- collections?: Array<{
61
- model: string;
62
- mode?: CollectionMode;
63
- path?: string;
64
- label?: string;
65
- section?: string;
66
- }>;
66
+ collections?: OonCollectionManifestDef[];
67
67
  pipelines?: Array<{
68
68
  name?: string;
69
69
  model?: string;
@@ -85,7 +85,7 @@ interface CentralUiManifest {
85
85
  }
86
86
  ```
87
87
 
88
- ### Opções de raiz
88
+ ## Opções de raiz
89
89
 
90
90
  | Campo | Tipo | Uso |
91
91
  | --- | --- | --- |
@@ -96,7 +96,7 @@ interface CentralUiManifest {
96
96
  | `layout` | `OonLayoutConfig` | Shell, sidebar, topbar, header, footer e slots. |
97
97
  | `navigation` | `OonNavigationConfig` | Modo do menu e itens manuais. |
98
98
  | `pages` | `OonPageDef[]` | Páginas por blocos da UI v2. |
99
- | `collections` | `Array` | Atalho para gerar views de coleção. |
99
+ | `collections` | `Array` | Views de coleção, simples ou avançadas. |
100
100
  | `pipelines` | `Array` | Atalho para gerar esteiras. |
101
101
  | `documents` | `Array` | Atalho para gerar documentos. |
102
102
 
@@ -125,34 +125,6 @@ interface OonLayoutConfig {
125
125
  | `content-only` | Experiência focada em conteúdo. |
126
126
  | `custom:<key>` | Shell registrado em `registry.layoutSlots`. |
127
127
 
128
- ### Slots
129
-
130
- | Campo | Valores | Uso |
131
- | --- | --- | --- |
132
- | `sidebar` | `core`, `none`, `custom:<key>` | Menu lateral. |
133
- | `topbar` | `core`, `none`, `custom:<key>` | Barra superior. |
134
- | `header` | `none`, `custom:<key>` | Header custom acima da página. |
135
- | `footer` | `core`, `none`, `custom:<key>` | Rodapé. |
136
- | `assistant` | `core`, `none`, `custom:<key>` | Assistente/copiloto, quando suportado. |
137
- | `main` | `core` | Área principal do Core. |
138
-
139
- ### Exemplo
140
-
141
- ```json
142
- {
143
- "schemaVersion": 2,
144
- "name": "Meus Apps",
145
- "slug": "meus-apps",
146
- "layout": {
147
- "shell": "portal",
148
- "sidebar": "none",
149
- "topbar": "none",
150
- "header": "custom:MeusAppsHeader",
151
- "footer": "core"
152
- }
153
- }
154
- ```
155
-
156
128
  ## Navigation
157
129
 
158
130
  ```ts
@@ -164,52 +136,24 @@ interface OonNavigationConfig {
164
136
  }
165
137
  ```
166
138
 
167
- ### Modos
168
-
169
- | Valor | Comportamento |
170
- | --- | --- |
171
- | `auto` | Core gera menu a partir de coleções, documentos, esteiras, dashboards e páginas. |
172
- | `manual` | Usa apenas `navigation.items`. |
173
- | `mixed` | Combina menu automático com itens manuais. |
139
+ ## Collections no central.ui.json
174
140
 
175
- ## Menu item
141
+ Atalho para coleções simples ou avançadas.
176
142
 
177
143
  ```ts
178
- interface OonMenuItem {
179
- label: string;
180
- href: string;
181
- icon?: ReactNode;
144
+ interface OonCollectionManifestDef {
145
+ model: string;
146
+ mode?: "full" | "minimal" | "dynamic";
147
+ path?: string;
148
+ label?: string;
182
149
  section?: string;
183
- order?: number;
184
- permissions?: string[];
185
- children?: OonMenuItem[];
186
- badge?: string | number;
187
- external?: boolean;
188
- hidden?: boolean;
189
- action?: string;
190
- renderer?: string;
150
+ list?: OonCollectionListConfig;
151
+ relations?: Record<string, OonRelationDef>;
152
+ detailModal?: OonDetailModalConfig;
191
153
  }
192
154
  ```
193
155
 
194
- ### Opções de menu
195
-
196
- | Campo | Uso |
197
- | --- | --- |
198
- | `label` | Texto do menu. |
199
- | `href` | Rota interna ou URL. |
200
- | `section` | Agrupamento visual. |
201
- | `order` | Ordenação. |
202
- | `permissions` | Permissões exigidas. |
203
- | `children` | Subitens. |
204
- | `badge` | Indicador simples. |
205
- | `external` | Link externo. |
206
- | `hidden` | Oculta item sem remover do manifesto. |
207
- | `action` | Chave de ação. |
208
- | `renderer` | Renderer customizado em `registry.menuRenderers`. |
209
-
210
- ## Collections no central.ui.json
211
-
212
- Atalho simples para coleções.
156
+ ### Exemplo simples
213
157
 
214
158
  ```json
215
159
  {
@@ -225,467 +169,278 @@ Atalho simples para coleções.
225
169
  }
226
170
  ```
227
171
 
228
- ### Campos aceitos no atalho `collections[]`
229
-
230
- | Campo | Tipo | Uso |
231
- | --- | --- | --- |
232
- | `model` | `string` | Model do backend. |
233
- | `mode` | `full`, `minimal`, `dynamic` | Modo de renderização. |
234
- | `path` | `string` | Rota. |
235
- | `label` | `string` | Nome no menu/tela. |
236
- | `section` | `string` | Grupo do menu. |
237
-
238
- ## CollectionViewDef completo
239
-
240
- Contrato interno completo quando a coleção já está em `ui.views` ou em blocos.
172
+ ### Exemplo avançado
241
173
 
242
- ```ts
243
- interface CollectionViewDef {
244
- type: "collection";
245
- id?: string;
246
- model: string;
247
- label?: string;
248
- path?: string;
249
- icon?: ReactNode;
250
- section?: string;
251
- mode?: "full" | "minimal" | "dynamic";
252
- columns?: OonColumnDef[];
253
- form?: OonFormFieldDef[];
254
- importExport?: boolean;
255
- renderer?: string;
256
- cardRenderer?: string;
257
- actions?: OonActionDef[];
258
- permissions?: string[];
259
- }
260
- ```
261
-
262
- ### Column
263
-
264
- ```ts
265
- interface OonColumnDef {
266
- field: string;
267
- label?: string;
268
- kind?: FieldKind;
269
- sortable?: boolean;
270
- width?: number;
271
- renderer?: string;
272
- permissions?: string[];
174
+ ```json
175
+ {
176
+ "collections": [
177
+ {
178
+ "model": "OrcamentoProjeto",
179
+ "mode": "dynamic",
180
+ "path": "/orcamentos-projetos",
181
+ "label": "Orçamentos/Projetos",
182
+ "section": "Operação",
183
+ "list": {
184
+ "filters": [
185
+ {
186
+ "field": "tipoRegistro",
187
+ "label": "Tipo",
188
+ "type": "select",
189
+ "options": [
190
+ { "label": "Todos", "value": "" },
191
+ { "label": "Orçamento", "value": "Orçamento" },
192
+ { "label": "Projeto", "value": "Projeto" }
193
+ ]
194
+ }
195
+ ],
196
+ "rowActions": [
197
+ { "type": "openDetailModal", "icon": "edit", "label": "Editar", "initialTab": "resumo" }
198
+ ]
199
+ },
200
+ "relations": {
201
+ "itens": { "model": "OrcamentoItem", "foreignKey": "projetoId", "parentKey": "_id" },
202
+ "pagamentos": { "model": "Pagamento", "foreignKey": "projetoId", "parentKey": "_id" }
203
+ },
204
+ "detailModal": {
205
+ "enabled": true,
206
+ "titleField": "nome",
207
+ "defaultTab": "resumo",
208
+ "tabs": []
209
+ }
210
+ }
211
+ ]
273
212
  }
274
213
  ```
275
214
 
276
- ### Form field
277
-
278
- ```ts
279
- interface OonFormFieldDef {
280
- field: string;
281
- label?: string;
282
- kind?: FieldKind;
283
- required?: boolean;
284
- options?: string[];
285
- ref?: string;
286
- renderer?: string;
287
- permissions?: string[];
288
- }
289
- ```
215
+ ## list
290
216
 
291
- ### Action
217
+ Configura filtros, colunas e ações do grid principal.
292
218
 
293
219
  ```ts
294
- interface OonActionDef {
295
- id: string;
296
- label?: string;
297
- renderer?: string;
298
- permissions?: string[];
299
- action?: string;
220
+ interface OonCollectionListConfig {
221
+ filters?: OonListFilterDef[];
222
+ columns?: Array<string | OonColumnDef>;
223
+ rowActions?: OonRowActionDef[];
300
224
  }
301
225
  ```
302
226
 
303
- ## Documents
227
+ ### list.filters
304
228
 
305
229
  ```json
306
230
  {
307
- "documents": [
308
- {
309
- "model": "DocumentoFiscal",
310
- "path": "/documentos-fiscais",
311
- "label": "Documentos Fiscais",
312
- "section": "Fiscal",
313
- "approval": true,
314
- "attachments": true
315
- }
231
+ "field": "tipoRegistro",
232
+ "label": "Tipo",
233
+ "type": "select",
234
+ "options": [
235
+ { "label": "Todos", "value": "" },
236
+ { "label": "Orçamento", "value": "Orçamento" }
316
237
  ]
317
238
  }
318
239
  ```
319
240
 
320
- ### Opções
241
+ Tipos previstos:
321
242
 
322
- | Campo | Uso |
323
- | --- | --- |
324
- | `model` | Model do documento. |
325
- | `mode` | Modo futuro/reservado. |
326
- | `path` | Rota. |
327
- | `label` | Nome exibido. |
328
- | `section` | Grupo do menu. |
329
- | `approval` | Habilita comportamento de aprovação. |
330
- | `attachments` | Habilita anexos. |
243
+ - `text`
244
+ - `select`
245
+ - `date`
246
+ - `dateRange`
247
+ - `numberRange`
248
+ - `boolean`
249
+ - `ref`
331
250
 
332
- ## Pipelines
251
+ ### list.rowActions
333
252
 
334
253
  ```json
335
254
  {
336
- "pipelines": [
337
- {
338
- "model": "Pedido",
339
- "stageField": "status",
340
- "path": "/pedidos/esteira",
341
- "label": "Esteira de Pedidos",
342
- "section": "Operação"
343
- }
344
- ]
255
+ "type": "openDetailModal",
256
+ "label": "Editar",
257
+ "icon": "edit",
258
+ "initialTab": "resumo"
345
259
  }
346
260
  ```
347
261
 
348
- ### Opções
349
-
350
- | Campo | Uso |
351
- | --- | --- |
352
- | `name` | Nome alternativo usado como model quando `model` não for informado. |
353
- | `model` | Model dos cards/tickets. |
354
- | `mode` | Modo futuro/reservado. |
355
- | `stageField` | Campo que define a coluna/etapa. |
356
- | `path` | Rota. |
357
- | `label` | Nome exibido. |
358
- | `section` | Grupo do menu. |
359
-
360
- ## PipelineViewDef completo
262
+ ## relations
361
263
 
362
- ```ts
363
- interface PipelineViewDef {
364
- type: "pipeline";
365
- id?: string;
366
- model: string;
367
- label?: string;
368
- path?: string;
369
- icon?: ReactNode;
370
- section?: string;
371
- stageField?: string;
372
- stages?: Array<{ id: string; label: string }>;
373
- permissions?: string[];
374
- }
375
- ```
264
+ Relações nomeadas entre model pai e models filhos.
376
265
 
377
- ## DocumentViewDef completo
378
-
379
- ```ts
380
- interface DocumentViewDef {
381
- type: "document";
382
- id?: string;
383
- model: string;
384
- label?: string;
385
- path?: string;
386
- icon?: ReactNode;
387
- section?: string;
388
- approval?: boolean;
389
- attachments?: boolean;
390
- permissions?: string[];
391
- }
392
- ```
393
-
394
- ## DashboardViewDef
395
-
396
- ```ts
397
- interface DashboardViewDef {
398
- type: "dashboard";
399
- id?: string;
400
- label?: string;
401
- path?: string;
402
- icon?: ReactNode;
403
- section?: string;
404
- widgets?: DashboardWidgetDef[];
405
- }
406
-
407
- interface DashboardWidgetDef {
408
- id: string;
409
- label: string;
410
- model?: string;
411
- kind?: "count" | "sum" | "custom";
412
- field?: string;
413
- render?: () => ReactNode;
266
+ ```json
267
+ {
268
+ "relations": {
269
+ "itens": {
270
+ "model": "OrcamentoItem",
271
+ "foreignKey": "projetoId",
272
+ "parentKey": "_id"
273
+ }
274
+ }
414
275
  }
415
276
  ```
416
277
 
417
- Observação: `render` é função React e não deve estar em JSON. Use somente em configuração TypeScript.
278
+ ## detailModal
418
279
 
419
- ## Pages UI v2
420
-
421
- Páginas permitem compor rotas por blocos.
280
+ Configura modal de detalhe/criação com abas.
422
281
 
423
282
  ```ts
424
- interface OonPageDef {
425
- id?: string;
426
- path: string;
427
- label?: string;
428
- title?: string;
429
- section?: string;
430
- icon?: ReactNode;
431
- layout?: "default" | "content-only" | "portal";
432
- blocks?: OonBlockDef[];
433
- component?: string;
434
- permissions?: string[];
435
- hidden?: boolean;
436
- order?: number;
283
+ interface OonDetailModalConfig {
284
+ enabled?: boolean;
285
+ titleField?: string;
286
+ size?: "md" | "lg" | "xl" | "full";
287
+ defaultTab?: string;
288
+ tabs: OonDetailTabDef[];
437
289
  }
438
290
  ```
439
291
 
440
- ### Opções de página
292
+ ### Abas suportadas
441
293
 
442
- | Campo | Uso |
443
- | --- | --- |
444
- | `id` | Identificador opcional. |
445
- | `path` | Rota. |
446
- | `label` | Nome no menu. |
447
- | `title` | Título da página. |
448
- | `section` | Grupo do menu. |
449
- | `layout` | Layout da página. |
450
- | `blocks` | Blocos renderizados. |
451
- | `component` | Componente custom em `registry.pageComponents`. |
452
- | `permissions` | Permissões da página. |
453
- | `hidden` | Oculta do menu. |
454
- | `order` | Ordenação no menu. |
455
-
456
- ## Blocks UI v2
457
-
458
- Tipos atuais:
459
-
460
- ```ts
461
- type OonBlockDef =
462
- | OonCustomBlockDef
463
- | OonCollectionDataGridBlockDef
464
- | OonCollectionCardsBlockDef
465
- | OonSectionBlockDef
466
- | OonMarkdownBlockDef
467
- | OonGenericBlockDef;
468
- ```
469
-
470
- ### Campos comuns
471
-
472
- ```ts
473
- interface OonBaseBlockDef {
474
- id?: string;
475
- title?: string;
476
- permissions?: string[];
477
- hidden?: boolean;
478
- }
479
- ```
294
+ - `summary`
295
+ - `form`
296
+ - `relatedGrid`
297
+ - `readonlyGrid`
298
+ - `customComponent`
480
299
 
481
- ### custom
300
+ ### Aba summary
482
301
 
483
302
  ```json
484
303
  {
485
- "type": "custom",
486
- "component": "MeusAppsHero",
487
- "props": {
488
- "titulo": "Bem-vindo"
489
- }
304
+ "id": "resumo",
305
+ "label": "Resumo",
306
+ "type": "summary",
307
+ "cards": [
308
+ { "label": "Itens", "source": "relatedCount", "relation": "itens" },
309
+ { "label": "Total", "field": "total", "format": "currency" }
310
+ ]
490
311
  }
491
312
  ```
492
313
 
493
- Renderiza componente de `registry.pageComponents.MeusAppsHero`.
494
-
495
- ### collection.datagrid
314
+ ### Aba form
496
315
 
497
316
  ```json
498
317
  {
499
- "type": "collection.datagrid",
500
- "model": "App",
501
- "mode": "dynamic",
502
- "title": "Apps"
318
+ "id": "dados",
319
+ "label": "Dados Principais",
320
+ "type": "form",
321
+ "groups": [
322
+ { "label": "Identificação", "fields": ["codigo", "nome", "status"] }
323
+ ]
503
324
  }
504
325
  ```
505
326
 
506
- Opções:
327
+ ### Aba relatedGrid
507
328
 
508
- ```ts
509
- interface OonCollectionDataGridBlockDef {
510
- type: "collection.datagrid";
511
- model: string;
512
- mode?: CollectionMode;
513
- renderer?: string;
514
- columns?: OonColumnDef[];
515
- form?: OonFormFieldDef[];
329
+ ```json
330
+ {
331
+ "id": "itens",
332
+ "label": "Itens",
333
+ "type": "relatedGrid",
334
+ "relation": "itens",
335
+ "editable": true,
336
+ "editMode": "inline",
337
+ "columns": [
338
+ { "field": "item", "editable": true },
339
+ { "field": "status", "editable": true },
340
+ { "field": "total", "editable": true, "format": "currency" }
341
+ ],
342
+ "rowActions": []
516
343
  }
517
344
  ```
518
345
 
519
- ### collection.cards
346
+ ### Aba readonlyGrid
520
347
 
521
348
  ```json
522
349
  {
523
- "type": "collection.cards",
524
- "model": "App",
525
- "renderer": "cards.appLauncher"
350
+ "id": "pagamentos",
351
+ "label": "Pagamentos",
352
+ "type": "readonlyGrid",
353
+ "relation": "pagamentos",
354
+ "columns": ["codigo", "descricao", "status", "valor"]
526
355
  }
527
356
  ```
528
357
 
529
- Renderiza cards usando `registry.cardRenderers.appLauncher` quando registrado.
358
+ ## rowActions
530
359
 
531
- ### section
360
+ Ações reutilizáveis em grid principal ou grids relacionados.
532
361
 
533
- ```json
534
- {
535
- "type": "section",
536
- "title": "Administração",
537
- "description": "Gestão dos apps e permissões",
538
- "blocks": [
539
- { "type": "collection.datagrid", "model": "Usuario" }
540
- ]
362
+ ```ts
363
+ interface OonRowActionDef {
364
+ id?: string;
365
+ label: string;
366
+ icon?: string;
367
+ type: "openDetailModal" | "navigate" | "apiAction" | "customAction";
368
+ method?: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
369
+ endpoint?: string;
370
+ initialTab?: string;
371
+ confirm?: { title?: string; description?: string };
372
+ disabledWhen?: OonConditionDef;
373
+ hiddenWhen?: OonConditionDef;
374
+ refresh?: string[];
375
+ roles?: string[];
376
+ permissions?: string[];
541
377
  }
542
378
  ```
543
379
 
544
- ### markdown
380
+ ### apiAction
545
381
 
546
382
  ```json
547
383
  {
548
- "type": "markdown",
549
- "content": "Texto institucional ou instruções da página."
384
+ "id": "gerarPagamento",
385
+ "label": "Gerar pagamento",
386
+ "type": "apiAction",
387
+ "method": "POST",
388
+ "endpoint": "/api/ss-eventos/orcamentos-itens/:id/gerar-pagamento",
389
+ "disabledWhen": { "field": "pagamentoId", "exists": true },
390
+ "refresh": ["self", "pagamentos", "resumo", "parent"]
550
391
  }
551
392
  ```
552
393
 
553
- ### Blocos reservados/genéricos
554
-
555
- O contrato já reserva:
556
-
557
- ```txt
558
- collection.form
559
- pipeline.board
560
- document.viewer
561
- dashboard.kpi
562
- tabs
563
- ```
564
-
565
- Esses blocos podem ser usados como contrato futuro, mas confirme suporte visual antes de depender deles em produção.
394
+ ## CollectionViewDef completo
566
395
 
567
- ## Registry
396
+ Contrato interno completo quando a coleção já está em `ui.views` ou em blocos.
568
397
 
569
398
  ```ts
570
- interface OonComponentRegistry {
571
- layoutSlots?: Record<string, OonRendererComponent<any>>;
572
- fieldRenderers?: Record<string, OonRendererComponent<any>>;
573
- cellRenderers?: Record<string, OonRendererComponent<any>>;
574
- cardRenderers?: Record<string, OonRendererComponent<any>>;
575
- actionRenderers?: Record<string, OonRendererComponent<any>>;
576
- pageComponents?: Record<string, OonRendererComponent<any>>;
577
- menuRenderers?: Record<string, OonRendererComponent<any>>;
578
- }
579
- ```
580
-
581
- ### Mapeamento de chaves
582
-
583
- | Chave no manifesto | Bucket esperado |
584
- | --- | --- |
585
- | `custom:MeusAppsHeader` | `layoutSlots` ou `pageComponents`, conforme uso. |
586
- | `fields.password` | `fieldRenderers` |
587
- | `cells.statusBadge` | `cellRenderers` |
588
- | `cards.appLauncher` | `cardRenderers` |
589
- | `actions.ativarApp` | `actionRenderers` |
590
- | `menus.portal` | `menuRenderers` |
591
-
592
- ## Exemplo v1 simples
593
-
594
- ```json
595
- {
596
- "name": "Central SS Eventos",
597
- "slug": "central-ss-eventos",
598
- "navigation": { "mode": "auto" },
599
- "collections": [
600
- { "model": "Orcamento", "label": "Orçamentos", "section": "Operação" },
601
- { "model": "Prestador", "label": "Prestadores", "section": "Cadastros" }
602
- ],
603
- "pipelines": [
604
- { "model": "Pagamento", "stageField": "status", "label": "Esteira de Pagamentos" }
605
- ],
606
- "documents": [
607
- { "model": "DocumentoFiscal", "label": "Documentos Fiscais", "approval": true, "attachments": true }
608
- ]
399
+ interface CollectionViewDef {
400
+ type: "collection";
401
+ id?: string;
402
+ model: string;
403
+ label?: string;
404
+ path?: string;
405
+ icon?: ReactNode;
406
+ section?: string;
407
+ mode?: "full" | "minimal" | "dynamic";
408
+ columns?: OonColumnDef[];
409
+ form?: OonFormFieldDef[];
410
+ importExport?: boolean;
411
+ renderer?: string;
412
+ cardRenderer?: string;
413
+ actions?: OonActionDef[];
414
+ permissions?: string[];
415
+ list?: OonCollectionListConfig;
416
+ relations?: Record<string, OonRelationDef>;
417
+ detailModal?: OonDetailModalConfig;
609
418
  }
610
419
  ```
611
420
 
612
- ## Exemplo v2 portal / Meus Apps
421
+ ## Compatibilidade
613
422
 
614
- ```json
615
- {
616
- "schemaVersion": 2,
617
- "name": "Meus Apps",
618
- "slug": "meus-apps",
619
- "layout": {
620
- "shell": "portal",
621
- "sidebar": "none",
622
- "topbar": "none",
623
- "header": "custom:MeusAppsHeader",
624
- "footer": "core"
625
- },
626
- "navigation": { "mode": "mixed" },
627
- "pages": [
628
- {
629
- "path": "/",
630
- "label": "Meus Apps",
631
- "layout": "content-only",
632
- "blocks": [
633
- { "type": "custom", "component": "MeusAppsHero" },
634
- { "type": "collection.cards", "model": "App", "renderer": "cards.appLauncher" }
635
- ]
636
- },
637
- {
638
- "path": "/admin/apps",
639
- "label": "Apps",
640
- "section": "Administração",
641
- "blocks": [
642
- { "type": "collection.datagrid", "model": "App", "mode": "dynamic" }
643
- ]
644
- }
645
- ]
646
- }
647
- ```
423
+ - Os campos `list`, `relations` e `detailModal` são opcionais.
424
+ - Manifestos existentes continuam funcionando.
425
+ - Sem `detailModal`, o Core mantém comportamento atual.
426
+ - Sem `form.groups`, o Core mantém formulário padrão.
648
427
 
649
- ## Exemplo de registry em TypeScript
650
-
651
- ```tsx
652
- import manifest from "../central.ui.json";
653
- import { manifestToConfig, start } from "@oondemand/oon-core-front";
654
- import { MeusAppsHeader } from "./overrides/MeusAppsHeader";
655
- import { AppLauncherCard } from "./overrides/AppLauncherCard";
656
-
657
- const config = manifestToConfig(manifest, {
658
- apiBaseUrl: import.meta.env.VITE_API_URL,
659
- });
660
-
661
- start({
662
- ...config,
663
- registry: {
664
- layoutSlots: {
665
- MeusAppsHeader,
666
- },
667
- cardRenderers: {
668
- "cards.appLauncher": AppLauncherCard,
669
- appLauncher: AppLauncherCard,
670
- },
671
- },
672
- });
673
- ```
428
+ ## UX avançada implementada
674
429
 
675
- ## Limitações atuais importantes
430
+ Campos aceitos em `collections[]`:
676
431
 
677
- - O atalho `collections[]` do `central.ui.json` apenas `model`, `mode`, `path`, `label` e `section`.
678
- - Para `columns`, `form`, `actions`, `renderer` e `cardRenderer`, use blocos UI v2, `ui.views` em configuração TypeScript ou evolução futura do conversor.
679
- - `registry` não deve ser escrito em JSON com componentes React. Use código TypeScript.
680
- - Blocos `collection.form`, `pipeline.board`, `document.viewer`, `dashboard.kpi` e `tabs` estão reservados no contrato, mas precisam de confirmação visual antes de uso em produção.
432
+ - `list.filters`: filtros acima do grid principal.
433
+ - `list.columns`: colunas declarativas opcionais.
434
+ - `list.rowActions`: ações por linha (`openDetailModal`, `navigate`, `apiAction`, `customAction`).
435
+ - `relations`: mapa de relações `{ model, foreignKey, parentKey }`.
436
+ - `detailModal`: modal genérica com `titleField`, `defaultTab`, `size` e `tabs`.
681
437
 
682
- ## Checklist para Codex
438
+ Tipos de aba:
683
439
 
684
- Antes de criar UI customizada, confirme:
440
+ - `summary`: cards com `field`, `relatedCount`, `relatedSum`, `relatedAvg` ou `customMetric`.
441
+ - `form`: campos ou `groups` do registro principal.
442
+ - `relatedGrid`: registros filhos, opcionalmente editáveis inline.
443
+ - `readonlyGrid`: registros filhos sem edição.
444
+ - `customComponent`: chave de componente registrado.
685
445
 
686
- - O requisito cabe em `collections`, `documents` ou `pipelines`?
687
- - O requisito cabe em `pages` + `blocks`?
688
- - O requisito cabe em renderer registrado por chave?
689
- - A permissão está declarada em página, bloco, menu ou backend?
690
- - Não há componente React serializado no JSON?
691
- - A Central continua funcionando com atualização do pacote Core?
446
+ `apiAction.endpoint` aceita `:id`, `:parentId`, `:fieldName` e `:parent.fieldName`. `refresh` aceita `self`, `parent`, `all` ou ids de abas.