@meistrari/minuta-react 1.1.0 → 1.2.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/README.md +47 -17
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -80,10 +80,12 @@ Tudo pelo seu backend, com `x-api-key` + `x-org-id` — seus usuários não prec
|
|
|
80
80
|
de conta no defesa.ai:
|
|
81
81
|
|
|
82
82
|
```
|
|
83
|
-
GET /data/case/:id/html
|
|
84
|
-
POST /data/case/:id/editions { name, html }
|
|
85
|
-
PATCH /data/case/:id/editions/:
|
|
86
|
-
|
|
83
|
+
GET /data/case/:id/html → { html, chrome, margins, source, editionId, variablesMarked, markedVariables }
|
|
84
|
+
POST /data/case/:id/editions { name, html } → cria a edição → { id, updatedAt, … }
|
|
85
|
+
PATCH /data/case/:id/editions/:id → salva → { updatedAt, updatedBy, … }
|
|
86
|
+
{ html, baseUpdatedAt } 400 sem o token; 409 se outro salvou antes
|
|
87
|
+
POST /data/case/:id/editions/:id/versions → marco no histórico (o PATCH NÃO cria versão)
|
|
88
|
+
POST /data/case/:id/docx → .docx do estado atual (a edição, se existir)
|
|
87
89
|
```
|
|
88
90
|
|
|
89
91
|
```tsx
|
|
@@ -91,15 +93,19 @@ POST /data/case/:id/docx → .docx do estado atual (a e
|
|
|
91
93
|
const doc = await api.get(`/case/${caseId}/html`)
|
|
92
94
|
<MinutaEditor key={caseId} html={doc.html} chrome={doc.chrome ?? undefined} margins={doc.margins ?? undefined} onChange={setHtml} />
|
|
93
95
|
|
|
94
|
-
// salvar (debounce do seu lado)
|
|
95
|
-
await api.patch(`/case/${caseId}/editions/${editionId}`, { html })
|
|
96
|
+
// salvar (debounce do seu lado): baseUpdatedAt = o updatedAt da última resposta
|
|
97
|
+
const saved = await api.patch(`/case/${caseId}/editions/${editionId}`, { html, baseUpdatedAt })
|
|
98
|
+
baseUpdatedAt = saved.updatedAt
|
|
96
99
|
|
|
97
100
|
// baixar — fiel ao preview, gerado no browser
|
|
98
101
|
const blob = await editor.current!.exportDocx()
|
|
99
102
|
```
|
|
100
103
|
|
|
101
|
-
Mande `x-user-email` / `x-user-name`
|
|
102
|
-
|
|
104
|
+
Mande `x-user-email` / `x-user-name` em toda chamada feita em nome de alguém:
|
|
105
|
+
nas rotas de edição viram `createdBy` (criação), `updatedBy` (último save de
|
|
106
|
+
conteúdo), `createdBy` de cada versão e `deletedBy` (descarte); no
|
|
107
|
+
`PATCH /data/case/:id` viram `updatedByEmail`/`updatedByName` do case. Sem os
|
|
108
|
+
headers a autoria fica `null` — nunca "a integração".
|
|
103
109
|
|
|
104
110
|
## Formulário ↔ documento
|
|
105
111
|
|
|
@@ -109,8 +115,9 @@ e o editor trata a marca como nó atômico. Isso dá as duas direções sem casa
|
|
|
109
115
|
texto (que erra quando o valor coincide com algo que já existia no template):
|
|
110
116
|
|
|
111
117
|
```tsx
|
|
112
|
-
// documento → formulário: clicou no trecho, foca o campo
|
|
113
|
-
|
|
118
|
+
// documento → formulário: clicou no trecho, foca o campo. `kind` diz o que foi
|
|
119
|
+
// clicado: 'text' (chip), 'image' (<img> marcada) ou 'missingImage' (aviso)
|
|
120
|
+
<MinutaEditor ref={editor} html={html} onVariableClick={v => focusField(v.variableId, v.kind)} />
|
|
114
121
|
|
|
115
122
|
// formulário → documento: focou o campo, rola até o trecho
|
|
116
123
|
editor.current!.scrollToVariable('nome_cliente')
|
|
@@ -135,17 +142,23 @@ edição criada a partir de HTML já resolvido (sem marcas) a ponte não existe,
|
|
|
135
142
|
| `<for item="c" in="contratos"> <variable name="c.numero">` | um chip por linha, com `data-variable-index` | `contratos[0].numero`, `contratos[1].numero`… |
|
|
136
143
|
| `<img variable="img_doc">` com valor | `<img data-variable-id data-parent-block-id>` | `img_doc` |
|
|
137
144
|
| `<img variable="img_doc">` sem valor | aviso `missing-image-warning` (nó `missingImage` no editor) | `img_doc` |
|
|
145
|
+
| `<variable name="valor">` sem valor | aviso vermelho `<span data-variable-id data-parent-block-id data-invalid-variable>` | `valor` |
|
|
138
146
|
| cada bloco do template | `<div data-minuta-block data-block-id data-block-title>` | `data-block-id` = id do bloco no `templateSpec` |
|
|
139
147
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
148
|
+
O aviso de texto sem valor é uma marca como as outras: entra em
|
|
149
|
+
`markedVariables`, dispara `onVariableClick` (`kind: 'text'`) e
|
|
150
|
+
`setVariableValue` o converte em chip comum ao receber valor. Booleans não
|
|
151
|
+
viram marca: eles decidem se um trecho ou bloco é emitido; a regra fica em
|
|
152
|
+
`displayCondition` e nos `<if condition>` do template (podem envolver várias
|
|
153
|
+
variáveis).
|
|
144
154
|
|
|
145
155
|
```tsx
|
|
146
156
|
// array: uma linha de cada vez
|
|
147
157
|
editor.current!.setVariableValue('contratos[1].numero', '375586874')
|
|
148
158
|
|
|
159
|
+
// texto sem valor: o aviso vermelho vira chip
|
|
160
|
+
editor.current!.setVariableValue('valor', 'R$ 1.000,00')
|
|
161
|
+
|
|
149
162
|
// imagem: troca a existente, ou transforma o aviso de "faltando" em imagem
|
|
150
163
|
editor.current!.setVariableImage('img_doc', urlAssinada) // → nº de ocorrências
|
|
151
164
|
editor.current!.scrollToVariable('img_faltando') // acha o aviso também
|
|
@@ -155,11 +168,28 @@ editor.current!.scrollToVariable('img_faltando') // acha o aviso
|
|
|
155
168
|
```
|
|
156
169
|
|
|
157
170
|
Sobre o fluxo de "enviar variáveis de volta": com uma edição viva o documento
|
|
158
|
-
**é** a edição — o `PATCH` de variáveis
|
|
159
|
-
apagaria o texto do advogado).
|
|
160
|
-
(
|
|
171
|
+
**é** a edição — o `PATCH /data/case/:id` de variáveis não re-renderiza a peça
|
|
172
|
+
(senão apagaria o texto do advogado). A API aceita a mudança e devolve
|
|
173
|
+
`activeEdition` (`{ id, name, updatedAt }` da edição que ficou como estava, ou
|
|
174
|
+
`null`) para o app decidir: ou o formulário trava quando a lapidação começa (o
|
|
175
|
+
modelo do defesa.ai), ou você mantém os dois vivos com `setVariableValue` +
|
|
161
176
|
salva a edição. Decisão de UX do seu app; as duas funcionam.
|
|
162
177
|
|
|
178
|
+
### Imagens inseridas pelo advogado
|
|
179
|
+
|
|
180
|
+
`uploadImage` devolve `{ src, vaultSrc }`: o editor grava a tag com o `src`
|
|
181
|
+
(presignado, expira) e `data-vault-src` (a `vault://`, durável). Ao reabrir uma
|
|
182
|
+
edição salva, renove os links antes de mostrar:
|
|
183
|
+
|
|
184
|
+
```tsx
|
|
185
|
+
// resolver: POST /data/vault/download-url { vaultUrl } → { downloadUrl } (pelo seu backend)
|
|
186
|
+
await editor.current!.refreshVaultImages(vaultUrl => api.post('/vault/download-url', { vaultUrl }).then(r => r.downloadUrl))
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
Não entra no undo nem dispara `onChange`. O `exportDocx()` do browser baixa
|
|
190
|
+
pelo `src`, então renove antes de exportar também; o `POST /data/case/:id/docx`
|
|
191
|
+
resolve `vault://` sozinho.
|
|
192
|
+
|
|
163
193
|
## Next.js (App Router)
|
|
164
194
|
|
|
165
195
|
O entry publicado carrega `'use client'`: dá para importar o componente direto
|