jsegd-bpm 0.1.8 → 0.1.10
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 +172 -70
- package/dist/jsegd-bpm.d.ts +39 -8
- package/dist/jsegd-bpm.js +1 -1
- package/dist/jsegd-bpm.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# jsEGD BPM
|
|
2
2
|
|
|
3
|
-
Biblioteca TypeScript de runtime para desenvolvimento de formulários e workflows
|
|
4
|
-
Fornece componentes de anexo, gerenciamento de workers para datasets, controle do ciclo de vida do
|
|
3
|
+
Biblioteca TypeScript de runtime para desenvolvimento de formulários e workflows em um ambiente BPM genérico.
|
|
4
|
+
Fornece componentes de anexo, gerenciamento de workers para datasets, controle do ciclo de vida do fluxo e utilitários de formulário integrados ao DOM.
|
|
5
5
|
|
|
6
6
|
> **Dependências par obrigatórias:** [`jsegd`](https://jsegd.netlify.app) ≥ 1.1.0 e [`alpinejs`](https://alpinejs.dev) ≥ 3.0.0
|
|
7
7
|
|
|
@@ -25,7 +25,7 @@ npm install jsegd-bpm jsegd alpinejs
|
|
|
25
25
|
|
|
26
26
|
## `WorkerManager` — Consultas de Dataset em Web Workers
|
|
27
27
|
|
|
28
|
-
Gerencia um pool de Web Workers para executar consultas à API de
|
|
28
|
+
Gerencia um pool de Web Workers para executar consultas à API de datasets fora da thread principal. Suporta deduplicação de requisições, cache em IndexedDB e cancelamento HTTP real via `AbortController`.
|
|
29
29
|
|
|
30
30
|
```typescript
|
|
31
31
|
import { WorkerManager, ConstraintsType } from 'jsegd-bpm'
|
|
@@ -37,8 +37,8 @@ cache.initialize({ storeName: 'datasets', cacheExpirationMs: 60 * 60 * 1000 })
|
|
|
37
37
|
|
|
38
38
|
const manager = new WorkerManager({ poolSize: 4, cache })
|
|
39
39
|
|
|
40
|
-
const { promise, cancel } = manager.query('https://
|
|
41
|
-
datasetId: '
|
|
40
|
+
const { promise, cancel } = manager.query('https://exemplo.local', {
|
|
41
|
+
datasetId: 'dsRegistros',
|
|
42
42
|
field: ['nome', 'email', 'matricula'],
|
|
43
43
|
constraintsField: ['ativo'],
|
|
44
44
|
constraintsInitialValue: ['true'],
|
|
@@ -87,13 +87,13 @@ Botão com dropdown para upload de arquivos vinculados ao processo.
|
|
|
87
87
|
|
|
88
88
|
```html
|
|
89
89
|
<!-- Tipo de anexo fixo -->
|
|
90
|
-
<attachment-button description="
|
|
90
|
+
<attachment-button description="DOCUMENTO"></attachment-button>
|
|
91
91
|
|
|
92
92
|
<!-- Prompt ao usuário para informar a descrição no upload -->
|
|
93
93
|
<attachment-button></attachment-button>
|
|
94
94
|
|
|
95
95
|
<!-- Somente leitura: oculta botão de adicionar -->
|
|
96
|
-
<attachment-button description="
|
|
96
|
+
<attachment-button description="DOCUMENTO" noaddbutton></attachment-button>
|
|
97
97
|
```
|
|
98
98
|
|
|
99
99
|
| Atributo | Tipo | Descrição |
|
|
@@ -117,7 +117,7 @@ Tabela que lista os anexos do processo com suporte a drag-and-drop para reordena
|
|
|
117
117
|
|
|
118
118
|
## `BeforeSendValidate` — Validação com feedback visual
|
|
119
119
|
|
|
120
|
-
Integra validação de campos com o sistema de erros visuais do tema
|
|
120
|
+
Integra validação de campos com o sistema de erros visuais do tema do formulário (`data-group` / `data-helper`).
|
|
121
121
|
|
|
122
122
|
```typescript
|
|
123
123
|
import { BeforeSendValidate } from 'jsegd-bpm'
|
|
@@ -126,9 +126,9 @@ import type { FieldError } from 'jsegd-bpm'
|
|
|
126
126
|
function validarFormulario() {
|
|
127
127
|
const erros: FieldError[] = []
|
|
128
128
|
|
|
129
|
-
const
|
|
130
|
-
if (!
|
|
131
|
-
erros.push({ field:
|
|
129
|
+
const campoTexto = document.querySelector<HTMLInputElement>('[name="nome"]')
|
|
130
|
+
if (!campoTexto?.value) {
|
|
131
|
+
erros.push({ field: campoTexto, errorMessage: 'Informe o nome' })
|
|
132
132
|
}
|
|
133
133
|
|
|
134
134
|
if (erros.length > 0) {
|
|
@@ -138,13 +138,115 @@ function validarFormulario() {
|
|
|
138
138
|
}
|
|
139
139
|
```
|
|
140
140
|
|
|
141
|
-
`throwValidationError` lança sempre — use dentro de `beforeSendValidate` do
|
|
141
|
+
`throwValidationError` lança sempre — use dentro de `beforeSendValidate` do formulário ou em hooks de `WorkflowViewPatcher`.
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
## `Validator` — Validação local e de submit
|
|
146
|
+
|
|
147
|
+
O `Validator` tem dois usos complementares:
|
|
148
|
+
- `Validator.add(...)` cria um validador isolado para cada instância de `Alpine.data(...)`
|
|
149
|
+
- `Validator.getInstance().register(code, rules)` agrupa várias regras de submit sob o mesmo código numérico
|
|
150
|
+
|
|
151
|
+
```typescript
|
|
152
|
+
import { Validator } from 'jsegd-bpm'
|
|
153
|
+
|
|
154
|
+
function departamentoComponent() {
|
|
155
|
+
const regras: Record<string, () => boolean | Promise<boolean>> = {
|
|
156
|
+
taxa: () => this.possuiTaxa === '1' && !this.taxa,
|
|
157
|
+
campoExtra: () => this.possuiTaxa === '1' && this.campoExtra === 'invalido',
|
|
158
|
+
campoIndependente: () => !this.campoIndependente,
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const validadores = Validator.add(regras)
|
|
162
|
+
|
|
163
|
+
return {
|
|
164
|
+
possuiTaxa: '0',
|
|
165
|
+
taxa: '',
|
|
166
|
+
campoExtra: '',
|
|
167
|
+
campoIndependente: '',
|
|
168
|
+
temErro: false,
|
|
169
|
+
|
|
170
|
+
async init() {
|
|
171
|
+
this.$watch('possuiTaxa', async () => {
|
|
172
|
+
await validadores.validate('taxa')
|
|
173
|
+
await validadores.validate('campoExtra')
|
|
174
|
+
this.temErro = await validadores.hasError()
|
|
175
|
+
})
|
|
176
|
+
},
|
|
177
|
+
|
|
178
|
+
async onInput(field: string) {
|
|
179
|
+
await validadores.validate(field)
|
|
180
|
+
this.temErro = await validadores.hasError()
|
|
181
|
+
},
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
`hasError()` e `validate(field)` são assíncronos — armazene o resultado em uma propriedade reativa (`temErro`) para que o Alpine reflita o estado no template. Funções síncronas continuam aceitas sem alteração.
|
|
187
|
+
|
|
188
|
+
```typescript
|
|
189
|
+
import { Validator } from 'jsegd-bpm'
|
|
190
|
+
|
|
191
|
+
const cancelarSubmit = Validator.getInstance().register(100, {
|
|
192
|
+
taxa: () => this.possuiTaxa === '1' && !this.taxa,
|
|
193
|
+
// exemplo de regra assíncrona — ex.: validação remota
|
|
194
|
+
campoExtra: async () => {
|
|
195
|
+
const resultado = await verificarCampoNoServidor(this.campoExtra)
|
|
196
|
+
return resultado.valido
|
|
197
|
+
},
|
|
198
|
+
campoIndependente: () => !this.campoIndependente,
|
|
199
|
+
})
|
|
200
|
+
|
|
201
|
+
try {
|
|
202
|
+
await Validator.getInstance().execute(100)
|
|
203
|
+
} catch (error) {
|
|
204
|
+
// impede o envio do formulário
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
cancelarSubmit()
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
`register(code, rules)` aceita tanto uma função única quanto um mapa de regras — síncronas ou assíncronas. `unregister(code, rules)` remove o mesmo conjunto informado.
|
|
211
|
+
|
|
212
|
+
### Exemplo no HTML
|
|
213
|
+
|
|
214
|
+
```html
|
|
215
|
+
<section x-data="departamentoComponent()">
|
|
216
|
+
<label>
|
|
217
|
+
Possui taxa?
|
|
218
|
+
<select x-model="possuiTaxa" @change="onInput('taxa')">
|
|
219
|
+
<option value="0">Nao</option>
|
|
220
|
+
<option value="1">Sim</option>
|
|
221
|
+
</select>
|
|
222
|
+
</label>
|
|
223
|
+
|
|
224
|
+
<label>
|
|
225
|
+
Taxa
|
|
226
|
+
<input type="text" x-model="taxa" @input="onInput('taxa')" />
|
|
227
|
+
</label>
|
|
228
|
+
|
|
229
|
+
<label>
|
|
230
|
+
Campo extra
|
|
231
|
+
<input type="text" x-model="campoExtra" @input="onInput('campoExtra')" />
|
|
232
|
+
</label>
|
|
233
|
+
|
|
234
|
+
<label>
|
|
235
|
+
Campo independente
|
|
236
|
+
<input type="text" x-model="campoIndependente" @input="onInput('campoIndependente')" />
|
|
237
|
+
</label>
|
|
238
|
+
|
|
239
|
+
<p x-show="temErro">Existem campos invalidos.</p>
|
|
240
|
+
</section>
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
> Como `hasError()` agora é assíncrono, use uma propriedade reativa (`temErro`) em vez de um getter direto no `x-show`.
|
|
142
244
|
|
|
143
245
|
---
|
|
144
246
|
|
|
145
247
|
## `WorkflowViewPatcher` — Hooks no widget de processo
|
|
146
248
|
|
|
147
|
-
Substitui métodos do widget nativo `ECM_WKFView` para interceptar o ciclo de envio do
|
|
249
|
+
Substitui métodos do widget nativo `ECM_WKFView` para interceptar o ciclo de envio do fluxo sem modificar o formulário.
|
|
148
250
|
|
|
149
251
|
```typescript
|
|
150
252
|
import { WorkflowViewPatcher } from 'jsegd-bpm'
|
|
@@ -159,8 +261,8 @@ WorkflowViewPatcher.patchAll()
|
|
|
159
261
|
WorkflowViewPatcher.addBeforeValidate(async () => {
|
|
160
262
|
// Executado antes da validação do widget.
|
|
161
263
|
// Lançar exceção cancela o envio e reabilita os botões.
|
|
162
|
-
const valor = document.querySelector<HTMLInputElement>('[name="
|
|
163
|
-
if (!valor) throw new Error('Campo
|
|
264
|
+
const valor = document.querySelector<HTMLInputElement>('[name="campo_principal"]')?.value
|
|
265
|
+
if (!valor) throw new Error('Campo principal obrigatório')
|
|
164
266
|
})
|
|
165
267
|
|
|
166
268
|
WorkflowViewPatcher.addAfterValidate(async () => {
|
|
@@ -178,7 +280,7 @@ WorkflowViewPatcher.addOnMoveError(() => {
|
|
|
178
280
|
|
|
179
281
|
WorkflowViewPatcher.addOnCompletionLinkAdd((message) => {
|
|
180
282
|
// Adiciona links à tela de conclusão após a movimentação
|
|
181
|
-
message.links.push({ description: 'Abrir
|
|
283
|
+
message.links.push({ description: 'Abrir próxima etapa', href: '/proxima-etapa' })
|
|
182
284
|
})
|
|
183
285
|
|
|
184
286
|
// Ao desmontar o formulário (SPAs): restaura os métodos originais
|
|
@@ -217,12 +319,12 @@ onde `{domínio}` é o identificador do **agregado de negócio** ao qual o campo
|
|
|
217
319
|
|
|
218
320
|
| Campo no DOM | Domínio | Atributo no modelo |
|
|
219
321
|
| ------------------- | ----------- | ------------------ |
|
|
220
|
-
| `
|
|
221
|
-
| `
|
|
222
|
-
| `vigencia_prazo`
|
|
223
|
-
| `
|
|
322
|
+
| `departamento_indice` | `departamento` | `indice` |
|
|
323
|
+
| `area_cep` | `area` | `cep` |
|
|
324
|
+
| `vigencia_prazo` | `vigencia` | `prazo` |
|
|
325
|
+
| `cadastro_cep` | `cadastro` | `cep` |
|
|
224
326
|
|
|
225
|
-
> **Importante:** o `{domínio}` é o prefixo persistido no banco
|
|
327
|
+
> **Importante:** o `{domínio}` é o prefixo persistido no banco — é parte integrante do nome da coluna e **não deve ser omitido** no HTML. O `load` e o `save` gerenciam a conversão entre o nome DOM e o atributo do modelo de forma transparente.
|
|
226
328
|
|
|
227
329
|
O `moduleId` do workflow (quem exibe o campo na tela) e o `stage` (em que fase do processo estamos) são **independentes** do domínio do campo — um mesmo módulo pode exibir campos de múltiplos domínios.
|
|
228
330
|
|
|
@@ -236,13 +338,13 @@ Lê e escreve diretamente nos elementos `input`, `select` e `textarea` do formul
|
|
|
236
338
|
```typescript
|
|
237
339
|
import { BpmFormRepository } from 'jsegd-bpm'
|
|
238
340
|
|
|
239
|
-
interface
|
|
341
|
+
interface Departamento {
|
|
240
342
|
indice: string
|
|
241
343
|
primeiroPagamento: string
|
|
242
344
|
reajuste: string
|
|
243
345
|
}
|
|
244
346
|
|
|
245
|
-
interface
|
|
347
|
+
interface Area {
|
|
246
348
|
cep: string
|
|
247
349
|
logradouro: string
|
|
248
350
|
municipio: string
|
|
@@ -255,22 +357,22 @@ interface ItemTabela {
|
|
|
255
357
|
|
|
256
358
|
const repo = new BpmFormRepository()
|
|
257
359
|
|
|
258
|
-
// Ler domínio '
|
|
259
|
-
// DOM:
|
|
260
|
-
const
|
|
261
|
-
console.log(
|
|
262
|
-
console.log(
|
|
360
|
+
// Ler domínio 'departamento' — lê campos com name="departamento_*" do DOM
|
|
361
|
+
// DOM: departamento_indice, departamento_primeiro_pagamento, departamento_reajuste
|
|
362
|
+
const departamento = repo.load<Departamento>('departamento')
|
|
363
|
+
console.log(departamento.indice) // valor de input[name="departamento_indice"]
|
|
364
|
+
console.log(departamento.primeiroPagamento) // valor de input[name="departamento_primeiro_pagamento"]
|
|
263
365
|
|
|
264
|
-
// Ler domínio '
|
|
265
|
-
const
|
|
266
|
-
console.log(
|
|
366
|
+
// Ler domínio 'area' — desambigua de 'cadastro_cep', 'departamento_cep', etc.
|
|
367
|
+
const area = repo.load<Area>('area')
|
|
368
|
+
console.log(area.cep) // valor de input[name="area_cep"]
|
|
267
369
|
|
|
268
370
|
// Ler tabela filha
|
|
269
371
|
const itens = repo.loadTable<ItemTabela>('tabItens')
|
|
270
372
|
|
|
271
373
|
// Persistir no DOM — save converte camelCase → prefixo+snake_case
|
|
272
|
-
repo.save<
|
|
273
|
-
// escreve: input[name="
|
|
374
|
+
repo.save<Departamento>('departamento', { indice: 'IGPM', primeiroPagamento: '2026-02-01' })
|
|
375
|
+
// escreve: input[name="departamento_indice"] e input[name="departamento_primeiro_pagamento"]
|
|
274
376
|
|
|
275
377
|
// Substituir linhas da tabela filha
|
|
276
378
|
repo.saveTable<ItemTabela>('tabItens', [
|
|
@@ -314,18 +416,18 @@ Processos existentes cujos campos não seguem a convenção `{domínio}_{atribut
|
|
|
314
416
|
```typescript
|
|
315
417
|
import { loadFields } from 'jsegd-bpm'
|
|
316
418
|
|
|
317
|
-
interface
|
|
419
|
+
interface Requisitante {
|
|
318
420
|
nome: string
|
|
319
421
|
email: string
|
|
320
422
|
}
|
|
321
423
|
|
|
322
|
-
// Campo legado: sufixo em vez de prefixo —
|
|
323
|
-
const dados = loadFields<
|
|
324
|
-
nome: '
|
|
325
|
-
email: '
|
|
424
|
+
// Campo legado: sufixo em vez de prefixo — nome_requisitante, email_requisitante
|
|
425
|
+
const dados = loadFields<Requisitante>({
|
|
426
|
+
nome: 'nome_requisitante',
|
|
427
|
+
email: 'email_requisitante',
|
|
326
428
|
})
|
|
327
|
-
// dados.nome === valor de input[name="
|
|
328
|
-
// dados.email === valor de input[name="
|
|
429
|
+
// dados.nome === valor de input[name="nome_requisitante"]
|
|
430
|
+
// dados.email === valor de input[name="email_requisitante"]
|
|
329
431
|
```
|
|
330
432
|
|
|
331
433
|
> **Regra:** `loadFields` **não faz parte de `IFormRepository`** — é um utilitário de compatibilidade para processos legados. Novos processos devem usar `repository.load(section)` exclusivamente.
|
|
@@ -346,59 +448,59 @@ import { defineWorkflowConfig } from 'jsegd-bpm'
|
|
|
346
448
|
export const workflowConfig = defineWorkflowConfig({
|
|
347
449
|
modules: [
|
|
348
450
|
{
|
|
349
|
-
moduleId: '
|
|
451
|
+
moduleId: 'areaInicial',
|
|
350
452
|
label: 'Dados Iniciais',
|
|
351
453
|
icon: 'file-text',
|
|
352
454
|
order: 1,
|
|
353
455
|
showInNavigation: true,
|
|
354
456
|
renderContent: true,
|
|
355
|
-
contentIncludePath: '
|
|
457
|
+
contentIncludePath: 'area-inicial.html',
|
|
356
458
|
},
|
|
357
459
|
{
|
|
358
|
-
moduleId: '
|
|
359
|
-
label: '
|
|
460
|
+
moduleId: 'areaA',
|
|
461
|
+
label: 'Área A',
|
|
360
462
|
icon: 'gavel',
|
|
361
463
|
order: 2,
|
|
362
464
|
showInNavigation: true,
|
|
363
465
|
renderContent: true,
|
|
364
|
-
contentIncludePath: '
|
|
466
|
+
contentIncludePath: 'area-a.html',
|
|
365
467
|
},
|
|
366
468
|
{
|
|
367
|
-
moduleId: '
|
|
368
|
-
label: '
|
|
469
|
+
moduleId: 'areaB',
|
|
470
|
+
label: 'Área B',
|
|
369
471
|
icon: 'dollar-sign',
|
|
370
472
|
order: 3,
|
|
371
473
|
showInNavigation: true,
|
|
372
474
|
renderContent: true,
|
|
373
|
-
contentIncludePath: '
|
|
475
|
+
contentIncludePath: 'area-b.html',
|
|
374
476
|
},
|
|
375
477
|
],
|
|
376
478
|
activities: [
|
|
377
479
|
{
|
|
378
480
|
sequence: 9,
|
|
379
|
-
description: '
|
|
481
|
+
description: 'Etapa inicial',
|
|
380
482
|
kind: 'human',
|
|
381
|
-
stage: '
|
|
382
|
-
modules: { visible: ['
|
|
483
|
+
stage: 'inicio',
|
|
484
|
+
modules: { visible: ['areaInicial'], editable: ['areaInicial'], active: 'areaInicial' },
|
|
383
485
|
},
|
|
384
486
|
{
|
|
385
487
|
sequence: 53,
|
|
386
|
-
description: '
|
|
488
|
+
description: 'Análise da área A',
|
|
387
489
|
kind: 'human',
|
|
388
|
-
stage: '
|
|
389
|
-
modules: { visible: ['
|
|
490
|
+
stage: 'analiseA',
|
|
491
|
+
modules: { visible: ['areaInicial', 'areaA'], editable: ['areaA'], active: 'areaA' },
|
|
390
492
|
},
|
|
391
493
|
{
|
|
392
494
|
sequence: 61,
|
|
393
|
-
description: 'Análise
|
|
495
|
+
description: 'Análise da área B',
|
|
394
496
|
kind: 'human',
|
|
395
|
-
stage: '
|
|
396
|
-
modules: { visible: ['
|
|
497
|
+
stage: 'analiseB',
|
|
498
|
+
modules: { visible: ['areaInicial', 'areaA', 'areaB'], editable: ['areaB'] },
|
|
397
499
|
},
|
|
398
500
|
{ sequence: 88, description: 'Roteamento interno', kind: 'routing' },
|
|
399
501
|
],
|
|
400
502
|
} as const)
|
|
401
|
-
// Tipo de ModuleId inferido: '
|
|
503
|
+
// Tipo de ModuleId inferido: 'areaInicial' | 'areaA' | 'areaB'
|
|
402
504
|
```
|
|
403
505
|
|
|
404
506
|
**`ActivityKind`** — controla o comportamento na barra de progresso:
|
|
@@ -425,10 +527,10 @@ const currentSequence: number = parent.ECM.workflowView.processDefinition.cardIn
|
|
|
425
527
|
const historyType: string | null = parent.ECM.workflowView.processDefinition.historyType ?? null
|
|
426
528
|
|
|
427
529
|
const completedStages = extractCompletedStages(rawHistory, workflowConfig.activities)
|
|
428
|
-
// Ex: ['
|
|
530
|
+
// Ex: ['inicio', 'analiseA']
|
|
429
531
|
|
|
430
532
|
const currentStage = extractCurrentStage(currentSequence, historyType, workflowConfig.activities)
|
|
431
|
-
// Ex: { stage: '
|
|
533
|
+
// Ex: { stage: 'analiseB', status: 'current' }
|
|
432
534
|
|
|
433
535
|
const processHistory: ProcessHistory = { completedStages, currentStage }
|
|
434
536
|
```
|
|
@@ -445,9 +547,9 @@ import { resolveWorkflowScreen } from 'jsegd-bpm'
|
|
|
445
547
|
const screen = resolveWorkflowScreen(workflowConfig, currentSequence, processHistory)
|
|
446
548
|
|
|
447
549
|
// Visibilidade e editabilidade
|
|
448
|
-
screen.isVisible('
|
|
449
|
-
screen.isEditable('
|
|
450
|
-
screen.isActive('
|
|
550
|
+
screen.isVisible('areaA') // true
|
|
551
|
+
screen.isEditable('areaB') // false
|
|
552
|
+
screen.isActive('areaA') // true
|
|
451
553
|
|
|
452
554
|
// Estado da atividade
|
|
453
555
|
screen.isAwaitingSystem() // true quando kind === 'system' ou status === 'waiting'
|
|
@@ -456,9 +558,9 @@ screen.isInSupport() // true para kind === 'support'
|
|
|
456
558
|
// Barra de progresso
|
|
457
559
|
const stages = screen.getProgressBarStages()
|
|
458
560
|
// [
|
|
459
|
-
// { stage: '
|
|
460
|
-
// { stage: '
|
|
461
|
-
// { stage: '
|
|
561
|
+
// { stage: 'inicio', status: 'done' },
|
|
562
|
+
// { stage: 'analiseA', status: 'done' },
|
|
563
|
+
// { stage: 'analiseB', status: 'current' },
|
|
462
564
|
// ]
|
|
463
565
|
|
|
464
566
|
// Módulos
|
|
@@ -518,7 +620,7 @@ customElements.define('attachments-table', AttachmentsTable)
|
|
|
518
620
|
|
|
519
621
|
// Alpine stores
|
|
520
622
|
const stores = registerStores(Alpine, {
|
|
521
|
-
|
|
623
|
+
departamento: { nome: '', area: '' },
|
|
522
624
|
ui: { carregando: false },
|
|
523
625
|
})
|
|
524
626
|
|
|
@@ -539,8 +641,8 @@ await WorkflowViewPatcher.init()
|
|
|
539
641
|
WorkflowViewPatcher.patchAll()
|
|
540
642
|
WorkflowViewPatcher.addBeforeValidate(async () => {
|
|
541
643
|
const repo = new BpmFormRepository()
|
|
542
|
-
const dados = repo.load<{ nome: string }>('
|
|
543
|
-
if (!dados.nome) throw new Error('Informe o nome
|
|
644
|
+
const dados = repo.load<{ nome: string }>('departamento')
|
|
645
|
+
if (!dados.nome) throw new Error('Informe o nome')
|
|
544
646
|
})
|
|
545
647
|
|
|
546
648
|
Alpine.start()
|
|
@@ -550,4 +652,4 @@ Alpine.start()
|
|
|
550
652
|
|
|
551
653
|
## Licença
|
|
552
654
|
|
|
553
|
-
Proprietária —
|
|
655
|
+
Proprietária — uso restrito a projetos autorizados.
|
package/dist/jsegd-bpm.d.ts
CHANGED
|
@@ -1076,7 +1076,12 @@ type StoreProxy<T extends StoreMap> = {
|
|
|
1076
1076
|
*/
|
|
1077
1077
|
declare function registerStores<T extends StoreMap>(alpine: Pick<Alpine, 'store'>, storeMap: T): StoreProxy<T>;
|
|
1078
1078
|
|
|
1079
|
-
type ValidationFn = (...args: unknown[]) => boolean
|
|
1079
|
+
type ValidationFn = (...args: unknown[]) => boolean | Promise<boolean>;
|
|
1080
|
+
type ValidationRules = Record<string, ValidationFn>;
|
|
1081
|
+
type ValidatorHandle = {
|
|
1082
|
+
validate(field?: string): Promise<boolean>;
|
|
1083
|
+
hasError(): Promise<boolean>;
|
|
1084
|
+
};
|
|
1080
1085
|
/**
|
|
1081
1086
|
* Validador tipado com padrão singleton.
|
|
1082
1087
|
* Permite registrar, remover e executar funções de validação associadas a códigos numéricos.
|
|
@@ -1097,24 +1102,50 @@ declare class Validator {
|
|
|
1097
1102
|
*/
|
|
1098
1103
|
static getInstance(): Validator;
|
|
1099
1104
|
/**
|
|
1100
|
-
*
|
|
1101
|
-
*
|
|
1105
|
+
* Cria um validador isolado para um conjunto de regras nomeadas.
|
|
1106
|
+
* Cada chamada retorna um handle independente, útil para instâncias locais de Alpine.data().
|
|
1107
|
+
* @example
|
|
1108
|
+
* const validadores = Validator.add({
|
|
1109
|
+
* taxa: () => this.possuiTaxa === '1' && !this.taxa,
|
|
1110
|
+
* campoExtra: () => this.possuiTaxa === '1' && this.campoExtra === 'invalido',
|
|
1111
|
+
* })
|
|
1112
|
+
*/
|
|
1113
|
+
static add(rules: ValidationRules): ValidatorHandle;
|
|
1114
|
+
private addRule;
|
|
1115
|
+
private removeRule;
|
|
1116
|
+
private normalizeRules;
|
|
1117
|
+
private unregisterAll;
|
|
1118
|
+
/**
|
|
1119
|
+
* Registra uma validação associada a um código numérico.
|
|
1120
|
+
* Aceita uma única função ou um lote de regras nomeadas.
|
|
1121
|
+
* Retorna uma função para cancelar o registro do mesmo conjunto informado.
|
|
1102
1122
|
* @example Validator.getInstance().register(100, () => value > 0)
|
|
1123
|
+
* @example Validator.getInstance().register(100, {
|
|
1124
|
+
* fieldA: () => valueA > 0,
|
|
1125
|
+
* fieldB: () => valueB !== '',
|
|
1126
|
+
* })
|
|
1103
1127
|
*/
|
|
1104
|
-
register(code: number,
|
|
1128
|
+
register(code: number, rules: ValidationFn): () => void;
|
|
1129
|
+
register(code: number, rules: ValidationRules): () => void;
|
|
1105
1130
|
/**
|
|
1106
|
-
* Remove uma
|
|
1131
|
+
* Remove uma validação associada a um código numérico.
|
|
1132
|
+
* Aceita uma única função ou o mesmo lote de regras nomeadas usado no registro.
|
|
1107
1133
|
* @example Validator.getInstance().unregister(100, myFn)
|
|
1134
|
+
* @example Validator.getInstance().unregister(100, {
|
|
1135
|
+
* fieldA: () => valueA > 0,
|
|
1136
|
+
* fieldB: () => valueB !== '',
|
|
1137
|
+
* })
|
|
1108
1138
|
*/
|
|
1109
|
-
unregister(code: number,
|
|
1139
|
+
unregister(code: number, rules: ValidationFn): void;
|
|
1140
|
+
unregister(code: number, rules: ValidationRules): void;
|
|
1110
1141
|
/**
|
|
1111
1142
|
* Executa todas as funções de validação registradas para o código informado.
|
|
1112
1143
|
* Lança um erro se qualquer função retornar `false`.
|
|
1113
1144
|
* Se não houver validações registradas para o código, retorna silenciosamente.
|
|
1114
|
-
* @example Validator.getInstance().execute(100)
|
|
1145
|
+
* @example await Validator.getInstance().execute(100)
|
|
1115
1146
|
* @throws {Error} Se alguma validação falhar.
|
|
1116
1147
|
*/
|
|
1117
|
-
execute(code: number): void
|
|
1148
|
+
execute(code: number): Promise<void>;
|
|
1118
1149
|
/**
|
|
1119
1150
|
* Remove todas as funções de validação de um código específico, ou limpa todos os registros se nenhum código for informado.
|
|
1120
1151
|
* @example
|