@fayz-ai/plugin-forms 0.1.5 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/dist/config.d.ts +4 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/data/supabase.d.ts.map +1 -1
- package/dist/data/tables.d.ts +7 -0
- package/dist/data/tables.d.ts.map +1 -0
- package/dist/index.cjs +49 -32
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +52 -35
- package/dist/index.js.map +1 -1
- package/dist/migrations/index.d.ts +8 -0
- package/dist/migrations/index.d.ts.map +1 -0
- package/dist/types.d.ts +1 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/views/CustomFormsSettingsTab.d.ts +1 -3
- package/dist/views/CustomFormsSettingsTab.d.ts.map +1 -1
- package/package.json +10 -6
- package/src/config.ts +5 -1
- package/src/data/supabase.ts +20 -19
- package/src/data/tables.ts +7 -0
- package/src/index.ts +22 -4
- package/src/migrations/000_plg_rename.sql +21 -0
- package/src/migrations/001_frm_base.sql +92 -56
- package/src/migrations/002_document_archetype.sql +130 -42
- package/src/migrations/index.ts +433 -0
- package/src/types.ts +4 -4
- package/src/views/CustomFormsSettingsTab.tsx +2 -19
|
@@ -0,0 +1,433 @@
|
|
|
1
|
+
// AUTO-GENERATED from 000_plg_rename.sql, 001_frm_base.sql, 002_document_archetype.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 MIGRATIONS: Array<{ id: string; sql: string }> = [
|
|
430
|
+
{ id: "000_plg_rename", sql: MIGRATION_000_PLG_RENAME },
|
|
431
|
+
{ id: "001_frm_base", sql: MIGRATION_001_FRM_BASE },
|
|
432
|
+
{ id: "002_document_archetype", sql: MIGRATION_002_DOCUMENT_ARCHETYPE },
|
|
433
|
+
]
|
package/src/types.ts
CHANGED
|
@@ -46,7 +46,7 @@ export interface FormSchema {
|
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
// ============================================================
|
|
49
|
-
// TEMPLATE (maps to
|
|
49
|
+
// TEMPLATE (maps to plg_forms_templates)
|
|
50
50
|
// ============================================================
|
|
51
51
|
|
|
52
52
|
export type TemplateCategory =
|
|
@@ -78,7 +78,7 @@ export interface FormTemplate {
|
|
|
78
78
|
}
|
|
79
79
|
|
|
80
80
|
// ============================================================
|
|
81
|
-
// DOCUMENT (maps to
|
|
81
|
+
// DOCUMENT (maps to plg_forms_documents)
|
|
82
82
|
// ============================================================
|
|
83
83
|
|
|
84
84
|
export type DocumentStatus = 'draft' | 'completed' | 'signed' | 'archived'
|
|
@@ -103,7 +103,7 @@ export interface FormDocument {
|
|
|
103
103
|
updatedBy?: string
|
|
104
104
|
createdAt: string
|
|
105
105
|
updatedAt: string
|
|
106
|
-
/** Document kind from
|
|
106
|
+
/** Document kind from public.documents */
|
|
107
107
|
kind?: string
|
|
108
108
|
/** File URL for image/attachment documents */
|
|
109
109
|
fileUrl?: string
|
|
@@ -113,7 +113,7 @@ export interface FormDocument {
|
|
|
113
113
|
}
|
|
114
114
|
|
|
115
115
|
// ============================================================
|
|
116
|
-
// DOCUMENT FILE (maps to
|
|
116
|
+
// DOCUMENT FILE (maps to plg_forms_document_files)
|
|
117
117
|
// ============================================================
|
|
118
118
|
|
|
119
119
|
export interface DocumentFile {
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import React, { useState, useCallback } from 'react'
|
|
2
2
|
import { TemplateListView } from './TemplateListView'
|
|
3
|
-
import { useTranslation } from '@fayz-ai/core'
|
|
4
|
-
import type { PluginRegistryDef } from '@fayz-ai/core'
|
|
5
3
|
import type { CustomFormsConfig } from '../config'
|
|
6
4
|
import type { CustomFormsDataProvider } from '../data/types'
|
|
7
5
|
import type { CustomFormsStore } from '../store'
|
|
@@ -11,11 +9,9 @@ interface CustomFormsSettingsTabProps {
|
|
|
11
9
|
config: CustomFormsConfig
|
|
12
10
|
provider: CustomFormsDataProvider
|
|
13
11
|
store: CustomFormsStore
|
|
14
|
-
registries?: PluginRegistryDef[]
|
|
15
12
|
}
|
|
16
13
|
|
|
17
|
-
export function CustomFormsSettingsTab({ config, provider, store
|
|
18
|
-
const t = useTranslation()
|
|
14
|
+
export function CustomFormsSettingsTab({ config, provider, store }: CustomFormsSettingsTabProps) {
|
|
19
15
|
const [builderTemplateId, setBuilderTemplateId] = useState<string | null>(null)
|
|
20
16
|
|
|
21
17
|
const handleNew = useCallback(() => {
|
|
@@ -46,8 +42,7 @@ export function CustomFormsSettingsTab({ config, provider, store, registries }:
|
|
|
46
42
|
checkHash()
|
|
47
43
|
}, [])
|
|
48
44
|
|
|
49
|
-
|
|
50
|
-
const templatesContent = builderTemplateId ? (
|
|
45
|
+
return builderTemplateId ? (
|
|
51
46
|
<BuilderWrapper
|
|
52
47
|
templateId={builderTemplateId === '__new__' ? undefined : builderTemplateId}
|
|
53
48
|
store={store}
|
|
@@ -63,18 +58,6 @@ export function CustomFormsSettingsTab({ config, provider, store, registries }:
|
|
|
63
58
|
onNew={handleNew}
|
|
64
59
|
/>
|
|
65
60
|
)
|
|
66
|
-
|
|
67
|
-
return (
|
|
68
|
-
<div style={{ padding: '24px' }}>
|
|
69
|
-
<div style={{ marginBottom: '16px' }}>
|
|
70
|
-
<h1 style={{ fontSize: '20px', fontWeight: 600, margin: 0 }}>{t('customForms.settingsTitle')}</h1>
|
|
71
|
-
<p style={{ color: 'var(--muted-foreground, #6b7280)', margin: '4px 0 0', fontSize: '14px' }}>
|
|
72
|
-
{t('customForms.settingsSubtitle')}
|
|
73
|
-
</p>
|
|
74
|
-
</div>
|
|
75
|
-
{templatesContent}
|
|
76
|
-
</div>
|
|
77
|
-
)
|
|
78
61
|
}
|
|
79
62
|
|
|
80
63
|
/** Lazy-loads the FormBuilder to avoid pulling @dnd-kit into the initial bundle */
|