@fayz-ai/plugin-forms 0.8.0 → 0.9.0-next.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/dist/{FormBuilder-KDANEL6Z.js → FormBuilder-7SWZQKFN.js} +130 -23
- package/dist/FormBuilder-7SWZQKFN.js.map +1 -0
- package/dist/agent-tools.d.ts +4 -0
- package/dist/agent-tools.d.ts.map +1 -0
- package/dist/chunk-W2ZNJGQC.js +438 -0
- package/dist/chunk-W2ZNJGQC.js.map +1 -0
- package/dist/components/DocumentFormDialog.d.ts +3 -1
- package/dist/components/DocumentFormDialog.d.ts.map +1 -1
- package/dist/components/DocumentList.d.ts.map +1 -1
- package/dist/components/FieldConfigDialog.d.ts.map +1 -1
- package/dist/components/FormBuilder.d.ts.map +1 -1
- package/dist/components/FormViewer.d.ts.map +1 -1
- package/dist/components/PersonDocumentsWidget.d.ts.map +1 -1
- package/dist/data/supabase.d.ts.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +142 -54
- package/dist/index.js.map +1 -1
- package/dist/lib/person-fields.d.ts +16 -0
- package/dist/lib/person-fields.d.ts.map +1 -0
- package/dist/lib/print.d.ts +17 -0
- package/dist/lib/print.d.ts.map +1 -0
- package/dist/lib/tenant.d.ts.map +1 -1
- package/dist/locales/en.d.ts.map +1 -1
- package/dist/locales/pt-BR.d.ts.map +1 -1
- package/dist/migrations/index.d.ts +1 -0
- package/dist/migrations/index.d.ts.map +1 -1
- package/dist/types.d.ts +14 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/views/CustomFormsSettingsTab.d.ts.map +1 -1
- package/dist/views/TemplateListView.d.ts.map +1 -1
- package/package.json +13 -11
- package/src/agent-tools.ts +73 -0
- package/src/components/DocumentFormDialog.tsx +37 -16
- package/src/components/DocumentList.tsx +12 -8
- package/src/components/FieldConfigDialog.tsx +40 -0
- package/src/components/FormBuilder.tsx +104 -22
- package/src/components/FormViewer.tsx +11 -1
- package/src/components/PersonDocumentsWidget.tsx +10 -1
- package/src/data/supabase.ts +2 -0
- package/src/index.ts +21 -0
- package/src/lib/person-fields.ts +67 -0
- package/src/lib/print.ts +207 -0
- package/src/lib/tenant.ts +6 -2
- package/src/locales/en.ts +20 -0
- package/src/locales/pt-BR.ts +20 -0
- package/src/migrations/003_agent_rpcs.sql +174 -0
- package/src/migrations/index.ts +178 -1
- package/src/types.ts +14 -0
- package/src/views/CustomFormsSettingsTab.tsx +23 -16
- package/src/views/TemplateListView.tsx +29 -18
- package/dist/FormBuilder-4VKYVZAC.cjs +0 -619
- package/dist/FormBuilder-4VKYVZAC.cjs.map +0 -1
- package/dist/FormBuilder-KDANEL6Z.js.map +0 -1
- package/dist/chunk-32I43T37.js +0 -198
- package/dist/chunk-32I43T37.js.map +0 -1
- package/dist/chunk-XLUULIVL.cjs +0 -200
- package/dist/chunk-XLUULIVL.cjs.map +0 -1
- package/dist/index.cjs +0 -1629
- package/dist/index.cjs.map +0 -1
package/dist/index.cjs
DELETED
|
@@ -1,1629 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
var chunkXLUULIVL_cjs = require('./chunk-XLUULIVL.cjs');
|
|
4
|
-
var React2 = require('react');
|
|
5
|
-
var core = require('@fayz-ai/core');
|
|
6
|
-
var saas = require('@fayz-ai/saas');
|
|
7
|
-
var zustand = require('zustand');
|
|
8
|
-
var lucideReact = require('lucide-react');
|
|
9
|
-
var ui = require('@fayz-ai/ui');
|
|
10
|
-
var jsxRuntime = require('react/jsx-runtime');
|
|
11
|
-
|
|
12
|
-
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
13
|
-
|
|
14
|
-
var React2__default = /*#__PURE__*/_interopDefault(React2);
|
|
15
|
-
|
|
16
|
-
// src/data/tables.ts
|
|
17
|
-
var T = {
|
|
18
|
-
documents: "plg_forms_documents",
|
|
19
|
-
templates: "plg_forms_templates",
|
|
20
|
-
documentFiles: "plg_forms_document_files",
|
|
21
|
-
categories: "plg_forms_categories"
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
// src/data/mock.ts
|
|
25
|
-
function uuid() {
|
|
26
|
-
return crypto.randomUUID();
|
|
27
|
-
}
|
|
28
|
-
function now() {
|
|
29
|
-
return (/* @__PURE__ */ new Date()).toISOString();
|
|
30
|
-
}
|
|
31
|
-
function createMockFormsProvider() {
|
|
32
|
-
const templates = [];
|
|
33
|
-
const documents = [];
|
|
34
|
-
const files = [];
|
|
35
|
-
return {
|
|
36
|
-
// ── Templates ──────────────────────────────────────────
|
|
37
|
-
async getTemplates(query) {
|
|
38
|
-
let filtered = templates.filter((t) => !t.isDeleted && t.isCurrent);
|
|
39
|
-
if (query.category) {
|
|
40
|
-
filtered = filtered.filter((t) => t.category === query.category);
|
|
41
|
-
}
|
|
42
|
-
if (query.search) {
|
|
43
|
-
const s = query.search.toLowerCase();
|
|
44
|
-
filtered = filtered.filter((t) => t.name.toLowerCase().includes(s));
|
|
45
|
-
}
|
|
46
|
-
const page = query.page ?? 1;
|
|
47
|
-
const pageSize = query.pageSize ?? 25;
|
|
48
|
-
const start = (page - 1) * pageSize;
|
|
49
|
-
return { data: filtered.slice(start, start + pageSize), total: filtered.length };
|
|
50
|
-
},
|
|
51
|
-
async getTemplateById(id) {
|
|
52
|
-
return templates.find((t) => t.id === id && !t.isDeleted) ?? null;
|
|
53
|
-
},
|
|
54
|
-
async createTemplate(input) {
|
|
55
|
-
const template = {
|
|
56
|
-
id: uuid(),
|
|
57
|
-
tenantId: "mock-tenant",
|
|
58
|
-
name: input.name,
|
|
59
|
-
description: input.description,
|
|
60
|
-
category: input.category,
|
|
61
|
-
version: 1,
|
|
62
|
-
isCurrent: true,
|
|
63
|
-
schema: input.schema,
|
|
64
|
-
specialty: input.specialty,
|
|
65
|
-
tags: input.tags ?? [],
|
|
66
|
-
metadata: input.metadata ?? {},
|
|
67
|
-
isActive: true,
|
|
68
|
-
isDeleted: false,
|
|
69
|
-
createdAt: now(),
|
|
70
|
-
updatedAt: now()
|
|
71
|
-
};
|
|
72
|
-
templates.push(template);
|
|
73
|
-
return template;
|
|
74
|
-
},
|
|
75
|
-
async updateTemplate(id, input) {
|
|
76
|
-
const idx = templates.findIndex((t) => t.id === id);
|
|
77
|
-
if (idx === -1) throw new Error("Template not found");
|
|
78
|
-
const existing = templates[idx];
|
|
79
|
-
const updated = {
|
|
80
|
-
...existing,
|
|
81
|
-
...input,
|
|
82
|
-
tags: input.tags ?? existing.tags,
|
|
83
|
-
metadata: input.metadata ?? existing.metadata,
|
|
84
|
-
schema: input.schema ?? existing.schema,
|
|
85
|
-
updatedAt: now()
|
|
86
|
-
};
|
|
87
|
-
templates[idx] = updated;
|
|
88
|
-
return updated;
|
|
89
|
-
},
|
|
90
|
-
async deleteTemplate(id) {
|
|
91
|
-
const idx = templates.findIndex((t) => t.id === id);
|
|
92
|
-
if (idx !== -1) templates[idx] = { ...templates[idx], isDeleted: true };
|
|
93
|
-
},
|
|
94
|
-
// ── Documents ──────────────────────────────────────────
|
|
95
|
-
async getDocuments(query) {
|
|
96
|
-
let filtered = documents.filter((d) => !d.isDeleted);
|
|
97
|
-
if (query.personId) {
|
|
98
|
-
filtered = filtered.filter((d) => d.personId === query.personId);
|
|
99
|
-
}
|
|
100
|
-
if (query.templateId) {
|
|
101
|
-
filtered = filtered.filter((d) => d.templateId === query.templateId);
|
|
102
|
-
}
|
|
103
|
-
if (query.status) {
|
|
104
|
-
const statuses = Array.isArray(query.status) ? query.status : [query.status];
|
|
105
|
-
filtered = filtered.filter((d) => statuses.includes(d.status));
|
|
106
|
-
}
|
|
107
|
-
if (query.search) {
|
|
108
|
-
const s = query.search.toLowerCase();
|
|
109
|
-
filtered = filtered.filter(
|
|
110
|
-
(d) => d.title?.toLowerCase().includes(s) || d.templateName?.toLowerCase().includes(s)
|
|
111
|
-
);
|
|
112
|
-
}
|
|
113
|
-
filtered.sort((a, b) => b.createdAt.localeCompare(a.createdAt));
|
|
114
|
-
const page = query.page ?? 1;
|
|
115
|
-
const pageSize = query.pageSize ?? 25;
|
|
116
|
-
const start = (page - 1) * pageSize;
|
|
117
|
-
return { data: filtered.slice(start, start + pageSize), total: filtered.length };
|
|
118
|
-
},
|
|
119
|
-
async getDocumentById(id) {
|
|
120
|
-
return documents.find((d) => d.id === id && !d.isDeleted) ?? null;
|
|
121
|
-
},
|
|
122
|
-
async createDocument(input) {
|
|
123
|
-
const template = templates.find((t) => t.id === input.templateId);
|
|
124
|
-
const doc = {
|
|
125
|
-
id: uuid(),
|
|
126
|
-
tenantId: "mock-tenant",
|
|
127
|
-
templateId: input.templateId ?? "",
|
|
128
|
-
templateName: template?.name,
|
|
129
|
-
templateCategory: template?.category,
|
|
130
|
-
personId: input.personId,
|
|
131
|
-
title: input.title,
|
|
132
|
-
data: input.data ?? {},
|
|
133
|
-
kind: input.kind ?? (input.templateId ? "form" : "attachment"),
|
|
134
|
-
status: input.status ?? "draft",
|
|
135
|
-
metadata: {},
|
|
136
|
-
isDeleted: false,
|
|
137
|
-
createdAt: now(),
|
|
138
|
-
updatedAt: now()
|
|
139
|
-
};
|
|
140
|
-
documents.push(doc);
|
|
141
|
-
return doc;
|
|
142
|
-
},
|
|
143
|
-
async updateDocument(id, input) {
|
|
144
|
-
const idx = documents.findIndex((d) => d.id === id);
|
|
145
|
-
if (idx === -1) throw new Error("Document not found");
|
|
146
|
-
const existing = documents[idx];
|
|
147
|
-
const updated = {
|
|
148
|
-
...existing,
|
|
149
|
-
...input,
|
|
150
|
-
data: input.data ?? existing.data,
|
|
151
|
-
updatedAt: now()
|
|
152
|
-
};
|
|
153
|
-
documents[idx] = updated;
|
|
154
|
-
return updated;
|
|
155
|
-
},
|
|
156
|
-
async deleteDocument(id) {
|
|
157
|
-
const idx = documents.findIndex((d) => d.id === id);
|
|
158
|
-
if (idx !== -1) documents[idx] = { ...documents[idx], isDeleted: true };
|
|
159
|
-
},
|
|
160
|
-
// ── Files ──────────────────────────────────────────────
|
|
161
|
-
async getDocumentFiles(documentId) {
|
|
162
|
-
return files.filter((f) => f.documentId === documentId);
|
|
163
|
-
},
|
|
164
|
-
async uploadFile(documentId, fieldKey, file) {
|
|
165
|
-
const docFile = {
|
|
166
|
-
id: uuid(),
|
|
167
|
-
tenantId: "mock-tenant",
|
|
168
|
-
documentId,
|
|
169
|
-
fieldKey,
|
|
170
|
-
fileUrl: URL.createObjectURL(file),
|
|
171
|
-
fileName: file.name,
|
|
172
|
-
fileSize: file.size,
|
|
173
|
-
mimeType: file.type,
|
|
174
|
-
sortOrder: files.filter((f) => f.documentId === documentId && f.fieldKey === fieldKey).length,
|
|
175
|
-
metadata: {},
|
|
176
|
-
createdAt: now()
|
|
177
|
-
};
|
|
178
|
-
files.push(docFile);
|
|
179
|
-
return docFile;
|
|
180
|
-
},
|
|
181
|
-
async deleteFile(fileId) {
|
|
182
|
-
const idx = files.findIndex((f) => f.id === fileId);
|
|
183
|
-
if (idx !== -1) files.splice(idx, 1);
|
|
184
|
-
}
|
|
185
|
-
};
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
// src/lib/tenant.ts
|
|
189
|
-
var currentTenantId;
|
|
190
|
-
function getFormsTenantId() {
|
|
191
|
-
return currentTenantId;
|
|
192
|
-
}
|
|
193
|
-
function getTenantId() {
|
|
194
|
-
return core.getActiveTenantId() ?? "";
|
|
195
|
-
}
|
|
196
|
-
function getClient() {
|
|
197
|
-
const supabase = core.getSupabaseClientOptional();
|
|
198
|
-
if (!supabase) throw new Error("Supabase not initialized");
|
|
199
|
-
return supabase;
|
|
200
|
-
}
|
|
201
|
-
function mapTemplate(r) {
|
|
202
|
-
return {
|
|
203
|
-
id: r.id,
|
|
204
|
-
tenantId: r.tenant_id,
|
|
205
|
-
name: r.name,
|
|
206
|
-
description: r.description,
|
|
207
|
-
category: r.category,
|
|
208
|
-
version: r.version,
|
|
209
|
-
isCurrent: r.is_current,
|
|
210
|
-
parentId: r.parent_id,
|
|
211
|
-
schema: r.schema,
|
|
212
|
-
specialty: r.specialty,
|
|
213
|
-
tags: r.tags ?? [],
|
|
214
|
-
metadata: r.metadata ?? {},
|
|
215
|
-
isActive: r.is_active,
|
|
216
|
-
isDeleted: r.is_deleted,
|
|
217
|
-
createdBy: r.created_by,
|
|
218
|
-
updatedBy: r.updated_by,
|
|
219
|
-
createdAt: r.created_at,
|
|
220
|
-
updatedAt: r.updated_at
|
|
221
|
-
};
|
|
222
|
-
}
|
|
223
|
-
function mapDocument(r) {
|
|
224
|
-
return {
|
|
225
|
-
id: r.id,
|
|
226
|
-
tenantId: r.tenant_id,
|
|
227
|
-
templateId: r.template_id ?? "",
|
|
228
|
-
templateName: r.template_name,
|
|
229
|
-
templateCategory: r.template_category,
|
|
230
|
-
personId: r.person_id,
|
|
231
|
-
personName: r.person_name,
|
|
232
|
-
title: r.title,
|
|
233
|
-
data: r.form_data ?? {},
|
|
234
|
-
status: r.status,
|
|
235
|
-
signedAt: r.signed_at,
|
|
236
|
-
signedBy: r.signed_by,
|
|
237
|
-
notes: r.notes,
|
|
238
|
-
metadata: r.metadata ?? {},
|
|
239
|
-
isDeleted: false,
|
|
240
|
-
createdBy: r.created_by,
|
|
241
|
-
updatedBy: r.updated_by,
|
|
242
|
-
createdAt: r.created_at,
|
|
243
|
-
updatedAt: r.updated_at,
|
|
244
|
-
// Extra fields from core document
|
|
245
|
-
kind: r.kind,
|
|
246
|
-
fileUrl: r.file_url,
|
|
247
|
-
fileName: r.file_name,
|
|
248
|
-
fileSize: r.file_size,
|
|
249
|
-
mimeType: r.mime_type
|
|
250
|
-
};
|
|
251
|
-
}
|
|
252
|
-
function mapFile(r) {
|
|
253
|
-
return {
|
|
254
|
-
id: r.id,
|
|
255
|
-
tenantId: r.tenant_id,
|
|
256
|
-
documentId: r.document_id,
|
|
257
|
-
fieldKey: r.field_key,
|
|
258
|
-
fileUrl: r.file_url,
|
|
259
|
-
fileName: r.file_name,
|
|
260
|
-
fileSize: r.file_size,
|
|
261
|
-
mimeType: r.mime_type,
|
|
262
|
-
sortOrder: r.sort_order,
|
|
263
|
-
metadata: r.metadata ?? {},
|
|
264
|
-
createdAt: r.created_at
|
|
265
|
-
};
|
|
266
|
-
}
|
|
267
|
-
function createSupabaseFormsProvider() {
|
|
268
|
-
return {
|
|
269
|
-
// ── Templates ──────────────────────────────────────────
|
|
270
|
-
async getTemplates(query) {
|
|
271
|
-
const db = getClient();
|
|
272
|
-
let qb = db.from(T.templates).select("*", { count: "exact" }).eq("tenant_id", getTenantId()).eq("is_current", true).eq("is_deleted", false);
|
|
273
|
-
if (query.category) qb = qb.eq("category", query.category);
|
|
274
|
-
if (query.search) qb = qb.ilike("name", `%${query.search}%`);
|
|
275
|
-
const page = query.page ?? 1;
|
|
276
|
-
const pageSize = query.pageSize ?? 25;
|
|
277
|
-
qb = qb.order("updated_at", { ascending: false }).range((page - 1) * pageSize, page * pageSize - 1);
|
|
278
|
-
const { data, count, error } = await qb;
|
|
279
|
-
if (error) throw new Error(error.message);
|
|
280
|
-
return { data: (data ?? []).map(mapTemplate), total: count ?? 0 };
|
|
281
|
-
},
|
|
282
|
-
async getTemplateById(id) {
|
|
283
|
-
const db = getClient();
|
|
284
|
-
const { data, error } = await db.from(T.templates).select("*").eq("id", id).eq("is_deleted", false).single();
|
|
285
|
-
if (error || !data) return null;
|
|
286
|
-
return mapTemplate(data);
|
|
287
|
-
},
|
|
288
|
-
async createTemplate(input) {
|
|
289
|
-
const db = getClient();
|
|
290
|
-
const tenantId = getTenantId();
|
|
291
|
-
const { data, error } = await db.from(T.templates).insert({
|
|
292
|
-
tenant_id: tenantId,
|
|
293
|
-
name: input.name,
|
|
294
|
-
description: input.description,
|
|
295
|
-
category: input.category,
|
|
296
|
-
schema: input.schema,
|
|
297
|
-
specialty: input.specialty,
|
|
298
|
-
tags: input.tags ?? [],
|
|
299
|
-
metadata: input.metadata ?? {}
|
|
300
|
-
}).select().single();
|
|
301
|
-
if (error) throw new Error(error.message);
|
|
302
|
-
return mapTemplate(data);
|
|
303
|
-
},
|
|
304
|
-
async updateTemplate(id, input) {
|
|
305
|
-
const db = getClient();
|
|
306
|
-
const tenantId = getTenantId();
|
|
307
|
-
const { count } = await db.from(T.documents).select("document_id", { count: "exact", head: true }).eq("template_id", id);
|
|
308
|
-
const hasDocuments = (count ?? 0) > 0;
|
|
309
|
-
if (hasDocuments && input.schema) {
|
|
310
|
-
const existing = await this.getTemplateById(id);
|
|
311
|
-
if (!existing) throw new Error("Template not found");
|
|
312
|
-
const { data: newRow, error: insertError } = await db.from(T.templates).insert({
|
|
313
|
-
tenant_id: tenantId,
|
|
314
|
-
name: input.name ?? existing.name,
|
|
315
|
-
description: input.description ?? existing.description,
|
|
316
|
-
category: input.category ?? existing.category,
|
|
317
|
-
schema: input.schema,
|
|
318
|
-
specialty: input.specialty ?? existing.specialty,
|
|
319
|
-
tags: input.tags ?? existing.tags,
|
|
320
|
-
metadata: input.metadata ?? existing.metadata,
|
|
321
|
-
is_active: input.isActive ?? existing.isActive,
|
|
322
|
-
version: existing.version + 1,
|
|
323
|
-
is_current: true,
|
|
324
|
-
parent_id: existing.parentId ?? existing.id
|
|
325
|
-
}).select().single();
|
|
326
|
-
if (insertError) throw new Error(insertError.message);
|
|
327
|
-
await db.from(T.templates).update({ is_current: false, updated_at: (/* @__PURE__ */ new Date()).toISOString() }).eq("id", id);
|
|
328
|
-
return mapTemplate(newRow);
|
|
329
|
-
}
|
|
330
|
-
const updates = { updated_at: (/* @__PURE__ */ new Date()).toISOString() };
|
|
331
|
-
if (input.name !== void 0) updates.name = input.name;
|
|
332
|
-
if (input.description !== void 0) updates.description = input.description;
|
|
333
|
-
if (input.category !== void 0) updates.category = input.category;
|
|
334
|
-
if (input.schema !== void 0) updates.schema = input.schema;
|
|
335
|
-
if (input.specialty !== void 0) updates.specialty = input.specialty;
|
|
336
|
-
if (input.tags !== void 0) updates.tags = input.tags;
|
|
337
|
-
if (input.metadata !== void 0) updates.metadata = input.metadata;
|
|
338
|
-
if (input.isActive !== void 0) updates.is_active = input.isActive;
|
|
339
|
-
const { data, error } = await db.from(T.templates).update(updates).eq("id", id).select().single();
|
|
340
|
-
if (error) throw new Error(error.message);
|
|
341
|
-
return mapTemplate(data);
|
|
342
|
-
},
|
|
343
|
-
async deleteTemplate(id) {
|
|
344
|
-
const db = getClient();
|
|
345
|
-
const { error } = await db.from(T.templates).update({ is_deleted: true, updated_at: (/* @__PURE__ */ new Date()).toISOString() }).eq("id", id);
|
|
346
|
-
if (error) throw new Error(error.message);
|
|
347
|
-
},
|
|
348
|
-
// ── Documents (public.documents + plg_forms_documents extension) ──
|
|
349
|
-
async getDocuments(query) {
|
|
350
|
-
const db = getClient();
|
|
351
|
-
let qb = db.from("v_documents").select("*", { count: "exact" }).eq("tenant_id", getTenantId()).eq("is_active", true);
|
|
352
|
-
if (query.personId) qb = qb.eq("person_id", query.personId);
|
|
353
|
-
if (query.templateId) qb = qb.eq("template_id", query.templateId);
|
|
354
|
-
if (query.status) {
|
|
355
|
-
const statuses = Array.isArray(query.status) ? query.status : [query.status];
|
|
356
|
-
qb = qb.in("status", statuses);
|
|
357
|
-
}
|
|
358
|
-
if (query.search) qb = qb.or(`title.ilike.%${query.search}%,template_name.ilike.%${query.search}%`);
|
|
359
|
-
const page = query.page ?? 1;
|
|
360
|
-
const pageSize = query.pageSize ?? 50;
|
|
361
|
-
qb = qb.order("created_at", { ascending: false }).range((page - 1) * pageSize, page * pageSize - 1);
|
|
362
|
-
const { data, count, error } = await qb;
|
|
363
|
-
if (error) throw new Error(error.message);
|
|
364
|
-
return { data: (data ?? []).map(mapDocument), total: count ?? 0 };
|
|
365
|
-
},
|
|
366
|
-
async getDocumentById(id) {
|
|
367
|
-
const db = getClient();
|
|
368
|
-
const { data, error } = await db.from("v_documents").select("*").eq("id", id).single();
|
|
369
|
-
if (error || !data) return null;
|
|
370
|
-
return mapDocument(data);
|
|
371
|
-
},
|
|
372
|
-
async createDocument(input) {
|
|
373
|
-
const tenantId = getTenantId();
|
|
374
|
-
const isForm = !!input.templateId;
|
|
375
|
-
const { data: coreDoc, error: coreError } = await getClient().from("documents").insert({
|
|
376
|
-
tenant_id: tenantId,
|
|
377
|
-
kind: input.kind ?? (isForm ? "form" : "attachment"),
|
|
378
|
-
person_id: input.personId,
|
|
379
|
-
title: input.title,
|
|
380
|
-
status: input.status ?? "draft",
|
|
381
|
-
file_url: input.fileUrl,
|
|
382
|
-
file_name: input.fileName,
|
|
383
|
-
file_size: input.fileSize,
|
|
384
|
-
mime_type: input.mimeType,
|
|
385
|
-
metadata: {}
|
|
386
|
-
}).select().single();
|
|
387
|
-
if (coreError) throw new Error(coreError.message);
|
|
388
|
-
if (isForm && input.templateId) {
|
|
389
|
-
const db = getClient();
|
|
390
|
-
const { error: extError } = await db.from(T.documents).insert({
|
|
391
|
-
document_id: coreDoc.id,
|
|
392
|
-
tenant_id: tenantId,
|
|
393
|
-
template_id: input.templateId,
|
|
394
|
-
data: input.data ?? {}
|
|
395
|
-
});
|
|
396
|
-
if (extError) {
|
|
397
|
-
await getClient().from("documents").delete().eq("id", coreDoc.id);
|
|
398
|
-
throw new Error(extError.message);
|
|
399
|
-
}
|
|
400
|
-
}
|
|
401
|
-
const doc = await this.getDocumentById(coreDoc.id);
|
|
402
|
-
return doc;
|
|
403
|
-
},
|
|
404
|
-
async updateDocument(id, input) {
|
|
405
|
-
const now2 = (/* @__PURE__ */ new Date()).toISOString();
|
|
406
|
-
const coreUpdates = { updated_at: now2 };
|
|
407
|
-
if (input.status !== void 0) coreUpdates.status = input.status;
|
|
408
|
-
if (input.title !== void 0) coreUpdates.title = input.title;
|
|
409
|
-
if (input.notes !== void 0) coreUpdates.notes = input.notes;
|
|
410
|
-
const { error: coreError } = await getClient().from("documents").update(coreUpdates).eq("id", id);
|
|
411
|
-
if (coreError) throw new Error(coreError.message);
|
|
412
|
-
if (input.data !== void 0) {
|
|
413
|
-
const db = getClient();
|
|
414
|
-
await db.from(T.documents).update({ data: input.data, updated_at: now2 }).eq("document_id", id);
|
|
415
|
-
}
|
|
416
|
-
const doc = await this.getDocumentById(id);
|
|
417
|
-
return doc;
|
|
418
|
-
},
|
|
419
|
-
async deleteDocument(id) {
|
|
420
|
-
const { error } = await getClient().from("documents").update({ is_active: false, updated_at: (/* @__PURE__ */ new Date()).toISOString() }).eq("id", id);
|
|
421
|
-
if (error) throw new Error(error.message);
|
|
422
|
-
},
|
|
423
|
-
// ── Files ──────────────────────────────────────────────
|
|
424
|
-
async getDocumentFiles(documentId) {
|
|
425
|
-
const db = getClient();
|
|
426
|
-
const { data, error } = await db.from(T.documentFiles).select("*").eq("document_id", documentId).order("sort_order");
|
|
427
|
-
if (error) throw new Error(error.message);
|
|
428
|
-
return (data ?? []).map(mapFile);
|
|
429
|
-
},
|
|
430
|
-
async uploadFile(documentId, fieldKey, file) {
|
|
431
|
-
const db = getClient();
|
|
432
|
-
const tenantId = getTenantId();
|
|
433
|
-
const ext = file.name.split(".").pop() ?? "bin";
|
|
434
|
-
const storagePath = `${tenantId}/forms/${documentId}/${fieldKey}/${crypto.randomUUID()}.${ext}`;
|
|
435
|
-
const { error: uploadError } = await db.storage.from("documents").upload(storagePath, file, { contentType: file.type });
|
|
436
|
-
if (uploadError) throw new Error(uploadError.message);
|
|
437
|
-
const { data: urlData } = db.storage.from("documents").getPublicUrl(storagePath);
|
|
438
|
-
const { data, error } = await db.from(T.documentFiles).insert({
|
|
439
|
-
tenant_id: tenantId,
|
|
440
|
-
document_id: documentId,
|
|
441
|
-
field_key: fieldKey,
|
|
442
|
-
file_url: urlData.publicUrl,
|
|
443
|
-
file_name: file.name,
|
|
444
|
-
file_size: file.size,
|
|
445
|
-
mime_type: file.type
|
|
446
|
-
}).select().single();
|
|
447
|
-
if (error) throw new Error(error.message);
|
|
448
|
-
return mapFile(data);
|
|
449
|
-
},
|
|
450
|
-
async deleteFile(fileId) {
|
|
451
|
-
const db = getClient();
|
|
452
|
-
const { error } = await db.from(T.documentFiles).delete().eq("id", fileId);
|
|
453
|
-
if (error) throw new Error(error.message);
|
|
454
|
-
}
|
|
455
|
-
};
|
|
456
|
-
}
|
|
457
|
-
function createCustomFormsStore(provider) {
|
|
458
|
-
return zustand.create((set) => ({
|
|
459
|
-
templates: [],
|
|
460
|
-
templatesTotal: 0,
|
|
461
|
-
templatesLoading: false,
|
|
462
|
-
templatesError: null,
|
|
463
|
-
documents: [],
|
|
464
|
-
documentsTotal: 0,
|
|
465
|
-
documentsLoading: false,
|
|
466
|
-
documentsError: null,
|
|
467
|
-
activeTemplate: null,
|
|
468
|
-
activeTemplateLoading: false,
|
|
469
|
-
activeDocument: null,
|
|
470
|
-
activeDocumentLoading: false,
|
|
471
|
-
async fetchTemplates(query) {
|
|
472
|
-
set({ templatesLoading: true, templatesError: null });
|
|
473
|
-
try {
|
|
474
|
-
const result = await provider.getTemplates(query ?? {});
|
|
475
|
-
set({ templates: result.data, templatesTotal: result.total });
|
|
476
|
-
} catch (e) {
|
|
477
|
-
set({ templatesError: e.message ?? "Failed to fetch templates" });
|
|
478
|
-
} finally {
|
|
479
|
-
set({ templatesLoading: false });
|
|
480
|
-
}
|
|
481
|
-
},
|
|
482
|
-
async fetchTemplateById(id) {
|
|
483
|
-
set({ activeTemplateLoading: true });
|
|
484
|
-
try {
|
|
485
|
-
const template = await provider.getTemplateById(id);
|
|
486
|
-
set({ activeTemplate: template });
|
|
487
|
-
return template;
|
|
488
|
-
} catch {
|
|
489
|
-
set({ activeTemplate: null });
|
|
490
|
-
return null;
|
|
491
|
-
} finally {
|
|
492
|
-
set({ activeTemplateLoading: false });
|
|
493
|
-
}
|
|
494
|
-
},
|
|
495
|
-
async createTemplate(input) {
|
|
496
|
-
const template = await provider.createTemplate(input);
|
|
497
|
-
set((s) => ({ templates: [template, ...s.templates], templatesTotal: s.templatesTotal + 1 }));
|
|
498
|
-
return template;
|
|
499
|
-
},
|
|
500
|
-
async updateTemplate(id, input) {
|
|
501
|
-
const template = await provider.updateTemplate(id, input);
|
|
502
|
-
set((s) => ({
|
|
503
|
-
templates: s.templates.map((t) => t.id === id ? template : t),
|
|
504
|
-
activeTemplate: s.activeTemplate?.id === id ? template : s.activeTemplate
|
|
505
|
-
}));
|
|
506
|
-
return template;
|
|
507
|
-
},
|
|
508
|
-
async deleteTemplate(id) {
|
|
509
|
-
await provider.deleteTemplate(id);
|
|
510
|
-
set((s) => ({
|
|
511
|
-
templates: s.templates.filter((t) => t.id !== id),
|
|
512
|
-
templatesTotal: s.templatesTotal - 1
|
|
513
|
-
}));
|
|
514
|
-
},
|
|
515
|
-
async fetchDocuments(query) {
|
|
516
|
-
set({ documentsLoading: true, documentsError: null });
|
|
517
|
-
try {
|
|
518
|
-
const result = await provider.getDocuments(query ?? {});
|
|
519
|
-
set({ documents: result.data, documentsTotal: result.total });
|
|
520
|
-
} catch (e) {
|
|
521
|
-
set({ documentsError: e.message ?? "Failed to fetch documents" });
|
|
522
|
-
} finally {
|
|
523
|
-
set({ documentsLoading: false });
|
|
524
|
-
}
|
|
525
|
-
},
|
|
526
|
-
async fetchDocumentById(id) {
|
|
527
|
-
set({ activeDocumentLoading: true });
|
|
528
|
-
try {
|
|
529
|
-
const doc = await provider.getDocumentById(id);
|
|
530
|
-
set({ activeDocument: doc });
|
|
531
|
-
return doc;
|
|
532
|
-
} catch {
|
|
533
|
-
set({ activeDocument: null });
|
|
534
|
-
return null;
|
|
535
|
-
} finally {
|
|
536
|
-
set({ activeDocumentLoading: false });
|
|
537
|
-
}
|
|
538
|
-
},
|
|
539
|
-
async createDocument(input) {
|
|
540
|
-
const doc = await provider.createDocument(input);
|
|
541
|
-
set((s) => ({ documents: [doc, ...s.documents], documentsTotal: s.documentsTotal + 1 }));
|
|
542
|
-
return doc;
|
|
543
|
-
},
|
|
544
|
-
async updateDocument(id, input) {
|
|
545
|
-
const doc = await provider.updateDocument(id, input);
|
|
546
|
-
set((s) => ({
|
|
547
|
-
documents: s.documents.map((d) => d.id === id ? doc : d),
|
|
548
|
-
activeDocument: s.activeDocument?.id === id ? doc : s.activeDocument
|
|
549
|
-
}));
|
|
550
|
-
return doc;
|
|
551
|
-
},
|
|
552
|
-
async deleteDocument(id) {
|
|
553
|
-
await provider.deleteDocument(id);
|
|
554
|
-
set((s) => ({
|
|
555
|
-
documents: s.documents.filter((d) => d.id !== id),
|
|
556
|
-
documentsTotal: s.documentsTotal - 1
|
|
557
|
-
}));
|
|
558
|
-
}
|
|
559
|
-
}));
|
|
560
|
-
}
|
|
561
|
-
|
|
562
|
-
// src/locales/en.ts
|
|
563
|
-
var en = {
|
|
564
|
-
// Settings
|
|
565
|
-
"customForms.settingsTitle": "Forms & Documents",
|
|
566
|
-
"customForms.settingsSubtitle": "Create and manage custom forms for your business",
|
|
567
|
-
// Templates
|
|
568
|
-
"customForms.templates": "Templates",
|
|
569
|
-
"customForms.newTemplate": "New Template",
|
|
570
|
-
"customForms.editTemplate": "Edit Template",
|
|
571
|
-
"customForms.templateName": "Form name",
|
|
572
|
-
"customForms.templateDescription": "Description",
|
|
573
|
-
"customForms.templateCategory": "Category",
|
|
574
|
-
"customForms.templateSpecialty": "Specialty",
|
|
575
|
-
"customForms.noTemplates": "No form templates yet",
|
|
576
|
-
"customForms.noTemplatesDescription": "Create your first custom form to start collecting structured data.",
|
|
577
|
-
"customForms.deleteTemplateConfirm": "Delete this form template? Existing documents will be preserved.",
|
|
578
|
-
// Categories
|
|
579
|
-
"customForms.category.anamnesis": "Anamnesis",
|
|
580
|
-
"customForms.category.evolution": "Evolution Note",
|
|
581
|
-
"customForms.category.report": "Report",
|
|
582
|
-
"customForms.category.contract": "Contract",
|
|
583
|
-
"customForms.category.general": "General",
|
|
584
|
-
// Builder
|
|
585
|
-
"customForms.builder.fields": "Fields",
|
|
586
|
-
"customForms.builder.fieldCount": "{{count}} fields",
|
|
587
|
-
"customForms.builder.settings": "Settings",
|
|
588
|
-
"customForms.builder.save": "Save",
|
|
589
|
-
"customForms.builder.saving": "Saving...",
|
|
590
|
-
"customForms.builder.saved": "Form saved",
|
|
591
|
-
"customForms.builder.emptyCanvas": "Drag fields from the left panel to start building your form",
|
|
592
|
-
"customForms.builder.fieldLabel": "Field label",
|
|
593
|
-
"customForms.builder.fieldPlaceholder": "Placeholder",
|
|
594
|
-
"customForms.builder.fieldRequired": "Required",
|
|
595
|
-
"customForms.builder.fieldOptions": "Options",
|
|
596
|
-
"customForms.builder.addOption": "Add option",
|
|
597
|
-
"customForms.builder.colSpan": "Column span",
|
|
598
|
-
"customForms.builder.deleteField": "Remove field",
|
|
599
|
-
// Field types
|
|
600
|
-
"customForms.fieldType.title": "Title",
|
|
601
|
-
"customForms.fieldType.text": "Simple Text",
|
|
602
|
-
"customForms.fieldType.memo": "Memo",
|
|
603
|
-
"customForms.fieldType.richtext": "Rich Text",
|
|
604
|
-
"customForms.fieldType.date": "Date",
|
|
605
|
-
"customForms.fieldType.select": "Select",
|
|
606
|
-
"customForms.fieldType.radio": "Radio",
|
|
607
|
-
"customForms.fieldType.tags": "Tags",
|
|
608
|
-
"customForms.fieldType.checkbox": "Checkbox",
|
|
609
|
-
"customForms.fieldType.image": "Image / Drawing",
|
|
610
|
-
"customForms.fieldType.gallery": "Gallery",
|
|
611
|
-
"customForms.fieldType.budget": "Budget",
|
|
612
|
-
// Documents
|
|
613
|
-
"customForms.documents": "Documents",
|
|
614
|
-
"customForms.addDocument": "Add Document",
|
|
615
|
-
"customForms.selectTemplate": "Select a form",
|
|
616
|
-
"customForms.noDocuments": "No documents yet",
|
|
617
|
-
"customForms.noDocumentsDescription": "Add a document to start recording information.",
|
|
618
|
-
"customForms.documentTitle": "Document title",
|
|
619
|
-
"customForms.saveAsDraft": "Save as Draft",
|
|
620
|
-
"customForms.saveAndComplete": "Save & Complete",
|
|
621
|
-
"customForms.deleteDocumentConfirm": "Delete this document?",
|
|
622
|
-
// Registry
|
|
623
|
-
"registry.form-categories": "Categories",
|
|
624
|
-
"registry.form-categories.description": "Categories for organizing form templates",
|
|
625
|
-
// Status
|
|
626
|
-
"customForms.status.draft": "Draft",
|
|
627
|
-
"customForms.status.completed": "Completed",
|
|
628
|
-
"customForms.status.signed": "Signed",
|
|
629
|
-
"customForms.status.archived": "Archived"
|
|
630
|
-
};
|
|
631
|
-
|
|
632
|
-
// src/locales/pt-BR.ts
|
|
633
|
-
var ptBR = {
|
|
634
|
-
// Settings
|
|
635
|
-
"customForms.settingsTitle": "Formul\xE1rios e Documentos",
|
|
636
|
-
"customForms.settingsSubtitle": "Crie e gerencie formul\xE1rios personalizados para o seu neg\xF3cio",
|
|
637
|
-
// Templates
|
|
638
|
-
"customForms.templates": "Modelos",
|
|
639
|
-
"customForms.newTemplate": "Novo Modelo",
|
|
640
|
-
"customForms.editTemplate": "Editar Modelo",
|
|
641
|
-
"customForms.templateName": "Nome do formul\xE1rio",
|
|
642
|
-
"customForms.templateDescription": "Descri\xE7\xE3o",
|
|
643
|
-
"customForms.templateCategory": "Categoria",
|
|
644
|
-
"customForms.templateSpecialty": "Especialidade",
|
|
645
|
-
"customForms.noTemplates": "Nenhum modelo criado",
|
|
646
|
-
"customForms.noTemplatesDescription": "Crie seu primeiro formul\xE1rio personalizado para come\xE7ar a coletar dados estruturados.",
|
|
647
|
-
"customForms.deleteTemplateConfirm": "Excluir este modelo de formul\xE1rio? Documentos existentes ser\xE3o preservados.",
|
|
648
|
-
// Categories
|
|
649
|
-
"customForms.category.anamnesis": "Anamnese",
|
|
650
|
-
"customForms.category.evolution": "Evolu\xE7\xE3o",
|
|
651
|
-
"customForms.category.report": "Laudo",
|
|
652
|
-
"customForms.category.contract": "Contrato",
|
|
653
|
-
"customForms.category.general": "Geral",
|
|
654
|
-
// Builder
|
|
655
|
-
"customForms.builder.fields": "Campos",
|
|
656
|
-
"customForms.builder.fieldCount": "{{count}} campos",
|
|
657
|
-
"customForms.builder.settings": "Configura\xE7\xF5es",
|
|
658
|
-
"customForms.builder.save": "Salvar",
|
|
659
|
-
"customForms.builder.saving": "Salvando...",
|
|
660
|
-
"customForms.builder.saved": "Formul\xE1rio salvo",
|
|
661
|
-
"customForms.builder.emptyCanvas": "Arraste os campos do painel esquerdo para come\xE7ar a montar seu formul\xE1rio",
|
|
662
|
-
"customForms.builder.fieldLabel": "R\xF3tulo do campo",
|
|
663
|
-
"customForms.builder.fieldPlaceholder": "Placeholder",
|
|
664
|
-
"customForms.builder.fieldRequired": "Obrigat\xF3rio",
|
|
665
|
-
"customForms.builder.fieldOptions": "Op\xE7\xF5es",
|
|
666
|
-
"customForms.builder.addOption": "Adicionar op\xE7\xE3o",
|
|
667
|
-
"customForms.builder.colSpan": "Largura em colunas",
|
|
668
|
-
"customForms.builder.deleteField": "Remover campo",
|
|
669
|
-
// Field types
|
|
670
|
-
"customForms.fieldType.title": "T\xEDtulo",
|
|
671
|
-
"customForms.fieldType.text": "Texto Simples",
|
|
672
|
-
"customForms.fieldType.memo": "Memorando",
|
|
673
|
-
"customForms.fieldType.richtext": "Texto Rico",
|
|
674
|
-
"customForms.fieldType.date": "Data",
|
|
675
|
-
"customForms.fieldType.select": "Sele\xE7\xE3o",
|
|
676
|
-
"customForms.fieldType.radio": "Radio",
|
|
677
|
-
"customForms.fieldType.tags": "Tags",
|
|
678
|
-
"customForms.fieldType.checkbox": "Checkbox",
|
|
679
|
-
"customForms.fieldType.image": "Imagem / Desenho",
|
|
680
|
-
"customForms.fieldType.gallery": "Galeria",
|
|
681
|
-
"customForms.fieldType.budget": "Or\xE7amento",
|
|
682
|
-
// Documents
|
|
683
|
-
"customForms.documents": "Documentos",
|
|
684
|
-
"customForms.addDocument": "Novo Documento",
|
|
685
|
-
"customForms.selectTemplate": "Selecione um formul\xE1rio",
|
|
686
|
-
"customForms.noDocuments": "Nenhum documento",
|
|
687
|
-
"customForms.noDocumentsDescription": "Adicione um documento para come\xE7ar a registrar informa\xE7\xF5es.",
|
|
688
|
-
"customForms.documentTitle": "T\xEDtulo do documento",
|
|
689
|
-
"customForms.saveAsDraft": "Salvar Rascunho",
|
|
690
|
-
"customForms.saveAndComplete": "Salvar e Finalizar",
|
|
691
|
-
"customForms.deleteDocumentConfirm": "Excluir este documento?",
|
|
692
|
-
// Registry
|
|
693
|
-
"registry.form-categories": "Categorias",
|
|
694
|
-
"registry.form-categories.description": "Categorias para organizar modelos de formul\xE1rios",
|
|
695
|
-
// Status
|
|
696
|
-
"customForms.status.draft": "Rascunho",
|
|
697
|
-
"customForms.status.completed": "Finalizado",
|
|
698
|
-
"customForms.status.signed": "Assinado",
|
|
699
|
-
"customForms.status.archived": "Arquivado"
|
|
700
|
-
};
|
|
701
|
-
|
|
702
|
-
// src/locales/index.ts
|
|
703
|
-
var customFormsLocales = {
|
|
704
|
-
en,
|
|
705
|
-
"pt-BR": ptBR
|
|
706
|
-
};
|
|
707
|
-
|
|
708
|
-
// src/config.ts
|
|
709
|
-
var DEFAULT_LABELS = {
|
|
710
|
-
pageTitle: "Custom Forms",
|
|
711
|
-
settingsLabel: "Forms & Documents",
|
|
712
|
-
settingsSubtitle: "Create and manage custom forms for your business",
|
|
713
|
-
templates: "Templates",
|
|
714
|
-
documents: "Documents",
|
|
715
|
-
newTemplate: "New Template",
|
|
716
|
-
addDocument: "Add Document"
|
|
717
|
-
};
|
|
718
|
-
function resolveConfig(options) {
|
|
719
|
-
return {
|
|
720
|
-
labels: { ...DEFAULT_LABELS, ...options?.labels }
|
|
721
|
-
};
|
|
722
|
-
}
|
|
723
|
-
var CATEGORY_COLORS = {
|
|
724
|
-
anamnesis: "bg-info-soft text-info-soft-foreground",
|
|
725
|
-
evolution: "bg-success-soft text-success-soft-foreground",
|
|
726
|
-
report: "bg-magic-soft text-magic-soft-foreground",
|
|
727
|
-
contract: "bg-warning-soft text-warning-soft-foreground",
|
|
728
|
-
general: "bg-muted text-muted-foreground"
|
|
729
|
-
};
|
|
730
|
-
function TemplateListView({ store, config, onEdit, onNew }) {
|
|
731
|
-
const t = core.useTranslation();
|
|
732
|
-
const [search, setSearch] = React2.useState("");
|
|
733
|
-
const templates = store((s) => s.templates);
|
|
734
|
-
const loading = store((s) => s.templatesLoading);
|
|
735
|
-
const orgId = getFormsTenantId();
|
|
736
|
-
React2.useEffect(() => {
|
|
737
|
-
return;
|
|
738
|
-
}, [orgId, search]);
|
|
739
|
-
const handleDelete = React2.useCallback(async (template) => {
|
|
740
|
-
if (!window.confirm(t("customForms.deleteTemplateConfirm"))) return;
|
|
741
|
-
await store.getState().deleteTemplate(template.id);
|
|
742
|
-
}, [store, t]);
|
|
743
|
-
const categoryLabel = (cat) => t(`customForms.category.${cat}`);
|
|
744
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4", children: [
|
|
745
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
|
|
746
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative flex-1 max-w-xs", children: [
|
|
747
|
-
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Search, { className: "absolute left-2.5 top-1/2 -translate-y-1/2 h-3.5 w-3.5 text-muted-foreground" }),
|
|
748
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
749
|
-
ui.Input,
|
|
750
|
-
{
|
|
751
|
-
value: search,
|
|
752
|
-
onChange: (e) => setSearch(e.target.value),
|
|
753
|
-
placeholder: t("common.search"),
|
|
754
|
-
className: "h-9 pl-8 text-sm"
|
|
755
|
-
}
|
|
756
|
-
)
|
|
757
|
-
] }),
|
|
758
|
-
/* @__PURE__ */ jsxRuntime.jsxs(ui.Button, { onClick: onNew, size: "sm", className: "h-9", children: [
|
|
759
|
-
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, { className: "h-3.5 w-3.5 mr-1.5" }),
|
|
760
|
-
t("customForms.newTemplate")
|
|
761
|
-
] })
|
|
762
|
-
] }),
|
|
763
|
-
!loading && templates.length === 0 && /* @__PURE__ */ jsxRuntime.jsx(ui.Card, { children: /* @__PURE__ */ jsxRuntime.jsxs(ui.CardContent, { className: "flex flex-col items-center justify-center py-16 text-center", children: [
|
|
764
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-12 w-12 items-center justify-center rounded-full bg-muted mb-4", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.FileText, { className: "h-5 w-5 text-muted-foreground" }) }),
|
|
765
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "font-medium", children: t("customForms.noTemplates") }),
|
|
766
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm text-muted-foreground mt-1 max-w-sm", children: t("customForms.noTemplatesDescription") }),
|
|
767
|
-
/* @__PURE__ */ jsxRuntime.jsxs(ui.Button, { onClick: onNew, size: "sm", className: "mt-4", children: [
|
|
768
|
-
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, { className: "h-3.5 w-3.5 mr-1.5" }),
|
|
769
|
-
t("customForms.newTemplate")
|
|
770
|
-
] })
|
|
771
|
-
] }) }),
|
|
772
|
-
templates.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid gap-3 sm:grid-cols-2 lg:grid-cols-3", children: templates.map((template) => /* @__PURE__ */ jsxRuntime.jsx(
|
|
773
|
-
ui.Card,
|
|
774
|
-
{
|
|
775
|
-
className: "group cursor-pointer hover:shadow-md transition-shadow",
|
|
776
|
-
onClick: () => onEdit(template),
|
|
777
|
-
children: /* @__PURE__ */ jsxRuntime.jsxs(ui.CardContent, { className: "p-4", children: [
|
|
778
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between gap-2", children: [
|
|
779
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
780
|
-
/* @__PURE__ */ jsxRuntime.jsx("h3", { className: "font-medium text-sm truncate", children: template.name }),
|
|
781
|
-
template.description && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground mt-0.5 line-clamp-2", children: template.description })
|
|
782
|
-
] }),
|
|
783
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-1 opacity-0 group-hover:opacity-100 transition-opacity", children: [
|
|
784
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
785
|
-
"button",
|
|
786
|
-
{
|
|
787
|
-
onClick: (e) => {
|
|
788
|
-
e.stopPropagation();
|
|
789
|
-
onEdit(template);
|
|
790
|
-
},
|
|
791
|
-
className: "p-1.5 rounded-md hover:bg-muted",
|
|
792
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Pencil, { className: "h-3.5 w-3.5 text-muted-foreground" })
|
|
793
|
-
}
|
|
794
|
-
),
|
|
795
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
796
|
-
"button",
|
|
797
|
-
{
|
|
798
|
-
onClick: (e) => {
|
|
799
|
-
e.stopPropagation();
|
|
800
|
-
handleDelete(template);
|
|
801
|
-
},
|
|
802
|
-
className: "p-1.5 rounded-md hover:bg-destructive/10",
|
|
803
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash2, { className: "h-3.5 w-3.5 text-destructive" })
|
|
804
|
-
}
|
|
805
|
-
)
|
|
806
|
-
] })
|
|
807
|
-
] }),
|
|
808
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 mt-3", children: [
|
|
809
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { variant: "secondary", className: `text-[10px] ${CATEGORY_COLORS[template.category]}`, children: categoryLabel(template.category) }),
|
|
810
|
-
/* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-[10px] text-muted-foreground", children: [
|
|
811
|
-
template.schema.fields.length,
|
|
812
|
-
" ",
|
|
813
|
-
t("customForms.builder.fields").toLowerCase()
|
|
814
|
-
] }),
|
|
815
|
-
template.version > 1 && /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-[10px] text-muted-foreground", children: [
|
|
816
|
-
"v",
|
|
817
|
-
template.version
|
|
818
|
-
] })
|
|
819
|
-
] })
|
|
820
|
-
] })
|
|
821
|
-
},
|
|
822
|
-
template.id
|
|
823
|
-
)) }),
|
|
824
|
-
loading && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "grid gap-3 sm:grid-cols-2 lg:grid-cols-3", children: Array.from({ length: 3 }).map((_, i) => /* @__PURE__ */ jsxRuntime.jsx(ui.Card, { children: /* @__PURE__ */ jsxRuntime.jsxs(ui.CardContent, { className: "p-4 space-y-3", children: [
|
|
825
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-4 w-2/3 bg-muted animate-pulse rounded" }),
|
|
826
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-3 w-full bg-muted animate-pulse rounded" }),
|
|
827
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-2", children: [
|
|
828
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-5 w-16 bg-muted animate-pulse rounded-full" }),
|
|
829
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-5 w-12 bg-muted animate-pulse rounded-full" })
|
|
830
|
-
] })
|
|
831
|
-
] }) }, i)) })
|
|
832
|
-
] });
|
|
833
|
-
}
|
|
834
|
-
function CustomFormsSettingsTab({ config, provider, store }) {
|
|
835
|
-
const [builderTemplateId, setBuilderTemplateId] = React2.useState(null);
|
|
836
|
-
const handleNew = React2.useCallback(() => {
|
|
837
|
-
setBuilderTemplateId("__new__");
|
|
838
|
-
}, []);
|
|
839
|
-
const handleEdit = React2.useCallback((template) => {
|
|
840
|
-
setBuilderTemplateId(template.id);
|
|
841
|
-
}, []);
|
|
842
|
-
const handleBackFromBuilder = React2.useCallback(() => {
|
|
843
|
-
setBuilderTemplateId(null);
|
|
844
|
-
store.getState().fetchTemplates();
|
|
845
|
-
}, [store]);
|
|
846
|
-
React2__default.default.useEffect(() => {
|
|
847
|
-
function checkHash() {
|
|
848
|
-
const hash = window.location.hash.slice(1) || "/";
|
|
849
|
-
const builderMatch = hash.match(/\/settings\/custom_forms\/templates\/(.+)/);
|
|
850
|
-
if (builderMatch && builderMatch[1] !== "new") {
|
|
851
|
-
setBuilderTemplateId(builderMatch[1]);
|
|
852
|
-
} else if (builderMatch && builderMatch[1] === "new") {
|
|
853
|
-
setBuilderTemplateId("__new__");
|
|
854
|
-
}
|
|
855
|
-
}
|
|
856
|
-
checkHash();
|
|
857
|
-
}, []);
|
|
858
|
-
return builderTemplateId ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
859
|
-
BuilderWrapper,
|
|
860
|
-
{
|
|
861
|
-
templateId: builderTemplateId === "__new__" ? void 0 : builderTemplateId,
|
|
862
|
-
store,
|
|
863
|
-
provider,
|
|
864
|
-
config,
|
|
865
|
-
onBack: handleBackFromBuilder
|
|
866
|
-
}
|
|
867
|
-
) : /* @__PURE__ */ jsxRuntime.jsx(
|
|
868
|
-
TemplateListView,
|
|
869
|
-
{
|
|
870
|
-
store,
|
|
871
|
-
config,
|
|
872
|
-
onEdit: handleEdit,
|
|
873
|
-
onNew: handleNew
|
|
874
|
-
}
|
|
875
|
-
);
|
|
876
|
-
}
|
|
877
|
-
function BuilderWrapper({
|
|
878
|
-
templateId,
|
|
879
|
-
store,
|
|
880
|
-
provider,
|
|
881
|
-
config,
|
|
882
|
-
onBack
|
|
883
|
-
}) {
|
|
884
|
-
const FormBuilderLazy = React2__default.default.lazy(
|
|
885
|
-
() => import('./FormBuilder-4VKYVZAC.cjs').then((m) => ({ default: m.FormBuilder }))
|
|
886
|
-
);
|
|
887
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
888
|
-
React2__default.default.Suspense,
|
|
889
|
-
{
|
|
890
|
-
fallback: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center py-12", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-6 w-6 border-2 border-primary border-t-transparent animate-spin rounded-full" }) }),
|
|
891
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
892
|
-
FormBuilderLazy,
|
|
893
|
-
{
|
|
894
|
-
templateId,
|
|
895
|
-
store,
|
|
896
|
-
provider,
|
|
897
|
-
config,
|
|
898
|
-
onBack
|
|
899
|
-
}
|
|
900
|
-
)
|
|
901
|
-
}
|
|
902
|
-
);
|
|
903
|
-
}
|
|
904
|
-
var STATUS_COLORS = {
|
|
905
|
-
draft: "bg-warning-soft text-warning-soft-foreground",
|
|
906
|
-
completed: "bg-success-soft text-success-soft-foreground",
|
|
907
|
-
signed: "bg-info-soft text-info-soft-foreground",
|
|
908
|
-
archived: "bg-muted text-muted-foreground"
|
|
909
|
-
};
|
|
910
|
-
var KIND_ICONS = {
|
|
911
|
-
form: lucideReact.FileText,
|
|
912
|
-
image: lucideReact.Image,
|
|
913
|
-
attachment: lucideReact.Paperclip,
|
|
914
|
-
prescription: lucideReact.FileText,
|
|
915
|
-
contract: lucideReact.FileText
|
|
916
|
-
};
|
|
917
|
-
function formatDateLabel(dateStr) {
|
|
918
|
-
const date = new Date(dateStr);
|
|
919
|
-
const now2 = /* @__PURE__ */ new Date();
|
|
920
|
-
const today = new Date(now2.getFullYear(), now2.getMonth(), now2.getDate());
|
|
921
|
-
const docDate = new Date(date.getFullYear(), date.getMonth(), date.getDate());
|
|
922
|
-
const diffDays = Math.round((today.getTime() - docDate.getTime()) / (1e3 * 60 * 60 * 24));
|
|
923
|
-
if (diffDays === 0) return "Hoje";
|
|
924
|
-
if (diffDays === 1) return "Ontem";
|
|
925
|
-
if (diffDays < 7) return `${diffDays} dias atr\xE1s`;
|
|
926
|
-
return date.toLocaleDateString(void 0, { day: "numeric", month: "long", year: "numeric" });
|
|
927
|
-
}
|
|
928
|
-
function groupByDate(docs) {
|
|
929
|
-
const groups = /* @__PURE__ */ new Map();
|
|
930
|
-
for (const doc of docs) {
|
|
931
|
-
const dateKey = doc.createdAt.slice(0, 10);
|
|
932
|
-
if (!groups.has(dateKey)) groups.set(dateKey, []);
|
|
933
|
-
groups.get(dateKey).push(doc);
|
|
934
|
-
}
|
|
935
|
-
return Array.from(groups.entries()).sort(([a], [b]) => b.localeCompare(a)).map(([date, items]) => ({
|
|
936
|
-
date,
|
|
937
|
-
label: formatDateLabel(date),
|
|
938
|
-
items
|
|
939
|
-
}));
|
|
940
|
-
}
|
|
941
|
-
function DocumentList({ personId, provider, store, onView, onFileDrop }) {
|
|
942
|
-
const t = core.useTranslation();
|
|
943
|
-
const [documents, setDocuments] = React2.useState([]);
|
|
944
|
-
const [loading, setLoading] = React2.useState(true);
|
|
945
|
-
const [isDragOver, setIsDragOver] = React2.useState(false);
|
|
946
|
-
const dragCounter = React2.useRef(0);
|
|
947
|
-
const fetchDocs = React2.useCallback(async () => {
|
|
948
|
-
setLoading(true);
|
|
949
|
-
try {
|
|
950
|
-
const result = await provider.getDocuments({ personId });
|
|
951
|
-
setDocuments(result.data);
|
|
952
|
-
} finally {
|
|
953
|
-
setLoading(false);
|
|
954
|
-
}
|
|
955
|
-
}, [personId, provider]);
|
|
956
|
-
React2.useEffect(() => {
|
|
957
|
-
const timer = setTimeout(() => fetchDocs(), 50);
|
|
958
|
-
return () => clearTimeout(timer);
|
|
959
|
-
}, [fetchDocs]);
|
|
960
|
-
React2.useEffect(() => {
|
|
961
|
-
const unsub = store.subscribe(() => fetchDocs());
|
|
962
|
-
return unsub;
|
|
963
|
-
}, [store, fetchDocs]);
|
|
964
|
-
const handleDelete = React2.useCallback(async (doc) => {
|
|
965
|
-
if (!window.confirm(t("customForms.deleteDocumentConfirm"))) return;
|
|
966
|
-
await provider.deleteDocument(doc.id);
|
|
967
|
-
setDocuments((prev) => prev.filter((d) => d.id !== doc.id));
|
|
968
|
-
}, [provider, t]);
|
|
969
|
-
const handleDragEnter = React2.useCallback((e) => {
|
|
970
|
-
e.preventDefault();
|
|
971
|
-
dragCounter.current++;
|
|
972
|
-
setIsDragOver(true);
|
|
973
|
-
}, []);
|
|
974
|
-
const handleDragLeave = React2.useCallback((e) => {
|
|
975
|
-
e.preventDefault();
|
|
976
|
-
dragCounter.current--;
|
|
977
|
-
if (dragCounter.current === 0) setIsDragOver(false);
|
|
978
|
-
}, []);
|
|
979
|
-
const handleDragOver = React2.useCallback((e) => {
|
|
980
|
-
e.preventDefault();
|
|
981
|
-
}, []);
|
|
982
|
-
const handleDrop = React2.useCallback((e) => {
|
|
983
|
-
e.preventDefault();
|
|
984
|
-
dragCounter.current = 0;
|
|
985
|
-
setIsDragOver(false);
|
|
986
|
-
const files = Array.from(e.dataTransfer.files);
|
|
987
|
-
if (files.length > 0 && onFileDrop) {
|
|
988
|
-
onFileDrop(files);
|
|
989
|
-
}
|
|
990
|
-
}, [onFileDrop]);
|
|
991
|
-
const groups = groupByDate(documents);
|
|
992
|
-
if (loading) {
|
|
993
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-4 py-4", children: Array.from({ length: 3 }).map((_, i) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
|
|
994
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-9 w-9 rounded-lg bg-muted animate-pulse shrink-0" }),
|
|
995
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 space-y-1.5", children: [
|
|
996
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-3.5 w-2/3 bg-muted animate-pulse rounded" }),
|
|
997
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-2.5 w-1/3 bg-muted animate-pulse rounded" })
|
|
998
|
-
] })
|
|
999
|
-
] }, i)) });
|
|
1000
|
-
}
|
|
1001
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1002
|
-
"div",
|
|
1003
|
-
{
|
|
1004
|
-
className: `relative min-h-[200px] rounded-xl border-2 border-dashed transition-colors ${isDragOver ? "border-primary bg-primary/5" : documents.length === 0 ? "border-border" : "border-transparent"}`,
|
|
1005
|
-
onDragEnter: handleDragEnter,
|
|
1006
|
-
onDragLeave: handleDragLeave,
|
|
1007
|
-
onDragOver: handleDragOver,
|
|
1008
|
-
onDrop: handleDrop,
|
|
1009
|
-
children: [
|
|
1010
|
-
isDragOver && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "absolute inset-0 z-10 flex flex-col items-center justify-center rounded-xl bg-primary/5", children: [
|
|
1011
|
-
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Upload, { className: "h-8 w-8 text-primary/50 mb-2" }),
|
|
1012
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-medium text-primary/70", children: "Solte os arquivos aqui" })
|
|
1013
|
-
] }),
|
|
1014
|
-
documents.length === 0 && !isDragOver && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center justify-center py-12 text-center", children: [
|
|
1015
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-12 w-12 items-center justify-center rounded-full bg-muted mb-3", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.FileText, { className: "h-5 w-5 text-muted-foreground" }) }),
|
|
1016
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-medium", children: t("customForms.noDocuments") }),
|
|
1017
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground mt-1 max-w-xs", children: t("customForms.noDocumentsDescription") }),
|
|
1018
|
-
/* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-[10px] text-muted-foreground mt-3 flex items-center gap-1", children: [
|
|
1019
|
-
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Upload, { className: "h-3 w-3" }),
|
|
1020
|
-
"Arraste arquivos para c\xE1"
|
|
1021
|
-
] })
|
|
1022
|
-
] }),
|
|
1023
|
-
groups.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-5 py-2", children: groups.map((group) => /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
|
|
1024
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 mb-2", children: [
|
|
1025
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-px flex-1 bg-border" }),
|
|
1026
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] font-semibold uppercase tracking-wider text-muted-foreground px-1", children: group.label }),
|
|
1027
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-px flex-1 bg-border" })
|
|
1028
|
-
] }),
|
|
1029
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "space-y-1.5", children: group.items.map((doc) => {
|
|
1030
|
-
const Icon = KIND_ICONS[doc.kind ?? "form"] ?? lucideReact.FileText;
|
|
1031
|
-
const time = new Date(doc.createdAt).toLocaleTimeString(void 0, { hour: "2-digit", minute: "2-digit" });
|
|
1032
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
1033
|
-
"div",
|
|
1034
|
-
{
|
|
1035
|
-
className: "group flex items-center gap-3 rounded-lg p-2.5 hover:bg-muted/50 cursor-pointer transition-colors",
|
|
1036
|
-
onClick: () => onView(doc),
|
|
1037
|
-
children: [
|
|
1038
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-9 w-9 items-center justify-center rounded-lg bg-primary/10 shrink-0", children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { className: "h-4 w-4 text-primary" }) }),
|
|
1039
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 min-w-0", children: [
|
|
1040
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-medium truncate", children: doc.title ?? doc.templateName ?? "Documento" }),
|
|
1041
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5 mt-0.5", children: [
|
|
1042
|
-
doc.templateName && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] text-muted-foreground", children: doc.templateName }),
|
|
1043
|
-
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-[10px] text-muted-foreground", children: time })
|
|
1044
|
-
] })
|
|
1045
|
-
] }),
|
|
1046
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { variant: "secondary", className: `text-[10px] shrink-0 ${STATUS_COLORS[doc.status] ?? ""}`, children: t(`customForms.status.${doc.status}`) }),
|
|
1047
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-0.5 opacity-0 group-hover:opacity-100 transition-opacity", children: [
|
|
1048
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1049
|
-
"button",
|
|
1050
|
-
{
|
|
1051
|
-
onClick: (e) => {
|
|
1052
|
-
e.stopPropagation();
|
|
1053
|
-
onView(doc);
|
|
1054
|
-
},
|
|
1055
|
-
className: "p-1.5 rounded-md hover:bg-muted",
|
|
1056
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Eye, { className: "h-3.5 w-3.5 text-muted-foreground" })
|
|
1057
|
-
}
|
|
1058
|
-
),
|
|
1059
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1060
|
-
"button",
|
|
1061
|
-
{
|
|
1062
|
-
onClick: (e) => {
|
|
1063
|
-
e.stopPropagation();
|
|
1064
|
-
handleDelete(doc);
|
|
1065
|
-
},
|
|
1066
|
-
className: "p-1.5 rounded-md hover:bg-destructive/10",
|
|
1067
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash2, { className: "h-3.5 w-3.5 text-destructive" })
|
|
1068
|
-
}
|
|
1069
|
-
)
|
|
1070
|
-
] })
|
|
1071
|
-
]
|
|
1072
|
-
},
|
|
1073
|
-
doc.id
|
|
1074
|
-
);
|
|
1075
|
-
}) })
|
|
1076
|
-
] }, group.date)) })
|
|
1077
|
-
]
|
|
1078
|
-
}
|
|
1079
|
-
);
|
|
1080
|
-
}
|
|
1081
|
-
function generateTitle(templateName, personName) {
|
|
1082
|
-
const date = (/* @__PURE__ */ new Date()).toLocaleDateString();
|
|
1083
|
-
return personName ? `${templateName} \u2014 ${personName} \u2014 ${date}` : `${templateName} \u2014 ${date}`;
|
|
1084
|
-
}
|
|
1085
|
-
function DocumentFormDialog({
|
|
1086
|
-
template: initialTemplate,
|
|
1087
|
-
existingDocument,
|
|
1088
|
-
personId,
|
|
1089
|
-
personName,
|
|
1090
|
-
provider,
|
|
1091
|
-
store,
|
|
1092
|
-
onSaved,
|
|
1093
|
-
onBack
|
|
1094
|
-
}) {
|
|
1095
|
-
const t = core.useTranslation();
|
|
1096
|
-
const [template, setTemplate] = React2.useState(initialTemplate);
|
|
1097
|
-
const [data, setData] = React2.useState(existingDocument?.data ?? {});
|
|
1098
|
-
const [saving, setSaving] = React2.useState(false);
|
|
1099
|
-
React2.useEffect(() => {
|
|
1100
|
-
if (existingDocument && !template) {
|
|
1101
|
-
provider.getTemplateById(existingDocument.templateId).then(setTemplate);
|
|
1102
|
-
}
|
|
1103
|
-
}, [existingDocument, template, provider]);
|
|
1104
|
-
const handleSave = React2.useCallback(
|
|
1105
|
-
async (status) => {
|
|
1106
|
-
if (!template) return;
|
|
1107
|
-
setSaving(true);
|
|
1108
|
-
try {
|
|
1109
|
-
const autoTitle = generateTitle(template.name, personName);
|
|
1110
|
-
if (existingDocument) {
|
|
1111
|
-
await store.getState().updateDocument(existingDocument.id, { data, status });
|
|
1112
|
-
} else {
|
|
1113
|
-
await store.getState().createDocument({
|
|
1114
|
-
templateId: template.id,
|
|
1115
|
-
personId,
|
|
1116
|
-
title: autoTitle,
|
|
1117
|
-
data,
|
|
1118
|
-
status
|
|
1119
|
-
});
|
|
1120
|
-
}
|
|
1121
|
-
onSaved();
|
|
1122
|
-
} finally {
|
|
1123
|
-
setSaving(false);
|
|
1124
|
-
}
|
|
1125
|
-
},
|
|
1126
|
-
[template, existingDocument, data, personId, personName, store, onSaved]
|
|
1127
|
-
);
|
|
1128
|
-
const snapshot = React2__default.default.useRef(null);
|
|
1129
|
-
React2.useEffect(() => {
|
|
1130
|
-
if (template && snapshot.current === null) snapshot.current = JSON.stringify(data);
|
|
1131
|
-
}, [template]);
|
|
1132
|
-
const dirty = snapshot.current !== null && JSON.stringify(data) !== snapshot.current;
|
|
1133
|
-
ui.useSaveBar({
|
|
1134
|
-
dirty,
|
|
1135
|
-
saving,
|
|
1136
|
-
onSave: () => {
|
|
1137
|
-
void handleSave("completed");
|
|
1138
|
-
},
|
|
1139
|
-
onDiscard: () => onBack(),
|
|
1140
|
-
saveLabel: t("customForms.saveAndComplete")
|
|
1141
|
-
});
|
|
1142
|
-
if (!template) {
|
|
1143
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center py-12", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-5 w-5 border-2 border-primary border-t-transparent animate-spin rounded-full" }) });
|
|
1144
|
-
}
|
|
1145
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4", children: [
|
|
1146
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
|
|
1147
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1148
|
-
"button",
|
|
1149
|
-
{
|
|
1150
|
-
onClick: onBack,
|
|
1151
|
-
className: "flex items-center gap-1 text-sm text-muted-foreground hover:text-foreground transition-colors",
|
|
1152
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowLeft, { className: "h-3.5 w-3.5" })
|
|
1153
|
-
}
|
|
1154
|
-
),
|
|
1155
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 min-w-0", children: [
|
|
1156
|
-
/* @__PURE__ */ jsxRuntime.jsx("h3", { className: "font-semibold text-sm", children: template.name }),
|
|
1157
|
-
/* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-xs text-muted-foreground", children: [
|
|
1158
|
-
personName && /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
|
|
1159
|
-
personName,
|
|
1160
|
-
" \xB7 "
|
|
1161
|
-
] }),
|
|
1162
|
-
(/* @__PURE__ */ new Date()).toLocaleDateString()
|
|
1163
|
-
] })
|
|
1164
|
-
] })
|
|
1165
|
-
] }),
|
|
1166
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "rounded-xl border p-4", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1167
|
-
chunkXLUULIVL_cjs.FormRenderer,
|
|
1168
|
-
{
|
|
1169
|
-
schema: template.schema,
|
|
1170
|
-
data,
|
|
1171
|
-
onChange: setData
|
|
1172
|
-
}
|
|
1173
|
-
) }),
|
|
1174
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-end gap-2", children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Button, { variant: "outline", size: "sm", onClick: () => handleSave("draft"), disabled: saving, children: [
|
|
1175
|
-
saving ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "h-3.5 w-3.5 mr-1.5 animate-spin" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Save, { className: "h-3.5 w-3.5 mr-1.5" }),
|
|
1176
|
-
t("customForms.saveAsDraft")
|
|
1177
|
-
] }) })
|
|
1178
|
-
] });
|
|
1179
|
-
}
|
|
1180
|
-
var STATUS_COLORS2 = {
|
|
1181
|
-
draft: "bg-warning-soft text-warning-soft-foreground",
|
|
1182
|
-
completed: "bg-success-soft text-success-soft-foreground",
|
|
1183
|
-
signed: "bg-info-soft text-info-soft-foreground",
|
|
1184
|
-
archived: "bg-muted text-muted-foreground"
|
|
1185
|
-
};
|
|
1186
|
-
function FormViewer({ document: doc, provider, onBack, onEdit }) {
|
|
1187
|
-
const t = core.useTranslation();
|
|
1188
|
-
const [template, setTemplate] = React2.useState(null);
|
|
1189
|
-
React2.useEffect(() => {
|
|
1190
|
-
if (!doc.templateId) return;
|
|
1191
|
-
const timer = setTimeout(() => {
|
|
1192
|
-
provider.getTemplateById(doc.templateId).then(setTemplate);
|
|
1193
|
-
}, 50);
|
|
1194
|
-
return () => clearTimeout(timer);
|
|
1195
|
-
}, [doc.templateId, provider]);
|
|
1196
|
-
if (!template) {
|
|
1197
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center py-12", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-5 w-5 border-2 border-primary border-t-transparent animate-spin rounded-full" }) });
|
|
1198
|
-
}
|
|
1199
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4", children: [
|
|
1200
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
|
|
1201
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1202
|
-
"button",
|
|
1203
|
-
{
|
|
1204
|
-
onClick: onBack,
|
|
1205
|
-
className: "flex items-center gap-1 text-sm text-muted-foreground hover:text-foreground transition-colors",
|
|
1206
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowLeft, { className: "h-3.5 w-3.5" })
|
|
1207
|
-
}
|
|
1208
|
-
),
|
|
1209
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 min-w-0", children: [
|
|
1210
|
-
/* @__PURE__ */ jsxRuntime.jsx("h3", { className: "font-semibold text-sm truncate", children: doc.title ?? template.name }),
|
|
1211
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground", children: new Date(doc.createdAt).toLocaleDateString() })
|
|
1212
|
-
] }),
|
|
1213
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { variant: "secondary", className: `text-[10px] ${STATUS_COLORS2[doc.status] ?? ""}`, children: t(`customForms.status.${doc.status}`) }),
|
|
1214
|
-
onEdit && doc.status === "draft" && /* @__PURE__ */ jsxRuntime.jsxs(ui.Button, { variant: "outline", size: "sm", className: "h-8", onClick: () => onEdit(doc), children: [
|
|
1215
|
-
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Pencil, { className: "h-3 w-3 mr-1" }),
|
|
1216
|
-
t("common.edit")
|
|
1217
|
-
] })
|
|
1218
|
-
] }),
|
|
1219
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "rounded-xl border p-4", children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
1220
|
-
chunkXLUULIVL_cjs.FormRenderer,
|
|
1221
|
-
{
|
|
1222
|
-
schema: template.schema,
|
|
1223
|
-
data: doc.data,
|
|
1224
|
-
onChange: () => {
|
|
1225
|
-
},
|
|
1226
|
-
readOnly: true
|
|
1227
|
-
}
|
|
1228
|
-
) })
|
|
1229
|
-
] });
|
|
1230
|
-
}
|
|
1231
|
-
|
|
1232
|
-
// src/document-types.ts
|
|
1233
|
-
var providers = [];
|
|
1234
|
-
function registerDocumentTypeProvider(provider) {
|
|
1235
|
-
if (providers.find((p) => p.id === provider.id)) return;
|
|
1236
|
-
providers.push(provider);
|
|
1237
|
-
}
|
|
1238
|
-
async function getAllDocumentTypes(personId) {
|
|
1239
|
-
const results = await Promise.all(
|
|
1240
|
-
providers.map((p) => Promise.resolve(p.getTypes(personId)))
|
|
1241
|
-
);
|
|
1242
|
-
const all = results.flat();
|
|
1243
|
-
all.sort((a, b) => (a.groupOrder ?? 50) - (b.groupOrder ?? 50) || (a.order ?? 0) - (b.order ?? 0));
|
|
1244
|
-
return all;
|
|
1245
|
-
}
|
|
1246
|
-
function AddDocumentDropdown({ personId, onSelect }) {
|
|
1247
|
-
const t = core.useTranslation();
|
|
1248
|
-
const [options, setOptions] = React2.useState([]);
|
|
1249
|
-
const [loaded, setLoaded] = React2.useState(false);
|
|
1250
|
-
const loadingRef = React2.useRef(false);
|
|
1251
|
-
const handleOpen = React2.useCallback((open) => {
|
|
1252
|
-
if (!open || loaded || loadingRef.current) return;
|
|
1253
|
-
loadingRef.current = true;
|
|
1254
|
-
getAllDocumentTypes(personId).then((opts) => {
|
|
1255
|
-
setOptions(opts);
|
|
1256
|
-
setLoaded(true);
|
|
1257
|
-
loadingRef.current = false;
|
|
1258
|
-
});
|
|
1259
|
-
}, [personId, loaded]);
|
|
1260
|
-
const groups = [];
|
|
1261
|
-
for (const opt of options) {
|
|
1262
|
-
let group = groups.find((g) => g.label === opt.group);
|
|
1263
|
-
if (!group) {
|
|
1264
|
-
group = { label: opt.group, items: [] };
|
|
1265
|
-
groups.push(group);
|
|
1266
|
-
}
|
|
1267
|
-
group.items.push(opt);
|
|
1268
|
-
}
|
|
1269
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(ui.Dropdown, { onOpenChange: handleOpen, children: [
|
|
1270
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.DropdownTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsxs(ui.Button, { size: "sm", className: "h-8", children: [
|
|
1271
|
-
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, { className: "h-3.5 w-3.5 mr-1" }),
|
|
1272
|
-
t("customForms.addDocument"),
|
|
1273
|
-
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { className: "h-3 w-3 ml-1 opacity-50" })
|
|
1274
|
-
] }) }),
|
|
1275
|
-
/* @__PURE__ */ jsxRuntime.jsxs(ui.DropdownContent, { align: "end", className: "w-56", children: [
|
|
1276
|
-
groups.map((group, gi) => /* @__PURE__ */ jsxRuntime.jsxs(React2__default.default.Fragment, { children: [
|
|
1277
|
-
gi > 0 && /* @__PURE__ */ jsxRuntime.jsx(ui.DropdownSeparator, {}),
|
|
1278
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.DropdownLabel, { className: "text-[10px] uppercase tracking-wider", children: group.label }),
|
|
1279
|
-
group.items.map((opt) => {
|
|
1280
|
-
const Icon = opt.icon;
|
|
1281
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(ui.DropdownItem, { onClick: () => onSelect(opt), className: "gap-2", children: [
|
|
1282
|
-
/* @__PURE__ */ jsxRuntime.jsx(Icon, { className: "h-4 w-4 text-muted-foreground shrink-0" }),
|
|
1283
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 min-w-0", children: [
|
|
1284
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm truncate", children: opt.label }),
|
|
1285
|
-
opt.description && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-[10px] text-muted-foreground truncate", children: opt.description })
|
|
1286
|
-
] })
|
|
1287
|
-
] }, opt.id);
|
|
1288
|
-
})
|
|
1289
|
-
] }, group.label)),
|
|
1290
|
-
!loaded && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center py-3", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "h-4 w-4 border-2 border-primary border-t-transparent animate-spin rounded-full" }) }),
|
|
1291
|
-
loaded && options.length === 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-2 py-3 text-center text-xs text-muted-foreground", children: t("customForms.noTemplates") })
|
|
1292
|
-
] })
|
|
1293
|
-
] });
|
|
1294
|
-
}
|
|
1295
|
-
function PersonDocumentsWidget({ item, config, provider, store }) {
|
|
1296
|
-
const t = core.useTranslation();
|
|
1297
|
-
const [view, setView] = React2.useState({ type: "list" });
|
|
1298
|
-
const fileInputRef = React2.useRef(null);
|
|
1299
|
-
const [pendingFileAccept, setPendingFileAccept] = React2.useState("*/*");
|
|
1300
|
-
const handleDocTypeSelect = React2.useCallback(async (option) => {
|
|
1301
|
-
if (option.id.startsWith("template:")) {
|
|
1302
|
-
const templateId = option.id.replace("template:", "");
|
|
1303
|
-
const template = await provider.getTemplateById(templateId);
|
|
1304
|
-
if (template) {
|
|
1305
|
-
setView({ type: "fill", template });
|
|
1306
|
-
}
|
|
1307
|
-
return;
|
|
1308
|
-
}
|
|
1309
|
-
if (option.id === "core:image") {
|
|
1310
|
-
setPendingFileAccept("image/*");
|
|
1311
|
-
setTimeout(() => fileInputRef.current?.click(), 0);
|
|
1312
|
-
return;
|
|
1313
|
-
}
|
|
1314
|
-
if (option.id === "core:attachment") {
|
|
1315
|
-
setPendingFileAccept("*/*");
|
|
1316
|
-
setTimeout(() => fileInputRef.current?.click(), 0);
|
|
1317
|
-
return;
|
|
1318
|
-
}
|
|
1319
|
-
}, [provider]);
|
|
1320
|
-
const handleFiles = React2.useCallback(async (files) => {
|
|
1321
|
-
for (const file of files) {
|
|
1322
|
-
const isImage = file.type.startsWith("image/");
|
|
1323
|
-
await store.getState().createDocument({
|
|
1324
|
-
kind: isImage ? "image" : "attachment",
|
|
1325
|
-
personId: item.id,
|
|
1326
|
-
title: file.name,
|
|
1327
|
-
fileName: file.name,
|
|
1328
|
-
fileSize: file.size,
|
|
1329
|
-
mimeType: file.type,
|
|
1330
|
-
status: "completed"
|
|
1331
|
-
});
|
|
1332
|
-
}
|
|
1333
|
-
}, [store, item.id]);
|
|
1334
|
-
const handleFileInputChange = React2.useCallback((e) => {
|
|
1335
|
-
const files = Array.from(e.target.files ?? []);
|
|
1336
|
-
if (files.length > 0) handleFiles(files);
|
|
1337
|
-
e.target.value = "";
|
|
1338
|
-
}, [handleFiles]);
|
|
1339
|
-
const handleViewDoc = React2.useCallback((doc) => {
|
|
1340
|
-
setView({ type: "view", document: doc });
|
|
1341
|
-
}, []);
|
|
1342
|
-
const handleEditDoc = React2.useCallback((doc) => {
|
|
1343
|
-
setView({ type: "edit", document: doc });
|
|
1344
|
-
}, []);
|
|
1345
|
-
const handleBack = React2.useCallback(() => {
|
|
1346
|
-
setView({ type: "list" });
|
|
1347
|
-
}, []);
|
|
1348
|
-
const handleSaved = React2.useCallback(() => {
|
|
1349
|
-
setView({ type: "list" });
|
|
1350
|
-
}, []);
|
|
1351
|
-
if (view.type === "fill") {
|
|
1352
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1353
|
-
DocumentFormDialog,
|
|
1354
|
-
{
|
|
1355
|
-
template: view.template,
|
|
1356
|
-
personId: item.id,
|
|
1357
|
-
personName: item.name,
|
|
1358
|
-
provider,
|
|
1359
|
-
store,
|
|
1360
|
-
onSaved: handleSaved,
|
|
1361
|
-
onBack: handleBack
|
|
1362
|
-
}
|
|
1363
|
-
);
|
|
1364
|
-
}
|
|
1365
|
-
if (view.type === "view") {
|
|
1366
|
-
const doc = view.document;
|
|
1367
|
-
const isForm = doc.kind === "form" || !doc.kind && doc.templateId;
|
|
1368
|
-
if (!isForm) {
|
|
1369
|
-
return /* @__PURE__ */ jsxRuntime.jsx(FileDocumentViewer, { document: doc, onBack: handleBack });
|
|
1370
|
-
}
|
|
1371
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1372
|
-
FormViewer,
|
|
1373
|
-
{
|
|
1374
|
-
document: doc,
|
|
1375
|
-
provider,
|
|
1376
|
-
onBack: handleBack,
|
|
1377
|
-
onEdit: handleEditDoc
|
|
1378
|
-
}
|
|
1379
|
-
);
|
|
1380
|
-
}
|
|
1381
|
-
if (view.type === "edit") {
|
|
1382
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
1383
|
-
DocumentFormDialog,
|
|
1384
|
-
{
|
|
1385
|
-
template: null,
|
|
1386
|
-
existingDocument: view.document,
|
|
1387
|
-
personId: item.id,
|
|
1388
|
-
personName: item.name,
|
|
1389
|
-
provider,
|
|
1390
|
-
store,
|
|
1391
|
-
onSaved: handleSaved,
|
|
1392
|
-
onBack: handleBack
|
|
1393
|
-
}
|
|
1394
|
-
);
|
|
1395
|
-
}
|
|
1396
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-3", children: [
|
|
1397
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
|
|
1398
|
-
/* @__PURE__ */ jsxRuntime.jsx("h3", { className: "text-sm font-semibold", children: t("customForms.documents") }),
|
|
1399
|
-
/* @__PURE__ */ jsxRuntime.jsx(AddDocumentDropdown, { personId: item.id, onSelect: handleDocTypeSelect })
|
|
1400
|
-
] }),
|
|
1401
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1402
|
-
DocumentList,
|
|
1403
|
-
{
|
|
1404
|
-
personId: item.id,
|
|
1405
|
-
provider,
|
|
1406
|
-
store,
|
|
1407
|
-
onView: handleViewDoc,
|
|
1408
|
-
onFileDrop: handleFiles
|
|
1409
|
-
}
|
|
1410
|
-
),
|
|
1411
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1412
|
-
"input",
|
|
1413
|
-
{
|
|
1414
|
-
ref: fileInputRef,
|
|
1415
|
-
type: "file",
|
|
1416
|
-
className: "hidden",
|
|
1417
|
-
accept: pendingFileAccept,
|
|
1418
|
-
multiple: true,
|
|
1419
|
-
onChange: handleFileInputChange
|
|
1420
|
-
}
|
|
1421
|
-
)
|
|
1422
|
-
] });
|
|
1423
|
-
}
|
|
1424
|
-
function FileDocumentViewer({ document: doc, onBack }) {
|
|
1425
|
-
const isImage = doc.kind === "image" || doc.mimeType?.startsWith("image/");
|
|
1426
|
-
const Icon = isImage ? lucideReact.Image : lucideReact.Paperclip;
|
|
1427
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "space-y-4", children: [
|
|
1428
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3", children: [
|
|
1429
|
-
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1430
|
-
"button",
|
|
1431
|
-
{
|
|
1432
|
-
onClick: onBack,
|
|
1433
|
-
className: "flex items-center gap-1 text-sm text-muted-foreground hover:text-foreground transition-colors",
|
|
1434
|
-
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowLeft, { className: "h-3.5 w-3.5" })
|
|
1435
|
-
}
|
|
1436
|
-
),
|
|
1437
|
-
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex-1 min-w-0", children: [
|
|
1438
|
-
/* @__PURE__ */ jsxRuntime.jsx("h3", { className: "font-semibold text-sm truncate", children: doc.title ?? doc.fileName ?? "Document" }),
|
|
1439
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-xs text-muted-foreground", children: new Date(doc.createdAt).toLocaleDateString() })
|
|
1440
|
-
] }),
|
|
1441
|
-
/* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { variant: "secondary", className: "text-[10px] bg-success/15 text-success", children: doc.status === "completed" ? "Finalizado" : doc.status })
|
|
1442
|
-
] }),
|
|
1443
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "rounded-xl border p-6 flex flex-col items-center gap-4", children: isImage && doc.fileUrl ? /* @__PURE__ */ jsxRuntime.jsx("img", { src: doc.fileUrl, alt: doc.fileName ?? "", className: "max-w-full max-h-[500px] rounded-lg object-contain" }) : /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col items-center gap-3 py-8", children: [
|
|
1444
|
-
/* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-16 w-16 items-center justify-center rounded-full bg-muted", children: /* @__PURE__ */ jsxRuntime.jsx(Icon, { className: "h-7 w-7 text-muted-foreground" }) }),
|
|
1445
|
-
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-medium", children: doc.fileName ?? doc.title ?? "Arquivo" }),
|
|
1446
|
-
doc.fileSize && /* @__PURE__ */ jsxRuntime.jsxs("p", { className: "text-xs text-muted-foreground", children: [
|
|
1447
|
-
(doc.fileSize / 1024).toFixed(1),
|
|
1448
|
-
" KB"
|
|
1449
|
-
] })
|
|
1450
|
-
] }) })
|
|
1451
|
-
] });
|
|
1452
|
-
}
|
|
1453
|
-
function registerBuiltInProviders(formsProvider) {
|
|
1454
|
-
registerDocumentTypeProvider({
|
|
1455
|
-
id: "custom_forms:templates",
|
|
1456
|
-
async getTypes() {
|
|
1457
|
-
try {
|
|
1458
|
-
const result = await formsProvider.getTemplates({});
|
|
1459
|
-
return result.data.map((tpl) => ({
|
|
1460
|
-
id: `template:${tpl.id}`,
|
|
1461
|
-
label: tpl.name,
|
|
1462
|
-
icon: lucideReact.FileText,
|
|
1463
|
-
group: "Formul\xE1rios",
|
|
1464
|
-
groupOrder: 10,
|
|
1465
|
-
order: 0,
|
|
1466
|
-
description: tpl.description,
|
|
1467
|
-
_templateId: tpl.id
|
|
1468
|
-
}));
|
|
1469
|
-
} catch {
|
|
1470
|
-
return [];
|
|
1471
|
-
}
|
|
1472
|
-
}
|
|
1473
|
-
});
|
|
1474
|
-
registerDocumentTypeProvider({
|
|
1475
|
-
id: "core:files",
|
|
1476
|
-
getTypes() {
|
|
1477
|
-
return [
|
|
1478
|
-
{ id: "core:image", label: "Imagem", icon: lucideReact.Image, group: "Arquivos", groupOrder: 90, order: 0 },
|
|
1479
|
-
{ id: "core:attachment", label: "Anexo", icon: lucideReact.Paperclip, group: "Arquivos", groupOrder: 90, order: 1 }
|
|
1480
|
-
];
|
|
1481
|
-
}
|
|
1482
|
-
});
|
|
1483
|
-
}
|
|
1484
|
-
function createCustomFormsPlugin(options) {
|
|
1485
|
-
core.registerTranslations(customFormsLocales);
|
|
1486
|
-
const config = resolveConfig(options);
|
|
1487
|
-
const provider = options?.dataProvider ?? core.createSafeDataProvider(
|
|
1488
|
-
() => createSupabaseFormsProvider(),
|
|
1489
|
-
() => createMockFormsProvider()
|
|
1490
|
-
);
|
|
1491
|
-
const store = createCustomFormsStore(provider);
|
|
1492
|
-
registerBuiltInProviders(provider);
|
|
1493
|
-
const formRegistries = [
|
|
1494
|
-
{
|
|
1495
|
-
id: "form-categories",
|
|
1496
|
-
entity: {
|
|
1497
|
-
name: "Category",
|
|
1498
|
-
namePlural: "Categories",
|
|
1499
|
-
icon: "FolderOpen",
|
|
1500
|
-
displayField: "name",
|
|
1501
|
-
defaultSort: "sort_order",
|
|
1502
|
-
fields: [
|
|
1503
|
-
{ key: "name", label: "Name", type: "text", required: true, showInTable: true },
|
|
1504
|
-
{ key: "icon", label: "Icon", type: "text", showInTable: true },
|
|
1505
|
-
{ key: "color", label: "Color", type: "color", showInTable: true },
|
|
1506
|
-
{ key: "sortOrder", label: "Order", type: "number", showInTable: true, defaultValue: 0 },
|
|
1507
|
-
{ key: "isActive", label: "Active", type: "boolean", showInTable: true, defaultValue: true, inlineToggle: true }
|
|
1508
|
-
],
|
|
1509
|
-
data: {
|
|
1510
|
-
table: T.categories,
|
|
1511
|
-
tenantScoped: true
|
|
1512
|
-
}
|
|
1513
|
-
},
|
|
1514
|
-
icon: "FolderOpen",
|
|
1515
|
-
description: "Categories for organizing form templates",
|
|
1516
|
-
seedData: [
|
|
1517
|
-
{ name: "Anamnese", sortOrder: 1, isActive: true },
|
|
1518
|
-
{ name: "Evolu\xE7\xE3o", sortOrder: 2, isActive: true },
|
|
1519
|
-
{ name: "Laudo", sortOrder: 3, isActive: true },
|
|
1520
|
-
{ name: "Contrato", sortOrder: 4, isActive: true },
|
|
1521
|
-
{ name: "Geral", sortOrder: 5, isActive: true }
|
|
1522
|
-
]
|
|
1523
|
-
}
|
|
1524
|
-
];
|
|
1525
|
-
const registries = [...formRegistries, ...options?.settingsRegistries ?? []];
|
|
1526
|
-
const SettingsComponent = () => React2__default.default.createElement(saas.PluginSettingsPanel, {
|
|
1527
|
-
title: config.labels.settingsLabel,
|
|
1528
|
-
subtitle: config.labels.settingsSubtitle,
|
|
1529
|
-
customTabs: [
|
|
1530
|
-
{
|
|
1531
|
-
id: "forms",
|
|
1532
|
-
label: config.labels.pageTitle,
|
|
1533
|
-
icon: "FileText",
|
|
1534
|
-
content: React2__default.default.createElement(CustomFormsSettingsTab, { config, provider, store })
|
|
1535
|
-
}
|
|
1536
|
-
],
|
|
1537
|
-
registries,
|
|
1538
|
-
routeBase: "/settings/custom_forms",
|
|
1539
|
-
hostPluginId: "custom_forms"
|
|
1540
|
-
});
|
|
1541
|
-
SettingsComponent.displayName = "CustomFormsSettingsPanel";
|
|
1542
|
-
const PersonDocumentsWidget2 = (props) => React2__default.default.createElement(PersonDocumentsWidget, { ...props, config, provider, store });
|
|
1543
|
-
PersonDocumentsWidget2.displayName = "PersonDocumentsWidget";
|
|
1544
|
-
return {
|
|
1545
|
-
id: "custom_forms",
|
|
1546
|
-
name: config.labels.pageTitle,
|
|
1547
|
-
icon: "FileText",
|
|
1548
|
-
version: "1.0.0",
|
|
1549
|
-
scope: options?.scope ?? "universal",
|
|
1550
|
-
verticalId: options?.verticalId,
|
|
1551
|
-
defaultEnabled: true,
|
|
1552
|
-
dependencies: [],
|
|
1553
|
-
declaredFeatures: [
|
|
1554
|
-
{ id: "custom_forms", label: config.labels.pageTitle, group: config.labels.pageTitle }
|
|
1555
|
-
],
|
|
1556
|
-
navigation: [],
|
|
1557
|
-
routes: [],
|
|
1558
|
-
settings: [
|
|
1559
|
-
{
|
|
1560
|
-
id: "custom_forms",
|
|
1561
|
-
label: config.labels.settingsLabel,
|
|
1562
|
-
icon: "FileText",
|
|
1563
|
-
component: SettingsComponent,
|
|
1564
|
-
order: 15
|
|
1565
|
-
}
|
|
1566
|
-
],
|
|
1567
|
-
widgets: [
|
|
1568
|
-
{
|
|
1569
|
-
id: "person-documents-tab",
|
|
1570
|
-
zone: "person.detail.documents",
|
|
1571
|
-
component: PersonDocumentsWidget2,
|
|
1572
|
-
order: 0
|
|
1573
|
-
}
|
|
1574
|
-
],
|
|
1575
|
-
registries,
|
|
1576
|
-
aiTools: [
|
|
1577
|
-
{
|
|
1578
|
-
id: "custom_forms.list-templates",
|
|
1579
|
-
name: "listFormTemplates",
|
|
1580
|
-
description: "Lists available form templates for the current tenant.",
|
|
1581
|
-
icon: "FileText",
|
|
1582
|
-
mode: "read",
|
|
1583
|
-
category: "Forms",
|
|
1584
|
-
parameters: {
|
|
1585
|
-
type: "object",
|
|
1586
|
-
properties: {
|
|
1587
|
-
category: {
|
|
1588
|
-
type: "string",
|
|
1589
|
-
description: "Filter by category: anamnesis, evolution, report, contract, general"
|
|
1590
|
-
}
|
|
1591
|
-
}
|
|
1592
|
-
},
|
|
1593
|
-
suggestions: [
|
|
1594
|
-
{ label: "What forms do we have?" },
|
|
1595
|
-
{ label: "Show me anamnesis templates" }
|
|
1596
|
-
],
|
|
1597
|
-
permission: { feature: "custom_forms", action: "read" }
|
|
1598
|
-
},
|
|
1599
|
-
{
|
|
1600
|
-
id: "custom_forms.list-documents",
|
|
1601
|
-
name: "listDocuments",
|
|
1602
|
-
description: "Lists filled documents, optionally filtered by person or status.",
|
|
1603
|
-
icon: "FileText",
|
|
1604
|
-
mode: "read",
|
|
1605
|
-
category: "Forms",
|
|
1606
|
-
parameters: {
|
|
1607
|
-
type: "object",
|
|
1608
|
-
properties: {
|
|
1609
|
-
personId: { type: "string", description: "Person UUID to filter by" },
|
|
1610
|
-
status: {
|
|
1611
|
-
type: "string",
|
|
1612
|
-
description: "Status filter: draft, completed, signed, archived"
|
|
1613
|
-
}
|
|
1614
|
-
}
|
|
1615
|
-
},
|
|
1616
|
-
suggestions: [
|
|
1617
|
-
{ label: "Show this client's documents" }
|
|
1618
|
-
],
|
|
1619
|
-
permission: { feature: "custom_forms", action: "read" }
|
|
1620
|
-
}
|
|
1621
|
-
],
|
|
1622
|
-
locales: customFormsLocales
|
|
1623
|
-
};
|
|
1624
|
-
}
|
|
1625
|
-
|
|
1626
|
-
exports.createCustomFormsPlugin = createCustomFormsPlugin;
|
|
1627
|
-
exports.registerDocumentTypeProvider = registerDocumentTypeProvider;
|
|
1628
|
-
//# sourceMappingURL=index.cjs.map
|
|
1629
|
-
//# sourceMappingURL=index.cjs.map
|