@fayz-ai/plugin-forms 0.9.0-next.1 → 0.9.1

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.
Files changed (59) hide show
  1. package/README.md +3 -3
  2. package/dist/{FormBuilder-7SWZQKFN.js → FormBuilder-JCNHXQXT.js} +4 -4
  3. package/dist/{FormBuilder-7SWZQKFN.js.map → FormBuilder-JCNHXQXT.js.map} +1 -1
  4. package/dist/{chunk-W2ZNJGQC.js → chunk-JCAWSDX6.js} +3 -3
  5. package/dist/chunk-JCAWSDX6.js.map +1 -0
  6. package/dist/components/AddDocumentDropdown.d.ts +1 -1
  7. package/dist/components/AddDocumentDropdown.d.ts.map +1 -1
  8. package/dist/{CustomFormsContext.d.ts → context.d.ts} +1 -1
  9. package/dist/context.d.ts.map +1 -0
  10. package/dist/index.d.ts +1 -1
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +4 -4
  13. package/dist/index.js.map +1 -1
  14. package/dist/lib/agent-tools.d.ts.map +1 -0
  15. package/dist/lib/document-types.d.ts.map +1 -0
  16. package/package.json +5 -7
  17. package/dist/CustomFormsContext.d.ts.map +0 -1
  18. package/dist/agent-tools.d.ts.map +0 -1
  19. package/dist/chunk-W2ZNJGQC.js.map +0 -1
  20. package/dist/document-types.d.ts.map +0 -1
  21. package/src/CustomFormsContext.tsx +0 -28
  22. package/src/agent-tools.ts +0 -73
  23. package/src/components/AddDocumentDropdown.tsx +0 -92
  24. package/src/components/BuilderCanvas.tsx +0 -87
  25. package/src/components/BuilderField.tsx +0 -81
  26. package/src/components/DocumentFormDialog.tsx +0 -155
  27. package/src/components/DocumentList.tsx +0 -254
  28. package/src/components/FieldConfigDialog.tsx +0 -243
  29. package/src/components/FieldPalette.tsx +0 -81
  30. package/src/components/FormBuilder.tsx +0 -427
  31. package/src/components/FormRenderer.tsx +0 -256
  32. package/src/components/FormViewer.tsx +0 -94
  33. package/src/components/PersonDocumentsWidget.tsx +0 -229
  34. package/src/components/TemplateSelector.tsx +0 -91
  35. package/src/config.ts +0 -43
  36. package/src/data/index.ts +0 -3
  37. package/src/data/mock.ts +0 -193
  38. package/src/data/supabase.ts +0 -408
  39. package/src/data/tables.ts +0 -7
  40. package/src/data/types.ts +0 -33
  41. package/src/document-types.ts +0 -51
  42. package/src/index.ts +0 -234
  43. package/src/lib/person-fields.ts +0 -67
  44. package/src/lib/print.ts +0 -207
  45. package/src/lib/tenant.ts +0 -9
  46. package/src/locales/en.ts +0 -95
  47. package/src/locales/index.ts +0 -7
  48. package/src/locales/pt-BR.ts +0 -95
  49. package/src/migrations/000_plg_rename.sql +0 -21
  50. package/src/migrations/001_frm_base.sql +0 -174
  51. package/src/migrations/002_document_archetype.sql +0 -223
  52. package/src/migrations/003_agent_rpcs.sql +0 -174
  53. package/src/migrations/index.ts +0 -610
  54. package/src/store.ts +0 -167
  55. package/src/types.ts +0 -217
  56. package/src/views/CustomFormsSettingsTab.tsx +0 -105
  57. package/src/views/TemplateListView.tsx +0 -174
  58. /package/dist/{agent-tools.d.ts → lib/agent-tools.d.ts} +0 -0
  59. /package/dist/{document-types.d.ts → lib/document-types.d.ts} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-tools.d.ts","sourceRoot":"","sources":["../../src/lib/agent-tools.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAoBjD,eAAO,MAAM,sBAAsB,EAAE,YA4CpC,CAAA;AAED,eAAO,MAAM,yBAAyB,QAM3B,CAAA"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"document-types.d.ts","sourceRoot":"","sources":["../../src/lib/document-types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAO9B,MAAM,WAAW,kBAAkB;IACjC,qEAAqE;IACrE,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,KAAK,CAAC,WAAW,CAAA;IACvB,yEAAyE;IACzE,KAAK,EAAE,MAAM,CAAA;IACb,mDAAmD;IACnD,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,8BAA8B;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,oCAAoC;IACpC,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB;AAED,MAAM,WAAW,oBAAoB;IACnC,4DAA4D;IAC5D,EAAE,EAAE,MAAM,CAAA;IACV,0DAA0D;IAC1D,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,kBAAkB,EAAE,GAAG,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAAA;CACjF;AAMD,wBAAgB,4BAA4B,CAAC,QAAQ,EAAE,oBAAoB,QAG1E;AAED,wBAAgB,wBAAwB,IAAI,oBAAoB,EAAE,CAEjE;AAED,wBAAsB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAQzF"}
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "fayz": {
4
4
  "status": "beta"
5
5
  },
6
- "version": "0.9.0-next.1",
6
+ "version": "0.9.1",
7
7
  "description": "Fayz SDK — plugin-forms plugin",
8
8
  "type": "module",
9
9
  "sideEffects": false,
@@ -12,14 +12,12 @@
12
12
  "types": "./dist/index.d.ts",
13
13
  "exports": {
14
14
  ".": {
15
- "source": "./src/index.ts",
16
15
  "types": "./dist/index.d.ts",
17
16
  "import": "./dist/index.js"
18
17
  }
19
18
  },
20
19
  "files": [
21
- "dist",
22
- "src"
20
+ "dist"
23
21
  ],
24
22
  "peerDependencies": {
25
23
  "react": "^18.0.0 || ^19.0.0",
@@ -32,9 +30,9 @@
32
30
  "@dnd-kit/modifiers": "^9.0.0",
33
31
  "@dnd-kit/sortable": "^10.0.0",
34
32
  "@dnd-kit/utilities": "^3.2.2",
35
- "@fayz-ai/saas": "^0.9.0-next.1",
36
- "@fayz-ai/ui": "^0.9.0-next.1",
37
- "@fayz-ai/core": "^0.9.0-next.1"
33
+ "@fayz-ai/core": "^0.10.0",
34
+ "@fayz-ai/admin": "^0.10.0",
35
+ "@fayz-ai/ui": "^0.10.0"
38
36
  },
39
37
  "devDependencies": {
40
38
  "@types/react": "^18.3.0",
@@ -1 +0,0 @@
1
- {"version":3,"file":"CustomFormsContext.d.ts","sourceRoot":"","sources":["../src/CustomFormsContext.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAoC,MAAM,OAAO,CAAA;AACxD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAA;AACjD,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAA;AAC3D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAE/C,MAAM,WAAW,uBAAuB;IACtC,MAAM,EAAE,iBAAiB,CAAA;IACzB,QAAQ,EAAE,uBAAuB,CAAA;IACjC,KAAK,EAAE,gBAAgB,CAAA;CACxB;AAID,wBAAgB,mBAAmB,CAAC,EAClC,KAAK,EACL,QAAQ,GACT,EAAE;IACD,KAAK,EAAE,uBAAuB,CAAA;IAC9B,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;CAC1B,uFAEA;AAED,wBAAgB,qBAAqB,IAAI,uBAAuB,CAI/D"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"agent-tools.d.ts","sourceRoot":"","sources":["../src/agent-tools.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AAoBjD,eAAO,MAAM,sBAAsB,EAAE,YA4CpC,CAAA;AAED,eAAO,MAAM,yBAAyB,QAM3B,CAAA"}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/components/FormRenderer.tsx","../src/lib/print.ts","../src/agent-tools.ts","../src/lib/person-fields.ts"],"names":["t"],"mappings":"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","file":"chunk-W2ZNJGQC.js","sourcesContent":["import React, { useCallback } from 'react'\nimport { Input } from '@fayz-ai/ui'\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@fayz-ai/ui'\nimport { Badge } from '@fayz-ai/ui'\nimport { useTranslation } from '@fayz-ai/core'\nimport type { FormFieldDef, FormSchema } from '../types'\n\ninterface FormRendererProps {\n schema: FormSchema\n data: Record<string, unknown>\n onChange: (data: Record<string, unknown>) => void\n readOnly?: boolean\n}\n\nfunction FieldRenderer({\n field,\n value,\n onChange,\n readOnly,\n}: {\n field: FormFieldDef\n value: unknown\n onChange: (value: unknown) => void\n readOnly?: boolean\n}) {\n const t = useTranslation()\n\n switch (field.type) {\n case 'title':\n return (\n <h3 className=\"text-base font-semibold pt-2 pb-1 border-b\">\n {field.label}\n </h3>\n )\n\n case 'text':\n return (\n <div className=\"space-y-1.5\">\n <label className=\"text-xs font-medium block\">\n {field.label}\n {field.required && <span className=\"text-destructive ml-0.5\">*</span>}\n </label>\n <Input\n value={(value as string) ?? ''}\n onChange={(e) => onChange(e.target.value)}\n placeholder={field.placeholder}\n className=\"h-9 text-sm\"\n readOnly={readOnly}\n />\n </div>\n )\n\n case 'memo':\n case 'richtext':\n return (\n <div className=\"space-y-1.5\">\n <label className=\"text-xs font-medium block\">\n {field.label}\n {field.required && <span className=\"text-destructive ml-0.5\">*</span>}\n </label>\n <textarea\n value={(value as string) ?? ''}\n onChange={(e: React.ChangeEvent<HTMLTextAreaElement>) => onChange(e.target.value)}\n placeholder={field.placeholder}\n className=\"flex w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 min-h-[100px]\"\n readOnly={readOnly}\n />\n </div>\n )\n\n case 'date':\n return (\n <div className=\"space-y-1.5\">\n <label className=\"text-xs font-medium block\">\n {field.label}\n {field.required && <span className=\"text-destructive ml-0.5\">*</span>}\n </label>\n <Input\n type=\"date\"\n value={(value as string) ?? ''}\n onChange={(e) => onChange(e.target.value)}\n className=\"h-9 text-sm\"\n readOnly={readOnly}\n />\n </div>\n )\n\n case 'select':\n return (\n <div className=\"space-y-1.5\">\n <label className=\"text-xs font-medium block\">\n {field.label}\n {field.required && <span className=\"text-destructive ml-0.5\">*</span>}\n </label>\n {readOnly ? (\n <p className=\"text-sm py-1\">{(value as string) ?? '-'}</p>\n ) : (\n <Select value={(value as string) ?? ''} onValueChange={onChange}>\n <SelectTrigger className=\"h-9 text-sm\">\n <SelectValue placeholder={field.placeholder ?? '...'} />\n </SelectTrigger>\n <SelectContent>\n {(field.options ?? []).map((opt) => (\n <SelectItem key={opt.value} value={opt.value}>\n {opt.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n )}\n </div>\n )\n\n case 'radio':\n return (\n <div className=\"space-y-1.5\">\n <label className=\"text-xs font-medium block\">\n {field.label}\n {field.required && <span className=\"text-destructive ml-0.5\">*</span>}\n </label>\n <div className=\"space-y-1\">\n {(field.options ?? []).map((opt) => (\n <label key={opt.value} className=\"flex items-center gap-2 cursor-pointer\">\n <input\n type=\"radio\"\n name={field.id}\n value={opt.value}\n checked={value === opt.value}\n onChange={() => onChange(opt.value)}\n disabled={readOnly}\n className=\"text-primary\"\n />\n <span className=\"text-sm\">{opt.label}</span>\n </label>\n ))}\n </div>\n </div>\n )\n\n case 'tags': {\n const selectedTags = Array.isArray(value) ? (value as string[]) : []\n return (\n <div className=\"space-y-1.5\">\n <label className=\"text-xs font-medium block\">\n {field.label}\n {field.required && <span className=\"text-destructive ml-0.5\">*</span>}\n </label>\n <div className=\"flex flex-wrap gap-1.5\">\n {(field.options ?? []).map((opt) => {\n const active = selectedTags.includes(opt.value)\n return (\n <Badge\n key={opt.value}\n variant={active ? 'default' : 'outline'}\n className={`cursor-pointer text-xs ${readOnly ? 'pointer-events-none' : ''}`}\n onClick={() => {\n if (readOnly) return\n onChange(\n active\n ? selectedTags.filter((t) => t !== opt.value)\n : [...selectedTags, opt.value],\n )\n }}\n >\n {opt.label}\n </Badge>\n )\n })}\n </div>\n </div>\n )\n }\n\n case 'checkbox':\n return (\n <label className=\"flex items-center gap-2 cursor-pointer py-1\">\n <input\n type=\"checkbox\"\n checked={!!value}\n onChange={(e) => onChange(e.target.checked)}\n disabled={readOnly}\n className=\"rounded border-input\"\n />\n <span className=\"text-sm font-medium\">\n {field.label}\n {field.required && <span className=\"text-destructive ml-0.5\">*</span>}\n </span>\n </label>\n )\n\n case 'image':\n case 'gallery':\n return (\n <div className=\"space-y-1.5\">\n <label className=\"text-xs font-medium block\">\n {field.label}\n {field.required && <span className=\"text-destructive ml-0.5\">*</span>}\n </label>\n <div className=\"flex items-center justify-center rounded-lg border-2 border-dashed py-8 text-sm text-muted-foreground\">\n {field.type === 'gallery' ? 'Gallery upload' : 'Image upload'} — v2\n </div>\n </div>\n )\n\n case 'budget':\n return (\n <div className=\"space-y-1.5\">\n <label className=\"text-xs font-medium block\">\n {field.label}\n {field.required && <span className=\"text-destructive ml-0.5\">*</span>}\n </label>\n <div className=\"flex items-center justify-center rounded-lg border-2 border-dashed py-8 text-sm text-muted-foreground\">\n Budget / line items — v2\n </div>\n </div>\n )\n\n default:\n return null\n }\n}\n\nexport function FormRenderer({ schema, data, onChange, readOnly }: FormRendererProps) {\n const sortedFields = [...schema.fields].sort((a, b) => a.row - b.row || a.col - b.col)\n\n const handleFieldChange = useCallback(\n (fieldId: string, value: unknown) => {\n onChange({ ...data, [fieldId]: value })\n },\n [data, onChange],\n )\n\n return (\n <div\n className=\"grid gap-x-4 gap-y-3\"\n style={{ gridTemplateColumns: 'repeat(12, 1fr)' }}\n >\n {sortedFields.map((field) => (\n <div key={field.id} style={{ gridColumn: `span ${field.colSpan}` }}>\n <FieldRenderer\n field={field}\n value={data[field.id]}\n onChange={(v) => handleFieldChange(field.id, v)}\n readOnly={readOnly}\n />\n </div>\n ))}\n </div>\n )\n}\n","// ============================================================\n// PRINT / PDF — render a filled document to a printable A4 page\n// ============================================================\n//\n// Browser print-to-PDF: we build a clean, self-contained A4 HTML document from\n// the template schema + saved data, drop it into a hidden iframe, and call\n// print() on it. The browser's print dialog covers both physical printing and\n// \"Save as PDF\". No external deps.\n\nimport type { FormDocument, FormFieldDef, FormTemplate } from '../types'\n\nfunction esc(value: unknown): string {\n return String(value ?? '').replace(/[&<>\"']/g, (c) =>\n ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '\"': '&quot;', \"'\": '&#39;' }[c] as string),\n )\n}\n\nfunction nl2br(value: string): string {\n return esc(value).replace(/\\r?\\n/g, '<br/>')\n}\n\nfunction formatDate(value: string): string {\n // ISO date (YYYY-MM-DD) → locale date; anything else passes through.\n const m = /^(\\d{4})-(\\d{2})-(\\d{2})/.exec(value)\n if (!m) return esc(value)\n return `${m[3]}/${m[2]}/${m[1]}`\n}\n\nfunction optionLabel(field: FormFieldDef, value: unknown): string {\n const opt = (field.options ?? []).find((o) => o.value === value)\n return esc(opt ? opt.label : value)\n}\n\n/** Render one field's value as an HTML fragment for the printed page. */\nfunction renderValue(field: FormFieldDef, value: unknown): string {\n const empty = '<span class=\"empty\">—</span>'\n switch (field.type) {\n case 'memo':\n case 'richtext':\n return value ? `<div class=\"long\">${nl2br(String(value))}</div>` : empty\n case 'date':\n return value ? formatDate(String(value)) : empty\n case 'select':\n case 'radio':\n return value ? optionLabel(field, value) : empty\n case 'tags': {\n const arr = Array.isArray(value) ? value : []\n return arr.length\n ? arr.map((v) => optionLabel(field, v)).join(', ')\n : empty\n }\n case 'checkbox':\n return value ? 'Sim' : 'Não'\n case 'image':\n case 'gallery':\n case 'budget':\n return '' // not meaningful on a printed contract\n default:\n return value ? esc(value) : empty\n }\n}\n\nexport interface BuildPrintOptions {\n /** Heading shown at the top of the page (defaults to the document title). */\n heading?: string\n /** Small line under the heading (e.g. person name). */\n subheading?: string\n}\n\n/** Build a full, self-contained printable HTML document. */\nexport function buildPrintHtml(\n template: FormTemplate,\n doc: FormDocument,\n opts: BuildPrintOptions = {},\n): string {\n const heading = opts.heading ?? doc.title ?? template.name\n const subParts = [opts.subheading ?? doc.personName, new Date(doc.createdAt).toLocaleDateString()]\n .filter(Boolean)\n .map((s) => esc(s))\n .join(' &middot; ')\n\n const fields = [...template.schema.fields].sort((a, b) => a.row - b.row || a.col - b.col)\n\n const clampSpan = (n: unknown) => Math.max(1, Math.min(12, Number(n) || 12))\n\n // Mirror the builder's 12-column grid so the print keeps the authored layout\n // (side-by-side fields stay side by side). Titles span the full row.\n const body = fields\n .map((field) => {\n if (field.type === 'title') {\n return `<h2 class=\"section\" style=\"grid-column: 1 / -1\">${esc(field.label)}</h2>`\n }\n const rendered = renderValue(field, doc.data[field.id])\n if (rendered === '') return ''\n const isLong = field.type === 'memo' || field.type === 'richtext'\n return `<div class=\"field${isLong ? ' field--long' : ''}\" style=\"grid-column: span ${clampSpan(field.colSpan)}\">\n <div class=\"label\">${esc(field.label)}</div>\n <div class=\"value\">${rendered}</div>\n </div>`\n })\n .join('\\n')\n\n return `<!doctype html>\n<html lang=\"pt-BR\">\n<head>\n<meta charset=\"utf-8\"/>\n<title>${esc(heading)}</title>\n<style>\n @page { size: A4; margin: 20mm; }\n * { box-sizing: border-box; }\n html, body { margin: 0; padding: 0; }\n body {\n font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;\n color: #111; font-size: 12pt; line-height: 1.5;\n -webkit-print-color-adjust: exact; print-color-adjust: exact;\n }\n .page { max-width: 170mm; margin: 0 auto; padding: 12mm 0; }\n header { border-bottom: 2px solid #111; padding-bottom: 8px; margin-bottom: 20px; }\n header h1 { font-size: 18pt; margin: 0 0 2px; }\n header .sub { font-size: 10pt; color: #555; }\n /* 12-column grid — mirrors the builder Preview (FormRenderer) so the print\n keeps the authored column layout. */\n .grid { display: grid; grid-template-columns: repeat(12, 1fr); column-gap: 16px; row-gap: 12px; align-items: start; }\n h2.section { font-size: 13pt; margin: 22px 0 4px; padding-bottom: 3px; border-bottom: 1px solid #ccc; }\n .grid > h2.section:first-child { margin-top: 4px; }\n .field { margin: 0; min-width: 0; }\n .field .label { font-size: 9pt; text-transform: uppercase; letter-spacing: .04em; color: #666; margin-bottom: 2px; }\n .field .value { font-size: 12pt; }\n .field--long .value .long { white-space: normal; text-align: justify; }\n .empty { color: #bbb; }\n footer { margin-top: 40px; font-size: 9pt; color: #888; text-align: center; }\n @media screen { body { background: #f3f3f3; } .page { background: #fff; max-width: 210mm; min-height: 297mm; padding: 20mm; box-shadow: 0 1px 8px rgba(0,0,0,.15); } }\n</style>\n</head>\n<body>\n <div class=\"page\">\n <header>\n <h1>${esc(heading)}</h1>\n ${subParts ? `<div class=\"sub\">${subParts}</div>` : ''}\n </header>\n <div class=\"grid\">\n ${body}\n </div>\n </div>\n</body>\n</html>`\n}\n\n/**\n * Print a prebuilt HTML document via a hidden iframe (popup-blocker safe).\n * The browser print dialog handles both printing and \"Save as PDF\".\n */\nexport function printHtml(html: string): void {\n if (typeof document === 'undefined') return\n const iframe = document.createElement('iframe')\n iframe.setAttribute('aria-hidden', 'true')\n iframe.style.position = 'fixed'\n iframe.style.right = '0'\n iframe.style.bottom = '0'\n iframe.style.width = '0'\n iframe.style.height = '0'\n iframe.style.border = '0'\n document.body.appendChild(iframe)\n\n const cleanup = () => {\n // Give the print dialog a beat before tearing the frame down.\n setTimeout(() => {\n if (iframe.parentNode) iframe.parentNode.removeChild(iframe)\n }, 1000)\n }\n\n const win = iframe.contentWindow\n const idoc = iframe.contentDocument || win?.document\n if (!win || !idoc) {\n cleanup()\n return\n }\n\n idoc.open()\n idoc.write(html)\n idoc.close()\n\n const doPrint = () => {\n try {\n win.focus()\n win.print()\n } finally {\n cleanup()\n }\n }\n\n // Wait for the frame document to be ready before printing.\n if (idoc.readyState === 'complete') {\n setTimeout(doPrint, 50)\n } else {\n iframe.addEventListener('load', () => setTimeout(doPrint, 50), { once: true })\n }\n}\n\n/** Convenience: build + print a document in one call. */\nexport function printDocument(\n template: FormTemplate,\n doc: FormDocument,\n opts?: BuildPrintOptions,\n): void {\n printHtml(buildPrintHtml(template, doc, opts))\n}\n","import type { PluginAITool } from '@fayz-ai/core'\n\n/**\n * The assistant's form-building tool. It is a SERVER-PLANE write (a pool RPC),\n * not a client-side surface mutation — so it works from any page and any\n * channel (in-app FAB, WhatsApp, MCP), and persists straight to the tenant's\n * form templates. This is the same pattern agenda/financial/marketing use.\n *\n * The builder surface (see FormBuilder) still publishes CONTEXT — which form is\n * open and its current fields with ids — so \"add an allergies field to THIS\n * form\" resolves to the right template. But the action itself is this tool.\n */\n\nconst CATEGORY_ENUM = ['anamnesis', 'evolution', 'report', 'contract', 'general'] as const\n\nconst FIELD_SHAPE =\n 'A field object: { type, label, required?, placeholder?, options?[] }. ' +\n 'type ∈ title|text|memo|richtext|date|select|radio|tags|checkbox|image|gallery|budget ' +\n '(title = section heading, no input). options are plain strings, only for select/radio/tags.'\n\nexport const createOrUpdateFormTool: PluginAITool = {\n id: 'custom_forms.createOrUpdateForm',\n name: 'createOrUpdateForm',\n description:\n 'Creates or edits a form template (anamnese, contrato, ficha…). Persisted server-side, so it works from ANY page — the user does NOT need the form builder open. ' +\n 'Omit templateId to CREATE (name required); pass templateId to EDIT an existing form. ' +\n 'Send the FULL desired field list in `fields` (replaces all fields), or `append_fields` to add to what already exists. ' +\n 'Layout is automatic — never send positions. Understand the intent and propose a sensible field set in one call rather than asking field by field. The user confirms before it is saved.',\n icon: 'FileText',\n mode: 'persist',\n limitKey: 'form_templates',\n permission: { feature: 'custom_forms', action: 'create' },\n execution: { plane: 'server', kind: 'rpc', rpc: 'agent_forms_upsert_template' },\n category: 'Forms',\n suggestions: [\n { label: 'Monte um formulário de anamnese' },\n { label: 'Adicione um campo de alergias neste formulário' },\n ],\n parameters: {\n type: 'object',\n properties: {\n templateId: {\n type: 'string',\n description: 'Existing form id to EDIT (from context or listFormTemplates). Omit to create a new form.',\n },\n name: { type: 'string', description: 'Form name — required when creating.' },\n category: {\n type: 'string',\n enum: [...CATEGORY_ENUM],\n description: 'anamnesis | evolution | report | contract | general',\n },\n description: { type: 'string', description: 'Optional short description of the form.' },\n fields: {\n type: 'array',\n description: `The FULL desired field list — REPLACES every existing field. Each item: ${FIELD_SHAPE}`,\n items: { type: 'object', description: FIELD_SHAPE },\n },\n append_fields: {\n type: 'array',\n description: `Fields to ADD to the existing form, keeping current ones. Use this for incremental edits instead of \\`fields\\`. Each item: ${FIELD_SHAPE}`,\n items: { type: 'object', description: FIELD_SHAPE },\n },\n },\n },\n}\n\nexport const FORM_BUILDER_INSTRUCTIONS = [\n 'Você está na tela do CONSTRUTOR DE FORMULÁRIOS. Para montar ou editar formulários use a tool createOrUpdateForm (ela persiste no backend e funciona mesmo fora desta tela).',\n 'O formulário aberto agora — id, nome, categoria e campos com seus ids — está no JSON da tela em contexto.',\n 'Para EDITAR este formulário, passe o templateId do contexto. Para adicionar campos mantendo os atuais, use append_fields; para redefinir tudo, use fields com a lista completa.',\n 'Ao criar do zero: entenda a intenção e proponha um conjunto de campos enxuto numa única chamada (não pergunte campo a campo). Depois confirme em uma frase o que foi montado.',\n 'Tipos de campo: title (cabeçalho de seção, sem input), text, memo (multilinha), richtext, date, select, radio, tags, checkbox, image, gallery, budget. Layout é automático.',\n].join(' ')\n","// ============================================================\n// PERSON FIELD MAPPING — prefill a document from a person record\n// ============================================================\n//\n// A template field can declare `map: '<personKey>'` (see FormFieldDef.map).\n// When a document is created from a person's profile, fields with a mapping are\n// pre-filled from that person's record. The person record reaching the widget\n// is the host app's CRUD detail `item`, keyed by the entity's field keys\n// (camelCase, e.g. `documentNumber`). We resolve tolerantly so the same mapping\n// works whether the host exposes camelCase or snake_case keys.\n\nimport type { FormSchema } from '../types'\n\n/** Curated, vertical-agnostic person keys offered in the field builder. */\nexport const PERSON_FIELD_OPTIONS: Array<{ value: string; labelKey: string }> = [\n { value: 'name', labelKey: 'customForms.personField.name' },\n { value: 'documentNumber', labelKey: 'customForms.personField.documentNumber' },\n { value: 'email', labelKey: 'customForms.personField.email' },\n { value: 'phone', labelKey: 'customForms.personField.phone' },\n { value: 'dateOfBirth', labelKey: 'customForms.personField.dateOfBirth' },\n { value: 'guardianName', labelKey: 'customForms.personField.guardianName' },\n { value: 'address', labelKey: 'customForms.personField.address' },\n { value: 'city', labelKey: 'customForms.personField.city' },\n { value: 'state', labelKey: 'customForms.personField.state' },\n]\n\nconst toSnake = (k: string) => k.replace(/[A-Z]/g, (m) => '_' + m.toLowerCase())\nconst toCamel = (k: string) => k.replace(/_([a-z])/g, (_, c: string) => c.toUpperCase())\n\n/** Read a person field value, tolerating camelCase/snake_case key variants. */\nexport function resolvePersonValue(\n person: Record<string, unknown> | undefined | null,\n mapKey: string | undefined,\n): unknown {\n if (!person || !mapKey) return undefined\n for (const key of [mapKey, toSnake(mapKey), toCamel(mapKey)]) {\n const v = person[key]\n if (v != null && v !== '') return v\n }\n return undefined\n}\n\n/**\n * Build the initial document data for a template. For each field, applies (in\n * priority order) an existing value, then the mapped person value, then the\n * field's static `defaultValue`. Never overwrites a value already present in\n * `existing`.\n */\nexport function prefillFromPerson(\n schema: FormSchema | undefined,\n person: Record<string, unknown> | undefined | null,\n existing: Record<string, unknown> = {},\n): Record<string, unknown> {\n const data: Record<string, unknown> = { ...existing }\n if (!schema) return data\n for (const field of schema.fields) {\n const current = data[field.id]\n if (current != null && current !== '') continue\n const mapped = field.map ? resolvePersonValue(person, field.map) : undefined\n if (mapped != null) {\n data[field.id] = mapped\n } else if (field.defaultValue != null) {\n data[field.id] = field.defaultValue\n }\n }\n return data\n}\n"]}
@@ -1 +0,0 @@
1
- {"version":3,"file":"document-types.d.ts","sourceRoot":"","sources":["../src/document-types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAO9B,MAAM,WAAW,kBAAkB;IACjC,qEAAqE;IACrE,EAAE,EAAE,MAAM,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,KAAK,CAAC,WAAW,CAAA;IACvB,yEAAyE;IACzE,KAAK,EAAE,MAAM,CAAA;IACb,mDAAmD;IACnD,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,8BAA8B;IAC9B,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,oCAAoC;IACpC,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB;AAED,MAAM,WAAW,oBAAoB;IACnC,4DAA4D;IAC5D,EAAE,EAAE,MAAM,CAAA;IACV,0DAA0D;IAC1D,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,kBAAkB,EAAE,GAAG,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAAA;CACjF;AAMD,wBAAgB,4BAA4B,CAAC,QAAQ,EAAE,oBAAoB,QAG1E;AAED,wBAAgB,wBAAwB,IAAI,oBAAoB,EAAE,CAEjE;AAED,wBAAsB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAQzF"}
@@ -1,28 +0,0 @@
1
- import React, { createContext, useContext } from 'react'
2
- import type { CustomFormsConfig } from './config'
3
- import type { CustomFormsDataProvider } from './data/types'
4
- import type { CustomFormsStore } from './store'
5
-
6
- export interface CustomFormsContextValue {
7
- config: CustomFormsConfig
8
- provider: CustomFormsDataProvider
9
- store: CustomFormsStore
10
- }
11
-
12
- const Ctx = createContext<CustomFormsContextValue | null>(null)
13
-
14
- export function CustomFormsProvider({
15
- value,
16
- children,
17
- }: {
18
- value: CustomFormsContextValue
19
- children: React.ReactNode
20
- }) {
21
- return React.createElement(Ctx.Provider, { value }, children)
22
- }
23
-
24
- export function useCustomFormsContext(): CustomFormsContextValue {
25
- const ctx = useContext(Ctx)
26
- if (!ctx) throw new Error('useCustomFormsContext must be used within CustomFormsProvider')
27
- return ctx
28
- }
@@ -1,73 +0,0 @@
1
- import type { PluginAITool } from '@fayz-ai/core'
2
-
3
- /**
4
- * The assistant's form-building tool. It is a SERVER-PLANE write (a pool RPC),
5
- * not a client-side surface mutation — so it works from any page and any
6
- * channel (in-app FAB, WhatsApp, MCP), and persists straight to the tenant's
7
- * form templates. This is the same pattern agenda/financial/marketing use.
8
- *
9
- * The builder surface (see FormBuilder) still publishes CONTEXT — which form is
10
- * open and its current fields with ids — so "add an allergies field to THIS
11
- * form" resolves to the right template. But the action itself is this tool.
12
- */
13
-
14
- const CATEGORY_ENUM = ['anamnesis', 'evolution', 'report', 'contract', 'general'] as const
15
-
16
- const FIELD_SHAPE =
17
- 'A field object: { type, label, required?, placeholder?, options?[] }. ' +
18
- 'type ∈ title|text|memo|richtext|date|select|radio|tags|checkbox|image|gallery|budget ' +
19
- '(title = section heading, no input). options are plain strings, only for select/radio/tags.'
20
-
21
- export const createOrUpdateFormTool: PluginAITool = {
22
- id: 'custom_forms.createOrUpdateForm',
23
- name: 'createOrUpdateForm',
24
- description:
25
- 'Creates or edits a form template (anamnese, contrato, ficha…). Persisted server-side, so it works from ANY page — the user does NOT need the form builder open. ' +
26
- 'Omit templateId to CREATE (name required); pass templateId to EDIT an existing form. ' +
27
- 'Send the FULL desired field list in `fields` (replaces all fields), or `append_fields` to add to what already exists. ' +
28
- 'Layout is automatic — never send positions. Understand the intent and propose a sensible field set in one call rather than asking field by field. The user confirms before it is saved.',
29
- icon: 'FileText',
30
- mode: 'persist',
31
- limitKey: 'form_templates',
32
- permission: { feature: 'custom_forms', action: 'create' },
33
- execution: { plane: 'server', kind: 'rpc', rpc: 'agent_forms_upsert_template' },
34
- category: 'Forms',
35
- suggestions: [
36
- { label: 'Monte um formulário de anamnese' },
37
- { label: 'Adicione um campo de alergias neste formulário' },
38
- ],
39
- parameters: {
40
- type: 'object',
41
- properties: {
42
- templateId: {
43
- type: 'string',
44
- description: 'Existing form id to EDIT (from context or listFormTemplates). Omit to create a new form.',
45
- },
46
- name: { type: 'string', description: 'Form name — required when creating.' },
47
- category: {
48
- type: 'string',
49
- enum: [...CATEGORY_ENUM],
50
- description: 'anamnesis | evolution | report | contract | general',
51
- },
52
- description: { type: 'string', description: 'Optional short description of the form.' },
53
- fields: {
54
- type: 'array',
55
- description: `The FULL desired field list — REPLACES every existing field. Each item: ${FIELD_SHAPE}`,
56
- items: { type: 'object', description: FIELD_SHAPE },
57
- },
58
- append_fields: {
59
- type: 'array',
60
- description: `Fields to ADD to the existing form, keeping current ones. Use this for incremental edits instead of \`fields\`. Each item: ${FIELD_SHAPE}`,
61
- items: { type: 'object', description: FIELD_SHAPE },
62
- },
63
- },
64
- },
65
- }
66
-
67
- export const FORM_BUILDER_INSTRUCTIONS = [
68
- 'Você está na tela do CONSTRUTOR DE FORMULÁRIOS. Para montar ou editar formulários use a tool createOrUpdateForm (ela persiste no backend e funciona mesmo fora desta tela).',
69
- 'O formulário aberto agora — id, nome, categoria e campos com seus ids — está no JSON da tela em contexto.',
70
- 'Para EDITAR este formulário, passe o templateId do contexto. Para adicionar campos mantendo os atuais, use append_fields; para redefinir tudo, use fields com a lista completa.',
71
- 'Ao criar do zero: entenda a intenção e proponha um conjunto de campos enxuto numa única chamada (não pergunte campo a campo). Depois confirme em uma frase o que foi montado.',
72
- 'Tipos de campo: title (cabeçalho de seção, sem input), text, memo (multilinha), richtext, date, select, radio, tags, checkbox, image, gallery, budget. Layout é automático.',
73
- ].join(' ')
@@ -1,92 +0,0 @@
1
- import React, { useState, useCallback, useRef } from 'react'
2
- import { Plus, ChevronDown } from 'lucide-react'
3
- import { Button } from '@fayz-ai/ui'
4
- import {
5
- Dropdown,
6
- DropdownTrigger,
7
- DropdownContent,
8
- DropdownItem,
9
- DropdownLabel,
10
- DropdownSeparator,
11
- } from '@fayz-ai/ui'
12
- import { useTranslation } from '@fayz-ai/core'
13
- import { getAllDocumentTypes, type DocumentTypeOption } from '../document-types'
14
-
15
- interface AddDocumentDropdownProps {
16
- personId: string
17
- onSelect: (option: DocumentTypeOption) => void
18
- }
19
-
20
- export function AddDocumentDropdown({ personId, onSelect }: AddDocumentDropdownProps) {
21
- const t = useTranslation()
22
- const [options, setOptions] = useState<DocumentTypeOption[]>([])
23
- const [loaded, setLoaded] = useState(false)
24
- const loadingRef = useRef(false)
25
-
26
- const handleOpen = useCallback((open: boolean) => {
27
- if (!open || loaded || loadingRef.current) return
28
- loadingRef.current = true
29
- getAllDocumentTypes(personId).then((opts) => {
30
- setOptions(opts)
31
- setLoaded(true)
32
- loadingRef.current = false
33
- })
34
- }, [personId, loaded])
35
-
36
- // Group options by group label
37
- const groups: Array<{ label: string; items: DocumentTypeOption[] }> = []
38
- for (const opt of options) {
39
- let group = groups.find((g) => g.label === opt.group)
40
- if (!group) {
41
- group = { label: opt.group, items: [] }
42
- groups.push(group)
43
- }
44
- group.items.push(opt)
45
- }
46
-
47
- return (
48
- <Dropdown onOpenChange={handleOpen}>
49
- <DropdownTrigger asChild>
50
- <Button size="sm" className="h-8">
51
- <Plus className="h-3.5 w-3.5 mr-1" />
52
- {t('customForms.addDocument')}
53
- <ChevronDown className="h-3 w-3 ml-1 opacity-50" />
54
- </Button>
55
- </DropdownTrigger>
56
- <DropdownContent align="end" className="w-56">
57
- {groups.map((group, gi) => (
58
- <React.Fragment key={group.label}>
59
- {gi > 0 && <DropdownSeparator />}
60
- <DropdownLabel className="text-[10px] uppercase tracking-wider">
61
- {group.label}
62
- </DropdownLabel>
63
- {group.items.map((opt) => {
64
- const Icon = opt.icon
65
- return (
66
- <DropdownItem key={opt.id} onClick={() => onSelect(opt)} className="gap-2">
67
- <Icon className="h-4 w-4 text-muted-foreground shrink-0" />
68
- <div className="flex-1 min-w-0">
69
- <p className="text-sm truncate">{opt.label}</p>
70
- {opt.description && (
71
- <p className="text-[10px] text-muted-foreground truncate">{opt.description}</p>
72
- )}
73
- </div>
74
- </DropdownItem>
75
- )
76
- })}
77
- </React.Fragment>
78
- ))}
79
- {!loaded && (
80
- <div className="flex items-center justify-center py-3">
81
- <div className="h-4 w-4 border-2 border-primary border-t-transparent animate-spin rounded-full" />
82
- </div>
83
- )}
84
- {loaded && options.length === 0 && (
85
- <div className="px-2 py-3 text-center text-xs text-muted-foreground">
86
- {t('customForms.noTemplates')}
87
- </div>
88
- )}
89
- </DropdownContent>
90
- </Dropdown>
91
- )
92
- }
@@ -1,87 +0,0 @@
1
- import React from 'react'
2
- import { useDroppable } from '@dnd-kit/core'
3
- import { SortableContext, verticalListSortingStrategy } from '@dnd-kit/sortable'
4
- import { useTranslation } from '@fayz-ai/core'
5
- import type { FormFieldDef, FormFieldType } from '../types'
6
- import { BuilderField } from './BuilderField'
7
- import { FIELD_TYPES, DEFAULT_COL_SPAN } from './FieldPalette'
8
-
9
- interface BuilderCanvasProps {
10
- fields: FormFieldDef[]
11
- selectedFieldId: string | null
12
- onSelectField: (field: FormFieldDef) => void
13
- onRemoveField: (fieldId: string) => void
14
- draggingFieldType: FormFieldType | null
15
- }
16
-
17
- function DropPreview({ fieldType }: { fieldType: FormFieldType }) {
18
- const t = useTranslation()
19
- const def = FIELD_TYPES.find((ft) => ft.type === fieldType)
20
- if (!def) return null
21
- const Icon = def.icon
22
-
23
- return (
24
- <div
25
- className="flex items-center gap-2 rounded-lg border-2 border-dashed border-primary/40 bg-primary/5 p-3"
26
- style={{ gridColumn: `span ${DEFAULT_COL_SPAN[fieldType] ?? 12}` }}
27
- >
28
- <Icon className="h-4 w-4 text-primary/50" />
29
- <span className="text-sm text-primary/50 font-medium">
30
- {t(`customForms.fieldType.${fieldType}`)}
31
- </span>
32
- </div>
33
- )
34
- }
35
-
36
- export function BuilderCanvas({
37
- fields,
38
- selectedFieldId,
39
- onSelectField,
40
- onRemoveField,
41
- draggingFieldType,
42
- }: BuilderCanvasProps) {
43
- const t = useTranslation()
44
- const { setNodeRef, isOver } = useDroppable({ id: 'builder-canvas' })
45
-
46
- const sortedFields = [...fields].sort((a, b) => a.row - b.row || a.col - b.col)
47
- const showPreview = isOver && draggingFieldType !== null
48
-
49
- return (
50
- <div
51
- ref={setNodeRef}
52
- className={`flex-1 min-h-[400px] rounded-xl border-2 border-dashed p-4 transition-colors ${
53
- isOver ? 'border-primary/50 bg-primary/5' : 'border-border bg-muted/20'
54
- }`}
55
- style={{
56
- backgroundImage:
57
- 'repeating-linear-gradient(90deg, transparent, transparent calc(8.333% - 1px), hsl(var(--border) / 0.3) calc(8.333% - 1px), hsl(var(--border) / 0.3) 8.333%)',
58
- backgroundSize: '100% 100%',
59
- }}
60
- >
61
- {fields.length === 0 && !showPreview && (
62
- <div className="flex items-center justify-center h-full text-muted-foreground text-sm">
63
- {t('customForms.builder.emptyCanvas')}
64
- </div>
65
- )}
66
-
67
- <SortableContext items={sortedFields.map((f) => f.id)} strategy={verticalListSortingStrategy}>
68
- <div
69
- className="grid gap-3"
70
- style={{ gridTemplateColumns: 'repeat(12, 1fr)' }}
71
- >
72
- {sortedFields.map((field) => (
73
- <BuilderField
74
- key={field.id}
75
- field={field}
76
- isSelected={field.id === selectedFieldId}
77
- onSelect={onSelectField}
78
- onRemove={onRemoveField}
79
- />
80
- ))}
81
-
82
- {showPreview && <DropPreview fieldType={draggingFieldType!} />}
83
- </div>
84
- </SortableContext>
85
- </div>
86
- )
87
- }
@@ -1,81 +0,0 @@
1
- import React from 'react'
2
- import { useSortable } from '@dnd-kit/sortable'
3
- import { CSS } from '@dnd-kit/utilities'
4
- import { GripVertical, X } from 'lucide-react'
5
- import { useTranslation } from '@fayz-ai/core'
6
- import type { FormFieldDef } from '../types'
7
- import { FIELD_TYPES } from './FieldPalette'
8
-
9
- interface BuilderFieldProps {
10
- field: FormFieldDef
11
- onSelect: (field: FormFieldDef) => void
12
- onRemove: (fieldId: string) => void
13
- isSelected: boolean
14
- }
15
-
16
- export function BuilderField({ field, onSelect, onRemove, isSelected }: BuilderFieldProps) {
17
- const t = useTranslation()
18
- const {
19
- attributes,
20
- listeners,
21
- setNodeRef,
22
- transform,
23
- transition,
24
- isDragging,
25
- } = useSortable({ id: field.id })
26
-
27
- const style: React.CSSProperties = {
28
- transform: CSS.Transform.toString(transform),
29
- transition,
30
- gridColumn: `span ${field.colSpan}`,
31
- opacity: isDragging ? 0.5 : 1,
32
- }
33
-
34
- const fieldTypeDef = FIELD_TYPES.find((ft) => ft.type === field.type)
35
- const Icon = fieldTypeDef?.icon
36
-
37
- return (
38
- <div
39
- ref={setNodeRef}
40
- style={style}
41
- className={`group relative flex items-start gap-2 rounded-lg border-2 bg-card p-3 transition-colors cursor-pointer ${
42
- isSelected
43
- ? 'border-primary ring-1 ring-primary/20'
44
- : 'border-border hover:border-primary/30'
45
- }`}
46
- onClick={() => onSelect(field)}
47
- >
48
- {/* Drag handle */}
49
- <button
50
- {...attributes}
51
- {...listeners}
52
- className="mt-0.5 cursor-grab active:cursor-grabbing text-muted-foreground/50 hover:text-muted-foreground"
53
- onClick={(e) => e.stopPropagation()}
54
- >
55
- <GripVertical className="h-4 w-4" />
56
- </button>
57
-
58
- {/* Content */}
59
- <div className="flex-1 min-w-0">
60
- <div className="flex items-center gap-1.5">
61
- {Icon && <Icon className="h-3.5 w-3.5 text-muted-foreground shrink-0" />}
62
- <span className="text-[10px] font-medium uppercase tracking-wider text-muted-foreground">
63
- {t(`customForms.fieldType.${field.type}`)}
64
- </span>
65
- </div>
66
- <p className="text-sm font-medium mt-0.5 truncate">{field.label}</p>
67
- {field.required && (
68
- <span className="text-[10px] text-destructive">*</span>
69
- )}
70
- </div>
71
-
72
- {/* Remove button */}
73
- <button
74
- onClick={(e) => { e.stopPropagation(); onRemove(field.id) }}
75
- className="absolute top-1.5 right-1.5 p-1 rounded-md opacity-0 group-hover:opacity-100 hover:bg-destructive/10 transition-opacity"
76
- >
77
- <X className="h-3 w-3 text-destructive" />
78
- </button>
79
- </div>
80
- )
81
- }