@fayz-ai/plugin-forms 0.11.1 → 0.13.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.
Files changed (38) hide show
  1. package/dist/{FormBuilder-YHJHF6HZ.js → FormBuilder-AO6V2YOT.js} +3 -3
  2. package/dist/{FormBuilder-YHJHF6HZ.js.map → FormBuilder-AO6V2YOT.js.map} +1 -1
  3. package/dist/{chunk-RBLXIKWB.js → chunk-2AIKIPGU.js} +51 -7
  4. package/dist/chunk-2AIKIPGU.js.map +1 -0
  5. package/dist/components/AddDocumentDropdown.d.ts +12 -1
  6. package/dist/components/AddDocumentDropdown.d.ts.map +1 -1
  7. package/dist/components/DocumentList.d.ts +4 -1
  8. package/dist/components/DocumentList.d.ts.map +1 -1
  9. package/dist/components/DocumentPreview.d.ts +10 -0
  10. package/dist/components/DocumentPreview.d.ts.map +1 -0
  11. package/dist/components/FormRenderer.d.ts.map +1 -1
  12. package/dist/components/PersonDocumentsWidget.d.ts +13 -1
  13. package/dist/components/PersonDocumentsWidget.d.ts.map +1 -1
  14. package/dist/components/PersonFormsAlert.d.ts.map +1 -1
  15. package/dist/data/mock.d.ts.map +1 -1
  16. package/dist/data/supabase.d.ts.map +1 -1
  17. package/dist/data/types.d.ts +11 -1
  18. package/dist/data/types.d.ts.map +1 -1
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +722 -181
  21. package/dist/index.js.map +1 -1
  22. package/dist/lib/categories.d.ts +69 -0
  23. package/dist/lib/categories.d.ts.map +1 -0
  24. package/dist/lib/person-requirements.d.ts +9 -0
  25. package/dist/lib/person-requirements.d.ts.map +1 -1
  26. package/dist/lib/plain-text.d.ts +9 -0
  27. package/dist/lib/plain-text.d.ts.map +1 -0
  28. package/dist/lib/tab-counts.d.ts +12 -0
  29. package/dist/lib/tab-counts.d.ts.map +1 -0
  30. package/dist/locales/en.d.ts.map +1 -1
  31. package/dist/locales/pt-BR.d.ts.map +1 -1
  32. package/dist/migrations/index.d.ts +2 -0
  33. package/dist/migrations/index.d.ts.map +1 -1
  34. package/dist/types.d.ts +16 -0
  35. package/dist/types.d.ts.map +1 -1
  36. package/dist/views/TemplateListView.d.ts.map +1 -1
  37. package/package.json +6 -5
  38. package/dist/chunk-RBLXIKWB.js.map +0 -1
@@ -0,0 +1,69 @@
1
+ import type React from 'react';
2
+ import type { TemplateCategory } from '../types';
3
+ /** Ordem de leitura, não alfabética: é a ordem em que o prontuário acontece. */
4
+ export declare const CATEGORY_ORDER: TemplateCategory[];
5
+ /** `groupOrder` do `Novo ▾`. Arquivos ficam em 90, depois de todo formulário. */
6
+ export declare const CATEGORY_GROUP_ORDER: Record<TemplateCategory, number>;
7
+ export declare const CATEGORY_ICONS: Record<TemplateCategory, React.ElementType>;
8
+ /**
9
+ * O rótulo FORA de React.
10
+ *
11
+ * `getTypes()` do registro de tipos de documento não é um componente, então não
12
+ * pode usar `useTranslation`. `tl` lê o locale corrente e é o mesmo caminho que
13
+ * o resto do SDK usa para texto fora de hook.
14
+ */
15
+ export declare function categoryLabel(category: TemplateCategory): string;
16
+ /** Categoria conhecida, ou `general`. Uma linha antiga com categoria vazia não
17
+ * pode sumir da tela por não caber no enum. */
18
+ export declare function normalizeCategory(value: unknown): TemplateCategory;
19
+ /**
20
+ * O ícone e a cor da bolha na linha do tempo.
21
+ *
22
+ * Nome de ícone (string) e não componente: é o que o `TimelineItem` do admin
23
+ * pede, porque ele resolve contra o lucide inteiro — o mesmo caminho que aba
24
+ * de ficha, linha de rail e widget de dashboard já usam.
25
+ *
26
+ * Os tons contam a leitura clínica: a anamnese é o começo (primária), a
27
+ * evolução é o que anda (info), o laudo conclui (sucesso), o contrato
28
+ * compromete (aviso).
29
+ */
30
+ export declare const CATEGORY_TIMELINE_ICON: Record<TemplateCategory, string>;
31
+ export declare const CATEGORY_TONE: Record<TemplateCategory, 'primary' | 'info' | 'success' | 'warning' | 'neutral'>;
32
+ /** A pastilha de um filtro do prontuário: a categoria de um formulário, ou
33
+ * `file` para o que não tem modelo (foto, anexo). */
34
+ export type DocumentBucket = TemplateCategory | 'file';
35
+ /** Um documento sem modelo não tem categoria — é foto ou anexo, e cai no balde
36
+ * próprio em vez de fingir ser "Geral". */
37
+ export declare function bucketOf(doc: {
38
+ kind?: string;
39
+ templateCategory?: unknown;
40
+ }): DocumentBucket;
41
+ /**
42
+ * De documento para item da linha do tempo — a MESMA `TimelineItem` que a aba
43
+ * Atividade monta, para as duas abas desenharem igual.
44
+ *
45
+ * O TÍTULO é o modelo ("Anamnese", "Evolução"), não o título que o upload deu
46
+ * ao arquivo: numa ficha clínica é o modelo que se procura. O `title` do
47
+ * documento só desce para a descrição quando ACRESCENTA algo — senão a linha
48
+ * diria a mesma coisa duas vezes.
49
+ *
50
+ * Pura de propósito: é o que dá para testar sem montar React nem banco.
51
+ */
52
+ export declare function documentToTimelineItem(doc: {
53
+ id: string;
54
+ kind?: string;
55
+ title?: string;
56
+ templateName?: string;
57
+ templateCategory?: unknown;
58
+ createdAt: string;
59
+ }, fallbackTitle: string): {
60
+ id: string;
61
+ sourceId: DocumentBucket;
62
+ occurredAt: string;
63
+ title: string;
64
+ description: string | undefined;
65
+ badge: string | undefined;
66
+ icon: string;
67
+ tone: "primary" | "info" | "success" | "warning" | "neutral";
68
+ };
69
+ //# sourceMappingURL=categories.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"categories.d.ts","sourceRoot":"","sources":["../../src/lib/categories.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAK9B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAA;AAiBhD,gFAAgF;AAChF,eAAO,MAAM,cAAc,EAAE,gBAAgB,EAE5C,CAAA;AAED,iFAAiF;AACjF,eAAO,MAAM,oBAAoB,EAAE,MAAM,CAAC,gBAAgB,EAAE,MAAM,CAMjE,CAAA;AAED,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,gBAAgB,EAAE,KAAK,CAAC,WAAW,CAMtE,CAAA;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,gBAAgB,GAAG,MAAM,CAQhE;AAED;gDACgD;AAChD,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,OAAO,GAAG,gBAAgB,CAIlE;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,sBAAsB,EAAE,MAAM,CAAC,gBAAgB,EAAE,MAAM,CAMnE,CAAA;AAED,eAAO,MAAM,aAAa,EAAE,MAAM,CAAC,gBAAgB,EAAE,SAAS,GAAG,MAAM,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,CAM1G,CAAA;AAED;sDACsD;AACtD,MAAM,MAAM,cAAc,GAAG,gBAAgB,GAAG,MAAM,CAAA;AAEtD;4CAC4C;AAC5C,wBAAgB,QAAQ,CAAC,GAAG,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,gBAAgB,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,cAAc,CAG3F;AAuBD;;;;;;;;;;GAUG;AACH,wBAAgB,sBAAsB,CACpC,GAAG,EAAE;IACH,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAA;IAChE,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAC9C,EACD,aAAa,EAAE,MAAM;;;;;;;;;EAgBtB"}
@@ -4,6 +4,15 @@ export interface PersonFormRequirement {
4
4
  templateName: string;
5
5
  category: string;
6
6
  validityDays: number | null;
7
+ /**
8
+ * A FALTA ALERTA, ou só falta?
9
+ *
10
+ * A segunda pergunta do V1 sobre o mesmo modelo (`alert_when_missing`, e ela
11
+ * nasce desligada): exigir põe a bola no checklist do atendimento; alertar
12
+ * leva a falta para a FRENTE dele — a faixa âmbar no agendamento e na ficha.
13
+ * Uma casa que exige anamnese e não quer a faixa deixa esta desligada.
14
+ */
15
+ alertWhenMissing: boolean;
7
16
  /** Quando foi preenchido pela última vez. `null` = nunca. */
8
17
  lastFilledAt: string | null;
9
18
  /** Até quando vale. `null` = não vence, ou nunca foi preenchido. */
@@ -1 +1 @@
1
- {"version":3,"file":"person-requirements.d.ts","sourceRoot":"","sources":["../../src/lib/person-requirements.ts"],"names":[],"mappings":"AAgBA,MAAM,MAAM,gBAAgB,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,CAAA;AAE9D,MAAM,WAAW,qBAAqB;IACpC,UAAU,EAAE,MAAM,CAAA;IAClB,YAAY,EAAE,MAAM,CAAA;IACpB,QAAQ,EAAE,MAAM,CAAA;IAChB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,6DAA6D;IAC7D,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,oEAAoE;IACpE,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,KAAK,EAAE,gBAAgB,CAAA;CACxB;AAED;;;;;;GAMG;AACH,wBAAsB,yBAAyB,CAC7C,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,qBAAqB,EAAE,CAAC,CAgBlC;AAED,0DAA0D;AAC1D,eAAO,MAAM,aAAa,GAAI,GAAG,qBAAqB,KAAG,OAA8B,CAAA"}
1
+ {"version":3,"file":"person-requirements.d.ts","sourceRoot":"","sources":["../../src/lib/person-requirements.ts"],"names":[],"mappings":"AAgBA,MAAM,MAAM,gBAAgB,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,CAAA;AAE9D,MAAM,WAAW,qBAAqB;IACpC,UAAU,EAAE,MAAM,CAAA;IAClB,YAAY,EAAE,MAAM,CAAA;IACpB,QAAQ,EAAE,MAAM,CAAA;IAChB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B;;;;;;;OAOG;IACH,gBAAgB,EAAE,OAAO,CAAA;IACzB,6DAA6D;IAC7D,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,oEAAoE;IACpE,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,KAAK,EAAE,gBAAgB,CAAA;CACxB;AAED;;;;;;GAMG;AACH,wBAAsB,yBAAyB,CAC7C,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,qBAAqB,EAAE,CAAC,CAiBlC;AAED,0DAA0D;AAC1D,eAAO,MAAM,aAAa,GAAI,GAAG,qBAAqB,KAAG,OAA8B,CAAA"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * HTML de molde → texto de uma linha por parágrafo.
3
+ *
4
+ * Devolve o valor intocado quando não há marcação nenhuma: a esmagadora maioria
5
+ * dos moldes é texto simples, e passá-los por um regex de tags seria cobrar de
6
+ * todo mundo o defeito de vinte e três.
7
+ */
8
+ export declare function htmlToPlainText(value: string | undefined): string | undefined;
9
+ //# sourceMappingURL=plain-text.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plain-text.d.ts","sourceRoot":"","sources":["../../src/lib/plain-text.ts"],"names":[],"mappings":"AA+DA;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAc7E"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Quantos registros e quantos arquivos esta pessoa tem.
3
+ *
4
+ * UMA consulta para as duas abas: as duas saem de `public.documents`, e pedir
5
+ * dois `count` seria pagar o mesmo `where` duas vezes. Só a coluna `kind` vem —
6
+ * o corpo do documento não interessa para contar.
7
+ *
8
+ * `is_active` e o status arquivado seguem a mesma regra da lista: o que não
9
+ * aparece na aba não pode ser contado na aba.
10
+ */
11
+ export declare function registerFormsTabCounts(): void;
12
+ //# sourceMappingURL=tab-counts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tab-counts.d.ts","sourceRoot":"","sources":["../../src/lib/tab-counts.ts"],"names":[],"mappings":"AAGA;;;;;;;;;GASG;AACH,wBAAgB,sBAAsB,IAAI,IAAI,CAwB7C"}
@@ -1 +1 @@
1
- {"version":3,"file":"en.d.ts","sourceRoot":"","sources":["../../src/locales/en.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,EAAE,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAiHrC,CAAA"}
1
+ {"version":3,"file":"en.d.ts","sourceRoot":"","sources":["../../src/locales/en.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,EAAE,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAwIrC,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"pt-BR.d.ts","sourceRoot":"","sources":["../../src/locales/pt-BR.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAiHvC,CAAA"}
1
+ {"version":3,"file":"pt-BR.d.ts","sourceRoot":"","sources":["../../src/locales/pt-BR.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CA6IvC,CAAA"}
@@ -1,6 +1,8 @@
1
1
  export declare const MIGRATION_000_BASELINE = "-- ============================================================================\n-- plugins/plugin-forms/src/migrations/000_baseline.sql \u2014 what installing this provisions, as one file.\n--\n-- GENERATED by scripts/emit-unit-baselines.mjs (#338). Do not edit by hand:\n-- change the schema, regenerate, and let scripts/check-chain-equivalence.mjs\n-- prove the result still matches.\n--\n-- This replaced 7 migration files. They are kept, unapplied, in\n-- migrations.archive/ \u2014 a year of decisions is worth reading even when it is no\n-- longer worth replaying.\n--\n-- Object order is fixed rather than dependency-sorted: schemas, types,\n-- sequences, tables, defaults, functions, constraints, views, indexes, foreign\n-- keys, triggers, row security, policies, grants. Dependencies BETWEEN units\n-- are carried by the order packages/db/chain.json declares.\n-- ============================================================================\n\n-- A LANGUAGE sql function binds its body at CREATE time, and 570 functions\n-- sorted by name are not sorted by who calls whom. This is what lets them load\n-- in any order; every body is still compiled on first call, so a genuinely\n-- broken reference surfaces then rather than never. Session-scoped.\nSET check_function_bodies = false;\n\n\n\n-- \u2500\u2500 default privileges: reset \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n-- Cleared BEFORE anything is created, so every object below gets the ACL the\n-- dump describes rather than the image's defaults on top of it. Restored at\n-- the end of this file.\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public REVOKE ALL ON SEQUENCES FROM anon, authenticated, service_role, fayz_connector$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public REVOKE ALL ON SEQUENCES FROM anon, authenticated, service_role, fayz_connector$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public REVOKE ALL ON SEQUENCES FROM anon, authenticated, service_role, fayz_connector$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public REVOKE ALL ON SEQUENCES FROM anon, authenticated, service_role, fayz_connector$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public REVOKE ALL ON FUNCTIONS FROM anon, authenticated, service_role, fayz_connector$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public REVOKE ALL ON FUNCTIONS FROM anon, authenticated, service_role, fayz_connector$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public REVOKE ALL ON FUNCTIONS FROM anon, authenticated, service_role, fayz_connector$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public REVOKE ALL ON FUNCTIONS FROM anon, authenticated, service_role, fayz_connector$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public REVOKE ALL ON TABLES FROM anon, authenticated, service_role, fayz_connector$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public REVOKE ALL ON TABLES FROM anon, authenticated, service_role, fayz_connector$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public REVOKE ALL ON TABLES FROM anon, authenticated, service_role, fayz_connector$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public REVOKE ALL ON TABLES FROM anon, authenticated, service_role, fayz_connector$stmt$;\nEND $dp$;\n\n\n-- \u2500\u2500 table \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nCREATE TABLE public.plg_forms_document_files (\n id uuid DEFAULT gen_random_uuid() NOT NULL,\n tenant_id uuid NOT NULL,\n document_id uuid NOT NULL,\n field_key text NOT NULL,\n file_url text NOT NULL,\n file_name text,\n file_size integer,\n mime_type text,\n sort_order integer DEFAULT 0,\n metadata jsonb DEFAULT '{}'::jsonb,\n created_at timestamp with time zone DEFAULT now() NOT NULL\n);\n\nCREATE TABLE public.plg_forms_documents (\n document_id uuid NOT NULL,\n tenant_id uuid NOT NULL,\n template_id uuid NOT NULL,\n data jsonb DEFAULT '{}'::jsonb NOT NULL,\n signed_at timestamp with time zone,\n signed_by uuid,\n created_at timestamp with time zone DEFAULT now() NOT NULL,\n updated_at timestamp with time zone DEFAULT now() NOT NULL\n);\n\nCREATE TABLE public.plg_forms_templates (\n id uuid DEFAULT gen_random_uuid() NOT NULL,\n tenant_id uuid NOT NULL,\n name text NOT NULL,\n description text,\n category text DEFAULT 'general'::text NOT NULL,\n version integer DEFAULT 1 NOT NULL,\n is_current boolean DEFAULT true NOT NULL,\n parent_id uuid,\n schema jsonb DEFAULT '{\"fields\": [], \"layout\": {\"columns\": 12}}'::jsonb NOT NULL,\n specialty text,\n tags text[] DEFAULT '{}'::text[],\n metadata jsonb DEFAULT '{}'::jsonb,\n is_active boolean DEFAULT true NOT NULL,\n is_deleted boolean DEFAULT false NOT NULL,\n created_by uuid,\n updated_by uuid,\n created_at timestamp with time zone DEFAULT now() NOT NULL,\n updated_at timestamp with time zone DEFAULT now() NOT NULL\n);\n\n\n-- \u2500\u2500 function \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nCREATE FUNCTION public.agent_forms_upsert_template(p_tenant_id uuid, p_actor_user_id uuid, p_payload jsonb) RETURNS jsonb\n LANGUAGE plpgsql SECURITY DEFINER\n SET search_path TO 'public'\n AS $$\nDECLARE\n v_denial jsonb;\n v_used int;\n v_template_id uuid;\n v_is_update boolean;\n v_name text;\n v_category text;\n v_description text;\n v_existing jsonb;\n v_input jsonb;\n v_merged jsonb;\n v_fields jsonb;\n v_schema jsonb;\n v_final_id uuid;\nBEGIN\n -- \u2500\u2500 payload \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n BEGIN\n v_template_id := NULLIF(p_payload->>'template_id','')::uuid;\n EXCEPTION WHEN OTHERS THEN\n RETURN jsonb_build_object('ok', false, 'error', 'invalid template_id');\n END;\n v_is_update := v_template_id IS NOT NULL;\n v_name := btrim(p_payload->>'name');\n v_category := lower(COALESCE(NULLIF(p_payload->>'category',''), 'general'));\n v_description := left(p_payload->>'description', 1000);\n IF v_category NOT IN ('anamnesis','evolution','report','contract','general') THEN\n v_category := 'general';\n END IF;\n\n IF NOT v_is_update AND (v_name IS NULL OR v_name = '') THEN\n RETURN jsonb_build_object('ok', false, 'error', 'name is required to create a form');\n END IF;\n\n -- \u2500\u2500 authorization: role \u2192 plan \u2192 form_templates cap \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n SELECT count(*) INTO v_used FROM plg_forms_templates\n WHERE tenant_id = p_tenant_id AND is_deleted = false;\n v_denial := agent_guard(p_tenant_id, p_actor_user_id, 'custom_forms',\n CASE WHEN v_is_update THEN 'update' ELSE 'create' END,\n 'form_templates', v_used, CASE WHEN v_is_update THEN 0 ELSE 1 END);\n IF v_denial IS NOT NULL THEN\n RETURN jsonb_build_object('ok', false, 'denial', v_denial);\n END IF;\n\n -- \u2500\u2500 current fields (for APPEND / to reject an unknown id) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n IF v_is_update THEN\n SELECT COALESCE(schema->'fields','[]'::jsonb) INTO v_existing\n FROM plg_forms_templates\n WHERE id = v_template_id AND tenant_id = p_tenant_id AND is_deleted = false;\n IF v_existing IS NULL THEN\n RETURN jsonb_build_object('ok', false, 'error', 'unknown template for this tenant');\n END IF;\n ELSE\n v_existing := '[]'::jsonb;\n END IF;\n\n -- \u2500\u2500 normalize the incoming fields to the builder's FormFieldDef shape \u2500\u2500\u2500\u2500\n -- (id, type, label, required, placeholder, col, colSpan, options) \u2014 row is\n -- assigned last, across the merged list.\n WITH src AS (\n SELECT CASE\n WHEN p_payload ? 'fields' THEN COALESCE(p_payload->'fields','[]'::jsonb)\n WHEN p_payload ? 'append_fields' THEN COALESCE(p_payload->'append_fields','[]'::jsonb)\n ELSE '[]'::jsonb\n END AS arr\n ),\n norm AS (\n SELECT f.ord, jsonb_strip_nulls(jsonb_build_object(\n 'id', COALESCE(NULLIF(f.value->>'id',''), gen_random_uuid()::text),\n 'type', COALESCE(NULLIF(f.value->>'type',''), 'text'),\n 'label', COALESCE(NULLIF(f.value->>'label',''), 'Campo'),\n 'required', COALESCE((f.value->>'required')::boolean, false),\n 'placeholder', NULLIF(f.value->>'placeholder',''),\n 'col', 0,\n 'colSpan', 12,\n 'options', CASE\n WHEN jsonb_typeof(f.value->'options') = 'array' AND jsonb_array_length(f.value->'options') > 0\n THEN (SELECT jsonb_agg(jsonb_build_object(\n 'label', opt,\n 'value', lower(regexp_replace(btrim(opt), '\\s+', '_', 'g'))))\n FROM jsonb_array_elements_text(f.value->'options') opt)\n ELSE NULL END\n )) AS field\n FROM src, jsonb_array_elements(src.arr) WITH ORDINALITY AS f(value, ord)\n )\n SELECT COALESCE(jsonb_agg(field ORDER BY ord), '[]'::jsonb) INTO v_input FROM norm;\n\n -- REPLACE with `fields`; otherwise APPEND onto what's already there.\n IF p_payload ? 'fields' THEN\n v_merged := v_input;\n ELSE\n v_merged := v_existing || v_input;\n END IF;\n\n -- sequential rows across the final list\n SELECT COALESCE(jsonb_agg(jsonb_set(e.value, '{row}', to_jsonb((e.ord - 1))) ORDER BY e.ord), '[]'::jsonb)\n INTO v_fields\n FROM jsonb_array_elements(v_merged) WITH ORDINALITY AS e(value, ord);\n\n v_schema := jsonb_build_object('layout', jsonb_build_object('columns', 12), 'fields', v_fields);\n\n -- \u2500\u2500 write + audit \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n IF v_is_update THEN\n UPDATE plg_forms_templates SET\n name = COALESCE(NULLIF(v_name,''), name),\n category = v_category,\n description = COALESCE(v_description, description),\n schema = v_schema,\n updated_by = p_actor_user_id,\n updated_at = now()\n WHERE id = v_template_id AND tenant_id = p_tenant_id AND is_deleted = false\n RETURNING id INTO v_final_id;\n ELSE\n INSERT INTO plg_forms_templates (tenant_id, name, category, description, schema, created_by, updated_by)\n VALUES (p_tenant_id, v_name, v_category, v_description, v_schema, p_actor_user_id, p_actor_user_id)\n RETURNING id INTO v_final_id;\n END IF;\n\n INSERT INTO audit_logs (tenant_id, user_id, action, entity_type, entity_id, metadata)\n VALUES (p_tenant_id, p_actor_user_id,\n CASE WHEN v_is_update THEN 'agent.updateFormTemplate' ELSE 'agent.createFormTemplate' END,\n 'form_template', v_final_id::text,\n jsonb_build_object('payload', p_payload));\n\n RETURN jsonb_build_object(\n 'ok', true,\n 'id', v_final_id,\n 'record', jsonb_build_object(\n 'ref', jsonb_build_object('id', v_final_id, 'resource', 'plg_forms_templates',\n 'archetype', 'custom_forms:template'),\n 'name', COALESCE(NULLIF(v_name,''), (SELECT name FROM plg_forms_templates WHERE id = v_final_id)),\n 'category', v_category,\n 'fieldCount', jsonb_array_length(v_fields)\n )\n );\nEND;\n$$;\n\n\n-- \u2500\u2500 constraint \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nALTER TABLE ONLY public.plg_forms_document_files\n ADD CONSTRAINT plg_forms_document_files_pkey1 PRIMARY KEY (id);\n\nALTER TABLE ONLY public.plg_forms_documents\n ADD CONSTRAINT plg_forms_documents_pkey1 PRIMARY KEY (document_id);\n\nALTER TABLE ONLY public.plg_forms_templates\n ADD CONSTRAINT plg_forms_templates_pkey PRIMARY KEY (id);\n\n\n-- \u2500\u2500 view \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nCREATE VIEW public.plg_forms_rep_submissions WITH (security_invoker='true') AS\n SELECT d.id AS submission_id,\n d.tenant_id,\n d.created_at,\n d.updated_at,\n d.title,\n d.status,\n d.kind,\n d.person_id,\n p.name AS person_name,\n p.email AS person_email,\n p.phone AS person_phone,\n f.template_id,\n COALESCE(NULLIF(t.name, ''::text), 'Sem modelo'::text) AS template_name,\n COALESCE(NULLIF(t.category, ''::text), 'Sem categoria'::text) AS template_category,\n f.signed_at,\n f.data AS form_data,\n 1 AS submissions,\n CASE\n WHEN (f.signed_at IS NOT NULL) THEN 1\n ELSE 0\n END AS is_signed,\n CASE\n WHEN (d.status = 'completed'::text) THEN 1\n ELSE 0\n END AS is_completed,\n CASE\n WHEN (d.status = 'draft'::text) THEN 1\n ELSE 0\n END AS is_draft\n FROM (((public.documents d\n JOIN public.plg_forms_documents f ON ((f.document_id = d.id)))\n LEFT JOIN public.plg_forms_templates t ON ((t.id = f.template_id)))\n LEFT JOIN public.people p ON ((p.id = d.person_id)))\n WHERE d.is_active;\n\nCREATE VIEW public.v_documents WITH (security_invoker='true') AS\n SELECT d.id,\n d.tenant_id,\n d.kind,\n d.person_id,\n d.title,\n d.description,\n d.status,\n d.file_url,\n d.file_name,\n d.file_size,\n d.mime_type,\n d.tags,\n d.notes,\n d.is_active,\n d.metadata,\n d.created_by,\n d.updated_by,\n d.created_at,\n d.updated_at,\n d.storage_provider,\n d.storage_bucket,\n d.storage_path,\n d.checksum,\n d.unit_id,\n d.subject_type,\n d.subject_id,\n f.template_id,\n f.data AS form_data,\n f.signed_at,\n f.signed_by,\n t.name AS template_name,\n t.category AS template_category,\n p.name AS person_name\n FROM (((public.documents d\n LEFT JOIN public.plg_forms_documents f ON ((f.document_id = d.id)))\n LEFT JOIN public.plg_forms_templates t ON ((t.id = f.template_id)))\n LEFT JOIN public.people p ON ((p.id = d.person_id)));\n\nCREATE VIEW public.v_forms_template_usage WITH (security_invoker='true') AS\n SELECT f.template_id,\n f.tenant_id,\n count(*) AS document_count,\n max(d.created_at) AS last_document_at\n FROM (public.plg_forms_documents f\n JOIN public.documents d ON (((d.id = f.document_id) AND (d.is_active = true))))\n GROUP BY f.template_id, f.tenant_id;\n\n\n-- \u2500\u2500 index \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nCREATE INDEX idx_plg_forms_templates_category ON public.plg_forms_templates USING btree (tenant_id, category);\n\nCREATE INDEX idx_plg_forms_templates_current ON public.plg_forms_templates USING btree (tenant_id, is_current, is_active) WHERE ((is_current = true) AND (is_active = true) AND (is_deleted = false));\n\nCREATE INDEX idx_plg_forms_templates_parent ON public.plg_forms_templates USING btree (parent_id);\n\nCREATE INDEX idx_plg_forms_templates_tenant ON public.plg_forms_templates USING btree (tenant_id);\n\n\n-- \u2500\u2500 fk constraint \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nALTER TABLE ONLY public.plg_forms_document_files\n ADD CONSTRAINT plg_forms_document_files_document_id_fkey1 FOREIGN KEY (document_id) REFERENCES public.documents(id) ON DELETE CASCADE;\n\nALTER TABLE ONLY public.plg_forms_document_files\n ADD CONSTRAINT plg_forms_document_files_tenant_id_fkey1 FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE;\n\nALTER TABLE ONLY public.plg_forms_documents\n ADD CONSTRAINT plg_forms_documents_document_id_fkey FOREIGN KEY (document_id) REFERENCES public.documents(id) ON DELETE CASCADE;\n\nALTER TABLE ONLY public.plg_forms_documents\n ADD CONSTRAINT plg_forms_documents_template_id_fkey1 FOREIGN KEY (template_id) REFERENCES public.plg_forms_templates(id);\n\nALTER TABLE ONLY public.plg_forms_documents\n ADD CONSTRAINT plg_forms_documents_tenant_id_fkey1 FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE;\n\nALTER TABLE ONLY public.plg_forms_templates\n ADD CONSTRAINT plg_forms_templates_parent_id_fkey FOREIGN KEY (parent_id) REFERENCES public.plg_forms_templates(id);\n\nALTER TABLE ONLY public.plg_forms_templates\n ADD CONSTRAINT plg_forms_templates_tenant_id_fkey FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE;\n\n\n-- \u2500\u2500 row security \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nALTER TABLE public.plg_forms_document_files ENABLE ROW LEVEL SECURITY;\n\nALTER TABLE public.plg_forms_documents ENABLE ROW LEVEL SECURITY;\n\nALTER TABLE public.plg_forms_templates ENABLE ROW LEVEL SECURITY;\n\n\n-- \u2500\u2500 policy \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nCREATE POLICY plg_forms_document_files_delete ON public.plg_forms_document_files FOR DELETE TO authenticated USING ((tenant_id IN ( SELECT public.user_tenant_ids() AS user_tenant_ids)));\n\nCREATE POLICY plg_forms_document_files_insert ON public.plg_forms_document_files FOR INSERT TO authenticated WITH CHECK ((tenant_id IN ( SELECT public.user_tenant_ids() AS user_tenant_ids)));\n\nCREATE POLICY plg_forms_document_files_select ON public.plg_forms_document_files FOR SELECT TO authenticated USING ((tenant_id IN ( SELECT public.user_tenant_ids() AS user_tenant_ids)));\n\nCREATE POLICY plg_forms_document_files_update ON public.plg_forms_document_files FOR UPDATE TO authenticated USING ((tenant_id IN ( SELECT public.user_tenant_ids() AS user_tenant_ids)));\n\nCREATE POLICY plg_forms_documents_delete ON public.plg_forms_documents FOR DELETE TO authenticated USING ((tenant_id IN ( SELECT public.user_tenant_ids() AS user_tenant_ids)));\n\nCREATE POLICY plg_forms_documents_insert ON public.plg_forms_documents FOR INSERT TO authenticated WITH CHECK ((tenant_id IN ( SELECT public.user_tenant_ids() AS user_tenant_ids)));\n\nCREATE POLICY plg_forms_documents_select ON public.plg_forms_documents FOR SELECT TO authenticated USING ((tenant_id IN ( SELECT public.user_tenant_ids() AS user_tenant_ids)));\n\nCREATE POLICY plg_forms_documents_update ON public.plg_forms_documents FOR UPDATE TO authenticated USING ((tenant_id IN ( SELECT public.user_tenant_ids() AS user_tenant_ids)));\n\nCREATE POLICY plg_forms_templates_delete ON public.plg_forms_templates FOR DELETE TO authenticated USING ((tenant_id IN ( SELECT public.user_tenant_ids() AS user_tenant_ids)));\n\nCREATE POLICY plg_forms_templates_insert ON public.plg_forms_templates FOR INSERT TO authenticated WITH CHECK ((tenant_id IN ( SELECT public.user_tenant_ids() AS user_tenant_ids)));\n\nCREATE POLICY plg_forms_templates_select ON public.plg_forms_templates FOR SELECT TO authenticated USING ((tenant_id IN ( SELECT public.user_tenant_ids() AS user_tenant_ids)));\n\nCREATE POLICY plg_forms_templates_update ON public.plg_forms_templates FOR UPDATE TO authenticated USING ((tenant_id IN ( SELECT public.user_tenant_ids() AS user_tenant_ids)));\n\n\n-- \u2500\u2500 acl \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nREVOKE ALL ON FUNCTION public.agent_forms_upsert_template(p_tenant_id uuid, p_actor_user_id uuid, p_payload jsonb) FROM PUBLIC;\nGRANT ALL ON FUNCTION public.agent_forms_upsert_template(p_tenant_id uuid, p_actor_user_id uuid, p_payload jsonb) TO authenticated;\nGRANT ALL ON FUNCTION public.agent_forms_upsert_template(p_tenant_id uuid, p_actor_user_id uuid, p_payload jsonb) TO service_role;\n\nGRANT MAINTAIN ON TABLE public.plg_forms_document_files TO anon;\nGRANT ALL ON TABLE public.plg_forms_document_files TO authenticated;\nGRANT ALL ON TABLE public.plg_forms_document_files TO service_role;\n\nGRANT MAINTAIN ON TABLE public.plg_forms_documents TO anon;\nGRANT ALL ON TABLE public.plg_forms_documents TO authenticated;\nGRANT ALL ON TABLE public.plg_forms_documents TO service_role;\n\nGRANT MAINTAIN ON TABLE public.plg_forms_templates TO anon;\nGRANT ALL ON TABLE public.plg_forms_templates TO authenticated;\nGRANT ALL ON TABLE public.plg_forms_templates TO service_role;\n\nGRANT MAINTAIN ON TABLE public.plg_forms_rep_submissions TO anon;\nGRANT ALL ON TABLE public.plg_forms_rep_submissions TO authenticated;\nGRANT ALL ON TABLE public.plg_forms_rep_submissions TO service_role;\n\nGRANT MAINTAIN ON TABLE public.v_documents TO anon;\nGRANT ALL ON TABLE public.v_documents TO authenticated;\nGRANT ALL ON TABLE public.v_documents TO service_role;\n\nGRANT MAINTAIN ON TABLE public.v_forms_template_usage TO anon;\nGRANT ALL ON TABLE public.v_forms_template_usage TO authenticated;\nGRANT ALL ON TABLE public.v_forms_template_usage TO service_role;\n\n\n-- \u2500\u2500 comment \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nCOMMENT ON VIEW public.plg_forms_rep_submissions IS 'One row per form submission (a document WITH a form behind it), joined to its template and the person who filled it.';\n\nCOMMENT ON VIEW public.v_documents IS 'Documents with their template joined. security_invoker: the reader''s own RLS decides, not the migration role''s (core 043, carried forward here so re-creating 002 cannot undo it).';\n\n\n-- \u2500\u2500 default privileges: restore \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n-- Put back, so the NEXT thing installed inherits what the chain had.\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON SEQUENCES TO postgres$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON SEQUENCES TO postgres$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON SEQUENCES TO anon$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON SEQUENCES TO anon$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON SEQUENCES TO authenticated$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON SEQUENCES TO authenticated$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON SEQUENCES TO service_role$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON SEQUENCES TO service_role$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON SEQUENCES TO postgres$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON SEQUENCES TO postgres$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON SEQUENCES TO anon$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON SEQUENCES TO anon$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON SEQUENCES TO authenticated$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON SEQUENCES TO authenticated$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON SEQUENCES TO service_role$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON SEQUENCES TO service_role$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON FUNCTIONS TO postgres$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON FUNCTIONS TO postgres$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON FUNCTIONS TO authenticated$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON FUNCTIONS TO authenticated$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON FUNCTIONS TO service_role$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON FUNCTIONS TO service_role$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON FUNCTIONS TO postgres$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON FUNCTIONS TO postgres$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON FUNCTIONS TO anon$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON FUNCTIONS TO anon$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON FUNCTIONS TO authenticated$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON FUNCTIONS TO authenticated$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON FUNCTIONS TO service_role$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON FUNCTIONS TO service_role$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON TABLES TO postgres$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON TABLES TO postgres$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT MAINTAIN ON TABLES TO anon$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT MAINTAIN ON TABLES TO anon$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON TABLES TO authenticated$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON TABLES TO authenticated$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON TABLES TO service_role$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE postgres IN SCHEMA public GRANT ALL ON TABLES TO service_role$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON TABLES TO postgres$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON TABLES TO postgres$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON TABLES TO anon$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON TABLES TO anon$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON TABLES TO authenticated$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON TABLES TO authenticated$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON TABLES TO service_role$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$ALTER DEFAULT PRIVILEGES FOR ROLE supabase_admin IN SCHEMA public GRANT ALL ON TABLES TO service_role$stmt$;\nEND $dp$;\n\nDO $dp$ BEGIN\n EXECUTE $stmt$--\n-- PostgreSQL database dump complete\n--$stmt$;\nEXCEPTION WHEN insufficient_privilege OR undefined_object THEN\n RAISE NOTICE 'baseline: not ours to set \u2014 %', $stmt$--\n-- PostgreSQL database dump complete\n--$stmt$;\nEND $dp$;\n";
2
2
  export declare const MIGRATION_001_O_FORMULARIO_DIZ_SE_ELE_E_EXIGIDO = "-- O formul\u00E1rio diz se ele \u00E9 exigido, e por quanto tempo vale.\n--\n-- O V1 abre o agendamento e avisa, assim que o cliente \u00E9 escolhido:\n--\n-- \"1 formul\u00E1rio pendente de preenchimento\"\n-- Anamnese \u00B7 Nunca preenchido\n--\n-- e, depois de respondido, a mesma faixa vira \"Anamnese em dia \u2014 v\u00E1lido at\u00E9\n-- 08/03/2027\". \u00C9 a PRIMEIRA exig\u00EAncia do atendimento e ela \u00E9 do CLIENTE, n\u00E3o do\n-- servi\u00E7o: vale para qualquer coisa que ele venha fazer.\n--\n-- O V2 tem os formul\u00E1rios (25 modelos, 575 respostas migradas) e n\u00E3o tem as\n-- duas perguntas que fazem o aviso existir:\n--\n-- este modelo \u00E9 EXIGIDO de todo cliente? \u2192 required_on_attendance\n-- a resposta VENCE? \u2192 validity_days\n--\n-- Sem a validade, uma anamnese de 2019 conta como preenchida para sempre \u2014 e\n-- anamnese velha \u00E9 pior que anamnese nenhuma, porque ningu\u00E9m vai perguntar de\n-- novo. O V1 usa seis meses; aqui a casa decide, e NULL = n\u00E3o vence.\n--\n-- Nada \u00E9 exigido por padr\u00E3o: `false` em todo modelo existente. Um aviso que\n-- nasce ligado em 25 modelos transforma a primeira abertura do agendamento numa\n-- lista de pend\u00EAncias que ningu\u00E9m pediu.\n\nALTER TABLE public.plg_forms_templates\n ADD COLUMN IF NOT EXISTS required_on_attendance boolean NOT NULL DEFAULT false;\n\nALTER TABLE public.plg_forms_templates\n ADD COLUMN IF NOT EXISTS validity_days integer;\n\nCOMMENT ON COLUMN public.plg_forms_templates.required_on_attendance IS\n 'V1: require_on_attendance \u2014 este modelo \u00E9 exigido de TODO cliente, antes de qualquer atendimento.';\nCOMMENT ON COLUMN public.plg_forms_templates.validity_days IS\n 'Por quantos dias a resposta vale. NULL = n\u00E3o vence. O V1 usa 180 na anamnese.';\n\n-- S\u00F3 os exigidos entram na conta do aviso, e s\u00E3o poucos: \u00EDndice parcial.\nCREATE INDEX IF NOT EXISTS idx_plg_forms_templates_required\n ON public.plg_forms_templates (tenant_id)\n WHERE required_on_attendance AND is_active AND NOT is_deleted;\n\n-- ---------------------------------------------------------------------------\n-- O que falta a ESTA pessoa\n--\n-- Fun\u00E7\u00E3o e n\u00E3o view: a pergunta \u00E9 sempre sobre UMA pessoa (a que est\u00E1 sendo\n-- agendada, a que chegou na recep\u00E7\u00E3o), e uma view que cruza 25 modelos com\n-- 123.484 pessoas para depois filtrar uma \u00E9 um scan por abertura de modal.\n--\n-- Devolve uma linha por modelo exigido, com o estado j\u00E1 decidido \u2014 `pending`,\n-- `expired` ou `valid`. Decidir no banco e n\u00E3o na tela \u00E9 o que faz as tr\u00EAs\n-- portas (agendamento, check-in, ficha) contarem a mesma hist\u00F3ria.\n-- ---------------------------------------------------------------------------\n\nCREATE OR REPLACE FUNCTION public.forms_person_requirements(\n p_tenant_id uuid,\n p_person_id uuid\n)\nRETURNS TABLE (\n template_id uuid,\n template_name text,\n category text,\n validity_days integer,\n last_filled_at timestamptz,\n valid_until date,\n state text\n)\nLANGUAGE sql\nSTABLE\nSECURITY INVOKER\n-- `search_path` qualificado no corpo: vazio e sem qualificar, o corpo compila\n-- no DDL e estoura na primeira chamada.\nSET search_path TO ''\nAS $$\n WITH exigidos AS (\n SELECT t.id, t.name, t.category, t.validity_days\n FROM public.plg_forms_templates t\n WHERE t.tenant_id = p_tenant_id\n AND t.required_on_attendance\n AND t.is_active\n AND NOT t.is_deleted\n ),\n ultima AS (\n SELECT fd.template_id, max(coalesce(fd.signed_at, fd.created_at)) AS em\n FROM public.plg_forms_documents fd\n JOIN public.documents d ON d.id = fd.document_id\n WHERE fd.tenant_id = p_tenant_id\n AND d.person_id = p_person_id\n GROUP BY fd.template_id\n )\n SELECT e.id,\n e.name,\n e.category,\n e.validity_days,\n u.em,\n CASE WHEN u.em IS NOT NULL AND e.validity_days IS NOT NULL\n THEN (u.em + make_interval(days => e.validity_days))::date\n END,\n CASE\n WHEN u.em IS NULL THEN 'pending'\n WHEN e.validity_days IS NULL THEN 'valid'\n WHEN (u.em + make_interval(days => e.validity_days))::date < current_date THEN 'expired'\n ELSE 'valid'\n END\n FROM exigidos e\n LEFT JOIN ultima u ON u.template_id = e.id\n ORDER BY e.name;\n$$;\n\nCOMMENT ON FUNCTION public.forms_person_requirements(uuid, uuid) IS\n 'Os formul\u00E1rios exigidos desta pessoa, com o estado j\u00E1 decidido (pending | expired | valid). Alimenta o aviso do agendamento, do check-in e da ficha.';\n\nGRANT EXECUTE ON FUNCTION public.forms_person_requirements(uuid, uuid) TO authenticated;\n";
3
3
  export declare const MIGRATION_002_A_FICHA_DO_V1_TEM_ONDE_MORAR = "-- A ficha do V1 tem onde morar.\n--\n-- 612.148 linhas do Espa\u00E7o Facial sem destino nenhum \u2014 o maior buraco isolado\n-- do porte inteiro, e 11% de todo o dado daquele tenant.\n--\n-- client_form_responses 611.831 a resposta: o prontu\u00E1rio declarado\n-- service_default_forms 314 qual servi\u00E7o exige qual formul\u00E1rio\n-- form_template_fields 256 o campo\n-- form_templates 23 o molde\n--\n-- \u2500\u2500 O que este plugin J\u00C1 tem, e por isso n\u00E3o se recria \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n--\n-- Medido no cluster em 22/09/2026, antes de escrever uma linha:\n--\n-- public.plg_forms_templates existe \u2014 com `schema jsonb`\n-- public.plg_forms_documents existe \u2014 extens\u00E3o de public.documents\n-- public.plg_forms_document_files existe\n-- public.document_kinds j\u00E1 declara kind='form', plugin='forms'\n--\n-- Isso muda o porte inteiro. O plano de 22/09 previa quatro tabelas novas;\n-- s\u00E3o UMA tabela nova e cinco colunas, porque:\n--\n-- `form_template_fields` N\u00C3O vira tabela. O V2 guarda o campo DENTRO do\n-- molde, em `plg_forms_templates.schema->'fields'`, e os moldes que j\u00E1\n-- existem no cluster (25 linhas, outros tenants) provam o formato:\n-- {id, col, row, type, label, colSpan, source{...}}. Criar\n-- plg_forms_template_fields seria abrir um segundo lugar para a mesma\n-- coisa \u2014 o de-para monta o jsonb no augment e pronto.\n--\n-- `client_form_responses` N\u00C3O vira tabela. Resposta de formul\u00E1rio \u00E9\n-- DOCUMENTO: `public.documents` (o arqu\u00E9tipo, com person_id, unit_id,\n-- subject_type/subject_id, notes) mais `plg_forms_documents` (a extens\u00E3o,\n-- com template_id e data). \u00C9 o mesmo par que `invoices` faz com\n-- `public.orders` + `plg_financial_invoices`.\n--\n-- \u2500\u2500 unit_id fica NULLABLE, e isso \u00E9 decis\u00E3o registrada \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n--\n-- O V1 n\u00E3o escopa ficha por unidade: `client_form_responses` n\u00E3o tem unit_id.\n-- O V2 escopa tudo por franquia. Das tr\u00EAs sa\u00EDdas poss\u00EDveis \u2014 nullable,\n-- NOT NULL pela unidade do profissional, NOT NULL pela unidade de cadastro \u2014\n-- ficou a NULLABLE, com null valendo para a franquia inteira. \u00C9 o que o V1 faz\n-- hoje na pr\u00E1tica, e as outras duas escondem a ficha do cliente que trocou de\n-- unidade. `documents.unit_id` j\u00E1 \u00E9 nullable: nada a alterar.\n--\n-- Idempotente.\n\n-- \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n-- 1. O molde ganha o que faz dele uma BOLA do atendimento\n-- \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n--\n-- `required_on_attendance` e `validity_days` j\u00E1 entraram na 001. Faltam as tr\u00EAs\n-- que a tela do V1 l\u00EA para decidir se pinta a bola, se ela alerta, e se o\n-- cliente pode responder sozinho. Sem elas o molde existe e n\u00E3o serve.\n\nALTER TABLE public.plg_forms_templates\n ADD COLUMN IF NOT EXISTS icon text;\nALTER TABLE public.plg_forms_templates\n ADD COLUMN IF NOT EXISTS alert_when_missing boolean NOT NULL DEFAULT false;\nALTER TABLE public.plg_forms_templates\n ADD COLUMN IF NOT EXISTS show_on_self_service boolean NOT NULL DEFAULT false;\n\nCOMMENT ON COLUMN public.plg_forms_templates.icon IS\n 'Nome do \u00EDcone com que a bola do atendimento \u00E9 pintada. \u2190 form_templates.icon';\nCOMMENT ON COLUMN public.plg_forms_templates.alert_when_missing IS\n 'A aus\u00EAncia da resposta alerta, em vez de s\u00F3 faltar. \u2190 form_templates.alert_when_missing';\nCOMMENT ON COLUMN public.plg_forms_templates.show_on_self_service IS\n 'O cliente responde sozinho, fora do atendimento. \u2190 form_templates.show_on_client_self_service';\n\n-- \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n-- 2. A resposta diz a QUAL servi\u00E7o ela se refere, e quem a colheu\n-- \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n--\n-- `documents` j\u00E1 responde de quem \u00E9 (person_id), de que unidade (unit_id) e a\n-- que ela se pendura (subject_type/subject_id \u2192 o agendamento). O que falta \u00E9\n-- do dom\u00EDnio do formul\u00E1rio: o servi\u00E7o e o profissional.\n--\n-- `documents.created_by` N\u00C3O serve para professional_id: created_by \u00E9 o usu\u00E1rio\n-- autenticado que gravou a linha, e no porte isso \u00E9 o motor. O V1 guarda\n-- `professional_id` apontando para uma PESSOA do cat\u00E1logo \u2014 outra pergunta.\n\nALTER TABLE public.plg_forms_documents\n ADD COLUMN IF NOT EXISTS product_id uuid;\nALTER TABLE public.plg_forms_documents\n ADD COLUMN IF NOT EXISTS professional_id uuid;\n\nDO $$\nBEGIN\n IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'plg_forms_documents_product_fk') THEN\n ALTER TABLE public.plg_forms_documents\n ADD CONSTRAINT plg_forms_documents_product_fk\n FOREIGN KEY (product_id) REFERENCES public.products(id) ON DELETE SET NULL;\n END IF;\n IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'plg_forms_documents_professional_fk') THEN\n ALTER TABLE public.plg_forms_documents\n ADD CONSTRAINT plg_forms_documents_professional_fk\n FOREIGN KEY (professional_id) REFERENCES public.people(id) ON DELETE SET NULL;\n END IF;\nEND $$;\n\nCOMMENT ON COLUMN public.plg_forms_documents.product_id IS\n 'O servi\u00E7o a que a ficha se refere. Servi\u00E7o \u00E9 produto no V2 (kind=''service''). \u2190 client_form_responses.service_id';\nCOMMENT ON COLUMN public.plg_forms_documents.professional_id IS\n 'Quem colheu a ficha. \u2190 client_form_responses.professional_id';\n\nCREATE INDEX IF NOT EXISTS plg_forms_documents_product_idx\n ON public.plg_forms_documents (tenant_id, product_id) WHERE product_id IS NOT NULL;\nCREATE INDEX IF NOT EXISTS plg_forms_documents_template_idx\n ON public.plg_forms_documents (tenant_id, template_id);\n\n-- O \u00EDndice que a tela do atendimento pede: as fichas DESTE agendamento.\n-- 611.831 documentos e a pergunta \u00E9 sempre por subject.\nCREATE INDEX IF NOT EXISTS documents_subject_idx\n ON public.documents (tenant_id, subject_type, subject_id)\n WHERE subject_id IS NOT NULL;\n\n-- \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n-- 3. Qual servi\u00E7o exige qual formul\u00E1rio\n-- \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n--\n-- A \u00FAnica tabela nova. 314 linhas, e \u00E9 a REGRA que gera a bola: sem ela o V2\n-- n\u00E3o sabe que \"Botox\" pede a ficha de anamnese facial.\n--\n-- Sem unit_id de prop\u00F3sito, pelo mesmo motivo de\n-- `plg_clinical_service_revisions`: \u00E9 CAT\u00C1LOGO. O que um servi\u00E7o exige vale na\n-- rede inteira, e escond\u00EA-lo por filial faria a bola aparecer numa unidade e\n-- sumir na outra para o mesmo servi\u00E7o.\n\nCREATE TABLE IF NOT EXISTS public.plg_forms_service_defaults (\n id uuid DEFAULT gen_random_uuid() NOT NULL,\n tenant_id uuid NOT NULL,\n product_id uuid NOT NULL,\n template_id uuid NOT NULL,\n -- Vocabul\u00E1rio medido no V1, n\u00E3o inventado: um CHECK fechado aqui viraria\n -- quarentena na primeira linha com um valor que ningu\u00E9m previu. O de-para\n -- normaliza; a tabela aceita o que vier e o default cobre o nulo.\n requirement_scope text DEFAULT 'always'::text NOT NULL,\n metadata jsonb DEFAULT '{}'::jsonb NOT NULL,\n created_at timestamp with time zone DEFAULT now() NOT NULL,\n updated_at timestamp with time zone DEFAULT now() NOT NULL,\n CONSTRAINT plg_forms_service_defaults_pkey PRIMARY KEY (id)\n);\n\nDO $$\nBEGIN\n IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'plg_forms_service_defaults_tenant_fk') THEN\n ALTER TABLE public.plg_forms_service_defaults\n ADD CONSTRAINT plg_forms_service_defaults_tenant_fk\n FOREIGN KEY (tenant_id) REFERENCES public.tenants(id) ON DELETE CASCADE;\n END IF;\n IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'plg_forms_service_defaults_product_fk') THEN\n ALTER TABLE public.plg_forms_service_defaults\n ADD CONSTRAINT plg_forms_service_defaults_product_fk\n FOREIGN KEY (product_id) REFERENCES public.products(id) ON DELETE CASCADE;\n END IF;\n IF NOT EXISTS (SELECT 1 FROM pg_constraint WHERE conname = 'plg_forms_service_defaults_template_fk') THEN\n ALTER TABLE public.plg_forms_service_defaults\n ADD CONSTRAINT plg_forms_service_defaults_template_fk\n FOREIGN KEY (template_id) REFERENCES public.plg_forms_templates(id) ON DELETE CASCADE;\n END IF;\nEND $$;\n\n-- O par \u00E9 \u00FAnico: um servi\u00E7o n\u00E3o exige o mesmo molde duas vezes.\nCREATE UNIQUE INDEX IF NOT EXISTS plg_forms_service_defaults_uq\n ON public.plg_forms_service_defaults (tenant_id, product_id, template_id);\nCREATE INDEX IF NOT EXISTS plg_forms_service_defaults_product_idx\n ON public.plg_forms_service_defaults (tenant_id, product_id);\n\nCOMMENT ON TABLE public.plg_forms_service_defaults IS\n 'Qual formul\u00E1rio cada servi\u00E7o exige \u2014 a regra que gera a bola do atendimento. \u2190 service_default_forms';\n\nDROP TRIGGER IF EXISTS plg_forms_service_defaults_updated_at ON public.plg_forms_service_defaults;\nCREATE TRIGGER plg_forms_service_defaults_updated_at\n BEFORE UPDATE ON public.plg_forms_service_defaults\n FOR EACH ROW EXECUTE FUNCTION public.handle_updated_at();\n\n-- \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n-- 4. Quem enxerga\n-- \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n--\n-- Mesma forma das outras plg_forms_*: cerca de TENANT, via user_tenant_ids().\n-- N\u00E3o \u00E9 a forma mais apertada do SDK, e de prop\u00F3sito \u2014 uma regra de cat\u00E1logo\n-- com pol\u00EDtica de unidade divergiria das outras tr\u00EAs tabelas do plugin e\n-- quebraria a tela que j\u00E1 existe. Apertar isso \u00E9 uma migration do plugin\n-- inteiro, n\u00E3o um efeito colateral do porte.\n\nALTER TABLE public.plg_forms_service_defaults ENABLE ROW LEVEL SECURITY;\nALTER TABLE public.plg_forms_service_defaults FORCE ROW LEVEL SECURITY;\nGRANT SELECT, INSERT, UPDATE, DELETE ON TABLE public.plg_forms_service_defaults TO authenticated;\nGRANT ALL ON TABLE public.plg_forms_service_defaults TO service_role;\n\nDROP POLICY IF EXISTS plg_forms_service_defaults_select ON public.plg_forms_service_defaults;\nCREATE POLICY plg_forms_service_defaults_select ON public.plg_forms_service_defaults\n FOR SELECT TO authenticated USING (tenant_id IN (SELECT user_tenant_ids()));\n\nDROP POLICY IF EXISTS plg_forms_service_defaults_insert ON public.plg_forms_service_defaults;\nCREATE POLICY plg_forms_service_defaults_insert ON public.plg_forms_service_defaults\n FOR INSERT TO authenticated WITH CHECK (tenant_id IN (SELECT user_tenant_ids()));\n\nDROP POLICY IF EXISTS plg_forms_service_defaults_update ON public.plg_forms_service_defaults;\nCREATE POLICY plg_forms_service_defaults_update ON public.plg_forms_service_defaults\n FOR UPDATE TO authenticated USING (tenant_id IN (SELECT user_tenant_ids()))\n WITH CHECK (tenant_id IN (SELECT user_tenant_ids()));\n\nDROP POLICY IF EXISTS plg_forms_service_defaults_delete ON public.plg_forms_service_defaults;\nCREATE POLICY plg_forms_service_defaults_delete ON public.plg_forms_service_defaults\n FOR DELETE TO authenticated USING (tenant_id IN (SELECT user_tenant_ids()));\n";
4
+ export declare const MIGRATION_003_ALERTAR_E_UMA_SEGUNDA_DECISAO = "-- Alertar \u00E9 uma SEGUNDA decis\u00E3o, e ela nasce desligada.\n--\n-- O V1 pergunta duas coisas sobre o mesmo modelo, e n\u00E3o uma:\n--\n-- \"Solicitar preenchimento deste formul\u00E1rio nos atendimentos\"\n-- \u2192 form_templates.require_on_attendance\n-- \"Alertar no cadastro do cliente que n\u00E3o tem um formul\u00E1rio deste tipo\n-- preenchido ou est\u00E1 fora da validade\"\n-- \u2192 form_templates.alert_when_missing\n--\n-- A primeira coloca a bola no checklist do atendimento \u2014 \u00E9 trabalho a fazer\n-- com a pessoa na cadeira. A segunda leva a falta para a FRENTE do atendimento:\n-- a faixa \u00E2mbar no cadastro do cliente e no formul\u00E1rio de agendamento\n-- (`ClientFormStatusAlerts`, que filtra pelas DUAS). Uma casa que exige\n-- anamnese e n\u00E3o quer a faixa deixa a segunda desligada, e o V1 respeita.\n--\n-- O V2 juntou as duas numa s\u00F3: `forms_person_requirements` devolve tudo que \u00E9\n-- exigido, e a faixa acende. O efeito na Cl\u00EDnica Sol \u00E9 uma anamnese importada\n-- (`required_on_attendance = true`, `alert_when_missing = false` \u2014 exatamente\n-- como estava no V1) virando \"1 item pendente com o paciente\" em TODO\n-- agendamento novo, que \u00E9 a faixa que aquela casa j\u00E1 tinha decidido n\u00E3o ter.\n--\n-- A coluna j\u00E1 existe desde a 002 e ningu\u00E9m a l\u00EA. Aqui ela passa a sair na\n-- resposta, e a tela decide o que fazer com ela \u2014 ver PersonFormsAlert: o que\n-- n\u00E3o alerta some da lista enquanto est\u00E1 pendente, e continua contando como\n-- \"em dia\" depois de preenchido.\n--\n-- Idempotente: s\u00F3 substitui a fun\u00E7\u00E3o.\n\nDROP FUNCTION IF EXISTS public.forms_person_requirements(uuid, uuid);\n\nCREATE FUNCTION public.forms_person_requirements(\n p_tenant_id uuid,\n p_person_id uuid\n)\nRETURNS TABLE (\n template_id uuid,\n template_name text,\n category text,\n validity_days integer,\n alert_when_missing boolean,\n last_filled_at timestamptz,\n valid_until date,\n state text\n)\nLANGUAGE sql\nSTABLE\nSECURITY INVOKER\n-- `search_path` qualificado no corpo: vazio e sem qualificar, o corpo compila\n-- no DDL e estoura na primeira chamada.\nSET search_path TO ''\nAS $$\n WITH exigidos AS (\n SELECT t.id, t.name, t.category, t.validity_days, t.alert_when_missing\n FROM public.plg_forms_templates t\n WHERE t.tenant_id = p_tenant_id\n AND t.required_on_attendance\n AND t.is_active\n AND NOT t.is_deleted\n ),\n ultima AS (\n SELECT fd.template_id, max(coalesce(fd.signed_at, fd.created_at)) AS em\n FROM public.plg_forms_documents fd\n JOIN public.documents d ON d.id = fd.document_id\n WHERE fd.tenant_id = p_tenant_id\n AND d.person_id = p_person_id\n GROUP BY fd.template_id\n )\n SELECT e.id,\n e.name,\n e.category,\n e.validity_days,\n e.alert_when_missing,\n u.em,\n CASE WHEN u.em IS NOT NULL AND e.validity_days IS NOT NULL\n THEN (u.em + make_interval(days => e.validity_days))::date\n END,\n CASE\n WHEN u.em IS NULL THEN 'pending'\n WHEN e.validity_days IS NULL THEN 'valid'\n WHEN (u.em + make_interval(days => e.validity_days))::date < current_date THEN 'expired'\n ELSE 'valid'\n END\n FROM exigidos e\n LEFT JOIN ultima u ON u.template_id = e.id\n ORDER BY e.name;\n$$;\n\nCOMMENT ON FUNCTION public.forms_person_requirements(uuid, uuid) IS\n 'Os formul\u00E1rios exigidos desta pessoa, com o estado j\u00E1 decidido (pending | expired | valid) e se a falta ALERTA (V1: alert_when_missing). Alimenta o aviso do agendamento, do check-in e da ficha.';\n";
5
+ export declare const MIGRATION_004_O_CONTRATO_E_UM_MODELO_QUE_SE_ASSINA = "-- ---------------------------------------------------------------------------\n-- 004 \u2014 o contrato \u00E9 um modelo que se ASSINA.\n--\n-- `plg_forms_contract_templates` \u2014 o `contract_templates` do V1.\n--\n-- \u2500\u2500 por que aqui, ao lado dos formul\u00E1rios \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n--\n-- Contrato e formul\u00E1rio s\u00E3o a mesma coisa pela METADE, e \u00E9 a metade que este\n-- plugin j\u00E1 resolve: um modelo com nome, um interruptor de ativo, e regras de\n-- QUANDO ele passa a ser exigido de um atendimento. `plugin-forms` j\u00E1 \u00E9 o dono\n-- disso \u2014 foi ele que ganhou `require_on_attendance` e `alert_when_missing` na\n-- 001.\n--\n-- \u2500\u2500 e por que N\u00C3O \u00E9 a mesma tabela \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n--\n-- A outra metade n\u00E3o se parece em nada. Um formul\u00E1rio \u00E9 uma COLE\u00C7\u00C3O DE\n-- PERGUNTAS \u2014 tem `plg_forms_fields`, cada uma com tipo, op\u00E7\u00F5es e valida\u00E7\u00E3o, e\n-- a resposta \u00E9 um conjunto de valores. Um contrato \u00E9 um TEXTO com vari\u00E1veis, e\n-- o que se guarda dele \u00E9 uma ASSINATURA: quem assinou, quando, e com que\n-- prova (a tela, a selfie).\n--\n-- Enfiar os dois numa tabela s\u00F3 obrigaria toda linha a carregar as colunas do\n-- outro \u2014 e a primeira consulta que perguntasse \u00ABquais modelos exigem\n-- assinatura\u00BB teria de saber, por fora, quais linhas s\u00E3o contrato.\n--\n-- \u2500\u2500 as tr\u00EAs perguntas de assinatura \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n--\n-- O V1 tem tr\u00EAs interruptores separados e eles n\u00E3o s\u00E3o redundantes:\n-- require_screen_signature \u2014 assinar na tela, com o dedo\n-- require_selfie \u2014 a foto de quem assinou, contra contesta\u00E7\u00E3o\n-- require_signature_for_execution \u2014 sem assinatura, o servi\u00E7o n\u00E3o \u00E9 executado\n--\n-- O terceiro \u00E9 o \u00FAnico que BLOQUEIA. Os dois primeiros dizem COMO se assina; o\n-- terceiro diz o que acontece se ningu\u00E9m assinar, e \u00E9 por isso que ele \u00E9 uma\n-- coluna e n\u00E3o um detalhe dos outros dois.\n-- ---------------------------------------------------------------------------\n\nCREATE TABLE IF NOT EXISTS public.plg_forms_contract_templates (\n id uuid DEFAULT gen_random_uuid() NOT NULL,\n tenant_id uuid NOT NULL,\n\n name text NOT NULL,\n -- O TEXTO do contrato, com as vari\u00E1veis que a gera\u00E7\u00E3o substitui\n -- ({{cliente.nome}}, {{servico.nome}}). Markdown: quem escreve um contrato\n -- precisa de par\u00E1grafo e negrito, e n\u00E3o de um editor de p\u00E1gina inteira.\n content text,\n\n -- QUANDO o contrato nasce. As duas portas do V1, e elas convivem: um\n -- or\u00E7amento aprovado gera o contrato do pacote, e a execu\u00E7\u00E3o gera o termo\n -- daquele procedimento.\n generate_on_execution boolean DEFAULT false NOT NULL,\n generate_on_quote_approval boolean DEFAULT false NOT NULL,\n\n -- Um contrato por atendimento, ou um por servi\u00E7o executado. \u00C9 o que decide\n -- se a cliente assina uma folha ou seis.\n group_services boolean DEFAULT true NOT NULL,\n\n -- COMO se assina, e o que acontece sem assinatura. Ver o cabe\u00E7alho: s\u00F3 o\n -- terceiro bloqueia.\n require_screen_signature boolean DEFAULT false NOT NULL,\n require_selfie boolean DEFAULT false NOT NULL,\n require_signature_for_execution boolean DEFAULT false NOT NULL,\n\n -- O vocabul\u00E1rio do V1 para o alcance e o v\u00EDnculo, guardado como veio: s\u00E3o\n -- duas escolhas que s\u00F3 o fluxo de atendimento sabe interpretar, e inventar\n -- um enum aqui decidiria por ele.\n execution_scope text,\n binding_mode text,\n\n notes text,\n tags text[] DEFAULT '{}'::text[],\n is_active boolean DEFAULT true NOT NULL,\n unit_id uuid,\n metadata jsonb DEFAULT '{}'::jsonb NOT NULL,\n created_at timestamp with time zone DEFAULT now() NOT NULL,\n updated_at timestamp with time zone DEFAULT now() NOT NULL,\n created_by uuid,\n\n CONSTRAINT plg_forms_contract_templates_pkey PRIMARY KEY (id)\n);\n\nCOMMENT ON TABLE public.plg_forms_contract_templates IS\n 'O modelo de contrato: um TEXTO com vari\u00E1veis que se assina. Irm\u00E3o do modelo de formul\u00E1rio (mesma metade de cima: nome, ativo, quando exigir), e diferente na de baixo \u2014 formul\u00E1rio \u00E9 cole\u00E7\u00E3o de perguntas, contrato \u00E9 texto e assinatura.';\n\nCOMMENT ON COLUMN public.plg_forms_contract_templates.require_signature_for_execution IS\n 'O \u00FAnico dos tr\u00EAs interruptores que BLOQUEIA: sem assinatura, o servi\u00E7o n\u00E3o \u00E9 executado.';\n\nCREATE INDEX IF NOT EXISTS plg_forms_contract_templates_tenant_idx\n ON public.plg_forms_contract_templates (tenant_id) WHERE is_active;\nCREATE INDEX IF NOT EXISTS plg_forms_contract_templates_tags_gin\n ON public.plg_forms_contract_templates USING gin (tags);\n\nALTER TABLE public.plg_forms_contract_templates ENABLE ROW LEVEL SECURITY;\nALTER TABLE public.plg_forms_contract_templates FORCE ROW LEVEL SECURITY;\n\nDROP POLICY IF EXISTS plg_forms_contract_templates_member_all ON public.plg_forms_contract_templates;\nCREATE POLICY plg_forms_contract_templates_member_all ON public.plg_forms_contract_templates\n TO authenticated\n USING (tenant_id IN (SELECT public.user_tenant_ids()))\n WITH CHECK (tenant_id IN (SELECT public.user_tenant_ids()));\n\nDROP TRIGGER IF EXISTS plg_forms_contract_templates_set_updated_at ON public.plg_forms_contract_templates;\nCREATE TRIGGER plg_forms_contract_templates_set_updated_at\n BEFORE UPDATE ON public.plg_forms_contract_templates\n FOR EACH ROW EXECUTE FUNCTION public.handle_updated_at();\n";
4
6
  export declare const MIGRATIONS: Array<{
5
7
  id: string;
6
8
  sql: string;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/migrations/index.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,sBAAsB,0+kCA0mBlC,CAAA;AAED,eAAO,MAAM,+CAA+C,mtJAgH3D,CAAA;AAED,eAAO,MAAM,0CAA0C,q5cA8MtD,CAAA;AAED,eAAO,MAAM,UAAU,EAAE,KAAK,CAAC;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAIzD,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/migrations/index.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,sBAAsB,0+kCA0mBlC,CAAA;AAED,eAAO,MAAM,+CAA+C,mtJAgH3D,CAAA;AAED,eAAO,MAAM,0CAA0C,q5cA8MtD,CAAA;AAED,eAAO,MAAM,2CAA2C,kqHA0FvD,CAAA;AAED,eAAO,MAAM,kDAAkD,kkMAyG9D,CAAA;AAED,eAAO,MAAM,UAAU,EAAE,KAAK,CAAC;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAMzD,CAAA"}
package/dist/types.d.ts CHANGED
@@ -61,6 +61,9 @@ export interface FormTemplate {
61
61
  requiredOnAttendance: boolean;
62
62
  /** Por quantos dias a resposta vale. `null` = não vence. */
63
63
  validityDays: number | null;
64
+ /** A falta ALERTA na frente do atendimento, em vez de só faltar no checklist
65
+ * (V1: `alert_when_missing`). Nasce desligada. */
66
+ alertWhenMissing: boolean;
64
67
  createdBy?: string;
65
68
  updatedBy?: string;
66
69
  createdAt: string;
@@ -121,7 +124,19 @@ export interface DocumentQuery {
121
124
  search?: string;
122
125
  page?: number;
123
126
  pageSize?: number;
127
+ /**
128
+ * Restringe por `documents.kind` — é o que separa o Prontuário (`form`) dos
129
+ * Arquivos (`image`, `attachment`). No SERVIDOR, e não filtrando o que
130
+ * voltou: a aba de arquivos de um paciente com 110 registros e 3 fotos
131
+ * trazia 50 linhas e mostrava zero foto.
132
+ */
133
+ kinds?: string[];
134
+ /** Restringe pela categoria do modelo (`anamnesis`, `evolution`…). */
135
+ templateCategory?: string;
124
136
  }
137
+ /** Quantos documentos esta pessoa tem, por balde do prontuário.
138
+ * A chave é a categoria do modelo, ou `file` para foto e anexo. */
139
+ export type DocumentCounts = Record<string, number>;
125
140
  export interface PaginatedResult<T> {
126
141
  data: T[];
127
142
  total: number;
@@ -159,6 +174,7 @@ export interface UpdateTemplateInput {
159
174
  isActive?: boolean;
160
175
  requiredOnAttendance?: boolean;
161
176
  validityDays?: number | null;
177
+ alertWhenMissing?: boolean;
162
178
  }
163
179
  export interface CreateDocumentInput {
164
180
  templateId?: string;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"TemplateListView.d.ts","sourceRoot":"","sources":["../../src/views/TemplateListView.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAoD,MAAM,OAAO,CAAA;AASxE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAA;AAChD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAA;AAClD,OAAO,KAAK,EAAE,YAAY,EAAoB,MAAM,UAAU,CAAA;AAE9D,UAAU,qBAAqB;IAC7B,KAAK,EAAE,gBAAgB,CAAA;IACvB,MAAM,EAAE,iBAAiB,CAAA;IACzB,MAAM,EAAE,CAAC,QAAQ,EAAE,YAAY,KAAK,IAAI,CAAA;IACxC,KAAK,EAAE,MAAM,IAAI,CAAA;CAClB;AAUD,wBAAgB,gBAAgB,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,qBAAqB,qBA2PvF"}
1
+ {"version":3,"file":"TemplateListView.d.ts","sourceRoot":"","sources":["../../src/views/TemplateListView.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAoD,MAAM,OAAO,CAAA;AASxE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAA;AAChD,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAA;AAClD,OAAO,KAAK,EAAE,YAAY,EAAoB,MAAM,UAAU,CAAA;AAE9D,UAAU,qBAAqB;IAC7B,KAAK,EAAE,gBAAgB,CAAA;IACvB,MAAM,EAAE,iBAAiB,CAAA;IACzB,MAAM,EAAE,CAAC,QAAQ,EAAE,YAAY,KAAK,IAAI,CAAA;IACxC,KAAK,EAAE,MAAM,IAAI,CAAA;CAClB;AAUD,wBAAgB,gBAAgB,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,qBAAqB,qBA+QvF"}
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "status": "beta",
5
5
  "dependencies": []
6
6
  },
7
- "version": "0.11.1",
7
+ "version": "0.13.0",
8
8
  "description": "Fayz SDK — plugin-forms plugin",
9
9
  "type": "module",
10
10
  "sideEffects": false,
@@ -31,9 +31,9 @@
31
31
  "@dnd-kit/modifiers": "^9.0.0",
32
32
  "@dnd-kit/sortable": "^10.0.0",
33
33
  "@dnd-kit/utilities": "^3.2.2",
34
- "@fayz-ai/core": "^0.31.0",
35
- "@fayz-ai/admin": "^0.31.0",
36
- "@fayz-ai/ui": "^0.31.0"
34
+ "@fayz-ai/core": "^0.33.0",
35
+ "@fayz-ai/admin": "^0.33.0",
36
+ "@fayz-ai/ui": "^0.33.0"
37
37
  },
38
38
  "devDependencies": {
39
39
  "@types/react": "^18.3.0",
@@ -57,6 +57,7 @@
57
57
  "build": "tsup && tsc -b --force",
58
58
  "dev": "tsup --watch",
59
59
  "typecheck": "tsc -b",
60
- "clean": "rm -rf dist .tsbuildinfo"
60
+ "clean": "rm -rf dist .tsbuildinfo",
61
+ "test": "vitest run"
61
62
  }
62
63
  }
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/lib/html-text.ts","../src/components/FormRenderer.tsx","../src/lib/print.ts","../src/lib/agent-tools.ts","../src/lib/person-fields.ts"],"names":["t","optionLabel"],"mappings":"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","file":"chunk-RBLXIKWB.js","sourcesContent":["// ============================================================\n// HTML guardado num campo de formulário → texto legível\n// ============================================================\n//\n// Os campos `richtext` (e alguns `memo` vindos de importação) guardam HTML: o\n// conteúdo trazido do Feegow chega como `<p dir=\"ltr\"><b id=\"docs-internal-guid-\n// …\">Está triste, mas sabe que vai superar.&nbsp;</b></p>` — colado de um Google\n// Docs, com atributos e tudo.\n//\n// Isso dá dois jeitos ERRADOS de mostrar, e o produto já teve os dois:\n//\n// • escapar e imprimir → a pessoa lê `&lt;p dir=&quot;ltr&quot;&gt;` numa\n// ficha de atendimento. Era o que a impressão fazia.\n// • injetar como HTML → a marcação some da tela, mas o documento passa a poder\n// trazer <script>/<img onerror> de dentro do banco para dentro da página.\n// Consertar a primeira coisa caindo na segunda seria trocar um problema de\n// leitura por um de execução.\n//\n// O certo é o terceiro: extrair o TEXTO do HTML e escapar esse texto. Fica\n// legível e nada do que estava guardado é executado — o parser monta os nós, e\n// `textContent` nunca roda nenhum deles.\n\n/** Parece HTML? Uma tag de abertura ou fechamento basta. */\nexport function looksLikeHtml(value: string): boolean {\n return /<\\/?[a-z][^>]*>/i.test(value)\n}\n\n// Blocos viram quebra de linha ANTES de extrair o texto. `textContent` sozinho\n// cola os parágrafos — \"…17.07.26Está triste, mas sabe…\" — e numa evolução\n// clínica o parágrafo É a estrutura do que foi dito.\nconst BLOCKS = 'p, div, li, tr, h1, h2, h3, h4, h5, h6, blockquote, section, article'\n\n/** Espaço rígido (U+00A0): o parser decodifica `&nbsp;` para ele, mas ele não\n * quebra linha e o `trim()` não o remove. Vira espaço comum. */\nconst NBSP = / /g\n\nexport function htmlToPlainText(value: string): string {\n if (!value) return ''\n\n if (typeof document === 'undefined') {\n // Sem DOM (teste em node, render no servidor). Um \"parser\" de regex é uma\n // aproximação, e está assumido como tal: derruba as tags para não devolver\n // marcação, e não pretende estar certo para HTML arbitrário.\n return value\n .replace(/<br\\s*\\/?>/gi, '\\n')\n .replace(/<\\/(p|div|li|tr|h[1-6]|blockquote)>/gi, '\\n')\n .replace(/<[^>]*>/g, '')\n .replace(/&nbsp;/g, ' ')\n .replace(/&lt;/g, '<')\n .replace(/&gt;/g, '>')\n .replace(/&quot;/g, '\"')\n .replace(/&#39;/g, \"'\")\n // `&amp;` por último: antes dos outros, ele transformaria `&amp;lt;` em\n // `<` — decodificando uma vez a mais do que o texto pedia.\n .replace(/&amp;/g, '&')\n .replace(/\\n{3,}/g, '\\n\\n')\n .trim()\n }\n\n const el = document.createElement('div')\n el.innerHTML = value\n for (const br of Array.from(el.querySelectorAll('br'))) {\n br.replaceWith('\\n')\n }\n for (const block of Array.from(el.querySelectorAll(BLOCKS))) {\n block.append('\\n')\n }\n\n return (el.textContent ?? '')\n .replace(NBSP, ' ')\n .replace(/[ \\t]+\\n/g, '\\n')\n .replace(/\\n{3,}/g, '\\n\\n')\n .trim()\n}\n\n/** Texto legível de um campo longo, seja ele HTML guardado ou texto puro. */\nexport function fieldTextValue(value: unknown): string {\n if (typeof value !== 'string') return ''\n // Um memo digitado à mão passa intacto: converter tudo faria \"a < b virou c\"\n // perder o que vem depois do `<`.\n return looksLikeHtml(value) ? htmlToPlainText(value) : value\n}\n\n// ============================================================\n// Sanitização — mostrar a FORMATAÇÃO sem executar o documento\n// ============================================================\n//\n// Texto puro resolve a legibilidade e perde o resto: negrito, parágrafo, lista.\n// Numa evolução clínica colada do Docs, o negrito costuma ser o que a\n// terapeuta destacou, e a lista é a lista. Achatar tudo é jogar fora sentido\n// que estava lá.\n//\n// Então o HTML volta a ser HTML — mas passando por uma lista de permissão. O\n// que não está nela não é \"provavelmente inofensivo\": é removido. Isso é o\n// contrário de uma lista de proibições, que só protege do que alguém lembrou\n// de proibir.\n//\n// `DOMParser` com 'text/html' monta um documento INERTE: nenhum script roda,\n// nenhuma imagem é buscada, nenhum `onerror` dispara — nem durante a análise,\n// nem enquanto caminhamos pela árvore. É por isso que a limpeza acontece aqui e\n// não com regex sobre a string, que nunca dá conta de HTML malformado (e é\n// justamente com HTML malformado que se burla um filtro de regex).\n\n/** Some com o elemento E com o conteúdo dele. Desembrulhar um <script> jogaria\n * o código para dentro do texto visível. */\nconst DROP_WITH_CONTENT = new Set([\n 'script', 'style', 'iframe', 'object', 'embed', 'svg', 'math', 'link', 'meta',\n 'form', 'input', 'button', 'textarea', 'select', 'template', 'noscript',\n])\n\n/** Some com a TAG e mantém os filhos: um `<font>` ou um `<span>` desconhecido\n * não deve levar embora a frase que estava dentro dele. */\nconst ALLOWED_TAGS = new Set([\n 'p', 'br', 'hr', 'div', 'span',\n 'b', 'strong', 'i', 'em', 'u', 's', 'sub', 'sup', 'mark', 'small',\n 'ul', 'ol', 'li', 'blockquote', 'pre', 'code',\n 'h1', 'h2', 'h3', 'h4', 'h5', 'h6',\n 'table', 'thead', 'tbody', 'tfoot', 'tr', 'td', 'th', 'caption',\n 'a',\n])\n\n/** Só o que é preciso para um link funcionar. `style`, `class`, `id` e todo\n * `on*` ficam de fora — o `id=\"docs-internal-guid-…\"` que vem do Google Docs\n * é lixo de origem, e `style` é caminho para `expression()`/`url(javascript:)`\n * em motores antigos. */\nconst ALLOWED_ATTRS: Record<string, Set<string>> = {\n a: new Set(['href', 'title']),\n td: new Set(['colspan', 'rowspan']),\n th: new Set(['colspan', 'rowspan']),\n}\n\nconst SAFE_HREF = /^(https?:|mailto:|tel:)/i\n\nfunction cleanElement(el: Element): void {\n // Percorre uma CÓPIA: remover/desembrulhar filhos mexe na lista viva.\n for (const child of Array.from(el.children)) {\n cleanElement(child)\n }\n\n const tag = el.tagName.toLowerCase()\n\n if (DROP_WITH_CONTENT.has(tag)) {\n el.remove()\n return\n }\n\n for (const attr of Array.from(el.attributes)) {\n const name = attr.name.toLowerCase()\n const allowed = ALLOWED_ATTRS[tag]?.has(name) ?? false\n if (!allowed) {\n el.removeAttribute(attr.name)\n continue\n }\n // `href=\"javascript:…\"` é o caso clássico de atributo permitido com valor\n // que não é.\n if (name === 'href' && !SAFE_HREF.test(attr.value.trim())) {\n el.removeAttribute(attr.name)\n }\n }\n\n if (!ALLOWED_TAGS.has(tag)) {\n el.replaceWith(...Array.from(el.childNodes))\n return\n }\n\n if (tag === 'a') {\n // Um <a> que perdeu o href (era `javascript:`) continuaria azul e\n // sublinhado, prometendo um destino que não existe. Vira o texto que é.\n if (!el.getAttribute('href')) {\n el.replaceWith(...Array.from(el.childNodes))\n return\n }\n el.setAttribute('target', '_blank')\n el.setAttribute('rel', 'noopener noreferrer nofollow')\n }\n}\n\n/**\n * HTML guardado → HTML seguro de renderizar.\n *\n * Sem DOMParser (node, SSR) devolve string vazia em vez de deixar passar: quem\n * chama tem o texto puro como alternativa, e \"não consegui limpar\" nunca pode\n * virar \"então mando cru\".\n */\nexport function sanitizeHtml(value: string): string {\n if (!value) return ''\n if (typeof DOMParser === 'undefined') return ''\n\n const doc = new DOMParser().parseFromString(value, 'text/html')\n for (const child of Array.from(doc.body.children)) {\n cleanElement(child)\n }\n return doc.body.innerHTML\n}\n\n/** O campo longo pronto para exibir: `html` quando dá para formatar com\n * segurança, `text` quando não (e o chamador escapa esse). */\nexport function fieldRichValue(value: unknown): { html: string } | { text: string } {\n if (typeof value !== 'string' || !value) return { text: '' }\n if (!looksLikeHtml(value)) return { text: value }\n const html = sanitizeHtml(value)\n return html ? { html } : { text: htmlToPlainText(value) }\n}\n","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 { fieldRichValue } from '../lib/html-text'\nimport type { FormFieldDef, FormSchema } from '../types'\n\n// O HTML sanitizado chega sem `class` nenhuma (a lista de permissão derruba\n// todas), então o espaçamento e os pesos vêm daqui, pelo seletor de filho. Sem\n// isto o Tailwind zera as margens de `<p>`/`<ul>` no preflight e os parágrafos\n// do documento voltam a se colar uns nos outros.\nconst RICH_TEXT_CLASS = [\n 'space-y-2',\n '[&_p]:leading-relaxed',\n '[&_b]:font-semibold [&_strong]:font-semibold',\n '[&_i]:italic [&_em]:italic',\n '[&_u]:underline',\n '[&_ul]:list-disc [&_ul]:pl-5 [&_ol]:list-decimal [&_ol]:pl-5 [&_li]:leading-relaxed',\n '[&_blockquote]:border-l-2 [&_blockquote]:border-border [&_blockquote]:pl-3 [&_blockquote]:text-muted-foreground',\n '[&_a]:text-primary [&_a]:underline',\n '[&_h1]:font-semibold [&_h2]:font-semibold [&_h3]:font-semibold',\n '[&_table]:w-full [&_td]:border [&_td]:border-border [&_td]:px-2 [&_td]:py-1',\n '[&_th]:border [&_th]:border-border [&_th]:px-2 [&_th]:py-1 [&_th]:font-medium',\n].join(' ')\n\ninterface FormRendererProps {\n schema: FormSchema\n data: Record<string, unknown>\n onChange: (data: Record<string, unknown>) => void\n readOnly?: boolean\n}\n\n/**\n * Leitura é uma APRESENTAÇÃO, não um formulário travado.\n *\n * `readOnly` chegava no `<input readOnly>` e parava aí: caixa, borda, cursor de\n * texto, radio marcado — pixel por pixel a mesma tela de preencher. Abrir uma\n * anamnese de fevereiro parecia estar editando a anamnese de fevereiro, e a\n * única pista do contrário era o campo não aceitar digitação. Aqui o valor vira\n * texto e some a moldura: dá para ler de cima a baixo sem confundir com um\n * rascunho aberto.\n */\nfunction ReadField({ label, children }: { label?: string; children: React.ReactNode }) {\n return (\n <div className=\"space-y-0.5\">\n {label && <p className=\"text-xs font-medium text-muted-foreground\">{label}</p>}\n <div className=\"text-sm text-foreground\">{children}</div>\n </div>\n )\n}\n\nfunction EmptyValue() {\n return <span className=\"text-muted-foreground/50\">—</span>\n}\n\n/** O rótulo escolhido, não o valor cru: um documento que mostra \"prefer_not_say\"\n * faz a pessoa traduzir de cabeça o que a tela já sabia dizer. */\nfunction optionLabel(field: FormFieldDef, value: unknown): string | null {\n if (value == null || value === '') return null\n const match = (field.options ?? []).find((o) => o.value === value)\n return match?.label ?? String(value)\n}\n\nfunction ReadValue({ field, value }: { field: FormFieldDef; value: unknown }) {\n switch (field.type) {\n case 'memo':\n case 'richtext': {\n // Formatação preservada, documento não executado: o valor passa pela\n // lista de permissão de lib/html-text.ts e volta como HTML seguro. O\n // negrito que a terapeuta aplicou continua negrito; o\n // `id=\"docs-internal-guid-…\"` que veio junto do Google Docs não sobrevive.\n //\n // Sem tag nenhuma (memo digitado à mão) é texto puro mesmo, com as\n // quebras respeitadas pelo `whitespace-pre-wrap`.\n const rich = fieldRichValue(value)\n return (\n <ReadField label={field.label}>\n {'html' in rich ? (\n <div className={RICH_TEXT_CLASS} dangerouslySetInnerHTML={{ __html: rich.html }} />\n ) : rich.text ? (\n <p className=\"whitespace-pre-wrap\">{rich.text}</p>\n ) : (\n <EmptyValue />\n )}\n </ReadField>\n )\n }\n\n case 'date': {\n const raw = typeof value === 'string' ? value : ''\n const date = raw ? new Date(raw) : null\n const text = date && !Number.isNaN(date.getTime())\n // `T00:00` faz o Date nascer em UTC e o fuso do Brasil o joga para o dia\n // anterior — daí o corte manual em vez de toLocaleDateString direto.\n ? raw.slice(0, 10).split('-').reverse().join('/')\n : raw\n return <ReadField label={field.label}>{text || <EmptyValue />}</ReadField>\n }\n\n case 'select':\n case 'radio': {\n const label = optionLabel(field, value)\n return <ReadField label={field.label}>{label ?? <EmptyValue />}</ReadField>\n }\n\n case 'tags': {\n const selected = Array.isArray(value) ? (value as string[]) : []\n return (\n <ReadField label={field.label}>\n {selected.length > 0 ? (\n <div className=\"flex flex-wrap gap-1.5\">\n {selected.map((v) => (\n <Badge key={v} variant=\"secondary\" className=\"text-xs\">\n {optionLabel(field, v) ?? v}\n </Badge>\n ))}\n </div>\n ) : (\n <EmptyValue />\n )}\n </ReadField>\n )\n }\n\n case 'checkbox':\n // Sem rótulo próprio: o texto do checkbox JÁ é a pergunta (\"Autoriza uso\n // de imagem\"), então repeti-lo acima da resposta diria duas vezes a mesma\n // coisa.\n return (\n <ReadField>\n <span className=\"font-medium\">{field.label}</span>\n <span className=\"text-muted-foreground\"> · </span>\n <span>{value ? 'Sim' : 'Não'}</span>\n </ReadField>\n )\n\n default: {\n const text = value == null || value === '' ? null : String(value)\n return <ReadField label={field.label}>{text ?? <EmptyValue />}</ReadField>\n }\n }\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 // Título e os blocos ainda não implementados continuam iguais nos dois modos:\n // um é estrutura do documento, os outros não têm valor para apresentar.\n if (readOnly && field.type !== 'title' && field.type !== 'image' && field.type !== 'gallery' && field.type !== 'budget') {\n return <ReadValue field={field} value={value} />\n }\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'\nimport { fieldRichValue } from './html-text'\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 // Texto primeiro, escape depois. `nl2br` já escapava — e era exatamente\n // por isso que a folha impressa saía com `&lt;p dir=&quot;ltr&quot;&gt;`\n // no lugar da evolução do atendimento: escapar HTML guardado mostra a\n // marcação. Ver lib/html-text.ts.\n // Agora o valor passa pela lista de permissão e a folha sai formatada,\n // igual à tela. O escape continua no caminho de texto puro, que é onde\n // ele sempre foi o certo.\n const rich = fieldRichValue(value)\n if ('html' in rich) return `<div class=\"long rich\">${rich.html}</div>`\n return rich.text ? `<div class=\"long\">${nl2br(rich.text)}</div>` : empty\n }\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 /* Conteudo formatado (sanitizado em lib/html-text.ts). O HTML chega sem\n class nenhuma, entao o estilo vem por tag. orphans/widows evitam que um\n paragrafo deixe uma linha solta virando a pagina. */\n .long.rich p { margin: 0 0 8px; orphans: 2; widows: 2; }\n .long.rich p:last-child { margin-bottom: 0; }\n .long.rich ul, .long.rich ol { margin: 0 0 8px; padding-left: 20px; }\n .long.rich li { margin: 0 0 3px; }\n .long.rich b, .long.rich strong { font-weight: 600; }\n .long.rich blockquote { margin: 0 0 8px; padding-left: 10px; border-left: 2px solid #ccc; color: #444; }\n .long.rich a { color: #111; text-decoration: underline; }\n .long.rich table { width: 100%; border-collapse: collapse; margin: 0 0 8px; }\n .long.rich td, .long.rich th { border: 1px solid #ccc; padding: 4px 6px; text-align: left; }\n .long.rich h1, .long.rich h2, .long.rich h3,\n .long.rich h4, .long.rich h5, .long.rich h6 { font-size: 12pt; font-weight: 600; margin: 10px 0 4px; }\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"]}