@fayz-ai/plugin-forms 0.1.5 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -5,6 +5,8 @@
5
5
  [![npm](https://img.shields.io/npm/v/@fayz-ai/plugin-forms.svg)](https://www.npmjs.com/package/@fayz-ai/plugin-forms)
6
6
  [![license](https://img.shields.io/npm/l/@fayz-ai/plugin-forms.svg)](https://github.com/FayaLabs/fayz-sdk/blob/main/LICENSE)
7
7
 
8
+ **Status:** beta — pre-1.0. Core surface is stable; some backend facets (see `PLUGIN_PATTERNS.md`) are still landing. APIs may change before 1.0.
9
+
8
10
  Every real business runs on forms it never wanted to build: anamnesis sheets, evolution notes, consent contracts, intake docs. plugin-forms kills that busywork. Define a template once, attach a filled document to a person, and the data lives in your tenant — not in a PDF nobody can query.
9
11
 
10
12
  It snaps into a `defineSaas` app as the document engine. A clinic fills health anamnesis, a salon collects consent forms, a studio signs contracts — same plugin, same `frm_*` tables, different templates. Documents surface right inside the person record via a detail-tab widget, and the AI assistant can list templates and a client's documents on demand.
package/dist/config.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { PluginScope, VerticalId } from '@fayz-ai/core';
1
+ import type { PluginRegistryDef, PluginScope, VerticalId } from '@fayz-ai/core';
2
2
  import type { CustomFormsDataProvider } from './data/types';
3
3
  export interface CustomFormsPluginOptions {
4
4
  scope?: PluginScope;
@@ -7,10 +7,13 @@ export interface CustomFormsPluginOptions {
7
7
  navSection?: 'main' | 'secondary' | 'settings';
8
8
  navPosition?: number;
9
9
  labels?: Partial<CustomFormsLabels>;
10
+ /** App-owned registries shown inside Forms & Documents settings. */
11
+ settingsRegistries?: PluginRegistryDef[];
10
12
  }
11
13
  export interface CustomFormsLabels {
12
14
  pageTitle: string;
13
15
  settingsLabel: string;
16
+ settingsSubtitle: string;
14
17
  templates: string;
15
18
  documents: string;
16
19
  newTemplate: string;
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;AAC5D,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAA;AAE3D,MAAM,WAAW,wBAAwB;IACvC,KAAK,CAAC,EAAE,WAAW,CAAA;IACnB,UAAU,CAAC,EAAE,UAAU,CAAA;IACvB,YAAY,CAAC,EAAE,uBAAuB,CAAA;IACtC,UAAU,CAAC,EAAE,MAAM,GAAG,WAAW,GAAG,UAAU,CAAA;IAC9C,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,MAAM,CAAC,EAAE,OAAO,CAAC,iBAAiB,CAAC,CAAA;CACpC;AAED,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,MAAM,CAAA;IACjB,aAAa,EAAE,MAAM,CAAA;IACrB,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;IACjB,WAAW,EAAE,MAAM,CAAA;IACnB,WAAW,EAAE,MAAM,CAAA;CACpB;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,iBAAiB,CAAA;CAC1B;AAWD,wBAAgB,aAAa,CAAC,OAAO,CAAC,EAAE,wBAAwB,GAAG,iBAAiB,CAInF"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;AAC/E,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,cAAc,CAAA;AAE3D,MAAM,WAAW,wBAAwB;IACvC,KAAK,CAAC,EAAE,WAAW,CAAA;IACnB,UAAU,CAAC,EAAE,UAAU,CAAA;IACvB,YAAY,CAAC,EAAE,uBAAuB,CAAA;IACtC,UAAU,CAAC,EAAE,MAAM,GAAG,WAAW,GAAG,UAAU,CAAA;IAC9C,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,MAAM,CAAC,EAAE,OAAO,CAAC,iBAAiB,CAAC,CAAA;IACnC,oEAAoE;IACpE,kBAAkB,CAAC,EAAE,iBAAiB,EAAE,CAAA;CACzC;AAED,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,MAAM,CAAA;IACjB,aAAa,EAAE,MAAM,CAAA;IACrB,gBAAgB,EAAE,MAAM,CAAA;IACxB,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;IACjB,WAAW,EAAE,MAAM,CAAA;IACnB,WAAW,EAAE,MAAM,CAAA;CACpB;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,iBAAiB,CAAA;CAC1B;AAYD,wBAAgB,aAAa,CAAC,OAAO,CAAC,EAAE,wBAAwB,GAAG,iBAAiB,CAInF"}
@@ -1 +1 @@
1
- {"version":3,"file":"supabase.d.ts","sourceRoot":"","sources":["../../src/data/supabase.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,SAAS,CAAA;AAgGtD,wBAAgB,2BAA2B,IAAI,uBAAuB,CAmTrE"}
1
+ {"version":3,"file":"supabase.d.ts","sourceRoot":"","sources":["../../src/data/supabase.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,SAAS,CAAA;AAiGtD,wBAAgB,2BAA2B,IAAI,uBAAuB,CAmTrE"}
@@ -0,0 +1,7 @@
1
+ export declare const T: {
2
+ readonly documents: "plg_forms_documents";
3
+ readonly templates: "plg_forms_templates";
4
+ readonly documentFiles: "plg_forms_document_files";
5
+ readonly categories: "plg_forms_categories";
6
+ };
7
+ //# sourceMappingURL=tables.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tables.d.ts","sourceRoot":"","sources":["../../src/data/tables.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,CAAC;;;;;CAKJ,CAAA"}
package/dist/index.cjs CHANGED
@@ -3,6 +3,7 @@
3
3
  var chunkXLUULIVL_cjs = require('./chunk-XLUULIVL.cjs');
4
4
  var React2 = require('react');
5
5
  var core = require('@fayz-ai/core');
6
+ var saas = require('@fayz-ai/saas');
6
7
  var zustand = require('zustand');
7
8
  var lucideReact = require('lucide-react');
8
9
  var ui = require('@fayz-ai/ui');
@@ -12,6 +13,14 @@ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
12
13
 
13
14
  var React2__default = /*#__PURE__*/_interopDefault(React2);
14
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
+
15
24
  // src/data/mock.ts
16
25
  function uuid() {
17
26
  return crypto.randomUUID();
@@ -260,7 +269,7 @@ function createSupabaseFormsProvider() {
260
269
  // ── Templates ──────────────────────────────────────────
261
270
  async getTemplates(query) {
262
271
  const db = getClient();
263
- let qb = db.from("frm_templates").select("*", { count: "exact" }).eq("tenant_id", getTenantId()).eq("is_current", true).eq("is_deleted", false);
272
+ let qb = db.from(T.templates).select("*", { count: "exact" }).eq("tenant_id", getTenantId()).eq("is_current", true).eq("is_deleted", false);
264
273
  if (query.category) qb = qb.eq("category", query.category);
265
274
  if (query.search) qb = qb.ilike("name", `%${query.search}%`);
266
275
  const page = query.page ?? 1;
@@ -272,14 +281,14 @@ function createSupabaseFormsProvider() {
272
281
  },
273
282
  async getTemplateById(id) {
274
283
  const db = getClient();
275
- const { data, error } = await db.from("frm_templates").select("*").eq("id", id).eq("is_deleted", false).single();
284
+ const { data, error } = await db.from(T.templates).select("*").eq("id", id).eq("is_deleted", false).single();
276
285
  if (error || !data) return null;
277
286
  return mapTemplate(data);
278
287
  },
279
288
  async createTemplate(input) {
280
289
  const db = getClient();
281
290
  const tenantId = getTenantId();
282
- const { data, error } = await db.from("frm_templates").insert({
291
+ const { data, error } = await db.from(T.templates).insert({
283
292
  tenant_id: tenantId,
284
293
  name: input.name,
285
294
  description: input.description,
@@ -295,12 +304,12 @@ function createSupabaseFormsProvider() {
295
304
  async updateTemplate(id, input) {
296
305
  const db = getClient();
297
306
  const tenantId = getTenantId();
298
- const { count } = await db.from("frm_documents").select("document_id", { count: "exact", head: true }).eq("template_id", id);
307
+ const { count } = await db.from(T.documents).select("document_id", { count: "exact", head: true }).eq("template_id", id);
299
308
  const hasDocuments = (count ?? 0) > 0;
300
309
  if (hasDocuments && input.schema) {
301
310
  const existing = await this.getTemplateById(id);
302
311
  if (!existing) throw new Error("Template not found");
303
- const { data: newRow, error: insertError } = await db.from("frm_templates").insert({
312
+ const { data: newRow, error: insertError } = await db.from(T.templates).insert({
304
313
  tenant_id: tenantId,
305
314
  name: input.name ?? existing.name,
306
315
  description: input.description ?? existing.description,
@@ -315,7 +324,7 @@ function createSupabaseFormsProvider() {
315
324
  parent_id: existing.parentId ?? existing.id
316
325
  }).select().single();
317
326
  if (insertError) throw new Error(insertError.message);
318
- await db.from("frm_templates").update({ is_current: false, updated_at: (/* @__PURE__ */ new Date()).toISOString() }).eq("id", id);
327
+ await db.from(T.templates).update({ is_current: false, updated_at: (/* @__PURE__ */ new Date()).toISOString() }).eq("id", id);
319
328
  return mapTemplate(newRow);
320
329
  }
321
330
  const updates = { updated_at: (/* @__PURE__ */ new Date()).toISOString() };
@@ -327,16 +336,16 @@ function createSupabaseFormsProvider() {
327
336
  if (input.tags !== void 0) updates.tags = input.tags;
328
337
  if (input.metadata !== void 0) updates.metadata = input.metadata;
329
338
  if (input.isActive !== void 0) updates.is_active = input.isActive;
330
- const { data, error } = await db.from("frm_templates").update(updates).eq("id", id).select().single();
339
+ const { data, error } = await db.from(T.templates).update(updates).eq("id", id).select().single();
331
340
  if (error) throw new Error(error.message);
332
341
  return mapTemplate(data);
333
342
  },
334
343
  async deleteTemplate(id) {
335
344
  const db = getClient();
336
- const { error } = await db.from("frm_templates").update({ is_deleted: true, updated_at: (/* @__PURE__ */ new Date()).toISOString() }).eq("id", id);
345
+ const { error } = await db.from(T.templates).update({ is_deleted: true, updated_at: (/* @__PURE__ */ new Date()).toISOString() }).eq("id", id);
337
346
  if (error) throw new Error(error.message);
338
347
  },
339
- // ── Documents (saas_core.documents + frm_documents extension) ──
348
+ // ── Documents (public.documents + plg_forms_documents extension) ──
340
349
  async getDocuments(query) {
341
350
  const db = getClient();
342
351
  let qb = db.from("v_documents").select("*", { count: "exact" }).eq("tenant_id", getTenantId()).eq("is_active", true);
@@ -363,7 +372,7 @@ function createSupabaseFormsProvider() {
363
372
  async createDocument(input) {
364
373
  const tenantId = getTenantId();
365
374
  const isForm = !!input.templateId;
366
- const { data: coreDoc, error: coreError } = await getClient().from("frm_core_documents").insert({
375
+ const { data: coreDoc, error: coreError } = await getClient().from("documents").insert({
367
376
  tenant_id: tenantId,
368
377
  kind: input.kind ?? (isForm ? "form" : "attachment"),
369
378
  person_id: input.personId,
@@ -378,14 +387,14 @@ function createSupabaseFormsProvider() {
378
387
  if (coreError) throw new Error(coreError.message);
379
388
  if (isForm && input.templateId) {
380
389
  const db = getClient();
381
- const { error: extError } = await db.from("frm_documents").insert({
390
+ const { error: extError } = await db.from(T.documents).insert({
382
391
  document_id: coreDoc.id,
383
392
  tenant_id: tenantId,
384
393
  template_id: input.templateId,
385
394
  data: input.data ?? {}
386
395
  });
387
396
  if (extError) {
388
- await getClient().from("frm_core_documents").delete().eq("id", coreDoc.id);
397
+ await getClient().from("documents").delete().eq("id", coreDoc.id);
389
398
  throw new Error(extError.message);
390
399
  }
391
400
  }
@@ -398,23 +407,23 @@ function createSupabaseFormsProvider() {
398
407
  if (input.status !== void 0) coreUpdates.status = input.status;
399
408
  if (input.title !== void 0) coreUpdates.title = input.title;
400
409
  if (input.notes !== void 0) coreUpdates.notes = input.notes;
401
- const { error: coreError } = await getClient().from("frm_core_documents").update(coreUpdates).eq("id", id);
410
+ const { error: coreError } = await getClient().from("documents").update(coreUpdates).eq("id", id);
402
411
  if (coreError) throw new Error(coreError.message);
403
412
  if (input.data !== void 0) {
404
413
  const db = getClient();
405
- await db.from("frm_documents").update({ data: input.data, updated_at: now2 }).eq("document_id", id);
414
+ await db.from(T.documents).update({ data: input.data, updated_at: now2 }).eq("document_id", id);
406
415
  }
407
416
  const doc = await this.getDocumentById(id);
408
417
  return doc;
409
418
  },
410
419
  async deleteDocument(id) {
411
- const { error } = await getClient().from("frm_core_documents").update({ is_active: false, updated_at: (/* @__PURE__ */ new Date()).toISOString() }).eq("id", id);
420
+ const { error } = await getClient().from("documents").update({ is_active: false, updated_at: (/* @__PURE__ */ new Date()).toISOString() }).eq("id", id);
412
421
  if (error) throw new Error(error.message);
413
422
  },
414
423
  // ── Files ──────────────────────────────────────────────
415
424
  async getDocumentFiles(documentId) {
416
425
  const db = getClient();
417
- const { data, error } = await db.from("frm_document_files").select("*").eq("document_id", documentId).order("sort_order");
426
+ const { data, error } = await db.from(T.documentFiles).select("*").eq("document_id", documentId).order("sort_order");
418
427
  if (error) throw new Error(error.message);
419
428
  return (data ?? []).map(mapFile);
420
429
  },
@@ -426,7 +435,7 @@ function createSupabaseFormsProvider() {
426
435
  const { error: uploadError } = await db.storage.from("documents").upload(storagePath, file, { contentType: file.type });
427
436
  if (uploadError) throw new Error(uploadError.message);
428
437
  const { data: urlData } = db.storage.from("documents").getPublicUrl(storagePath);
429
- const { data, error } = await db.from("frm_document_files").insert({
438
+ const { data, error } = await db.from(T.documentFiles).insert({
430
439
  tenant_id: tenantId,
431
440
  document_id: documentId,
432
441
  field_key: fieldKey,
@@ -440,7 +449,7 @@ function createSupabaseFormsProvider() {
440
449
  },
441
450
  async deleteFile(fileId) {
442
451
  const db = getClient();
443
- const { error } = await db.from("frm_document_files").delete().eq("id", fileId);
452
+ const { error } = await db.from(T.documentFiles).delete().eq("id", fileId);
444
453
  if (error) throw new Error(error.message);
445
454
  }
446
455
  };
@@ -700,6 +709,7 @@ var customFormsLocales = {
700
709
  var DEFAULT_LABELS = {
701
710
  pageTitle: "Custom Forms",
702
711
  settingsLabel: "Forms & Documents",
712
+ settingsSubtitle: "Create and manage custom forms for your business",
703
713
  templates: "Templates",
704
714
  documents: "Documents",
705
715
  newTemplate: "New Template",
@@ -821,8 +831,7 @@ function TemplateListView({ store, config, onEdit, onNew }) {
821
831
  ] }) }, i)) })
822
832
  ] });
823
833
  }
824
- function CustomFormsSettingsTab({ config, provider, store, registries }) {
825
- const t = core.useTranslation();
834
+ function CustomFormsSettingsTab({ config, provider, store }) {
826
835
  const [builderTemplateId, setBuilderTemplateId] = React2.useState(null);
827
836
  const handleNew = React2.useCallback(() => {
828
837
  setBuilderTemplateId("__new__");
@@ -846,7 +855,7 @@ function CustomFormsSettingsTab({ config, provider, store, registries }) {
846
855
  }
847
856
  checkHash();
848
857
  }, []);
849
- const templatesContent = builderTemplateId ? /* @__PURE__ */ jsxRuntime.jsx(
858
+ return builderTemplateId ? /* @__PURE__ */ jsxRuntime.jsx(
850
859
  BuilderWrapper,
851
860
  {
852
861
  templateId: builderTemplateId === "__new__" ? void 0 : builderTemplateId,
@@ -864,13 +873,6 @@ function CustomFormsSettingsTab({ config, provider, store, registries }) {
864
873
  onNew: handleNew
865
874
  }
866
875
  );
867
- return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { padding: "24px" }, children: [
868
- /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { marginBottom: "16px" }, children: [
869
- /* @__PURE__ */ jsxRuntime.jsx("h1", { style: { fontSize: "20px", fontWeight: 600, margin: 0 }, children: t("customForms.settingsTitle") }),
870
- /* @__PURE__ */ jsxRuntime.jsx("p", { style: { color: "var(--muted-foreground, #6b7280)", margin: "4px 0 0", fontSize: "14px" }, children: t("customForms.settingsSubtitle") })
871
- ] }),
872
- templatesContent
873
- ] });
874
876
  }
875
877
  function BuilderWrapper({
876
878
  templateId,
@@ -1505,7 +1507,7 @@ function createCustomFormsPlugin(options) {
1505
1507
  { key: "isActive", label: "Active", type: "boolean", showInTable: true, defaultValue: true, inlineToggle: true }
1506
1508
  ],
1507
1509
  data: {
1508
- table: "frm_categories",
1510
+ table: T.categories,
1509
1511
  tenantScoped: true
1510
1512
  }
1511
1513
  },
@@ -1520,8 +1522,23 @@ function createCustomFormsPlugin(options) {
1520
1522
  ]
1521
1523
  }
1522
1524
  ];
1523
- const SettingsComponent = () => React2__default.default.createElement(CustomFormsSettingsTab, { config, provider, store, registries: formRegistries });
1524
- SettingsComponent.displayName = "CustomFormsSettingsTab";
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";
1525
1542
  const PersonDocumentsWidget2 = (props) => React2__default.default.createElement(PersonDocumentsWidget, { ...props, config, provider, store });
1526
1543
  PersonDocumentsWidget2.displayName = "PersonDocumentsWidget";
1527
1544
  return {
@@ -1555,7 +1572,7 @@ function createCustomFormsPlugin(options) {
1555
1572
  order: 0
1556
1573
  }
1557
1574
  ],
1558
- registries: formRegistries,
1575
+ registries,
1559
1576
  aiTools: [
1560
1577
  {
1561
1578
  id: "custom_forms.list-templates",