@fayz-ai/forms 0.0.0-stage → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +34 -2
- package/dist/chunk-2KVTEW7I.js +211 -0
- package/dist/chunk-2KVTEW7I.js.map +1 -0
- package/dist/chunk-JO4REIZC.js +429 -0
- package/dist/chunk-JO4REIZC.js.map +1 -0
- package/dist/chunk-L2KPTYH4.js +11 -0
- package/dist/chunk-L2KPTYH4.js.map +1 -0
- package/dist/chunk-LQJONAVT.js +25 -0
- package/dist/chunk-LQJONAVT.js.map +1 -0
- package/dist/data/mock.d.ts +3 -0
- package/dist/data/mock.d.ts.map +1 -0
- package/dist/data/mock.js +4 -0
- package/dist/data/mock.js.map +1 -0
- package/dist/data/provider.d.ts +55 -0
- package/dist/data/provider.d.ts.map +1 -0
- package/dist/data/provider.js +3 -0
- package/dist/data/provider.js.map +1 -0
- package/dist/data/supabase.d.ts +6 -0
- package/dist/data/supabase.d.ts.map +1 -0
- package/dist/data/supabase.js +4 -0
- package/dist/data/supabase.js.map +1 -0
- package/dist/data/tables.d.ts +7 -0
- package/dist/data/tables.d.ts.map +1 -0
- package/dist/data/tables.js +3 -0
- package/dist/data/tables.js.map +1 -0
- package/dist/domain/document.d.ts +84 -0
- package/dist/domain/document.d.ts.map +1 -0
- package/dist/domain/document.js +3 -0
- package/dist/domain/document.js.map +1 -0
- package/dist/domain/requirement.d.ts +27 -0
- package/dist/domain/requirement.d.ts.map +1 -0
- package/dist/domain/requirement.js +3 -0
- package/dist/domain/requirement.js.map +1 -0
- package/dist/domain/template.d.ts +135 -0
- package/dist/domain/template.d.ts.map +1 -0
- package/dist/domain/template.js +3 -0
- package/dist/domain/template.js.map +1 -0
- package/dist/store.d.ts +37 -0
- package/dist/store.d.ts.map +1 -0
- package/dist/store.js +138 -0
- package/dist/store.js.map +1 -0
- package/dist/views/zones.d.ts +75 -0
- package/dist/views/zones.d.ts.map +1 -0
- package/dist/views/zones.js +11 -0
- package/dist/views/zones.js.map +1 -0
- package/package.json +48 -3
|
@@ -0,0 +1,429 @@
|
|
|
1
|
+
import { T } from './chunk-L2KPTYH4.js';
|
|
2
|
+
import { getSupabaseClientOptional, getActiveTenantId } from '@fayz-ai/core';
|
|
3
|
+
|
|
4
|
+
function getTenantId() {
|
|
5
|
+
return getActiveTenantId() ?? "";
|
|
6
|
+
}
|
|
7
|
+
function getClient() {
|
|
8
|
+
const supabase = getSupabaseClientOptional();
|
|
9
|
+
if (!supabase) throw new Error("Supabase not initialized");
|
|
10
|
+
return supabase;
|
|
11
|
+
}
|
|
12
|
+
var MEDIA_BUCKET = "attachments";
|
|
13
|
+
var SIGNED_TTL_S = 3600;
|
|
14
|
+
var signedCache = /* @__PURE__ */ new Map();
|
|
15
|
+
var USAGE_FALLBACK_LIMIT = 5e3;
|
|
16
|
+
async function countTemplateUsageClientSide(db, tenantId) {
|
|
17
|
+
try {
|
|
18
|
+
const { data, error, count } = await db.from("v_documents").select("template_id, created_at", { count: "exact" }).eq("tenant_id", tenantId).eq("is_active", true).not("template_id", "is", null).limit(USAGE_FALLBACK_LIMIT);
|
|
19
|
+
if (error) return {};
|
|
20
|
+
if (typeof count === "number" && count > USAGE_FALLBACK_LIMIT) return {};
|
|
21
|
+
const usage = {};
|
|
22
|
+
for (const row of data ?? []) {
|
|
23
|
+
if (!row.template_id) continue;
|
|
24
|
+
const current = usage[row.template_id] ?? { documentCount: 0, lastDocumentAt: null };
|
|
25
|
+
current.documentCount += 1;
|
|
26
|
+
if (!current.lastDocumentAt || row.created_at > current.lastDocumentAt) {
|
|
27
|
+
current.lastDocumentAt = row.created_at;
|
|
28
|
+
}
|
|
29
|
+
usage[row.template_id] = current;
|
|
30
|
+
}
|
|
31
|
+
return usage;
|
|
32
|
+
} catch {
|
|
33
|
+
return {};
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
function mapTemplate(r) {
|
|
37
|
+
return {
|
|
38
|
+
requiredOnAttendance: r.required_on_attendance ?? false,
|
|
39
|
+
validityDays: r.validity_days ?? null,
|
|
40
|
+
alertWhenMissing: r.alert_when_missing ?? false,
|
|
41
|
+
id: r.id,
|
|
42
|
+
tenantId: r.tenant_id,
|
|
43
|
+
name: r.name,
|
|
44
|
+
description: r.description,
|
|
45
|
+
category: r.category,
|
|
46
|
+
version: r.version,
|
|
47
|
+
isCurrent: r.is_current,
|
|
48
|
+
parentId: r.parent_id,
|
|
49
|
+
schema: r.schema,
|
|
50
|
+
specialty: r.specialty,
|
|
51
|
+
tags: r.tags ?? [],
|
|
52
|
+
metadata: r.metadata ?? {},
|
|
53
|
+
isActive: r.is_active,
|
|
54
|
+
isDeleted: r.is_deleted,
|
|
55
|
+
createdBy: r.created_by,
|
|
56
|
+
updatedBy: r.updated_by,
|
|
57
|
+
createdAt: r.created_at,
|
|
58
|
+
updatedAt: r.updated_at
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
function mapDocument(r) {
|
|
62
|
+
return {
|
|
63
|
+
id: r.id,
|
|
64
|
+
tenantId: r.tenant_id,
|
|
65
|
+
templateId: r.template_id ?? "",
|
|
66
|
+
templateName: r.template_name,
|
|
67
|
+
templateCategory: r.template_category,
|
|
68
|
+
personId: r.person_id,
|
|
69
|
+
personName: r.person_name,
|
|
70
|
+
title: r.title,
|
|
71
|
+
data: r.form_data ?? {},
|
|
72
|
+
status: r.status,
|
|
73
|
+
signedAt: r.signed_at,
|
|
74
|
+
signedBy: r.signed_by,
|
|
75
|
+
notes: r.notes,
|
|
76
|
+
metadata: r.metadata ?? {},
|
|
77
|
+
isDeleted: false,
|
|
78
|
+
createdBy: r.created_by,
|
|
79
|
+
updatedBy: r.updated_by,
|
|
80
|
+
createdAt: r.created_at,
|
|
81
|
+
updatedAt: r.updated_at,
|
|
82
|
+
kind: r.kind,
|
|
83
|
+
fileUrl: r.file_url,
|
|
84
|
+
fileName: r.file_name,
|
|
85
|
+
fileSize: r.file_size,
|
|
86
|
+
mimeType: r.mime_type
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
function mapFile(r) {
|
|
90
|
+
return {
|
|
91
|
+
id: r.id,
|
|
92
|
+
tenantId: r.tenant_id,
|
|
93
|
+
documentId: r.document_id,
|
|
94
|
+
fieldKey: r.field_key,
|
|
95
|
+
fileUrl: r.file_url,
|
|
96
|
+
fileName: r.file_name,
|
|
97
|
+
fileSize: r.file_size,
|
|
98
|
+
mimeType: r.mime_type,
|
|
99
|
+
sortOrder: r.sort_order,
|
|
100
|
+
metadata: r.metadata ?? {},
|
|
101
|
+
createdAt: r.created_at
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
function mapRequirement(r) {
|
|
105
|
+
return {
|
|
106
|
+
templateId: r.template_id,
|
|
107
|
+
templateName: r.template_name,
|
|
108
|
+
category: r.category,
|
|
109
|
+
validityDays: r.validity_days ?? null,
|
|
110
|
+
alertWhenMissing: r.alert_when_missing ?? false,
|
|
111
|
+
lastFilledAt: r.last_filled_at ?? null,
|
|
112
|
+
validUntil: r.valid_until ?? null,
|
|
113
|
+
state: r.state
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
async function getPersonFormRequirements(tenantId, personId) {
|
|
117
|
+
const { data, error } = await getClient().rpc("forms_person_requirements", {
|
|
118
|
+
p_tenant_id: tenantId,
|
|
119
|
+
p_person_id: personId
|
|
120
|
+
});
|
|
121
|
+
if (error) throw error;
|
|
122
|
+
return (data ?? []).map(mapRequirement);
|
|
123
|
+
}
|
|
124
|
+
function createSupabaseFormsProvider() {
|
|
125
|
+
return {
|
|
126
|
+
async getTemplates(query) {
|
|
127
|
+
const db = getClient();
|
|
128
|
+
let qb = db.from(T.templates).select("*", { count: "exact" }).eq("tenant_id", getTenantId()).eq("is_current", true).eq("is_deleted", false).eq("is_active", true);
|
|
129
|
+
if (query.category) qb = qb.eq("category", query.category);
|
|
130
|
+
if (query.search) qb = qb.ilike("name", `%${query.search}%`);
|
|
131
|
+
const page = query.page ?? 1;
|
|
132
|
+
const pageSize = query.pageSize ?? 25;
|
|
133
|
+
qb = qb.order("updated_at", { ascending: false }).range((page - 1) * pageSize, page * pageSize - 1);
|
|
134
|
+
const { data, count, error } = await qb;
|
|
135
|
+
if (error) throw new Error(error.message);
|
|
136
|
+
return { data: (data ?? []).map(mapTemplate), total: count ?? 0 };
|
|
137
|
+
},
|
|
138
|
+
async getTemplateById(id) {
|
|
139
|
+
const db = getClient();
|
|
140
|
+
const { data, error } = await db.from(T.templates).select("*").eq("id", id).eq("is_deleted", false).single();
|
|
141
|
+
if (error || !data) return null;
|
|
142
|
+
return mapTemplate(data);
|
|
143
|
+
},
|
|
144
|
+
async createTemplate(input) {
|
|
145
|
+
const db = getClient();
|
|
146
|
+
const tenantId = getTenantId();
|
|
147
|
+
const { data, error } = await db.from(T.templates).insert({
|
|
148
|
+
tenant_id: tenantId,
|
|
149
|
+
name: input.name,
|
|
150
|
+
description: input.description,
|
|
151
|
+
category: input.category,
|
|
152
|
+
schema: input.schema,
|
|
153
|
+
specialty: input.specialty,
|
|
154
|
+
tags: input.tags ?? [],
|
|
155
|
+
metadata: input.metadata ?? {}
|
|
156
|
+
}).select().single();
|
|
157
|
+
if (error) throw new Error(error.message);
|
|
158
|
+
return mapTemplate(data);
|
|
159
|
+
},
|
|
160
|
+
async updateTemplate(id, input) {
|
|
161
|
+
const db = getClient();
|
|
162
|
+
const tenantId = getTenantId();
|
|
163
|
+
const { count } = await db.from(T.documents).select("document_id", { count: "exact", head: true }).eq("template_id", id);
|
|
164
|
+
const hasDocuments = (count ?? 0) > 0;
|
|
165
|
+
if (hasDocuments && input.schema) {
|
|
166
|
+
const existing = await this.getTemplateById(id);
|
|
167
|
+
if (!existing) throw new Error("Template not found");
|
|
168
|
+
const { data: newRow, error: insertError } = await db.from(T.templates).insert({
|
|
169
|
+
tenant_id: tenantId,
|
|
170
|
+
name: input.name ?? existing.name,
|
|
171
|
+
description: input.description ?? existing.description,
|
|
172
|
+
category: input.category ?? existing.category,
|
|
173
|
+
schema: input.schema,
|
|
174
|
+
specialty: input.specialty ?? existing.specialty,
|
|
175
|
+
tags: input.tags ?? existing.tags,
|
|
176
|
+
metadata: input.metadata ?? existing.metadata,
|
|
177
|
+
is_active: input.isActive ?? existing.isActive,
|
|
178
|
+
version: existing.version + 1,
|
|
179
|
+
is_current: true,
|
|
180
|
+
parent_id: existing.parentId ?? existing.id,
|
|
181
|
+
// Requirement settings belong to the template, not the version being archived.
|
|
182
|
+
required_on_attendance: input.requiredOnAttendance ?? existing.requiredOnAttendance,
|
|
183
|
+
validity_days: input.validityDays !== void 0 ? input.validityDays : existing.validityDays,
|
|
184
|
+
alert_when_missing: input.alertWhenMissing ?? existing.alertWhenMissing
|
|
185
|
+
}).select().single();
|
|
186
|
+
if (insertError) throw new Error(insertError.message);
|
|
187
|
+
await db.from(T.templates).update({ is_current: false, updated_at: (/* @__PURE__ */ new Date()).toISOString() }).eq("id", id);
|
|
188
|
+
return mapTemplate(newRow);
|
|
189
|
+
}
|
|
190
|
+
const updates = { updated_at: (/* @__PURE__ */ new Date()).toISOString() };
|
|
191
|
+
if (input.name !== void 0) updates.name = input.name;
|
|
192
|
+
if (input.description !== void 0) updates.description = input.description;
|
|
193
|
+
if (input.category !== void 0) updates.category = input.category;
|
|
194
|
+
if (input.schema !== void 0) updates.schema = input.schema;
|
|
195
|
+
if (input.specialty !== void 0) updates.specialty = input.specialty;
|
|
196
|
+
if (input.tags !== void 0) updates.tags = input.tags;
|
|
197
|
+
if (input.metadata !== void 0) updates.metadata = input.metadata;
|
|
198
|
+
if (input.isActive !== void 0) updates.is_active = input.isActive;
|
|
199
|
+
if (input.requiredOnAttendance !== void 0) updates.required_on_attendance = input.requiredOnAttendance;
|
|
200
|
+
if (input.validityDays !== void 0) updates.validity_days = input.validityDays;
|
|
201
|
+
if (input.alertWhenMissing !== void 0) updates.alert_when_missing = input.alertWhenMissing;
|
|
202
|
+
const { data, error } = await db.from(T.templates).update(updates).eq("id", id).select().single();
|
|
203
|
+
if (error) throw new Error(error.message);
|
|
204
|
+
return mapTemplate(data);
|
|
205
|
+
},
|
|
206
|
+
async deleteTemplate(id) {
|
|
207
|
+
const db = getClient();
|
|
208
|
+
const { error } = await db.from(T.templates).update({ is_deleted: true, updated_at: (/* @__PURE__ */ new Date()).toISOString() }).eq("id", id);
|
|
209
|
+
if (error) throw new Error(error.message);
|
|
210
|
+
},
|
|
211
|
+
// `v_forms_template_usage` aggregates in the database; pools without that view fall back to a capped client-side tally.
|
|
212
|
+
async getTemplateUsage() {
|
|
213
|
+
const db = getClient();
|
|
214
|
+
const tenantId = getTenantId();
|
|
215
|
+
if (!tenantId) return {};
|
|
216
|
+
const { data, error } = await db.from("v_forms_template_usage").select("template_id, document_count, last_document_at").eq("tenant_id", tenantId);
|
|
217
|
+
if (!error) {
|
|
218
|
+
const usage = {};
|
|
219
|
+
for (const row of data ?? []) {
|
|
220
|
+
usage[row.template_id] = {
|
|
221
|
+
documentCount: Number(row.document_count ?? 0),
|
|
222
|
+
lastDocumentAt: row.last_document_at ?? null
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
return usage;
|
|
226
|
+
}
|
|
227
|
+
return countTemplateUsageClientSide(db, tenantId);
|
|
228
|
+
},
|
|
229
|
+
async getDocuments(query) {
|
|
230
|
+
const db = getClient();
|
|
231
|
+
let qb = db.from("v_documents").select("*", { count: "exact" }).eq("tenant_id", getTenantId()).eq("is_active", true);
|
|
232
|
+
if (query.personId) qb = qb.eq("person_id", query.personId);
|
|
233
|
+
if (query.templateId) qb = qb.eq("template_id", query.templateId);
|
|
234
|
+
if (query.status) {
|
|
235
|
+
const statuses = Array.isArray(query.status) ? query.status : [query.status];
|
|
236
|
+
qb = qb.in("status", statuses);
|
|
237
|
+
}
|
|
238
|
+
if (query.search) qb = qb.or(`title.ilike.%${query.search}%,template_name.ilike.%${query.search}%`);
|
|
239
|
+
if (query.kinds?.length) qb = qb.in("kind", query.kinds);
|
|
240
|
+
if (query.templateCategory) qb = qb.eq("template_category", query.templateCategory);
|
|
241
|
+
const page = query.page ?? 1;
|
|
242
|
+
const pageSize = query.pageSize ?? 50;
|
|
243
|
+
qb = qb.order("created_at", { ascending: false }).range((page - 1) * pageSize, page * pageSize - 1);
|
|
244
|
+
const { data, count, error } = await qb;
|
|
245
|
+
if (error) throw new Error(error.message);
|
|
246
|
+
return { data: (data ?? []).map(mapDocument), total: count ?? 0 };
|
|
247
|
+
},
|
|
248
|
+
// Two light columns grouped client-side: PostgREST has no `group by`.
|
|
249
|
+
async getDocumentCounts(query) {
|
|
250
|
+
const db = getClient();
|
|
251
|
+
let qb = db.from("v_documents").select("kind, template_category").eq("tenant_id", getTenantId()).eq("person_id", query.personId).eq("is_active", true).neq("status", "archived");
|
|
252
|
+
if (query.kinds?.length) qb = qb.in("kind", query.kinds);
|
|
253
|
+
const { data, error } = await qb;
|
|
254
|
+
if (error) return {};
|
|
255
|
+
const counts = {};
|
|
256
|
+
for (const row of data ?? []) {
|
|
257
|
+
const bucket = row.kind === "image" || row.kind === "attachment" ? "file" : row.template_category || "general";
|
|
258
|
+
counts[bucket] = (counts[bucket] ?? 0) + 1;
|
|
259
|
+
}
|
|
260
|
+
return counts;
|
|
261
|
+
},
|
|
262
|
+
async getDocumentById(id) {
|
|
263
|
+
const db = getClient();
|
|
264
|
+
const { data, error } = await db.from("v_documents").select("*").eq("id", id).single();
|
|
265
|
+
if (error || !data) return null;
|
|
266
|
+
return mapDocument(data);
|
|
267
|
+
},
|
|
268
|
+
async createDocument(input) {
|
|
269
|
+
const tenantId = getTenantId();
|
|
270
|
+
const isForm = !!input.templateId;
|
|
271
|
+
const { data: coreDoc, error: coreError } = await getClient().from("documents").insert({
|
|
272
|
+
tenant_id: tenantId,
|
|
273
|
+
kind: input.kind ?? (isForm ? "form" : "attachment"),
|
|
274
|
+
person_id: input.personId,
|
|
275
|
+
title: input.title,
|
|
276
|
+
status: input.status ?? "draft",
|
|
277
|
+
file_url: input.fileUrl,
|
|
278
|
+
file_name: input.fileName,
|
|
279
|
+
file_size: input.fileSize,
|
|
280
|
+
mime_type: input.mimeType,
|
|
281
|
+
subject_type: input.subjectType,
|
|
282
|
+
subject_id: input.subjectId,
|
|
283
|
+
metadata: {}
|
|
284
|
+
}).select().single();
|
|
285
|
+
if (coreError) throw new Error(coreError.message);
|
|
286
|
+
if (isForm && input.templateId) {
|
|
287
|
+
const db = getClient();
|
|
288
|
+
const { error: extError } = await db.from(T.documents).insert({
|
|
289
|
+
document_id: coreDoc.id,
|
|
290
|
+
tenant_id: tenantId,
|
|
291
|
+
template_id: input.templateId,
|
|
292
|
+
data: input.data ?? {},
|
|
293
|
+
product_id: input.productId ?? null,
|
|
294
|
+
professional_id: input.professionalId ?? null
|
|
295
|
+
});
|
|
296
|
+
if (extError) {
|
|
297
|
+
await getClient().from("documents").delete().eq("id", coreDoc.id);
|
|
298
|
+
throw new Error(extError.message);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
const doc = await this.getDocumentById(coreDoc.id);
|
|
302
|
+
return doc;
|
|
303
|
+
},
|
|
304
|
+
async updateDocument(id, input) {
|
|
305
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
306
|
+
const coreUpdates = { updated_at: now };
|
|
307
|
+
if (input.status !== void 0) coreUpdates.status = input.status;
|
|
308
|
+
if (input.title !== void 0) coreUpdates.title = input.title;
|
|
309
|
+
if (input.notes !== void 0) coreUpdates.notes = input.notes;
|
|
310
|
+
const { error: coreError } = await getClient().from("documents").update(coreUpdates).eq("id", id);
|
|
311
|
+
if (coreError) throw new Error(coreError.message);
|
|
312
|
+
if (input.data !== void 0) {
|
|
313
|
+
const db = getClient();
|
|
314
|
+
await db.from(T.documents).update({ data: input.data, updated_at: now }).eq("document_id", id);
|
|
315
|
+
}
|
|
316
|
+
const doc = await this.getDocumentById(id);
|
|
317
|
+
return doc;
|
|
318
|
+
},
|
|
319
|
+
async deleteDocument(id) {
|
|
320
|
+
const { error } = await getClient().from("documents").update({ is_active: false, updated_at: (/* @__PURE__ */ new Date()).toISOString() }).eq("id", id);
|
|
321
|
+
if (error) throw new Error(error.message);
|
|
322
|
+
},
|
|
323
|
+
async getDocumentFiles(documentId) {
|
|
324
|
+
const db = getClient();
|
|
325
|
+
const { data, error } = await db.from(T.documentFiles).select("*").eq("document_id", documentId).order("sort_order");
|
|
326
|
+
if (error) throw new Error(error.message);
|
|
327
|
+
return (data ?? []).map(mapFile);
|
|
328
|
+
},
|
|
329
|
+
async uploadFile(documentId, fieldKey, file) {
|
|
330
|
+
const db = getClient();
|
|
331
|
+
const tenantId = getTenantId();
|
|
332
|
+
const ext = file.name.split(".").pop() ?? "bin";
|
|
333
|
+
const storagePath = `${tenantId}/forms/${documentId}/${fieldKey}/${crypto.randomUUID()}.${ext}`;
|
|
334
|
+
const { error: uploadError } = await db.storage.from("documents").upload(storagePath, file, { contentType: file.type });
|
|
335
|
+
if (uploadError) throw new Error(uploadError.message);
|
|
336
|
+
const { data: urlData } = db.storage.from("documents").getPublicUrl(storagePath);
|
|
337
|
+
const { data, error } = await db.from(T.documentFiles).insert({
|
|
338
|
+
tenant_id: tenantId,
|
|
339
|
+
document_id: documentId,
|
|
340
|
+
field_key: fieldKey,
|
|
341
|
+
file_url: urlData.publicUrl,
|
|
342
|
+
file_name: file.name,
|
|
343
|
+
file_size: file.size,
|
|
344
|
+
mime_type: file.type
|
|
345
|
+
}).select().single();
|
|
346
|
+
if (error) throw new Error(error.message);
|
|
347
|
+
return mapFile(data);
|
|
348
|
+
},
|
|
349
|
+
async uploadMedia(blob, folder, fileName) {
|
|
350
|
+
const tenantId = getTenantId();
|
|
351
|
+
const ext = (fileName.split(".").pop() || "bin").toLowerCase();
|
|
352
|
+
const path = `${tenantId}/forms/${folder}/${crypto.randomUUID()}.${ext}`;
|
|
353
|
+
const { error } = await getClient().storage.from(MEDIA_BUCKET).upload(path, blob, { contentType: blob.type || void 0, upsert: false });
|
|
354
|
+
if (error) throw new Error(error.message);
|
|
355
|
+
return { path };
|
|
356
|
+
},
|
|
357
|
+
async mediaUrl(path) {
|
|
358
|
+
const cached = signedCache.get(path);
|
|
359
|
+
if (cached && cached.until > Date.now()) return cached.url;
|
|
360
|
+
const { data, error } = await getClient().storage.from(MEDIA_BUCKET).createSignedUrl(path, SIGNED_TTL_S);
|
|
361
|
+
if (error || !data?.signedUrl) return null;
|
|
362
|
+
signedCache.set(path, { url: data.signedUrl, until: Date.now() + (SIGNED_TTL_S - 60) * 1e3 });
|
|
363
|
+
return data.signedUrl;
|
|
364
|
+
},
|
|
365
|
+
async searchCatalog(term) {
|
|
366
|
+
const db = getClient();
|
|
367
|
+
const tenantId = getTenantId();
|
|
368
|
+
const like = term.trim() ? `%${term.trim()}%` : null;
|
|
369
|
+
let sq = db.from("products").select("id, name, price, category:categories(name)").eq("tenant_id", tenantId).eq("kind", "service").eq("is_active", true).order("name").limit(500);
|
|
370
|
+
if (like) sq = sq.ilike("name", like);
|
|
371
|
+
let pq = db.from("service_packages").select("id, name, price").eq("tenant_id", tenantId).eq("is_active", true).order("name").limit(100);
|
|
372
|
+
if (like) pq = pq.ilike("name", like);
|
|
373
|
+
const [sv, pk] = await Promise.all([sq, pq]);
|
|
374
|
+
if (sv.error) throw new Error(sv.error.message);
|
|
375
|
+
return [
|
|
376
|
+
...(sv.data ?? []).map((r) => ({
|
|
377
|
+
kind: "service",
|
|
378
|
+
id: r.id,
|
|
379
|
+
name: r.name,
|
|
380
|
+
price: Number(r.price) || 0,
|
|
381
|
+
group: r.category?.name ?? null
|
|
382
|
+
})),
|
|
383
|
+
...(pk.data ?? []).map((r) => ({
|
|
384
|
+
kind: "package",
|
|
385
|
+
id: r.id,
|
|
386
|
+
name: r.name,
|
|
387
|
+
price: Number(r.price) || 0,
|
|
388
|
+
group: null
|
|
389
|
+
}))
|
|
390
|
+
];
|
|
391
|
+
},
|
|
392
|
+
async getLatestAnswers(personId, templateIds) {
|
|
393
|
+
if (!templateIds.length) return [];
|
|
394
|
+
const { data, error } = await getClient().from(T.documents).select("template_id, data, document:documents!inner(person_id, created_at, updated_at)").in("template_id", templateIds).eq("document.person_id", personId).order("created_at", { ascending: false, referencedTable: "documents" }).limit(50);
|
|
395
|
+
if (error) throw new Error(error.message);
|
|
396
|
+
const latest = /* @__PURE__ */ new Map();
|
|
397
|
+
for (const r of data ?? []) {
|
|
398
|
+
const at = r.document?.updated_at ?? r.document?.created_at ?? "";
|
|
399
|
+
const cur = latest.get(r.template_id);
|
|
400
|
+
if (!cur || at > cur.at) latest.set(r.template_id, { templateId: r.template_id, data: r.data ?? {}, at });
|
|
401
|
+
}
|
|
402
|
+
return [...latest.values()];
|
|
403
|
+
},
|
|
404
|
+
async getPackageServices(packageId) {
|
|
405
|
+
const { data, error } = await getClient().from("service_package_items").select("product_id, quantity, unit_price, product:products(name, price, kind)").eq("package_id", packageId).order("sort_order");
|
|
406
|
+
if (error) throw new Error(error.message);
|
|
407
|
+
return (data ?? []).filter((r) => r.product_id && (r.product?.kind ?? "service") === "service").map((r) => ({
|
|
408
|
+
serviceId: r.product_id,
|
|
409
|
+
name: r.product?.name ?? "",
|
|
410
|
+
unitPrice: Number(r.unit_price ?? r.product?.price) || 0,
|
|
411
|
+
quantity: Math.max(1, Number(r.quantity) || 1)
|
|
412
|
+
}));
|
|
413
|
+
},
|
|
414
|
+
async deleteFile(fileId) {
|
|
415
|
+
const db = getClient();
|
|
416
|
+
const { error } = await db.from(T.documentFiles).delete().eq("id", fileId);
|
|
417
|
+
if (error) throw new Error(error.message);
|
|
418
|
+
},
|
|
419
|
+
async getPersonRequirements(personId) {
|
|
420
|
+
const tenantId = getTenantId();
|
|
421
|
+
if (!tenantId) return [];
|
|
422
|
+
return getPersonFormRequirements(tenantId, personId);
|
|
423
|
+
}
|
|
424
|
+
};
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
export { createSupabaseFormsProvider, getPersonFormRequirements };
|
|
428
|
+
//# sourceMappingURL=chunk-JO4REIZC.js.map
|
|
429
|
+
//# sourceMappingURL=chunk-JO4REIZC.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/data/supabase.ts"],"names":[],"mappings":"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","file":"chunk-JO4REIZC.js","sourcesContent":["import type { SupabaseClient } from '@supabase/supabase-js'\nimport { getActiveTenantId, getSupabaseClientOptional } from '@fayz-ai/core'\nimport type {\n CreateDocumentInput,\n DocumentCounts,\n DocumentFile,\n DocumentQuery,\n DocumentStatus,\n FormDocument,\n PaginatedResult,\n UpdateDocumentInput,\n} from '../domain/document'\nimport type { PersonFormRequirement, RequirementState } from '../domain/requirement'\nimport type {\n CreateTemplateInput,\n FormSchema,\n FormTemplate,\n TemplateCategory,\n TemplateQuery,\n TemplateUsage,\n UpdateTemplateInput,\n} from '../domain/template'\nimport type { FormsDataProvider } from './provider'\nimport { T } from './tables'\n\ninterface TemplateRow {\n id: string\n tenant_id: string\n name: string\n description?: string\n category: TemplateCategory\n version: number\n is_current: boolean\n parent_id?: string\n schema: FormSchema\n specialty?: string\n tags: string[] | null\n metadata: Record<string, unknown> | null\n is_active: boolean\n is_deleted: boolean\n required_on_attendance: boolean | null\n validity_days: number | null\n alert_when_missing: boolean | null\n created_by?: string\n updated_by?: string\n created_at: string\n updated_at: string\n}\n\ninterface DocumentRow {\n id: string\n tenant_id: string\n template_id: string | null\n template_name?: string\n template_category?: TemplateCategory\n person_id?: string\n person_name?: string\n title?: string\n form_data: Record<string, unknown> | null\n status: DocumentStatus\n signed_at?: string\n signed_by?: string\n notes?: string\n metadata: Record<string, unknown> | null\n created_by?: string\n updated_by?: string\n created_at: string\n updated_at: string\n kind?: string\n file_url?: string\n file_name?: string\n file_size?: number\n mime_type?: string\n}\n\ninterface FileRow {\n id: string\n tenant_id: string\n document_id: string\n field_key: string\n file_url: string\n file_name?: string\n file_size?: number\n mime_type?: string\n sort_order: number\n metadata: Record<string, unknown> | null\n created_at: string\n}\n\ninterface RequirementRow {\n template_id: string\n template_name: string\n category: string\n validity_days: number | null\n alert_when_missing?: boolean\n last_filled_at: string | null\n valid_until: string | null\n state: RequirementState\n}\n\nfunction getTenantId(): string {\n return getActiveTenantId() ?? ''\n}\n\nfunction getClient(): SupabaseClient {\n const supabase = getSupabaseClientOptional() as SupabaseClient | null\n if (!supabase) throw new Error('Supabase not initialized')\n return supabase\n}\n\n// Patient photos are health data: private bucket, tenant-folder RLS, signed reads.\nconst MEDIA_BUCKET = 'attachments'\nconst SIGNED_TTL_S = 3600\nconst signedCache = new Map<string, { url: string; until: number }>()\n\n/** Teto do fallback de contagem. Acima disso a agregação no navegador estaria\n * somando um recorte e chamando de total — some em vez de mentir. */\n/** Above this the client-side tally would be a partial count passed off as a total, so it gives up. */\nconst USAGE_FALLBACK_LIMIT = 5000\n\nasync function countTemplateUsageClientSide(\n db: SupabaseClient,\n tenantId: string,\n): Promise<Record<string, TemplateUsage>> {\n try {\n const { data, error, count } = await db\n .from('v_documents')\n .select('template_id, created_at', { count: 'exact' })\n .eq('tenant_id', tenantId)\n .eq('is_active', true)\n .not('template_id', 'is', null)\n .limit(USAGE_FALLBACK_LIMIT)\n\n if (error) return {}\n if (typeof count === 'number' && count > USAGE_FALLBACK_LIMIT) return {}\n\n const usage: Record<string, TemplateUsage> = {}\n for (const row of (data ?? []) as Pick<DocumentRow, 'template_id' | 'created_at'>[]) {\n if (!row.template_id) continue\n const current = usage[row.template_id] ?? { documentCount: 0, lastDocumentAt: null }\n current.documentCount += 1\n if (!current.lastDocumentAt || row.created_at > current.lastDocumentAt) {\n current.lastDocumentAt = row.created_at\n }\n usage[row.template_id] = current\n }\n return usage\n } catch {\n return {}\n }\n}\n\nfunction mapTemplate(r: TemplateRow): FormTemplate {\n return {\n requiredOnAttendance: r.required_on_attendance ?? false,\n validityDays: r.validity_days ?? null,\n alertWhenMissing: r.alert_when_missing ?? false,\n id: r.id,\n tenantId: r.tenant_id,\n name: r.name,\n description: r.description,\n category: r.category,\n version: r.version,\n isCurrent: r.is_current,\n parentId: r.parent_id,\n schema: r.schema,\n specialty: r.specialty,\n tags: r.tags ?? [],\n metadata: r.metadata ?? {},\n isActive: r.is_active,\n isDeleted: r.is_deleted,\n createdBy: r.created_by,\n updatedBy: r.updated_by,\n createdAt: r.created_at,\n updatedAt: r.updated_at,\n }\n}\n\nfunction mapDocument(r: DocumentRow): FormDocument {\n return {\n id: r.id,\n tenantId: r.tenant_id,\n templateId: r.template_id ?? '',\n templateName: r.template_name,\n templateCategory: r.template_category,\n personId: r.person_id,\n personName: r.person_name,\n title: r.title,\n data: r.form_data ?? {},\n status: r.status,\n signedAt: r.signed_at,\n signedBy: r.signed_by,\n notes: r.notes,\n metadata: r.metadata ?? {},\n isDeleted: false,\n createdBy: r.created_by,\n updatedBy: r.updated_by,\n createdAt: r.created_at,\n updatedAt: r.updated_at,\n kind: r.kind,\n fileUrl: r.file_url,\n fileName: r.file_name,\n fileSize: r.file_size,\n mimeType: r.mime_type,\n }\n}\n\nfunction mapFile(r: FileRow): DocumentFile {\n return {\n id: r.id,\n tenantId: r.tenant_id,\n documentId: r.document_id,\n fieldKey: r.field_key,\n fileUrl: r.file_url,\n fileName: r.file_name,\n fileSize: r.file_size,\n mimeType: r.mime_type,\n sortOrder: r.sort_order,\n metadata: r.metadata ?? {},\n createdAt: r.created_at,\n }\n}\n\nfunction mapRequirement(r: RequirementRow): PersonFormRequirement {\n return {\n templateId: r.template_id,\n templateName: r.template_name,\n category: r.category,\n validityDays: r.validity_days ?? null,\n alertWhenMissing: r.alert_when_missing ?? false,\n lastFilledAt: r.last_filled_at ?? null,\n validUntil: r.valid_until ?? null,\n state: r.state,\n }\n}\n\n/** The forms this person must have filled in this tenant; empty when the house requires none. */\nexport async function getPersonFormRequirements(tenantId: string, personId: string): Promise<PersonFormRequirement[]> {\n const { data, error } = await getClient().rpc('forms_person_requirements', {\n p_tenant_id: tenantId,\n p_person_id: personId,\n })\n if (error) throw error\n return ((data ?? []) as RequirementRow[]).map(mapRequirement)\n}\n\nexport function createSupabaseFormsProvider(): FormsDataProvider {\n return {\n async getTemplates(query: TemplateQuery): Promise<PaginatedResult<FormTemplate>> {\n const db = getClient()\n let qb = db\n .from(T.templates)\n .select('*', { count: 'exact' })\n .eq('tenant_id', getTenantId())\n .eq('is_current', true)\n .eq('is_deleted', false)\n // Archived templates (isActive=false) leave the list and the picker.\n .eq('is_active', true)\n\n if (query.category) qb = qb.eq('category', query.category)\n if (query.search) qb = qb.ilike('name', `%${query.search}%`)\n\n const page = query.page ?? 1\n const pageSize = query.pageSize ?? 25\n qb = qb\n .order('updated_at', { ascending: false })\n .range((page - 1) * pageSize, page * pageSize - 1)\n\n const { data, count, error } = await qb\n if (error) throw new Error(error.message)\n return { data: ((data ?? []) as TemplateRow[]).map(mapTemplate), total: count ?? 0 }\n },\n\n async getTemplateById(id: string): Promise<FormTemplate | null> {\n const db = getClient()\n const { data, error } = await db\n .from(T.templates)\n .select('*')\n .eq('id', id)\n .eq('is_deleted', false)\n .single()\n if (error || !data) return null\n return mapTemplate(data)\n },\n\n async createTemplate(input: CreateTemplateInput): Promise<FormTemplate> {\n const db = getClient()\n const tenantId = getTenantId()\n const { data, error } = await db\n .from(T.templates)\n .insert({\n tenant_id: tenantId,\n name: input.name,\n description: input.description,\n category: input.category,\n schema: input.schema,\n specialty: input.specialty,\n tags: input.tags ?? [],\n metadata: input.metadata ?? {},\n })\n .select()\n .single()\n if (error) throw new Error(error.message)\n return mapTemplate(data)\n },\n\n async updateTemplate(id: string, input: UpdateTemplateInput): Promise<FormTemplate> {\n const db = getClient()\n const tenantId = getTenantId()\n\n const { count } = await db\n .from(T.documents)\n .select('document_id', { count: 'exact', head: true })\n .eq('template_id', id)\n\n const hasDocuments = (count ?? 0) > 0\n\n if (hasDocuments && input.schema) {\n const existing = await this.getTemplateById(id)\n if (!existing) throw new Error('Template not found')\n\n const { data: newRow, error: insertError } = await db\n .from(T.templates)\n .insert({\n tenant_id: tenantId,\n name: input.name ?? existing.name,\n description: input.description ?? existing.description,\n category: input.category ?? existing.category,\n schema: input.schema,\n specialty: input.specialty ?? existing.specialty,\n tags: input.tags ?? existing.tags,\n metadata: input.metadata ?? existing.metadata,\n is_active: input.isActive ?? existing.isActive,\n version: existing.version + 1,\n is_current: true,\n parent_id: existing.parentId ?? existing.id,\n // Requirement settings belong to the template, not the version being archived.\n required_on_attendance: input.requiredOnAttendance ?? existing.requiredOnAttendance,\n validity_days: input.validityDays !== undefined ? input.validityDays : existing.validityDays,\n alert_when_missing: input.alertWhenMissing ?? existing.alertWhenMissing,\n })\n .select()\n .single()\n if (insertError) throw new Error(insertError.message)\n\n await db\n .from(T.templates)\n .update({ is_current: false, updated_at: new Date().toISOString() })\n .eq('id', id)\n\n return mapTemplate(newRow)\n }\n\n const updates: Record<string, unknown> = { updated_at: new Date().toISOString() }\n if (input.name !== undefined) updates.name = input.name\n if (input.description !== undefined) updates.description = input.description\n if (input.category !== undefined) updates.category = input.category\n if (input.schema !== undefined) updates.schema = input.schema\n if (input.specialty !== undefined) updates.specialty = input.specialty\n if (input.tags !== undefined) updates.tags = input.tags\n if (input.metadata !== undefined) updates.metadata = input.metadata\n if (input.isActive !== undefined) updates.is_active = input.isActive\n if (input.requiredOnAttendance !== undefined) updates.required_on_attendance = input.requiredOnAttendance\n if (input.validityDays !== undefined) updates.validity_days = input.validityDays\n if (input.alertWhenMissing !== undefined) updates.alert_when_missing = input.alertWhenMissing\n\n const { data, error } = await db\n .from(T.templates)\n .update(updates)\n .eq('id', id)\n .select()\n .single()\n if (error) throw new Error(error.message)\n return mapTemplate(data)\n },\n\n async deleteTemplate(id: string): Promise<void> {\n const db = getClient()\n const { error } = await db\n .from(T.templates)\n .update({ is_deleted: true, updated_at: new Date().toISOString() })\n .eq('id', id)\n if (error) throw new Error(error.message)\n },\n\n // `v_forms_template_usage` aggregates in the database; pools without that view fall back to a capped client-side tally.\n async getTemplateUsage(): Promise<Record<string, TemplateUsage>> {\n const db = getClient()\n const tenantId = getTenantId()\n if (!tenantId) return {}\n\n const { data, error } = await db\n .from('v_forms_template_usage')\n .select('template_id, document_count, last_document_at')\n .eq('tenant_id', tenantId)\n\n if (!error) {\n const usage: Record<string, TemplateUsage> = {}\n for (const row of (data ?? []) as { template_id: string; document_count: number | null; last_document_at: string | null }[]) {\n usage[row.template_id] = {\n documentCount: Number(row.document_count ?? 0),\n lastDocumentAt: row.last_document_at ?? null,\n }\n }\n return usage\n }\n\n return countTemplateUsageClientSide(db, tenantId)\n },\n\n\n async getDocuments(query: DocumentQuery): Promise<PaginatedResult<FormDocument>> {\n const db = getClient()\n let qb = db\n .from('v_documents')\n .select('*', { count: 'exact' })\n .eq('tenant_id', getTenantId())\n .eq('is_active', true)\n\n if (query.personId) qb = qb.eq('person_id', query.personId)\n if (query.templateId) qb = qb.eq('template_id', query.templateId)\n if (query.status) {\n const statuses = Array.isArray(query.status) ? query.status : [query.status]\n qb = qb.in('status', statuses)\n }\n if (query.search) qb = qb.or(`title.ilike.%${query.search}%,template_name.ilike.%${query.search}%`)\n // Filtered on the server: filtering a page afterwards hid every photo of a long record.\n if (query.kinds?.length) qb = qb.in('kind', query.kinds)\n if (query.templateCategory) qb = qb.eq('template_category', query.templateCategory)\n\n const page = query.page ?? 1\n const pageSize = query.pageSize ?? 50\n qb = qb\n .order('created_at', { ascending: false })\n .range((page - 1) * pageSize, page * pageSize - 1)\n\n const { data, count, error } = await qb\n if (error) throw new Error(error.message)\n return { data: ((data ?? []) as DocumentRow[]).map(mapDocument), total: count ?? 0 }\n },\n\n // Two light columns grouped client-side: PostgREST has no `group by`.\n async getDocumentCounts(query: { personId: string; kinds?: string[] }): Promise<DocumentCounts> {\n const db = getClient()\n let qb = db\n .from('v_documents')\n .select('kind, template_category')\n .eq('tenant_id', getTenantId())\n .eq('person_id', query.personId)\n .eq('is_active', true)\n .neq('status', 'archived')\n if (query.kinds?.length) qb = qb.in('kind', query.kinds)\n\n const { data, error } = await qb\n if (error) return {}\n const counts: DocumentCounts = {}\n for (const row of (data ?? []) as Pick<DocumentRow, 'kind' | 'template_category'>[]) {\n const bucket = row.kind === 'image' || row.kind === 'attachment'\n ? 'file'\n : (row.template_category || 'general')\n counts[bucket] = (counts[bucket] ?? 0) + 1\n }\n return counts\n },\n\n async getDocumentById(id: string): Promise<FormDocument | null> {\n const db = getClient()\n const { data, error } = await db\n .from('v_documents')\n .select('*')\n .eq('id', id)\n .single()\n if (error || !data) return null\n return mapDocument(data)\n },\n\n async createDocument(input: CreateDocumentInput): Promise<FormDocument> {\n const tenantId = getTenantId()\n const isForm = !!input.templateId\n\n const { data: coreDoc, error: coreError } = await getClient()\n .from('documents')\n .insert({\n tenant_id: tenantId,\n kind: input.kind ?? (isForm ? 'form' : 'attachment'),\n person_id: input.personId,\n title: input.title,\n status: input.status ?? 'draft',\n file_url: input.fileUrl,\n file_name: input.fileName,\n file_size: input.fileSize,\n mime_type: input.mimeType,\n subject_type: input.subjectType,\n subject_id: input.subjectId,\n metadata: {},\n })\n .select()\n .single()\n if (coreError) throw new Error(coreError.message)\n\n if (isForm && input.templateId) {\n const db = getClient()\n const { error: extError } = await db\n .from(T.documents)\n .insert({\n document_id: coreDoc.id,\n tenant_id: tenantId,\n template_id: input.templateId,\n data: input.data ?? {},\n product_id: input.productId ?? null,\n professional_id: input.professionalId ?? null,\n })\n if (extError) {\n // Undo the core row so no orphan document is left behind.\n await getClient().from('documents').delete().eq('id', coreDoc.id)\n throw new Error(extError.message)\n }\n }\n\n const doc = await this.getDocumentById(coreDoc.id)\n return doc!\n },\n\n async updateDocument(id: string, input: UpdateDocumentInput): Promise<FormDocument> {\n const now = new Date().toISOString()\n\n const coreUpdates: Record<string, unknown> = { updated_at: now }\n if (input.status !== undefined) coreUpdates.status = input.status\n if (input.title !== undefined) coreUpdates.title = input.title\n if (input.notes !== undefined) coreUpdates.notes = input.notes\n\n const { error: coreError } = await getClient()\n .from('documents')\n .update(coreUpdates)\n .eq('id', id)\n if (coreError) throw new Error(coreError.message)\n\n if (input.data !== undefined) {\n const db = getClient()\n await db\n .from(T.documents)\n .update({ data: input.data, updated_at: now })\n .eq('document_id', id)\n }\n\n const doc = await this.getDocumentById(id)\n return doc!\n },\n\n async deleteDocument(id: string): Promise<void> {\n const { error } = await getClient()\n .from('documents')\n .update({ is_active: false, updated_at: new Date().toISOString() })\n .eq('id', id)\n if (error) throw new Error(error.message)\n },\n\n\n async getDocumentFiles(documentId: string): Promise<DocumentFile[]> {\n const db = getClient()\n const { data, error } = await db\n .from(T.documentFiles)\n .select('*')\n .eq('document_id', documentId)\n .order('sort_order')\n if (error) throw new Error(error.message)\n return ((data ?? []) as FileRow[]).map(mapFile)\n },\n\n async uploadFile(documentId: string, fieldKey: string, file: File): Promise<DocumentFile> {\n const db = getClient()\n const tenantId = getTenantId()\n const ext = file.name.split('.').pop() ?? 'bin'\n const storagePath = `${tenantId}/forms/${documentId}/${fieldKey}/${crypto.randomUUID()}.${ext}`\n\n const { error: uploadError } = await db.storage\n .from('documents')\n .upload(storagePath, file, { contentType: file.type })\n if (uploadError) throw new Error(uploadError.message)\n\n const { data: urlData } = db.storage.from('documents').getPublicUrl(storagePath)\n\n const { data, error } = await db\n .from(T.documentFiles)\n .insert({\n tenant_id: tenantId,\n document_id: documentId,\n field_key: fieldKey,\n file_url: urlData.publicUrl,\n file_name: file.name,\n file_size: file.size,\n mime_type: file.type,\n })\n .select()\n .single()\n if (error) throw new Error(error.message)\n return mapFile(data)\n },\n\n async uploadMedia(blob: Blob, folder: string, fileName: string) {\n const tenantId = getTenantId()\n const ext = (fileName.split('.').pop() || 'bin').toLowerCase()\n const path = `${tenantId}/forms/${folder}/${crypto.randomUUID()}.${ext}`\n const { error } = await getClient().storage\n .from(MEDIA_BUCKET)\n .upload(path, blob, { contentType: blob.type || undefined, upsert: false })\n if (error) throw new Error(error.message)\n return { path }\n },\n\n async mediaUrl(path: string) {\n const cached = signedCache.get(path)\n if (cached && cached.until > Date.now()) return cached.url\n const { data, error } = await getClient().storage.from(MEDIA_BUCKET).createSignedUrl(path, SIGNED_TTL_S)\n if (error || !data?.signedUrl) return null\n signedCache.set(path, { url: data.signedUrl, until: Date.now() + (SIGNED_TTL_S - 60) * 1000 })\n return data.signedUrl\n },\n\n async searchCatalog(term: string) {\n const db = getClient()\n const tenantId = getTenantId()\n const like = term.trim() ? `%${term.trim()}%` : null\n let sq = db.from('products')\n .select('id, name, price, category:categories(name)')\n .eq('tenant_id', tenantId).eq('kind', 'service').eq('is_active', true)\n .order('name').limit(500)\n if (like) sq = sq.ilike('name', like)\n let pq = db.from('service_packages')\n .select('id, name, price')\n .eq('tenant_id', tenantId).eq('is_active', true)\n .order('name').limit(100)\n if (like) pq = pq.ilike('name', like)\n const [sv, pk] = await Promise.all([sq, pq])\n if (sv.error) throw new Error(sv.error.message)\n return [\n ...((sv.data ?? []) as any[]).map((r) => ({\n kind: 'service' as const, id: r.id, name: r.name, price: Number(r.price) || 0,\n group: r.category?.name ?? null,\n })),\n ...((pk.data ?? []) as any[]).map((r) => ({\n kind: 'package' as const, id: r.id, name: r.name, price: Number(r.price) || 0, group: null,\n })),\n ]\n },\n\n async getLatestAnswers(personId: string, templateIds: string[]) {\n if (!templateIds.length) return []\n const { data, error } = await getClient()\n .from(T.documents)\n .select('template_id, data, document:documents!inner(person_id, created_at, updated_at)')\n .in('template_id', templateIds)\n .eq('document.person_id', personId)\n .order('created_at', { ascending: false, referencedTable: 'documents' })\n .limit(50)\n if (error) throw new Error(error.message)\n const latest = new Map<string, { templateId: string; data: Record<string, unknown>; at: string }>()\n for (const r of (data ?? []) as any[]) {\n const at = r.document?.updated_at ?? r.document?.created_at ?? ''\n const cur = latest.get(r.template_id)\n if (!cur || at > cur.at) latest.set(r.template_id, { templateId: r.template_id, data: r.data ?? {}, at })\n }\n return [...latest.values()]\n },\n\n async getPackageServices(packageId: string) {\n const { data, error } = await getClient()\n .from('service_package_items')\n .select('product_id, quantity, unit_price, product:products(name, price, kind)')\n .eq('package_id', packageId)\n .order('sort_order')\n if (error) throw new Error(error.message)\n return ((data ?? []) as any[])\n .filter((r) => r.product_id && (r.product?.kind ?? 'service') === 'service')\n .map((r) => ({\n serviceId: r.product_id,\n name: r.product?.name ?? '',\n unitPrice: Number(r.unit_price ?? r.product?.price) || 0,\n quantity: Math.max(1, Number(r.quantity) || 1),\n }))\n },\n\n async deleteFile(fileId: string): Promise<void> {\n const db = getClient()\n const { error } = await db\n .from(T.documentFiles)\n .delete()\n .eq('id', fileId)\n if (error) throw new Error(error.message)\n },\n\n async getPersonRequirements(personId: string): Promise<PersonFormRequirement[]> {\n const tenantId = getTenantId()\n if (!tenantId) return []\n return getPersonFormRequirements(tenantId, personId)\n },\n }\n}\n"]}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// src/data/tables.ts
|
|
2
|
+
var T = {
|
|
3
|
+
documents: "plg_forms_documents",
|
|
4
|
+
templates: "plg_forms_templates",
|
|
5
|
+
documentFiles: "plg_forms_document_files",
|
|
6
|
+
categories: "plg_forms_categories"
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export { T };
|
|
10
|
+
//# sourceMappingURL=chunk-L2KPTYH4.js.map
|
|
11
|
+
//# sourceMappingURL=chunk-L2KPTYH4.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/data/tables.ts"],"names":[],"mappings":";AAAO,IAAM,CAAA,GAAI;AAAA,EACf,SAAA,EAAW,qBAAA;AAAA,EACX,SAAA,EAAW,qBAAA;AAAA,EACX,aAAA,EAAe,0BAAA;AAAA,EACf,UAAA,EAAY;AACd","file":"chunk-L2KPTYH4.js","sourcesContent":["export const T = {\n documents: 'plg_forms_documents',\n templates: 'plg_forms_templates',\n documentFiles: 'plg_forms_document_files',\n categories: 'plg_forms_categories',\n} as const\n"]}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
// src/domain/requirement.ts
|
|
2
|
+
var isOutstanding = (r) => r.state !== "valid";
|
|
3
|
+
var shownInChecklist = (r) => r.alertWhenMissing || !isOutstanding(r);
|
|
4
|
+
var DAY_MS = 864e5;
|
|
5
|
+
function personRequirements(templates, documents, personId, today) {
|
|
6
|
+
return templates.filter((t) => t.requiredOnAttendance && t.isActive && !t.isDeleted).map((t) => {
|
|
7
|
+
const lastFilledAt = documents.filter((d) => d.personId === personId && d.templateId === t.id).map((d) => d.signedAt ?? d.createdAt).reduce((latest, at) => latest && latest >= at ? latest : at, null);
|
|
8
|
+
const validUntil = lastFilledAt && t.validityDays != null ? new Date(Date.parse(lastFilledAt) + t.validityDays * DAY_MS).toISOString().slice(0, 10) : null;
|
|
9
|
+
const state = !lastFilledAt ? "pending" : validUntil && validUntil < today ? "expired" : "valid";
|
|
10
|
+
return {
|
|
11
|
+
templateId: t.id,
|
|
12
|
+
templateName: t.name,
|
|
13
|
+
category: t.category,
|
|
14
|
+
validityDays: t.validityDays,
|
|
15
|
+
alertWhenMissing: t.alertWhenMissing,
|
|
16
|
+
lastFilledAt,
|
|
17
|
+
validUntil,
|
|
18
|
+
state
|
|
19
|
+
};
|
|
20
|
+
}).sort((a, b) => a.templateName.localeCompare(b.templateName));
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export { isOutstanding, personRequirements, shownInChecklist };
|
|
24
|
+
//# sourceMappingURL=chunk-LQJONAVT.js.map
|
|
25
|
+
//# sourceMappingURL=chunk-LQJONAVT.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/domain/requirement.ts"],"names":[],"mappings":";AAqBO,IAAM,aAAA,GAAgB,CAAC,CAAA,KAAsC,CAAA,CAAE,KAAA,KAAU;AAGzE,IAAM,mBAAmB,CAAC,CAAA,KAAsC,EAAE,gBAAA,IAAoB,CAAC,cAAc,CAAC;AAE7G,IAAM,MAAA,GAAS,KAAA;AAMR,SAAS,kBAAA,CACd,SAAA,EACA,SAAA,EACA,QAAA,EACA,KAAA,EACyB;AACzB,EAAA,OAAO,SAAA,CACJ,MAAA,CAAO,CAAC,CAAA,KAAM,EAAE,oBAAA,IAAwB,CAAA,CAAE,QAAA,IAAY,CAAC,CAAA,CAAE,SAAS,CAAA,CAClE,GAAA,CAAI,CAAC,CAAA,KAAM;AACV,IAAA,MAAM,YAAA,GAAe,SAAA,CAClB,MAAA,CAAO,CAAC,CAAA,KAAM,CAAA,CAAE,QAAA,KAAa,QAAA,IAAY,CAAA,CAAE,UAAA,KAAe,CAAA,CAAE,EAAE,EAC9D,GAAA,CAAI,CAAC,CAAA,KAAM,CAAA,CAAE,QAAA,IAAY,CAAA,CAAE,SAAS,CAAA,CACpC,MAAA,CAAsB,CAAC,MAAA,EAAQ,EAAA,KAAQ,MAAA,IAAU,MAAA,IAAU,EAAA,GAAK,MAAA,GAAS,IAAK,IAAI,CAAA;AACrF,IAAA,MAAM,UAAA,GAAa,gBAAgB,CAAA,CAAE,YAAA,IAAgB,OACjD,IAAI,IAAA,CAAK,KAAK,KAAA,CAAM,YAAY,IAAI,CAAA,CAAE,YAAA,GAAe,MAAM,CAAA,CAAE,WAAA,GAAc,KAAA,CAAM,CAAA,EAAG,EAAE,CAAA,GACtF,IAAA;AACJ,IAAA,MAAM,QAA0B,CAAC,YAAA,GAAe,YAC5C,UAAA,IAAc,UAAA,GAAa,QAAQ,SAAA,GACnC,OAAA;AACJ,IAAA,OAAO;AAAA,MACL,YAAY,CAAA,CAAE,EAAA;AAAA,MACd,cAAc,CAAA,CAAE,IAAA;AAAA,MAChB,UAAU,CAAA,CAAE,QAAA;AAAA,MACZ,cAAc,CAAA,CAAE,YAAA;AAAA,MAChB,kBAAkB,CAAA,CAAE,gBAAA;AAAA,MACpB,YAAA;AAAA,MACA,UAAA;AAAA,MACA;AAAA,KACF;AAAA,EACF,CAAC,CAAA,CACA,IAAA,CAAK,CAAC,CAAA,EAAG,CAAA,KAAM,CAAA,CAAE,YAAA,CAAa,aAAA,CAAc,CAAA,CAAE,YAAY,CAAC,CAAA;AAChE","file":"chunk-LQJONAVT.js","sourcesContent":["import type { FormDocument } from './document'\nimport type { FormTemplate } from './template'\n\n/** Decided by the database (`forms_person_requirements`), so every screen agrees on validity. */\nexport type RequirementState = 'pending' | 'expired' | 'valid'\n\nexport interface PersonFormRequirement {\n templateId: string\n templateName: string\n category: string\n validityDays: number | null\n /** The template asks for a missing answer to be shown up front, not only in the checklist. */\n alertWhenMissing: boolean\n /** `null` when never filled. */\n lastFilledAt: string | null\n /** `null` when it never expires or was never filled. */\n validUntil: string | null\n state: RequirementState\n}\n\n/** Never filled or expired. */\nexport const isOutstanding = (r: PersonFormRequirement): boolean => r.state !== 'valid'\n\n/** What the person checklist shows: everything up to date, and only the gaps that ask to alert. */\nexport const shownInChecklist = (r: PersonFormRequirement): boolean => r.alertWhenMissing || !isOutstanding(r)\n\nconst DAY_MS = 86_400_000\n\n/**\n * The same answer `forms_person_requirements` gives in SQL: every active template required on\n * attendance, with the person's latest answer (signed, else created) and whether it still holds on `today`.\n */\nexport function personRequirements(\n templates: readonly FormTemplate[],\n documents: readonly FormDocument[],\n personId: string,\n today: string,\n): PersonFormRequirement[] {\n return templates\n .filter((t) => t.requiredOnAttendance && t.isActive && !t.isDeleted)\n .map((t) => {\n const lastFilledAt = documents\n .filter((d) => d.personId === personId && d.templateId === t.id)\n .map((d) => d.signedAt ?? d.createdAt)\n .reduce<string | null>((latest, at) => (latest && latest >= at ? latest : at), null)\n const validUntil = lastFilledAt && t.validityDays != null\n ? new Date(Date.parse(lastFilledAt) + t.validityDays * DAY_MS).toISOString().slice(0, 10)\n : null\n const state: RequirementState = !lastFilledAt ? 'pending'\n : validUntil && validUntil < today ? 'expired'\n : 'valid'\n return {\n templateId: t.id,\n templateName: t.name,\n category: t.category,\n validityDays: t.validityDays,\n alertWhenMissing: t.alertWhenMissing,\n lastFilledAt,\n validUntil,\n state,\n }\n })\n .sort((a, b) => a.templateName.localeCompare(b.templateName))\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mock.d.ts","sourceRoot":"","sources":["../../src/data/mock.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AAUnD,wBAAgB,uBAAuB,IAAI,iBAAiB,CA4N3D"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"names":[],"mappings":"","file":"mock.js"}
|