jsegd-bpm 0.1.0
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/LICENSE.md +48 -0
- package/README.md +553 -0
- package/dist/assets/workflow-C51JnySw.css +72 -0
- package/dist/jsegd-bpm.d.ts +1045 -0
- package/dist/jsegd-bpm.js +2 -0
- package/dist/jsegd-bpm.js.map +1 -0
- package/dist/jsegd-fluig.d.ts +892 -0
- package/dist/jsegd-fluig.js +2 -0
- package/dist/jsegd-fluig.js.map +1 -0
- package/package.json +59 -0
package/LICENSE.md
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# END USER LICENSE AGREEMENT (EULA)
|
|
2
|
+
|
|
3
|
+
Este Contrato de Licença de Usuário Final ("EULA") é um acordo legal entre você (seja uma pessoa física ou jurídica) e EGD CONSULTORIA EM TECNOLOGIA LTDA, uma empresa devidamente registrada no Brasil, com sede em AV PAULISTA, 1106 SALA 01 ANDAR 16, BELA VISTA, SÃO PAULO/SP ("Licenciante"), que rege o uso do software jsEGD BPM (o "Software"). Ao instalar, copiar ou de outra forma usar o Software, você concorda em se vincular aos termos deste EULA.
|
|
4
|
+
|
|
5
|
+
## Concessão de Licença
|
|
6
|
+
|
|
7
|
+
O Licenciante concede ao Usuário uma licença limitada, não exclusiva e intransferível para usar o Software, de acordo com os termos e condições deste EULA.
|
|
8
|
+
|
|
9
|
+
Uso Permitido: O Usuário pode usar o Software apenas para fins internos, em conformidade com a documentação fornecida.
|
|
10
|
+
|
|
11
|
+
Propriedade: O Software é licenciado, não vendido. O Usuário reconhece que o Licenciante mantém a propriedade intelectual de todo o Software e quaisquer atualizações ou versões subsequentes.
|
|
12
|
+
|
|
13
|
+
## Restrições
|
|
14
|
+
|
|
15
|
+
O Usuário não pode, sob nenhuma circunstância, realizar qualquer uma das seguintes ações:
|
|
16
|
+
|
|
17
|
+
* Modificação: Modificar, adaptar, traduzir ou criar obras derivadas com base no Software.
|
|
18
|
+
* Descompilação e Engenharia Reversa: Descompilar, fazer engenharia reversa ou tentar derivar o código-fonte do Software, exceto se expressamente permitido pela legislação aplicável.
|
|
19
|
+
* Redistribuição: Redistribuir, sublicenciar, vender ou transferir de qualquer forma o Software a terceiros.
|
|
20
|
+
* Uso Comercial: O Software é fornecido para uso interno. O Usuário não pode usá-lo para fins comerciais de revenda, aluguel ou locação.
|
|
21
|
+
|
|
22
|
+
## Licença Limitada
|
|
23
|
+
|
|
24
|
+
A licença concedida ao Usuário é não exclusiva, não transferível e revogável. O Licenciante se reserva o direito de revogar a licença a qualquer momento, caso o Usuário viole os termos deste EULA.
|
|
25
|
+
|
|
26
|
+
## Garantias e Isenção de Responsabilidade
|
|
27
|
+
|
|
28
|
+
O Licenciante fornece o Software "no estado em que se encontra", sem quaisquer garantias explícitas ou implícitas, incluindo, mas não se limitando a, garantias de adequação a um propósito específico ou de não violação.
|
|
29
|
+
|
|
30
|
+
O Licenciante não será responsável por qualquer dano direto, indireto, incidental, especial ou consequente que possa resultar do uso ou incapacidade de usar o Software, mesmo que o Licenciante tenha sido avisado da possibilidade de tais danos.
|
|
31
|
+
|
|
32
|
+
## Propriedade Intelectual
|
|
33
|
+
|
|
34
|
+
O Usuário reconhece que todos os direitos de propriedade intelectual sobre o Software, incluindo, mas não se limitando a, patentes, direitos autorais e marcas registradas, pertencem exclusivamente ao Licenciante.
|
|
35
|
+
|
|
36
|
+
O Usuário não adquire nenhum direito de propriedade sobre o Software, exceto pela licença limitada concedida por este EULA.
|
|
37
|
+
|
|
38
|
+
## Rescisão
|
|
39
|
+
|
|
40
|
+
Este EULA entrará em vigor na data de aceitação do Usuário e continuará em vigor até ser rescindido. O Licenciante pode rescindir este EULA a qualquer momento, caso o Usuário viole qualquer termo deste contrato. Após a rescisão, o Usuário deve desinstalar e destruir todas as cópias do Software.
|
|
41
|
+
|
|
42
|
+
## Lei Aplicável
|
|
43
|
+
|
|
44
|
+
Este EULA será regido e interpretado de acordo com as leis do Brasil. Qualquer disputa relacionada a este EULA será resolvida exclusivamente nos tribunais competentes de São Paulo/SP, independente de outro, por mais privilegiado que seja.
|
|
45
|
+
|
|
46
|
+
## Aceitação
|
|
47
|
+
|
|
48
|
+
Ao instalar ou usar o Software, o Usuário concorda com os termos deste EULA. Se o Usuário não concordar com os termos, deverá interromper imediatamente a instalação ou o uso do Software e destruir qualquer cópia do Software em sua posse.
|
package/README.md
ADDED
|
@@ -0,0 +1,553 @@
|
|
|
1
|
+
# jsEGD BPM
|
|
2
|
+
|
|
3
|
+
Biblioteca TypeScript de runtime para desenvolvimento de formulários e workflows no ambiente **EGD BPM** — EGD Tecnologia.
|
|
4
|
+
Fornece componentes de anexo, gerenciamento de workers para datasets, controle do ciclo de vida do workflow e utilitários de formulário integrados ao DOM do EGD BPM.
|
|
5
|
+
|
|
6
|
+
> **Dependências par obrigatórias:** [`jsegd`](https://jsegd.netlify.app) ≥ 1.1.0 e [`alpinejs`](https://alpinejs.dev) ≥ 3.0.0
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## Instalação
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
npm install jsegd-bpm jsegd alpinejs
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## Entradas do pacote
|
|
19
|
+
|
|
20
|
+
| Entrada | Importação | Conteúdo |
|
|
21
|
+
| --------- | ------------- | ----------------------------------------------------------------------------- |
|
|
22
|
+
| Principal | `jsegd-bpm` | Workers, anexos, validação, workflow, form-repository, workflow-view e alpine |
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## `WorkerManager` — Consultas de Dataset em Web Workers
|
|
27
|
+
|
|
28
|
+
Gerencia um pool de Web Workers para executar consultas à API de Datasets do EGD BPM fora da thread principal. Suporta deduplicação de requisições, cache em IndexedDB e cancelamento HTTP real via `AbortController`.
|
|
29
|
+
|
|
30
|
+
```typescript
|
|
31
|
+
import { WorkerManager, ConstraintsType } from 'jsegd-bpm'
|
|
32
|
+
import { LocalCache } from 'jsegd'
|
|
33
|
+
|
|
34
|
+
// Compartilhando um cache existente entre componentes:
|
|
35
|
+
const cache = LocalCache.getInstance()
|
|
36
|
+
cache.initialize({ storeName: 'datasets', cacheExpirationMs: 60 * 60 * 1000 })
|
|
37
|
+
|
|
38
|
+
const manager = new WorkerManager({ poolSize: 4, cache })
|
|
39
|
+
|
|
40
|
+
const { promise, cancel } = manager.query('https://tenant.bpm.empresa.com', {
|
|
41
|
+
datasetId: 'dsColaboradores',
|
|
42
|
+
field: ['nome', 'email', 'matricula'],
|
|
43
|
+
constraintsField: ['ativo'],
|
|
44
|
+
constraintsInitialValue: ['true'],
|
|
45
|
+
constraintsType: [ConstraintsType.MUST],
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
// Cancelar requisição em andamento, se necessário:
|
|
49
|
+
// cancel()
|
|
50
|
+
|
|
51
|
+
const result = await promise
|
|
52
|
+
console.log(result.columns) // ['nome', 'email', 'matricula']
|
|
53
|
+
console.log(result.values) // [{ nome: 'Ana', email: 'ana@co.com', matricula: '1234' }, ...]
|
|
54
|
+
|
|
55
|
+
// Liberar recursos ao desmontar o componente:
|
|
56
|
+
manager.terminate()
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
**`WorkerManagerOptions`**
|
|
60
|
+
|
|
61
|
+
| Opção | Tipo | Padrão | Descrição |
|
|
62
|
+
| -------------- | -------------- | ------ | -------------------------------------------------------------- |
|
|
63
|
+
| `poolSize` | `number` | `4` | Número de workers paralelos |
|
|
64
|
+
| `cache` | `ICache` | — | Instância de cache existente (compartilhada entre componentes) |
|
|
65
|
+
| `cacheOptions` | `CacheOptions` | — | Cria um `LocalCache` interno com `storeName` e TTL em ms |
|
|
66
|
+
|
|
67
|
+
> Se nem `cache` nem `cacheOptions` for informado, o gerenciador opera sem cache.
|
|
68
|
+
|
|
69
|
+
**`ConstraintsType`** — enum: `MUST`, `SHOULD`, `MUST_NOT`.
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## `AttachButton` / `AttachmentsTable` — Componentes de Anexo
|
|
74
|
+
|
|
75
|
+
Custom Elements prontos para uso em formulários EGD BPM. Registre-os uma vez no ponto de entrada da aplicação.
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
import { AttachButton, AttachmentsTable } from 'jsegd-bpm'
|
|
79
|
+
|
|
80
|
+
customElements.define('attachment-button', AttachButton)
|
|
81
|
+
customElements.define('attachments-table', AttachmentsTable)
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### `<attachment-button>`
|
|
85
|
+
|
|
86
|
+
Botão com dropdown para upload de arquivos vinculados ao processo.
|
|
87
|
+
|
|
88
|
+
```html
|
|
89
|
+
<!-- Tipo de anexo fixo -->
|
|
90
|
+
<attachment-button description="CONTRATO"></attachment-button>
|
|
91
|
+
|
|
92
|
+
<!-- Prompt ao usuário para informar a descrição no upload -->
|
|
93
|
+
<attachment-button></attachment-button>
|
|
94
|
+
|
|
95
|
+
<!-- Somente leitura: oculta botão de adicionar -->
|
|
96
|
+
<attachment-button description="CONTRATO" noaddbutton></attachment-button>
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
| Atributo | Tipo | Descrição |
|
|
100
|
+
| ---------------- | -------- | ---------------------------------------------------------------- |
|
|
101
|
+
| `description` | `string` | Tipo do anexo. Vazio abre modal para o usuário informar |
|
|
102
|
+
| `accept` | `string` | Filtro de tipos aceitos pelo seletor nativo (ex: `".pdf,.docx"`) |
|
|
103
|
+
| `noaddbutton` | — | Oculta o botão de adicionar |
|
|
104
|
+
| `noviewbutton` | — | Oculta o botão de visualizar |
|
|
105
|
+
| `noeditbutton` | — | Oculta o botão de editar descrição |
|
|
106
|
+
| `nodeletebutton` | — | Oculta o botão de excluir |
|
|
107
|
+
|
|
108
|
+
### `<attachments-table>`
|
|
109
|
+
|
|
110
|
+
Tabela que lista os anexos do processo com suporte a drag-and-drop para reordenação.
|
|
111
|
+
|
|
112
|
+
```html
|
|
113
|
+
<attachments-table mode="edit"></attachments-table>
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## `BeforeSendValidate` — Validação com feedback visual
|
|
119
|
+
|
|
120
|
+
Integra validação de campos com o sistema de erros visuais do tema EGD BPM (`data-group` / `data-helper`).
|
|
121
|
+
|
|
122
|
+
```typescript
|
|
123
|
+
import { BeforeSendValidate } from 'jsegd-bpm'
|
|
124
|
+
import type { FieldError } from 'jsegd-bpm'
|
|
125
|
+
|
|
126
|
+
function validarFormulario() {
|
|
127
|
+
const erros: FieldError[] = []
|
|
128
|
+
|
|
129
|
+
const campoNome = document.querySelector<HTMLInputElement>('[name="nome_solicitante"]')
|
|
130
|
+
if (!campoNome?.value) {
|
|
131
|
+
erros.push({ field: campoNome, errorMessage: 'Informe o nome do solicitante' })
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (erros.length > 0) {
|
|
135
|
+
BeforeSendValidate.throwValidationError(erros)
|
|
136
|
+
// Lança Error com HTML formatado para o widget de processo exibir
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
`throwValidationError` lança sempre — use dentro de `beforeSendValidate` do EGD BPM ou em hooks de `WorkflowViewPatcher`.
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
## `WorkflowViewPatcher` — Hooks no widget de processo
|
|
146
|
+
|
|
147
|
+
Substitui métodos do widget nativo `ECM_WKFView` para interceptar o ciclo de envio do processo sem modificar o EGD BPM.
|
|
148
|
+
|
|
149
|
+
```typescript
|
|
150
|
+
import { WorkflowViewPatcher } from 'jsegd-bpm'
|
|
151
|
+
|
|
152
|
+
// 1. Inicializar — aguarda o widget carregar no frame pai
|
|
153
|
+
await WorkflowViewPatcher.init()
|
|
154
|
+
|
|
155
|
+
// 2. Aplicar todos os patches registrados
|
|
156
|
+
WorkflowViewPatcher.patchAll()
|
|
157
|
+
|
|
158
|
+
// 3. Registrar hooks
|
|
159
|
+
WorkflowViewPatcher.addBeforeValidate(async () => {
|
|
160
|
+
// Executado antes da validação do widget.
|
|
161
|
+
// Lançar exceção cancela o envio e reabilita os botões.
|
|
162
|
+
const valor = document.querySelector<HTMLInputElement>('[name="campo_critico"]')?.value
|
|
163
|
+
if (!valor) throw new Error('Campo crítico obrigatório')
|
|
164
|
+
})
|
|
165
|
+
|
|
166
|
+
WorkflowViewPatcher.addAfterValidate(async () => {
|
|
167
|
+
// Executado após validação bem-sucedida, antes do envio efetivo.
|
|
168
|
+
await salvarRascunho()
|
|
169
|
+
})
|
|
170
|
+
|
|
171
|
+
WorkflowViewPatcher.addOnMoveSuccess(() => {
|
|
172
|
+
console.log('Processo movimentado com sucesso')
|
|
173
|
+
})
|
|
174
|
+
|
|
175
|
+
WorkflowViewPatcher.addOnMoveError(() => {
|
|
176
|
+
console.error('Erro na movimentação — verifique os logs')
|
|
177
|
+
})
|
|
178
|
+
|
|
179
|
+
WorkflowViewPatcher.addOnCompletionLinkAdd((message) => {
|
|
180
|
+
// Adiciona links à tela de conclusão após a movimentação
|
|
181
|
+
message.links.push({ description: 'Abrir tarefa seguinte', href: '/minha-tarefa' })
|
|
182
|
+
})
|
|
183
|
+
|
|
184
|
+
// Ao desmontar o formulário (SPAs): restaura os métodos originais
|
|
185
|
+
WorkflowViewPatcher.restore()
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
**Métodos estáticos**
|
|
189
|
+
|
|
190
|
+
| Método | Descrição |
|
|
191
|
+
| ---------------------------- | ------------------------------------------------------- |
|
|
192
|
+
| `init(timeout?)` | Aguarda `ECM_WKFView` ficar disponível no frame pai |
|
|
193
|
+
| `patchAll()` | Aplica todos os overrides registrados |
|
|
194
|
+
| `patch(attr)` | Aplica um override específico |
|
|
195
|
+
| `restore()` | Reverte todos os métodos e limpa hooks e flags internos |
|
|
196
|
+
| `addBeforeValidate(fn)` | Hook antes da validação do widget |
|
|
197
|
+
| `addAfterValidate(fn)` | Hook após validação, antes do envio |
|
|
198
|
+
| `addOnMoveSuccess(fn)` | Callback de sucesso na movimentação |
|
|
199
|
+
| `addOnMoveError(fn)` | Callback de erro na movimentação |
|
|
200
|
+
| `addOnCompletionLinkAdd(fn)` | Manipula links da tela de conclusão |
|
|
201
|
+
|
|
202
|
+
---
|
|
203
|
+
|
|
204
|
+
## `IFormRepository` — Acesso tipado ao formulário
|
|
205
|
+
|
|
206
|
+
Interface agnóstica ao DOM com duas implementações: `BpmFormRepository` (produção) e `MockFormRepository` (testes unitários).
|
|
207
|
+
|
|
208
|
+
### Convenção de nomenclatura de campos
|
|
209
|
+
|
|
210
|
+
Os campos do formulário EGD BPM devem seguir o padrão:
|
|
211
|
+
|
|
212
|
+
```
|
|
213
|
+
{domínio}_{atributo_snake_case}
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
onde `{domínio}` é o identificador do **agregado de negócio** ao qual o campo pertence — não a tela em que aparece nem a fase do workflow. O total não deve ultrapassar **30 caracteres** (limite de coluna no banco EGD).
|
|
217
|
+
|
|
218
|
+
| Campo no DOM | Domínio | Atributo no modelo |
|
|
219
|
+
| ------------------- | ----------- | ------------------ |
|
|
220
|
+
| `contrato_indice` | `contrato` | `indice` |
|
|
221
|
+
| `imovel_cep` | `imovel` | `cep` |
|
|
222
|
+
| `vigencia_prazo` | `vigencia` | `prazo` |
|
|
223
|
+
| `fornecedor_cep` | `fornecedor`| `cep` |
|
|
224
|
+
|
|
225
|
+
> **Importante:** o `{domínio}` é o prefixo persistido no banco pelo Fluig — é 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
|
+
|
|
227
|
+
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
|
+
|
|
229
|
+
### Produção — `BpmFormRepository`
|
|
230
|
+
|
|
231
|
+
Lê e escreve diretamente nos elementos `input`, `select` e `textarea` do formulário EGD BPM. Encapsula toda a interação com `wdkAddChild` para tabelas filhas.
|
|
232
|
+
|
|
233
|
+
`load(section)` remove o prefixo `{section}_` e converte `snake_case → camelCase` antes de retornar.
|
|
234
|
+
`save(section, data)` faz o caminho inverso: converte `camelCase → snake_case` e adiciona o prefixo ao escrever no DOM.
|
|
235
|
+
|
|
236
|
+
```typescript
|
|
237
|
+
import { BpmFormRepository } from 'jsegd-bpm'
|
|
238
|
+
|
|
239
|
+
interface Contrato {
|
|
240
|
+
indice: string
|
|
241
|
+
primeiroPagamento: string
|
|
242
|
+
reajuste: string
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
interface Imovel {
|
|
246
|
+
cep: string
|
|
247
|
+
logradouro: string
|
|
248
|
+
municipio: string
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
interface ItemTabela {
|
|
252
|
+
descricao: string
|
|
253
|
+
valor: string
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const repo = new BpmFormRepository()
|
|
257
|
+
|
|
258
|
+
// Ler domínio 'contrato' — lê campos com name="contrato_*" do DOM
|
|
259
|
+
// DOM: contrato_indice, contrato_primeiro_pagamento, contrato_reajuste
|
|
260
|
+
const contrato = repo.load<Contrato>('contrato')
|
|
261
|
+
console.log(contrato.indice) // valor de input[name="contrato_indice"]
|
|
262
|
+
console.log(contrato.primeiroPagamento) // valor de input[name="contrato_primeiro_pagamento"]
|
|
263
|
+
|
|
264
|
+
// Ler domínio 'imovel' — desambigua de 'fornecedor_cep', 'contrato_cep', etc.
|
|
265
|
+
const imovel = repo.load<Imovel>('imovel')
|
|
266
|
+
console.log(imovel.cep) // valor de input[name="imovel_cep"]
|
|
267
|
+
|
|
268
|
+
// Ler tabela filha
|
|
269
|
+
const itens = repo.loadTable<ItemTabela>('tabItens')
|
|
270
|
+
|
|
271
|
+
// Persistir no DOM — save converte camelCase → prefixo+snake_case
|
|
272
|
+
repo.save<Contrato>('contrato', { indice: 'IGPM', primeiroPagamento: '2026-02-01' })
|
|
273
|
+
// escreve: input[name="contrato_indice"] e input[name="contrato_primeiro_pagamento"]
|
|
274
|
+
|
|
275
|
+
// Substituir linhas da tabela filha
|
|
276
|
+
repo.saveTable<ItemTabela>('tabItens', [
|
|
277
|
+
{ descricao: 'Notebook', valor: '4500.00' },
|
|
278
|
+
{ descricao: 'Mouse', valor: '120.00' },
|
|
279
|
+
])
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
### Testes — `MockFormRepository`
|
|
283
|
+
|
|
284
|
+
Implementação in-memory sem dependência de DOM. Aplica a mesma lógica de prefixo e conversão de `BpmFormRepository` para que os testes reflitam o comportamento real.
|
|
285
|
+
|
|
286
|
+
```typescript
|
|
287
|
+
import { MockFormRepository } from 'jsegd-bpm'
|
|
288
|
+
|
|
289
|
+
interface Vigencia {
|
|
290
|
+
prazo: string
|
|
291
|
+
tipoDePrazo: string
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
const repo = new MockFormRepository()
|
|
295
|
+
|
|
296
|
+
// save armazena internamente como 'vigencia_prazo', 'vigencia_tipo_de_prazo'
|
|
297
|
+
repo.save<Vigencia>('vigencia', { prazo: '12', tipoDePrazo: 'Meses' })
|
|
298
|
+
|
|
299
|
+
// load recupera { prazo: '12', tipoDePrazo: 'Meses' }
|
|
300
|
+
const data = repo.load<Vigencia>('vigencia')
|
|
301
|
+
// data.prazo === '12'
|
|
302
|
+
// data.tipoDePrazo === 'Meses'
|
|
303
|
+
|
|
304
|
+
repo.saveTable('tabItens', [{ descricao: 'Cadeira', valor: '800.00' }])
|
|
305
|
+
const itens = repo.loadTable('tabItens')
|
|
306
|
+
|
|
307
|
+
repo.reset() // limpa todos os dados
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
### Processos legados — `loadFields`
|
|
311
|
+
|
|
312
|
+
Processos existentes cujos campos não seguem a convenção `{domínio}_{atributo}` não devem ser alterados para evitar migração de dados no banco. Para esses casos, `jsegd-bpm` exporta a função utilitária `loadFields`, que recebe um mapeamento explícito `{ propriedadeModelo: nomeDOM }`:
|
|
313
|
+
|
|
314
|
+
```typescript
|
|
315
|
+
import { loadFields } from 'jsegd-bpm'
|
|
316
|
+
|
|
317
|
+
interface Solicitante {
|
|
318
|
+
nome: string
|
|
319
|
+
email: string
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
// Campo legado: sufixo em vez de prefixo — nome_solicitante, email_solicitante
|
|
323
|
+
const dados = loadFields<Solicitante>({
|
|
324
|
+
nome: 'nome_solicitante',
|
|
325
|
+
email: 'email_solicitante',
|
|
326
|
+
})
|
|
327
|
+
// dados.nome === valor de input[name="nome_solicitante"]
|
|
328
|
+
// dados.email === valor de input[name="email_solicitante"]
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
> **Regra:** `loadFields` **não faz parte de `IFormRepository`** — é um utilitário de compatibilidade para processos legados. Novos processos devem usar `repository.load(section)` exclusivamente.
|
|
332
|
+
|
|
333
|
+
---
|
|
334
|
+
|
|
335
|
+
## Módulo `workflow/` — Configuração e controle de tela
|
|
336
|
+
|
|
337
|
+
Conjunto de utilitários para mapear as atividades BPM em estados de tela — barra de progresso, visibilidade de módulos e editabilidade — sem hardcode de números de sequência nos Presenters.
|
|
338
|
+
|
|
339
|
+
### `defineWorkflowConfig` — Configuração tipada
|
|
340
|
+
|
|
341
|
+
Define módulos e atividades em uma declaração única. O tipo `ModuleId` é inferido automaticamente — nenhum type alias manual é necessário.
|
|
342
|
+
|
|
343
|
+
```typescript
|
|
344
|
+
import { defineWorkflowConfig } from 'jsegd-bpm'
|
|
345
|
+
|
|
346
|
+
export const workflowConfig = defineWorkflowConfig({
|
|
347
|
+
modules: [
|
|
348
|
+
{
|
|
349
|
+
moduleId: 'solicitante',
|
|
350
|
+
label: 'Dados Iniciais',
|
|
351
|
+
icon: 'file-text',
|
|
352
|
+
order: 1,
|
|
353
|
+
showInNavigation: true,
|
|
354
|
+
renderContent: true,
|
|
355
|
+
contentIncludePath: 'solicitante.html',
|
|
356
|
+
},
|
|
357
|
+
{
|
|
358
|
+
moduleId: 'juridico',
|
|
359
|
+
label: 'Jurídico',
|
|
360
|
+
icon: 'gavel',
|
|
361
|
+
order: 2,
|
|
362
|
+
showInNavigation: true,
|
|
363
|
+
renderContent: true,
|
|
364
|
+
contentIncludePath: 'juridico.html',
|
|
365
|
+
},
|
|
366
|
+
{
|
|
367
|
+
moduleId: 'financeiro',
|
|
368
|
+
label: 'Financeiro',
|
|
369
|
+
icon: 'dollar-sign',
|
|
370
|
+
order: 3,
|
|
371
|
+
showInNavigation: true,
|
|
372
|
+
renderContent: true,
|
|
373
|
+
contentIncludePath: 'financeiro.html',
|
|
374
|
+
},
|
|
375
|
+
],
|
|
376
|
+
activities: [
|
|
377
|
+
{
|
|
378
|
+
sequence: 9,
|
|
379
|
+
description: 'Solicitação inicial',
|
|
380
|
+
kind: 'human',
|
|
381
|
+
stage: 'solicitacao',
|
|
382
|
+
modules: { visible: ['solicitante'], editable: ['solicitante'], active: 'solicitante' },
|
|
383
|
+
},
|
|
384
|
+
{
|
|
385
|
+
sequence: 53,
|
|
386
|
+
description: 'Aprovação jurídica',
|
|
387
|
+
kind: 'human',
|
|
388
|
+
stage: 'juridico',
|
|
389
|
+
modules: { visible: ['solicitante', 'juridico'], editable: ['juridico'], active: 'juridico' },
|
|
390
|
+
},
|
|
391
|
+
{
|
|
392
|
+
sequence: 61,
|
|
393
|
+
description: 'Análise financeira',
|
|
394
|
+
kind: 'human',
|
|
395
|
+
stage: 'financeiro',
|
|
396
|
+
modules: { visible: ['solicitante', 'juridico', 'financeiro'], editable: ['financeiro'] },
|
|
397
|
+
},
|
|
398
|
+
{ sequence: 88, description: 'Roteamento interno', kind: 'routing' },
|
|
399
|
+
],
|
|
400
|
+
} as const)
|
|
401
|
+
// Tipo de ModuleId inferido: 'solicitante' | 'juridico' | 'financeiro'
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
**`ActivityKind`** — controla o comportamento na barra de progresso:
|
|
405
|
+
|
|
406
|
+
| Valor | Comportamento |
|
|
407
|
+
| ----------- | ------------------------------------------------------------------- |
|
|
408
|
+
| `'human'` | Ação de usuário — estágio atual destacado na barra |
|
|
409
|
+
| `'system'` | Aguardando retorno externo (webhook) — exibe spinner |
|
|
410
|
+
| `'support'` | Suporte transversal — sobrepõe banner de alerta; não avança a barra |
|
|
411
|
+
| `'routing'` | Roteamento interno do motor BPM — invisível na barra |
|
|
412
|
+
|
|
413
|
+
---
|
|
414
|
+
|
|
415
|
+
### `extractCompletedStages` / `extractCurrentStage` — Histórico do processo
|
|
416
|
+
|
|
417
|
+
Parseia o histórico bruto retornado por `parent.ECM.workflowView.processDefinition.urlHistory` e produz um `ProcessHistory` para consumo pelo `ScreenContext`.
|
|
418
|
+
|
|
419
|
+
```typescript
|
|
420
|
+
import { extractCompletedStages, extractCurrentStage } from 'jsegd-bpm'
|
|
421
|
+
import type { BpmHistoryMap, ProcessHistory } from 'jsegd-bpm'
|
|
422
|
+
|
|
423
|
+
const rawHistory = parent.ECM.workflowView.processDefinition.urlHistory as BpmHistoryMap
|
|
424
|
+
const currentSequence: number = parent.ECM.workflowView.processDefinition.cardIndex
|
|
425
|
+
const historyType: string | null = parent.ECM.workflowView.processDefinition.historyType ?? null
|
|
426
|
+
|
|
427
|
+
const completedStages = extractCompletedStages(rawHistory, workflowConfig.activities)
|
|
428
|
+
// Ex: ['solicitacao', 'juridico']
|
|
429
|
+
|
|
430
|
+
const currentStage = extractCurrentStage(currentSequence, historyType, workflowConfig.activities)
|
|
431
|
+
// Ex: { stage: 'financeiro', status: 'current' }
|
|
432
|
+
|
|
433
|
+
const processHistory: ProcessHistory = { completedStages, currentStage }
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
---
|
|
437
|
+
|
|
438
|
+
### `resolveWorkflowScreen` + `ScreenContext` — Estado da tela
|
|
439
|
+
|
|
440
|
+
Cruza a configuração com o histórico para produzir um `ScreenContext` — o único objeto que os Presenters precisam consultar.
|
|
441
|
+
|
|
442
|
+
```typescript
|
|
443
|
+
import { resolveWorkflowScreen } from 'jsegd-bpm'
|
|
444
|
+
|
|
445
|
+
const screen = resolveWorkflowScreen(workflowConfig, currentSequence, processHistory)
|
|
446
|
+
|
|
447
|
+
// Visibilidade e editabilidade
|
|
448
|
+
screen.isVisible('juridico') // true
|
|
449
|
+
screen.isEditable('financeiro') // false
|
|
450
|
+
screen.isActive('juridico') // true
|
|
451
|
+
|
|
452
|
+
// Estado da atividade
|
|
453
|
+
screen.isAwaitingSystem() // true quando kind === 'system' ou status === 'waiting'
|
|
454
|
+
screen.isInSupport() // true para kind === 'support'
|
|
455
|
+
|
|
456
|
+
// Barra de progresso
|
|
457
|
+
const stages = screen.getProgressBarStages()
|
|
458
|
+
// [
|
|
459
|
+
// { stage: 'solicitacao', status: 'done' },
|
|
460
|
+
// { stage: 'juridico', status: 'done' },
|
|
461
|
+
// { stage: 'financeiro', status: 'current' },
|
|
462
|
+
// ]
|
|
463
|
+
|
|
464
|
+
// Módulos
|
|
465
|
+
const visiveis = screen.getVisibleModules() // ResolvedModule[] — apenas isVisible = true
|
|
466
|
+
const navegacao = screen.getNavigationModules() // ResolvedModule[] — apenas showInNavigation = true e visível
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
---
|
|
470
|
+
|
|
471
|
+
## `registerStores` — Alpine stores tipados
|
|
472
|
+
|
|
473
|
+
Registra múltiplos Alpine stores em uma única chamada e retorna um proxy tipado para acesso direto sem casting manual.
|
|
474
|
+
|
|
475
|
+
```typescript
|
|
476
|
+
import Alpine from 'alpinejs'
|
|
477
|
+
import { registerStores } from 'jsegd-bpm'
|
|
478
|
+
|
|
479
|
+
type PerfilId = 'admin' | 'analista' | 'visualizador'
|
|
480
|
+
|
|
481
|
+
const stores = registerStores(Alpine, {
|
|
482
|
+
usuario: { nome: '', perfil: '' as PerfilId, matricula: '' },
|
|
483
|
+
ui: { carregando: false, erro: null as string | null },
|
|
484
|
+
})
|
|
485
|
+
|
|
486
|
+
Alpine.start()
|
|
487
|
+
|
|
488
|
+
// Acesso tipado — sem Alpine.store('usuario') as UsuarioStore
|
|
489
|
+
stores.usuario.nome // string
|
|
490
|
+
stores.usuario.perfil // PerfilId
|
|
491
|
+
stores.ui.carregando // boolean
|
|
492
|
+
```
|
|
493
|
+
|
|
494
|
+
> `registerStores` aceita `Pick<Alpine, 'store'>` — compatível com qualquer mock em testes unitários.
|
|
495
|
+
|
|
496
|
+
---
|
|
497
|
+
|
|
498
|
+
## Uso típico em um formulário EGD BPM
|
|
499
|
+
|
|
500
|
+
```typescript
|
|
501
|
+
import Alpine from 'alpinejs'
|
|
502
|
+
import {
|
|
503
|
+
AttachButton,
|
|
504
|
+
AttachmentsTable,
|
|
505
|
+
WorkflowViewPatcher,
|
|
506
|
+
BpmFormRepository,
|
|
507
|
+
extractCompletedStages,
|
|
508
|
+
extractCurrentStage,
|
|
509
|
+
resolveWorkflowScreen,
|
|
510
|
+
registerStores,
|
|
511
|
+
} from 'jsegd-bpm'
|
|
512
|
+
import type { BpmHistoryMap } from 'jsegd-bpm'
|
|
513
|
+
import { workflowConfig } from './workflow.config'
|
|
514
|
+
|
|
515
|
+
// Registrar Custom Elements
|
|
516
|
+
customElements.define('attachment-button', AttachButton)
|
|
517
|
+
customElements.define('attachments-table', AttachmentsTable)
|
|
518
|
+
|
|
519
|
+
// Alpine stores
|
|
520
|
+
const stores = registerStores(Alpine, {
|
|
521
|
+
solicitante: { nome: '', departamento: '' },
|
|
522
|
+
ui: { carregando: false },
|
|
523
|
+
})
|
|
524
|
+
|
|
525
|
+
// Histórico e tela
|
|
526
|
+
const rawHistory = parent.ECM.workflowView.processDefinition.urlHistory as BpmHistoryMap
|
|
527
|
+
const seq: number = parent.ECM.workflowView.processDefinition.cardIndex
|
|
528
|
+
const histType: string | null = parent.ECM.workflowView.processDefinition.historyType ?? null
|
|
529
|
+
|
|
530
|
+
const processHistory = {
|
|
531
|
+
completedStages: extractCompletedStages(rawHistory, workflowConfig.activities),
|
|
532
|
+
currentStage: extractCurrentStage(seq, histType, workflowConfig.activities),
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
const screen = resolveWorkflowScreen(workflowConfig, seq, processHistory)
|
|
536
|
+
|
|
537
|
+
// Patcher
|
|
538
|
+
await WorkflowViewPatcher.init()
|
|
539
|
+
WorkflowViewPatcher.patchAll()
|
|
540
|
+
WorkflowViewPatcher.addBeforeValidate(async () => {
|
|
541
|
+
const repo = new BpmFormRepository()
|
|
542
|
+
const dados = repo.load<{ nome: string }>('solicitante')
|
|
543
|
+
if (!dados.nome) throw new Error('Informe o nome do solicitante')
|
|
544
|
+
})
|
|
545
|
+
|
|
546
|
+
Alpine.start()
|
|
547
|
+
```
|
|
548
|
+
|
|
549
|
+
---
|
|
550
|
+
|
|
551
|
+
## Licença
|
|
552
|
+
|
|
553
|
+
Proprietária — EGD Tecnologia. Uso restrito a projetos autorizados.
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
:host {
|
|
2
|
+
display: inline-block;
|
|
3
|
+
position: relative;
|
|
4
|
+
font-family: Arial, sans-serif;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
.dropdown {
|
|
8
|
+
position: relative;
|
|
9
|
+
display: inline-block;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
.dropdown-button {
|
|
13
|
+
background-color: #fff;
|
|
14
|
+
color: #555;
|
|
15
|
+
padding: 6px 10px;
|
|
16
|
+
font-size: 14px;
|
|
17
|
+
border: 1px solid #ccc;
|
|
18
|
+
cursor: pointer;
|
|
19
|
+
border-radius: 0 5px 5px 0;
|
|
20
|
+
display: flex;
|
|
21
|
+
align-items: center;
|
|
22
|
+
gap: 5px;
|
|
23
|
+
height: 32px;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.dropdown-button:hover {
|
|
27
|
+
color: #555;
|
|
28
|
+
border-color: #adadad;
|
|
29
|
+
background-color: #e6e6e6;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
.dropdown-menu {
|
|
33
|
+
display: none;
|
|
34
|
+
position: absolute;
|
|
35
|
+
top: 100%;
|
|
36
|
+
right: 0;
|
|
37
|
+
background-color: #fff;
|
|
38
|
+
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.2);
|
|
39
|
+
min-width: 150px;
|
|
40
|
+
list-style: none;
|
|
41
|
+
padding: 0;
|
|
42
|
+
margin: 0;
|
|
43
|
+
border-radius: 0 0 2px 2px;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
.dropdown-menu a {
|
|
47
|
+
display: flex;
|
|
48
|
+
gap: 10px;
|
|
49
|
+
padding: 5px;
|
|
50
|
+
box-sizing: border-box;
|
|
51
|
+
width: 100%;
|
|
52
|
+
text-decoration: none;
|
|
53
|
+
color: #333;
|
|
54
|
+
font-size: 14px;
|
|
55
|
+
justify-content: end;
|
|
56
|
+
align-items: center;
|
|
57
|
+
line-height: 1.5rem;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
.dropdown-menu a:hover {
|
|
61
|
+
background-color: #f1f1f1;
|
|
62
|
+
color: #1d1b1b;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.dropdown:hover .dropdown-menu {
|
|
66
|
+
display: block;
|
|
67
|
+
z-index: 9999;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.fs-display-none {
|
|
71
|
+
display: none !important;
|
|
72
|
+
}
|