feat(documents): suporte a linha condicional (conditional) + catálogo completo
Necessário pra importar os 7 templates reais do legado (fornecidos pelo usuário via export do OrçaFácil): o Termo de Contratação STFC usa o nó `conditional` (envelope em volta de uma tableRow, franquia de minutos/0800) — sem suporte, tanto o backend quanto o editor Tiptap rejeitavam/renderizavam em branco esse documento específico, mesmo bug já documentado no export do legado. A condição em si nunca é avaliada nesta leva (fora de escopo) — a linha sempre passa adiante, comportamento explícito e documentado. Também adiciona ao variable-catalog.ts as ~25 variáveis que faltavam (tarifação STFC por banda, 0800, portabilidade, prazo de instalação), descobertas comparando contra as chaves reais usadas nos 7 templates importados. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -13,8 +13,18 @@ import { TableCell } from "@tiptap/extension-table-cell";
|
||||
import type { VariableCatalogEntry } from "../../../lib/api/documents.js";
|
||||
import { MergeField } from "./mergeFieldExtension.js";
|
||||
import { PageBreak } from "./pageBreakExtension.js";
|
||||
import { ConditionalRow } from "./conditionalRowExtension.js";
|
||||
import { EditorToolbar } from "./EditorToolbar.js";
|
||||
|
||||
/**
|
||||
* `Table` precisa aceitar `conditional` como filho direto, além de
|
||||
* `tableRow` — sem isso o Tiptap rejeita o documento inteiro (renderiza em
|
||||
* branco) sempre que uma tabela contém uma linha condicional, exatamente o
|
||||
* bug documentado no export dos templates do legado (README dentro de
|
||||
* `document_templates_export.zip`) e já corrigido lá da mesma forma.
|
||||
*/
|
||||
const TableWithConditional = Table.extend({ content: "(tableRow|conditional)+" });
|
||||
|
||||
/**
|
||||
* Editor de rascunho real (Tiptap) — substitui o `<textarea>` de texto puro
|
||||
* das levas anteriores. `content` é só o valor INICIAL (Tiptap é
|
||||
@@ -39,12 +49,13 @@ export function DocumentEditor({
|
||||
FontSize,
|
||||
TextAlign.configure({ types: ["heading", "paragraph"] }),
|
||||
Image,
|
||||
Table.configure({ resizable: false }),
|
||||
TableWithConditional.configure({ resizable: false }),
|
||||
TableRow,
|
||||
TableHeader,
|
||||
TableCell,
|
||||
MergeField,
|
||||
PageBreak,
|
||||
ConditionalRow,
|
||||
],
|
||||
content,
|
||||
onUpdate: ({ editor: updated }) => onChange(updated.getJSON()),
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
import { mergeAttributes, Node } from "@tiptap/core";
|
||||
|
||||
/**
|
||||
* Envolve uma única `tableRow` que só deveria aparecer no documento final se
|
||||
* uma condição bater contra o contexto (avaliada no backend — ver
|
||||
* apps/api/src/documents/editor-content-renderer.ts, case 'conditional').
|
||||
* Porta de `ConditionalRow.jsx` (export dos templates do legado,
|
||||
* `/home/handix/document_templates_export.zip`) — necessário pra abrir o
|
||||
* Termo de Contratação STFC, o único dos 7 templates importados que usa
|
||||
* linhas condicionais (portabilidade/franquia de minutos).
|
||||
*
|
||||
* Nesta leva a condição NUNCA é avaliada de verdade (fora de escopo, ver
|
||||
* docs/progress.md) — a linha sempre aparece no PDF final, igual ao
|
||||
* comportamento do backend quando `conditional` não é resolvido.
|
||||
*
|
||||
* Renderiza como `<tbody style="display:contents">` (sem caixa própria) —
|
||||
* a `<tr>` de dentro participa do layout da tabela normalmente, só o
|
||||
* "envelope" de identificação some visualmente no editor.
|
||||
*/
|
||||
export const ConditionalRow = Node.create({
|
||||
name: "conditional",
|
||||
group: "block",
|
||||
content: "tableRow",
|
||||
isolating: true,
|
||||
|
||||
addAttributes() {
|
||||
return {
|
||||
field: { default: null },
|
||||
operator: { default: "exists" },
|
||||
value: { default: null },
|
||||
};
|
||||
},
|
||||
|
||||
parseHTML() {
|
||||
return [{ tag: "tbody[data-conditional-field]" }];
|
||||
},
|
||||
|
||||
renderHTML({ node, HTMLAttributes }) {
|
||||
return [
|
||||
"tbody",
|
||||
mergeAttributes(HTMLAttributes, {
|
||||
style: "display:contents",
|
||||
"data-conditional-field": node.attrs.field || "",
|
||||
title: `Linha condicional — sempre visível nesta leva (avaliação real de "${node.attrs.field || "?"}" é trabalho futuro)`,
|
||||
}),
|
||||
0,
|
||||
];
|
||||
},
|
||||
|
||||
addNodeView() {
|
||||
return ({ node }) => {
|
||||
const dom = document.createElement("tbody");
|
||||
dom.style.cssText = "display:contents";
|
||||
dom.setAttribute("data-conditional-field", node.attrs.field || "");
|
||||
dom.title = `Linha condicional — sempre visível nesta leva (avaliação real de "${node.attrs.field || "?"}" é trabalho futuro)`;
|
||||
return { dom, contentDOM: dom };
|
||||
};
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user