@fayz-ai/plugin-forms 0.9.0 → 0.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/README.md +3 -3
  2. package/dist/{FormBuilder-7SWZQKFN.js → FormBuilder-JCNHXQXT.js} +4 -4
  3. package/dist/{FormBuilder-7SWZQKFN.js.map → FormBuilder-JCNHXQXT.js.map} +1 -1
  4. package/dist/{chunk-W2ZNJGQC.js → chunk-JCAWSDX6.js} +3 -3
  5. package/dist/chunk-JCAWSDX6.js.map +1 -0
  6. package/dist/components/AddDocumentDropdown.d.ts +1 -1
  7. package/dist/components/AddDocumentDropdown.d.ts.map +1 -1
  8. package/dist/{CustomFormsContext.d.ts → context.d.ts} +1 -1
  9. package/dist/context.d.ts.map +1 -0
  10. package/dist/index.d.ts +1 -1
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +4 -4
  13. package/dist/index.js.map +1 -1
  14. package/dist/lib/agent-tools.d.ts.map +1 -0
  15. package/dist/lib/document-types.d.ts.map +1 -0
  16. package/package.json +5 -7
  17. package/dist/CustomFormsContext.d.ts.map +0 -1
  18. package/dist/agent-tools.d.ts.map +0 -1
  19. package/dist/chunk-W2ZNJGQC.js.map +0 -1
  20. package/dist/document-types.d.ts.map +0 -1
  21. package/src/CustomFormsContext.tsx +0 -28
  22. package/src/agent-tools.ts +0 -73
  23. package/src/components/AddDocumentDropdown.tsx +0 -92
  24. package/src/components/BuilderCanvas.tsx +0 -87
  25. package/src/components/BuilderField.tsx +0 -81
  26. package/src/components/DocumentFormDialog.tsx +0 -155
  27. package/src/components/DocumentList.tsx +0 -254
  28. package/src/components/FieldConfigDialog.tsx +0 -243
  29. package/src/components/FieldPalette.tsx +0 -81
  30. package/src/components/FormBuilder.tsx +0 -427
  31. package/src/components/FormRenderer.tsx +0 -256
  32. package/src/components/FormViewer.tsx +0 -94
  33. package/src/components/PersonDocumentsWidget.tsx +0 -229
  34. package/src/components/TemplateSelector.tsx +0 -91
  35. package/src/config.ts +0 -43
  36. package/src/data/index.ts +0 -3
  37. package/src/data/mock.ts +0 -193
  38. package/src/data/supabase.ts +0 -408
  39. package/src/data/tables.ts +0 -7
  40. package/src/data/types.ts +0 -33
  41. package/src/document-types.ts +0 -51
  42. package/src/index.ts +0 -234
  43. package/src/lib/person-fields.ts +0 -67
  44. package/src/lib/print.ts +0 -207
  45. package/src/lib/tenant.ts +0 -9
  46. package/src/locales/en.ts +0 -95
  47. package/src/locales/index.ts +0 -7
  48. package/src/locales/pt-BR.ts +0 -95
  49. package/src/migrations/000_plg_rename.sql +0 -21
  50. package/src/migrations/001_frm_base.sql +0 -174
  51. package/src/migrations/002_document_archetype.sql +0 -223
  52. package/src/migrations/003_agent_rpcs.sql +0 -174
  53. package/src/migrations/index.ts +0 -610
  54. package/src/store.ts +0 -167
  55. package/src/types.ts +0 -217
  56. package/src/views/CustomFormsSettingsTab.tsx +0 -105
  57. package/src/views/TemplateListView.tsx +0 -174
  58. /package/dist/{agent-tools.d.ts → lib/agent-tools.d.ts} +0 -0
  59. /package/dist/{document-types.d.ts → lib/document-types.d.ts} +0 -0
@@ -1,610 +0,0 @@
1
- // AUTO-GENERATED from 000_plg_rename.sql, 001_frm_base.sql, 002_document_archetype.sql, 003_agent_rpcs.sql — regenerate with scripts/embed-migrations.mjs
2
- // SQL files are the source of truth; this inline copy lets the manifest declare
3
- // migrations as data. Do not edit by hand — run the embed script instead.
4
-
5
- export const MIGRATION_000_PLG_RENAME = `-- 000_plg_rename.sql — rename legacy forms tables to plg_forms_* for pools
6
- -- provisioned before the industry-pool rename. Guarded: fires only when the legacy
7
- -- name exists and the target does not, so fresh pools skip every branch.
8
- -- The core \`documents\` archetype table is NOT renamed — it stays public.documents.
9
- DO $$
10
- BEGIN
11
- IF to_regclass('public.frm_documents') IS NOT NULL AND to_regclass('public.plg_forms_documents') IS NULL THEN
12
- ALTER TABLE public.frm_documents RENAME TO plg_forms_documents;
13
- END IF;
14
- IF to_regclass('public.frm_templates') IS NOT NULL AND to_regclass('public.plg_forms_templates') IS NULL THEN
15
- ALTER TABLE public.frm_templates RENAME TO plg_forms_templates;
16
- END IF;
17
- IF to_regclass('public.frm_document_files') IS NOT NULL AND to_regclass('public.plg_forms_document_files') IS NULL THEN
18
- ALTER TABLE public.frm_document_files RENAME TO plg_forms_document_files;
19
- END IF;
20
- -- frm_categories: registry-declared (form-template categories); no base-table
21
- -- DDL ships in this plugin, but rename in place if a pool created one.
22
- IF to_regclass('public.frm_categories') IS NOT NULL AND to_regclass('public.plg_forms_categories') IS NULL THEN
23
- ALTER TABLE public.frm_categories RENAME TO plg_forms_categories;
24
- END IF;
25
- END $$;
26
- `
27
-
28
- export const MIGRATION_001_FRM_BASE = `-- ============================================================
29
- -- Custom Forms Plugin — Base Tables
30
- -- ============================================================
31
-
32
- -- plg_forms_templates: form template definitions (versioned)
33
- CREATE TABLE IF NOT EXISTS public.plg_forms_templates (
34
- id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
35
- tenant_id uuid NOT NULL REFERENCES public.tenants(id) ON DELETE CASCADE,
36
- name text NOT NULL,
37
- description text,
38
- category text NOT NULL DEFAULT 'general',
39
- version integer NOT NULL DEFAULT 1,
40
- is_current boolean NOT NULL DEFAULT true,
41
- parent_id uuid REFERENCES public.plg_forms_templates(id),
42
- schema jsonb NOT NULL DEFAULT '{"fields":[],"layout":{"columns":12}}',
43
- specialty text,
44
- tags text[] DEFAULT '{}',
45
- metadata jsonb DEFAULT '{}',
46
- is_active boolean NOT NULL DEFAULT true,
47
- is_deleted boolean NOT NULL DEFAULT false,
48
- created_by uuid,
49
- updated_by uuid,
50
- created_at timestamptz NOT NULL DEFAULT now(),
51
- updated_at timestamptz NOT NULL DEFAULT now()
52
- );
53
-
54
- ALTER TABLE public.plg_forms_templates ENABLE ROW LEVEL SECURITY;
55
-
56
- CREATE INDEX IF NOT EXISTS idx_plg_forms_templates_tenant
57
- ON public.plg_forms_templates(tenant_id);
58
- CREATE INDEX IF NOT EXISTS idx_plg_forms_templates_parent
59
- ON public.plg_forms_templates(parent_id);
60
- CREATE INDEX IF NOT EXISTS idx_plg_forms_templates_category
61
- ON public.plg_forms_templates(tenant_id, category);
62
- CREATE INDEX IF NOT EXISTS idx_plg_forms_templates_current
63
- ON public.plg_forms_templates(tenant_id, is_current, is_active)
64
- WHERE is_current = true AND is_active = true AND is_deleted = false;
65
-
66
- DROP POLICY IF EXISTS "plg_forms_templates_select" ON public.plg_forms_templates;
67
- CREATE POLICY "plg_forms_templates_select" ON public.plg_forms_templates
68
- FOR SELECT TO authenticated
69
- USING (tenant_id IN (SELECT public.user_tenant_ids()));
70
- DROP POLICY IF EXISTS "plg_forms_templates_insert" ON public.plg_forms_templates;
71
- CREATE POLICY "plg_forms_templates_insert" ON public.plg_forms_templates
72
- FOR INSERT TO authenticated
73
- WITH CHECK (tenant_id IN (SELECT public.user_tenant_ids()));
74
- DROP POLICY IF EXISTS "plg_forms_templates_update" ON public.plg_forms_templates;
75
- CREATE POLICY "plg_forms_templates_update" ON public.plg_forms_templates
76
- FOR UPDATE TO authenticated
77
- USING (tenant_id IN (SELECT public.user_tenant_ids()));
78
- DROP POLICY IF EXISTS "plg_forms_templates_delete" ON public.plg_forms_templates;
79
- CREATE POLICY "plg_forms_templates_delete" ON public.plg_forms_templates
80
- FOR DELETE TO authenticated
81
- USING (tenant_id IN (SELECT public.user_tenant_ids()));
82
-
83
- -- plg_forms_documents: filled form instances
84
- CREATE TABLE IF NOT EXISTS public.plg_forms_documents (
85
- id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
86
- tenant_id uuid NOT NULL REFERENCES public.tenants(id) ON DELETE CASCADE,
87
- template_id uuid NOT NULL REFERENCES public.plg_forms_templates(id),
88
- person_id uuid REFERENCES public.people(id) ON DELETE SET NULL,
89
- title text,
90
- data jsonb NOT NULL DEFAULT '{}',
91
- status text NOT NULL DEFAULT 'draft',
92
- signed_at timestamptz,
93
- signed_by uuid,
94
- notes text,
95
- metadata jsonb DEFAULT '{}',
96
- is_deleted boolean NOT NULL DEFAULT false,
97
- created_by uuid,
98
- updated_by uuid,
99
- created_at timestamptz NOT NULL DEFAULT now(),
100
- updated_at timestamptz NOT NULL DEFAULT now()
101
- );
102
-
103
- ALTER TABLE public.plg_forms_documents ENABLE ROW LEVEL SECURITY;
104
-
105
- CREATE INDEX IF NOT EXISTS idx_plg_forms_documents_tenant
106
- ON public.plg_forms_documents(tenant_id);
107
- -- person_id/status only exist in the pre-archetype shape this file creates;
108
- -- converted pools (salon) already carry the archetype extension shape
109
- -- (document_id PK, no person_id) — 002 owns that shape, so guard these.
110
- DO $$
111
- BEGIN
112
- IF EXISTS (
113
- SELECT 1 FROM information_schema.columns
114
- WHERE table_schema = 'public' AND table_name = 'plg_forms_documents'
115
- AND column_name = 'person_id'
116
- ) THEN
117
- EXECUTE 'CREATE INDEX IF NOT EXISTS idx_plg_forms_documents_person
118
- ON public.plg_forms_documents(person_id)';
119
- EXECUTE 'CREATE INDEX IF NOT EXISTS idx_plg_forms_documents_status
120
- ON public.plg_forms_documents(tenant_id, status)';
121
- END IF;
122
- END $$;
123
- CREATE INDEX IF NOT EXISTS idx_plg_forms_documents_template
124
- ON public.plg_forms_documents(template_id);
125
-
126
- DROP POLICY IF EXISTS "plg_forms_documents_select" ON public.plg_forms_documents;
127
- CREATE POLICY "plg_forms_documents_select" ON public.plg_forms_documents
128
- FOR SELECT TO authenticated
129
- USING (tenant_id IN (SELECT public.user_tenant_ids()));
130
- DROP POLICY IF EXISTS "plg_forms_documents_insert" ON public.plg_forms_documents;
131
- CREATE POLICY "plg_forms_documents_insert" ON public.plg_forms_documents
132
- FOR INSERT TO authenticated
133
- WITH CHECK (tenant_id IN (SELECT public.user_tenant_ids()));
134
- DROP POLICY IF EXISTS "plg_forms_documents_update" ON public.plg_forms_documents;
135
- CREATE POLICY "plg_forms_documents_update" ON public.plg_forms_documents
136
- FOR UPDATE TO authenticated
137
- USING (tenant_id IN (SELECT public.user_tenant_ids()));
138
- DROP POLICY IF EXISTS "plg_forms_documents_delete" ON public.plg_forms_documents;
139
- CREATE POLICY "plg_forms_documents_delete" ON public.plg_forms_documents
140
- FOR DELETE TO authenticated
141
- USING (tenant_id IN (SELECT public.user_tenant_ids()));
142
-
143
- -- plg_forms_document_files: file attachments for image/gallery/drawing fields
144
- CREATE TABLE IF NOT EXISTS public.plg_forms_document_files (
145
- id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
146
- tenant_id uuid NOT NULL REFERENCES public.tenants(id) ON DELETE CASCADE,
147
- document_id uuid NOT NULL REFERENCES public.plg_forms_documents(id) ON DELETE CASCADE,
148
- field_key text NOT NULL,
149
- file_url text NOT NULL,
150
- file_name text,
151
- file_size integer,
152
- mime_type text,
153
- sort_order integer DEFAULT 0,
154
- metadata jsonb DEFAULT '{}',
155
- created_at timestamptz NOT NULL DEFAULT now()
156
- );
157
-
158
- ALTER TABLE public.plg_forms_document_files ENABLE ROW LEVEL SECURITY;
159
-
160
- CREATE INDEX IF NOT EXISTS idx_plg_forms_document_files_document
161
- ON public.plg_forms_document_files(document_id);
162
-
163
- DROP POLICY IF EXISTS "plg_forms_document_files_select" ON public.plg_forms_document_files;
164
- CREATE POLICY "plg_forms_document_files_select" ON public.plg_forms_document_files
165
- FOR SELECT TO authenticated
166
- USING (tenant_id IN (SELECT public.user_tenant_ids()));
167
- DROP POLICY IF EXISTS "plg_forms_document_files_insert" ON public.plg_forms_document_files;
168
- CREATE POLICY "plg_forms_document_files_insert" ON public.plg_forms_document_files
169
- FOR INSERT TO authenticated
170
- WITH CHECK (tenant_id IN (SELECT public.user_tenant_ids()));
171
- DROP POLICY IF EXISTS "plg_forms_document_files_update" ON public.plg_forms_document_files;
172
- CREATE POLICY "plg_forms_document_files_update" ON public.plg_forms_document_files
173
- FOR UPDATE TO authenticated
174
- USING (tenant_id IN (SELECT public.user_tenant_ids()));
175
- DROP POLICY IF EXISTS "plg_forms_document_files_delete" ON public.plg_forms_document_files;
176
- CREATE POLICY "plg_forms_document_files_delete" ON public.plg_forms_document_files
177
- FOR DELETE TO authenticated
178
- USING (tenant_id IN (SELECT public.user_tenant_ids()));
179
-
180
- -- View: pre-archetype read model (needs person_id; archetype pools get
181
- -- v_documents from 002 instead, which also drops this view when migrating).
182
- DO $$
183
- BEGIN
184
- IF EXISTS (
185
- SELECT 1 FROM information_schema.columns
186
- WHERE table_schema = 'public' AND table_name = 'plg_forms_documents'
187
- AND column_name = 'person_id'
188
- ) THEN
189
- EXECUTE $v$
190
- CREATE OR REPLACE VIEW public.v_frm_documents AS
191
- SELECT
192
- d.*,
193
- t.name AS template_name,
194
- t.category AS template_category,
195
- p.name AS person_name
196
- FROM public.plg_forms_documents d
197
- LEFT JOIN public.plg_forms_templates t ON t.id = d.template_id
198
- LEFT JOIN public.people p ON p.id = d.person_id
199
- $v$;
200
- END IF;
201
- END $$;
202
- `
203
-
204
- export const MIGRATION_002_DOCUMENT_ARCHETYPE = `-- ============================================================
205
- -- Document Archetype — public.documents
206
- -- A document is any record associated with a person (or standalone):
207
- -- forms, images, attachments, prescriptions, contracts, etc.
208
- -- The \`kind\` column discriminates the type.
209
- --
210
- -- DATA SAFETY (converted pools): plg_forms_documents may already hold REAL
211
- -- rows in its PRE-archetype shape (its own \`id\` PK, no \`document_id\` column —
212
- -- see 001_frm_base.sql). This migration MIGRATES those rows into the new
213
- -- document archetype instead of dropping them:
214
- -- * pre-archetype + rows -> copy each legacy row into public.documents
215
- -- (reusing its id) + into the new extension
216
- -- table, migrate its files, then drop legacy.
217
- -- * pre-archetype + empty -> plain drop + create (fresh shape).
218
- -- * already new shape -> no-op.
219
- -- ============================================================
220
-
221
- CREATE TABLE IF NOT EXISTS public.documents (
222
- id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
223
- tenant_id uuid NOT NULL REFERENCES public.tenants(id) ON DELETE CASCADE,
224
- kind text NOT NULL DEFAULT 'attachment',
225
- -- kind: 'form', 'image', 'attachment', 'prescription', 'contract', etc.
226
- person_id uuid REFERENCES public.people(id) ON DELETE SET NULL,
227
- title text,
228
- description text,
229
- status text NOT NULL DEFAULT 'draft',
230
- -- status: 'draft', 'completed', 'signed', 'archived'
231
- file_url text,
232
- file_name text,
233
- file_size integer,
234
- mime_type text,
235
- tags text[] DEFAULT '{}',
236
- notes text,
237
- is_active boolean NOT NULL DEFAULT true,
238
- metadata jsonb DEFAULT '{}',
239
- created_by uuid,
240
- updated_by uuid,
241
- created_at timestamptz NOT NULL DEFAULT now(),
242
- updated_at timestamptz NOT NULL DEFAULT now()
243
- );
244
-
245
- ALTER TABLE public.documents ENABLE ROW LEVEL SECURITY;
246
-
247
- CREATE INDEX IF NOT EXISTS idx_documents_tenant ON public.documents(tenant_id, kind);
248
- CREATE INDEX IF NOT EXISTS idx_documents_person ON public.documents(person_id);
249
- CREATE INDEX IF NOT EXISTS idx_documents_status ON public.documents(tenant_id, status);
250
-
251
- DROP POLICY IF EXISTS "documents_select" ON public.documents;
252
- CREATE POLICY "documents_select" ON public.documents
253
- FOR SELECT TO authenticated
254
- USING (tenant_id IN (SELECT public.user_tenant_ids()));
255
- DROP POLICY IF EXISTS "documents_insert" ON public.documents;
256
- CREATE POLICY "documents_insert" ON public.documents
257
- FOR INSERT TO authenticated
258
- WITH CHECK (tenant_id IN (SELECT public.user_tenant_ids()));
259
- DROP POLICY IF EXISTS "documents_update" ON public.documents;
260
- CREATE POLICY "documents_update" ON public.documents
261
- FOR UPDATE TO authenticated
262
- USING (tenant_id IN (SELECT public.user_tenant_ids()));
263
- DROP POLICY IF EXISTS "documents_delete" ON public.documents;
264
- CREATE POLICY "documents_delete" ON public.documents
265
- FOR DELETE TO authenticated
266
- USING (tenant_id IN (SELECT public.user_tenant_ids()));
267
-
268
- -- ============================================================
269
- -- Reconcile plg_forms_documents / plg_forms_document_files with the archetype.
270
- -- ============================================================
271
-
272
- -- Step 1: if a PRE-archetype plg_forms_documents is present (no document_id
273
- -- column), rename it (and its files table + dependent view) out of the way so
274
- -- the new archetype-shaped tables can be created and — when it holds rows —
275
- -- its data copied across. Fully guarded, so already-migrated pools skip this.
276
- DO $$
277
- BEGIN
278
- IF to_regclass('public.plg_forms_documents') IS NOT NULL
279
- AND NOT EXISTS (
280
- SELECT 1 FROM information_schema.columns
281
- WHERE table_schema = 'public'
282
- AND table_name = 'plg_forms_documents'
283
- AND column_name = 'document_id'
284
- ) THEN
285
- DROP VIEW IF EXISTS public.v_frm_documents;
286
- ALTER TABLE public.plg_forms_documents RENAME TO plg_forms_documents_legacy;
287
- IF to_regclass('public.plg_forms_document_files') IS NOT NULL THEN
288
- ALTER TABLE public.plg_forms_document_files RENAME TO plg_forms_document_files_legacy;
289
- END IF;
290
- END IF;
291
- END $$;
292
-
293
- -- Step 2: create the new archetype-shaped extension tables. IF NOT EXISTS makes
294
- -- this a no-op on pools already migrated to the new shape; after Step 1 the
295
- -- names are free on pre-archetype pools.
296
-
297
- -- plg_forms_documents: extension table for form-type documents
298
- CREATE TABLE IF NOT EXISTS public.plg_forms_documents (
299
- document_id uuid PRIMARY KEY REFERENCES public.documents(id) ON DELETE CASCADE,
300
- tenant_id uuid NOT NULL REFERENCES public.tenants(id) ON DELETE CASCADE,
301
- template_id uuid NOT NULL REFERENCES public.plg_forms_templates(id),
302
- data jsonb NOT NULL DEFAULT '{}',
303
- signed_at timestamptz,
304
- signed_by uuid,
305
- created_at timestamptz NOT NULL DEFAULT now(),
306
- updated_at timestamptz NOT NULL DEFAULT now()
307
- );
308
-
309
- ALTER TABLE public.plg_forms_documents ENABLE ROW LEVEL SECURITY;
310
-
311
- CREATE INDEX IF NOT EXISTS idx_plg_forms_documents_template ON public.plg_forms_documents(template_id);
312
-
313
- DROP POLICY IF EXISTS "plg_forms_documents_select" ON public.plg_forms_documents;
314
- CREATE POLICY "plg_forms_documents_select" ON public.plg_forms_documents
315
- FOR SELECT TO authenticated
316
- USING (tenant_id IN (SELECT public.user_tenant_ids()));
317
- DROP POLICY IF EXISTS "plg_forms_documents_insert" ON public.plg_forms_documents;
318
- CREATE POLICY "plg_forms_documents_insert" ON public.plg_forms_documents
319
- FOR INSERT TO authenticated
320
- WITH CHECK (tenant_id IN (SELECT public.user_tenant_ids()));
321
- DROP POLICY IF EXISTS "plg_forms_documents_update" ON public.plg_forms_documents;
322
- CREATE POLICY "plg_forms_documents_update" ON public.plg_forms_documents
323
- FOR UPDATE TO authenticated
324
- USING (tenant_id IN (SELECT public.user_tenant_ids()));
325
- DROP POLICY IF EXISTS "plg_forms_documents_delete" ON public.plg_forms_documents;
326
- CREATE POLICY "plg_forms_documents_delete" ON public.plg_forms_documents
327
- FOR DELETE TO authenticated
328
- USING (tenant_id IN (SELECT public.user_tenant_ids()));
329
-
330
- -- plg_forms_document_files: file attachments for form fields
331
- CREATE TABLE IF NOT EXISTS public.plg_forms_document_files (
332
- id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
333
- tenant_id uuid NOT NULL REFERENCES public.tenants(id) ON DELETE CASCADE,
334
- document_id uuid NOT NULL REFERENCES public.documents(id) ON DELETE CASCADE,
335
- field_key text NOT NULL,
336
- file_url text NOT NULL,
337
- file_name text,
338
- file_size integer,
339
- mime_type text,
340
- sort_order integer DEFAULT 0,
341
- metadata jsonb DEFAULT '{}',
342
- created_at timestamptz NOT NULL DEFAULT now()
343
- );
344
-
345
- ALTER TABLE public.plg_forms_document_files ENABLE ROW LEVEL SECURITY;
346
-
347
- CREATE INDEX IF NOT EXISTS idx_plg_forms_document_files_document ON public.plg_forms_document_files(document_id);
348
-
349
- DROP POLICY IF EXISTS "plg_forms_document_files_select" ON public.plg_forms_document_files;
350
- CREATE POLICY "plg_forms_document_files_select" ON public.plg_forms_document_files
351
- FOR SELECT TO authenticated
352
- USING (tenant_id IN (SELECT public.user_tenant_ids()));
353
- DROP POLICY IF EXISTS "plg_forms_document_files_insert" ON public.plg_forms_document_files;
354
- CREATE POLICY "plg_forms_document_files_insert" ON public.plg_forms_document_files
355
- FOR INSERT TO authenticated
356
- WITH CHECK (tenant_id IN (SELECT public.user_tenant_ids()));
357
- DROP POLICY IF EXISTS "plg_forms_document_files_update" ON public.plg_forms_document_files;
358
- CREATE POLICY "plg_forms_document_files_update" ON public.plg_forms_document_files
359
- FOR UPDATE TO authenticated
360
- USING (tenant_id IN (SELECT public.user_tenant_ids()));
361
- DROP POLICY IF EXISTS "plg_forms_document_files_delete" ON public.plg_forms_document_files;
362
- CREATE POLICY "plg_forms_document_files_delete" ON public.plg_forms_document_files
363
- FOR DELETE TO authenticated
364
- USING (tenant_id IN (SELECT public.user_tenant_ids()));
365
-
366
- -- Step 3: if legacy rows exist, migrate them, then drop the quarantined legacy
367
- -- tables. Reusing each legacy id as the documents.id keeps every legacy file's
368
- -- document_id FK valid after the copy. ON CONFLICT DO NOTHING makes a partial
369
- -- re-run safe.
370
- DO $$
371
- BEGIN
372
- IF to_regclass('public.plg_forms_documents_legacy') IS NOT NULL THEN
373
- -- 3a. base document (one per legacy form document; is_active mirrors NOT is_deleted)
374
- INSERT INTO public.documents (
375
- id, tenant_id, kind, person_id, title, status, notes, metadata,
376
- is_active, created_by, updated_by, created_at, updated_at
377
- )
378
- SELECT
379
- id, tenant_id, 'form', person_id, title, status, notes, metadata,
380
- NOT COALESCE(is_deleted, false), created_by, updated_by, created_at, updated_at
381
- FROM public.plg_forms_documents_legacy
382
- ON CONFLICT (id) DO NOTHING;
383
-
384
- -- 3b. form extension row (keyed by the reused document id)
385
- INSERT INTO public.plg_forms_documents (
386
- document_id, tenant_id, template_id, data, signed_at, signed_by, created_at, updated_at
387
- )
388
- SELECT
389
- id, tenant_id, template_id, data, signed_at, signed_by, created_at, updated_at
390
- FROM public.plg_forms_documents_legacy
391
- ON CONFLICT (document_id) DO NOTHING;
392
-
393
- -- 3c. file attachments (document_id already points at the reused id)
394
- IF to_regclass('public.plg_forms_document_files_legacy') IS NOT NULL THEN
395
- INSERT INTO public.plg_forms_document_files (
396
- id, tenant_id, document_id, field_key, file_url, file_name,
397
- file_size, mime_type, sort_order, metadata, created_at
398
- )
399
- SELECT
400
- id, tenant_id, document_id, field_key, file_url, file_name,
401
- file_size, mime_type, sort_order, metadata, created_at
402
- FROM public.plg_forms_document_files_legacy
403
- ON CONFLICT (id) DO NOTHING;
404
- END IF;
405
- END IF;
406
-
407
- -- Drop legacy remnants now that any data has been copied across.
408
- DROP TABLE IF EXISTS public.plg_forms_document_files_legacy;
409
- DROP TABLE IF EXISTS public.plg_forms_documents_legacy;
410
- END $$;
411
-
412
- -- View: all documents for a person with form data joined when applicable
413
- CREATE OR REPLACE VIEW public.v_documents AS
414
- SELECT
415
- d.*,
416
- f.template_id,
417
- f.data AS form_data,
418
- f.signed_at,
419
- f.signed_by,
420
- t.name AS template_name,
421
- t.category AS template_category,
422
- p.name AS person_name
423
- FROM public.documents d
424
- LEFT JOIN public.plg_forms_documents f ON f.document_id = d.id
425
- LEFT JOIN public.plg_forms_templates t ON t.id = f.template_id
426
- LEFT JOIN public.people p ON p.id = d.person_id;
427
- `
428
-
429
- export const MIGRATION_003_AGENT_RPCS = `-- ============================================================================
430
- -- plugin-forms 003: server-plane agent write RPC.
431
- --
432
- -- public.agent_forms_upsert_template — the assistant BUILDS or EDITS a form
433
- -- template from any surface or channel (in-app FAB, WhatsApp, MCP). Same
434
- -- contract as every agent_* RPC (agenda 005, financial 009):
435
- -- (p_tenant_id, p_actor_user_id, p_payload jsonb) → jsonb
436
- -- {ok:true, id, record:{...}} | {ok:false, denial:{...}} | {ok:false, error}
437
- --
438
- -- The grid layout is computed HERE (col 0, sequential rows, full-width span) so
439
- -- the model only ever sends {type,label,required?,options?} — never positions.
440
- --
441
- -- Payload:
442
- -- template_id? uuid — omit to CREATE, provide to EDIT
443
- -- name? text — required on create
444
- -- category? text — anamnesis|evolution|report|contract|general
445
- -- description? text
446
- -- fields? [{type,label,required?,placeholder?,options?[]}] — REPLACE all
447
- -- append_fields? [{...}] — APPEND to existing
448
- --
449
- -- GRANT authenticated+service_role — NEVER anon (the broker calls with the pool
450
- -- service key and injects tenant/actor; in-browser the signed-in user calls it
451
- -- directly and spine RLS/actor guards bind them to their own tenant).
452
- -- ============================================================================
453
-
454
- CREATE OR REPLACE FUNCTION public.agent_forms_upsert_template(
455
- p_tenant_id uuid,
456
- p_actor_user_id uuid,
457
- p_payload jsonb
458
- ) RETURNS jsonb
459
- LANGUAGE plpgsql SECURITY DEFINER
460
- SET search_path = public
461
- AS $$
462
- DECLARE
463
- v_denial jsonb;
464
- v_used int;
465
- v_template_id uuid;
466
- v_is_update boolean;
467
- v_name text;
468
- v_category text;
469
- v_description text;
470
- v_existing jsonb;
471
- v_input jsonb;
472
- v_merged jsonb;
473
- v_fields jsonb;
474
- v_schema jsonb;
475
- v_final_id uuid;
476
- BEGIN
477
- -- ── payload ──────────────────────────────────────────────────────────────
478
- BEGIN
479
- v_template_id := NULLIF(p_payload->>'template_id','')::uuid;
480
- EXCEPTION WHEN OTHERS THEN
481
- RETURN jsonb_build_object('ok', false, 'error', 'invalid template_id');
482
- END;
483
- v_is_update := v_template_id IS NOT NULL;
484
- v_name := btrim(p_payload->>'name');
485
- v_category := lower(COALESCE(NULLIF(p_payload->>'category',''), 'general'));
486
- v_description := left(p_payload->>'description', 1000);
487
- IF v_category NOT IN ('anamnesis','evolution','report','contract','general') THEN
488
- v_category := 'general';
489
- END IF;
490
-
491
- IF NOT v_is_update AND (v_name IS NULL OR v_name = '') THEN
492
- RETURN jsonb_build_object('ok', false, 'error', 'name is required to create a form');
493
- END IF;
494
-
495
- -- ── authorization: role → plan → form_templates cap ──────────────────────
496
- SELECT count(*) INTO v_used FROM plg_forms_templates
497
- WHERE tenant_id = p_tenant_id AND is_deleted = false;
498
- v_denial := agent_guard(p_tenant_id, p_actor_user_id, 'custom_forms',
499
- CASE WHEN v_is_update THEN 'update' ELSE 'create' END,
500
- 'form_templates', v_used, CASE WHEN v_is_update THEN 0 ELSE 1 END);
501
- IF v_denial IS NOT NULL THEN
502
- RETURN jsonb_build_object('ok', false, 'denial', v_denial);
503
- END IF;
504
-
505
- -- ── current fields (for APPEND / to reject an unknown id) ────────────────
506
- IF v_is_update THEN
507
- SELECT COALESCE(schema->'fields','[]'::jsonb) INTO v_existing
508
- FROM plg_forms_templates
509
- WHERE id = v_template_id AND tenant_id = p_tenant_id AND is_deleted = false;
510
- IF v_existing IS NULL THEN
511
- RETURN jsonb_build_object('ok', false, 'error', 'unknown template for this tenant');
512
- END IF;
513
- ELSE
514
- v_existing := '[]'::jsonb;
515
- END IF;
516
-
517
- -- ── normalize the incoming fields to the builder's FormFieldDef shape ────
518
- -- (id, type, label, required, placeholder, col, colSpan, options) — row is
519
- -- assigned last, across the merged list.
520
- WITH src AS (
521
- SELECT CASE
522
- WHEN p_payload ? 'fields' THEN COALESCE(p_payload->'fields','[]'::jsonb)
523
- WHEN p_payload ? 'append_fields' THEN COALESCE(p_payload->'append_fields','[]'::jsonb)
524
- ELSE '[]'::jsonb
525
- END AS arr
526
- ),
527
- norm AS (
528
- SELECT f.ord, jsonb_strip_nulls(jsonb_build_object(
529
- 'id', COALESCE(NULLIF(f.value->>'id',''), gen_random_uuid()::text),
530
- 'type', COALESCE(NULLIF(f.value->>'type',''), 'text'),
531
- 'label', COALESCE(NULLIF(f.value->>'label',''), 'Campo'),
532
- 'required', COALESCE((f.value->>'required')::boolean, false),
533
- 'placeholder', NULLIF(f.value->>'placeholder',''),
534
- 'col', 0,
535
- 'colSpan', 12,
536
- 'options', CASE
537
- WHEN jsonb_typeof(f.value->'options') = 'array' AND jsonb_array_length(f.value->'options') > 0
538
- THEN (SELECT jsonb_agg(jsonb_build_object(
539
- 'label', opt,
540
- 'value', lower(regexp_replace(btrim(opt), '\\s+', '_', 'g'))))
541
- FROM jsonb_array_elements_text(f.value->'options') opt)
542
- ELSE NULL END
543
- )) AS field
544
- FROM src, jsonb_array_elements(src.arr) WITH ORDINALITY AS f(value, ord)
545
- )
546
- SELECT COALESCE(jsonb_agg(field ORDER BY ord), '[]'::jsonb) INTO v_input FROM norm;
547
-
548
- -- REPLACE with \`fields\`; otherwise APPEND onto what's already there.
549
- IF p_payload ? 'fields' THEN
550
- v_merged := v_input;
551
- ELSE
552
- v_merged := v_existing || v_input;
553
- END IF;
554
-
555
- -- sequential rows across the final list
556
- SELECT COALESCE(jsonb_agg(jsonb_set(e.value, '{row}', to_jsonb((e.ord - 1))) ORDER BY e.ord), '[]'::jsonb)
557
- INTO v_fields
558
- FROM jsonb_array_elements(v_merged) WITH ORDINALITY AS e(value, ord);
559
-
560
- v_schema := jsonb_build_object('layout', jsonb_build_object('columns', 12), 'fields', v_fields);
561
-
562
- -- ── write + audit ────────────────────────────────────────────────────────
563
- IF v_is_update THEN
564
- UPDATE plg_forms_templates SET
565
- name = COALESCE(NULLIF(v_name,''), name),
566
- category = v_category,
567
- description = COALESCE(v_description, description),
568
- schema = v_schema,
569
- updated_by = p_actor_user_id,
570
- updated_at = now()
571
- WHERE id = v_template_id AND tenant_id = p_tenant_id AND is_deleted = false
572
- RETURNING id INTO v_final_id;
573
- ELSE
574
- INSERT INTO plg_forms_templates (tenant_id, name, category, description, schema, created_by, updated_by)
575
- VALUES (p_tenant_id, v_name, v_category, v_description, v_schema, p_actor_user_id, p_actor_user_id)
576
- RETURNING id INTO v_final_id;
577
- END IF;
578
-
579
- INSERT INTO audit_logs (tenant_id, user_id, action, entity_type, entity_id, metadata)
580
- VALUES (p_tenant_id, p_actor_user_id,
581
- CASE WHEN v_is_update THEN 'agent.updateFormTemplate' ELSE 'agent.createFormTemplate' END,
582
- 'form_template', v_final_id::text,
583
- jsonb_build_object('payload', p_payload));
584
-
585
- RETURN jsonb_build_object(
586
- 'ok', true,
587
- 'id', v_final_id,
588
- 'record', jsonb_build_object(
589
- 'ref', jsonb_build_object('id', v_final_id, 'resource', 'plg_forms_templates',
590
- 'archetype', 'custom_forms:template'),
591
- 'name', COALESCE(NULLIF(v_name,''), (SELECT name FROM plg_forms_templates WHERE id = v_final_id)),
592
- 'category', v_category,
593
- 'fieldCount', jsonb_array_length(v_fields)
594
- )
595
- );
596
- END;
597
- $$;
598
-
599
- REVOKE ALL ON FUNCTION public.agent_forms_upsert_template(uuid, uuid, jsonb) FROM public;
600
- REVOKE EXECUTE ON FUNCTION public.agent_forms_upsert_template(uuid, uuid, jsonb) FROM anon;
601
- GRANT EXECUTE ON FUNCTION public.agent_forms_upsert_template(uuid, uuid, jsonb)
602
- TO authenticated, service_role;
603
- `
604
-
605
- export const MIGRATIONS: Array<{ id: string; sql: string }> = [
606
- { id: "000_plg_rename", sql: MIGRATION_000_PLG_RENAME },
607
- { id: "001_frm_base", sql: MIGRATION_001_FRM_BASE },
608
- { id: "002_document_archetype", sql: MIGRATION_002_DOCUMENT_ARCHETYPE },
609
- { id: "003_agent_rpcs", sql: MIGRATION_003_AGENT_RPCS },
610
- ]