@nextlyhq/plugin-form-builder 0.0.2-alpha.3 → 0.0.2-alpha.30
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/{EmailFieldEditor-MMOXXL7P.js → EmailFieldEditor-ZZ4VSKKB.js} +3 -3
- package/dist/EmailFieldEditor-ZZ4VSKKB.js.map +1 -0
- package/dist/admin/index.d.ts +4 -19
- package/dist/admin/index.js +9 -15
- package/dist/admin/index.js.map +1 -1
- package/dist/components/index.d.ts +1 -1
- package/dist/{index-C0kBvYjm.d.ts → index-BRumOEsE.d.ts} +29 -29
- package/dist/index.d.ts +6 -5
- package/dist/index.js +171 -109
- package/dist/index.js.map +1 -1
- package/package.json +12 -9
- package/dist/EmailFieldEditor-MMOXXL7P.js.map +0 -1
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { J as SubmissionDetail, t as SubmissionDetailProps, K as SubmissionList, u as SubmissionListProps } from '../index-
|
|
1
|
+
export { J as SubmissionDetail, t as SubmissionDetailProps, K as SubmissionList, u as SubmissionListProps } from '../index-BRumOEsE.js';
|
|
2
2
|
import 'react';
|
|
3
3
|
import 'nextly';
|
|
@@ -33,6 +33,27 @@ interface EmailConfig {
|
|
|
33
33
|
cc?: string | string[];
|
|
34
34
|
bcc?: string | string[];
|
|
35
35
|
}
|
|
36
|
+
/**
|
|
37
|
+
* @experimental A resolved outgoing form notification (template-based) as built
|
|
38
|
+
* by the submission handler, before it is sent. This is the value transformed by
|
|
39
|
+
* the `form-builder.beforeEmail` D63 filter seam.
|
|
40
|
+
*/
|
|
41
|
+
interface FormEmailNotification {
|
|
42
|
+
/** Resolved recipient address. */
|
|
43
|
+
to: string;
|
|
44
|
+
/** Email template slug to render. */
|
|
45
|
+
templateSlug: string;
|
|
46
|
+
/** Template variables (submitted data + form metadata). */
|
|
47
|
+
variables: Record<string, unknown>;
|
|
48
|
+
/** Email provider id; undefined = system default. */
|
|
49
|
+
providerId?: string;
|
|
50
|
+
/** CC addresses, if any. */
|
|
51
|
+
cc?: string[];
|
|
52
|
+
/** BCC addresses, if any. */
|
|
53
|
+
bcc?: string[];
|
|
54
|
+
/** Source notification id; undefined for filter-injected emails. */
|
|
55
|
+
notificationId?: string;
|
|
56
|
+
}
|
|
36
57
|
/**
|
|
37
58
|
* A single email notification integration stored on a form.
|
|
38
59
|
* Matches the shape saved by the form builder admin UI.
|
|
@@ -200,26 +221,6 @@ interface FormBuilderPluginOptions {
|
|
|
200
221
|
defaultFields: FieldDefinition[];
|
|
201
222
|
}) => FieldDefinition[]);
|
|
202
223
|
};
|
|
203
|
-
/**
|
|
204
|
-
* @deprecated Use `formOverrides` and `formSubmissionOverrides` instead.
|
|
205
|
-
* This option is kept for backward compatibility.
|
|
206
|
-
*/
|
|
207
|
-
collections?: {
|
|
208
|
-
forms?: Partial<CollectionConfig> & {
|
|
209
|
-
slug?: string;
|
|
210
|
-
labels?: {
|
|
211
|
-
singular?: string;
|
|
212
|
-
plural?: string;
|
|
213
|
-
};
|
|
214
|
-
};
|
|
215
|
-
submissions?: Partial<CollectionConfig> & {
|
|
216
|
-
slug?: string;
|
|
217
|
-
labels?: {
|
|
218
|
-
singular?: string;
|
|
219
|
-
plural?: string;
|
|
220
|
-
};
|
|
221
|
-
};
|
|
222
|
-
};
|
|
223
224
|
/**
|
|
224
225
|
* Configure which field types are available in the form builder.
|
|
225
226
|
*
|
|
@@ -272,25 +273,24 @@ interface FormBuilderPluginOptions {
|
|
|
272
273
|
/**
|
|
273
274
|
* Hook called before sending email notifications.
|
|
274
275
|
*
|
|
275
|
-
*
|
|
276
|
-
*
|
|
276
|
+
* Runs as the `form-builder.beforeEmail` D63 filter: the resolved outgoing
|
|
277
|
+
* notifications (template + recipient + variables) are threaded through this
|
|
278
|
+
* callback before they are sent. Allows modifying, adding, or filtering the
|
|
279
|
+
* outgoing notifications. Return the modified array.
|
|
277
280
|
*
|
|
278
281
|
* @example
|
|
279
282
|
* ```typescript
|
|
280
283
|
* beforeEmail: async ({ emails, form, submission }) => {
|
|
281
|
-
* // Add BCC to all
|
|
282
|
-
* return emails.map(
|
|
283
|
-
* ...email,
|
|
284
|
-
* bcc: ['archive@example.com'],
|
|
285
|
-
* }));
|
|
284
|
+
* // Add BCC to all outgoing notifications
|
|
285
|
+
* return emails.map((e) => ({ ...e, bcc: ['archive@example.com'] }));
|
|
286
286
|
* }
|
|
287
287
|
* ```
|
|
288
288
|
*/
|
|
289
289
|
beforeEmail?: (args: {
|
|
290
|
-
emails:
|
|
290
|
+
emails: FormEmailNotification[];
|
|
291
291
|
form: FormDocument;
|
|
292
292
|
submission: SubmissionDocument;
|
|
293
|
-
}) => Promise<
|
|
293
|
+
}) => Promise<FormEmailNotification[]> | FormEmailNotification[];
|
|
294
294
|
/**
|
|
295
295
|
* Email notification settings.
|
|
296
296
|
*/
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import * as nextly from 'nextly';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
|
|
2
|
+
import { CollectionConfig, PluginContext } from 'nextly';
|
|
3
|
+
import { PluginDefinition } from '@nextlyhq/plugin-sdk';
|
|
4
|
+
import { R as ResolvedFormBuilderConfig, F as FormBuilderPluginOptions, a as FormConfig, b as FormSettings, c as FormNotifications, C as CheckboxFormField, D as DateFormField, E as EmailFormField, d as FileFormField, H as HiddenFormField, N as NumberFormField, P as PhoneFormField, e as RadioFormField, S as SelectFormField, T as TextFormField, f as TextareaFormField, g as TimeFormField, U as UrlFormField, h as ConditionalLogic, i as FormField, j as SubmissionDocument, k as FormDocument, W as WebhookEvent } from './index-BRumOEsE.js';
|
|
5
|
+
export { B as BaseFormField, l as ConditionalLogicCondition, m as EmailConfig, n as FieldBlockConfig, o as FormAccess, p as FormFieldType, q as FormHookContext, r as FormHooks, s as FormSubmission, t as SubmissionDetailProps, u as SubmissionListProps, V as ValidationRules, v as WebhookConfig, w as isCheckboxFormField, x as isEmailFormField, y as isFileFormField, z as isFormField, A as isFormFieldType, G as isSelectFormField, I as isTextFormField } from './index-BRumOEsE.js';
|
|
5
6
|
import { z } from 'zod';
|
|
6
7
|
import 'react';
|
|
7
8
|
|
|
@@ -463,7 +464,7 @@ declare const text: (config: Omit<TextFormField, "type">) => TextFormField;
|
|
|
463
464
|
* name: 'workEmail',
|
|
464
465
|
* label: 'Work Email',
|
|
465
466
|
* required: true,
|
|
466
|
-
* placeholder: 'you@
|
|
467
|
+
* placeholder: 'you@nextly.local',
|
|
467
468
|
* })
|
|
468
469
|
* ```
|
|
469
470
|
*/
|
|
@@ -1528,7 +1529,7 @@ declare function triggerWebhooks(options: TriggerWebhooksOptions): Promise<Trigg
|
|
|
1528
1529
|
* // In submission handler - fire and forget
|
|
1529
1530
|
* fireWebhooks(
|
|
1530
1531
|
* { event: 'submission.created', form, submission },
|
|
1531
|
-
* pluginContext.
|
|
1532
|
+
* pluginContext.logger
|
|
1532
1533
|
* );
|
|
1533
1534
|
*
|
|
1534
1535
|
* // Continue with response immediately
|
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/plugin.ts
|
|
2
|
-
import {
|
|
2
|
+
import { definePlugin } from "@nextlyhq/plugin-sdk";
|
|
3
3
|
|
|
4
4
|
// src/collections/forms.ts
|
|
5
5
|
import {
|
|
@@ -425,8 +425,8 @@ function submissionsCollection(pluginConfig) {
|
|
|
425
425
|
|
|
426
426
|
// src/plugin.ts
|
|
427
427
|
function resolveConfig(options) {
|
|
428
|
-
const formOverrides = options.formOverrides ||
|
|
429
|
-
const submissionOverrides = options.formSubmissionOverrides ||
|
|
428
|
+
const formOverrides = options.formOverrides || {};
|
|
429
|
+
const submissionOverrides = options.formSubmissionOverrides || {};
|
|
430
430
|
return {
|
|
431
431
|
formOverrides: {
|
|
432
432
|
...formOverrides,
|
|
@@ -495,41 +495,108 @@ function formBuilder(options = {}) {
|
|
|
495
495
|
const resolvedConfig = resolveConfig(options);
|
|
496
496
|
const formsCol = formsCollection(resolvedConfig);
|
|
497
497
|
const submissionsCol = submissionsCollection(resolvedConfig);
|
|
498
|
-
const plugin = {
|
|
498
|
+
const plugin = definePlugin({
|
|
499
499
|
name: "@nextlyhq/plugin-form-builder",
|
|
500
|
-
version
|
|
501
|
-
|
|
500
|
+
// Keep in sync with package.json `version` (guarded by package-metadata.test).
|
|
501
|
+
version: "0.0.2-alpha.24",
|
|
502
|
+
nextly: ">=0.0.2-alpha.21",
|
|
503
|
+
// Declarative schema: the merged pipeline folds these into the
|
|
504
|
+
// app schema — no manual `setup()` append needed. Just register the plugin.
|
|
505
|
+
contributes: {
|
|
506
|
+
collections: [formsCol, submissionsCol],
|
|
507
|
+
// Custom permission — gates submission export beyond CRUD. The
|
|
508
|
+
// canonical example third-party plugin authors copy.
|
|
509
|
+
permissions: [
|
|
510
|
+
{
|
|
511
|
+
action: "export",
|
|
512
|
+
resource: "submissions",
|
|
513
|
+
label: "Export Submissions",
|
|
514
|
+
description: "Export form submissions as CSV/JSON",
|
|
515
|
+
group: "Form Builder"
|
|
516
|
+
}
|
|
517
|
+
],
|
|
518
|
+
// HTTP route — exported at
|
|
519
|
+
// /api/plugins/@nextlyhq/plugin-form-builder/submissions/export. Secure by
|
|
520
|
+
// default: gated by the custom `export-submissions` permission. Reads
|
|
521
|
+
// via the secure-by-default service path as the authed user and
|
|
522
|
+
// resolves its OWN slug through `ctx.self`. The canonical
|
|
523
|
+
// contributes.routes example for third-party authors.
|
|
524
|
+
routes: [
|
|
525
|
+
{
|
|
526
|
+
method: "GET",
|
|
527
|
+
path: "/submissions/export",
|
|
528
|
+
requiredPermission: "export-submissions",
|
|
529
|
+
handler: async (_req, ctx) => {
|
|
530
|
+
const declaredSlug = resolvedConfig.formSubmissionOverrides.slug;
|
|
531
|
+
const slug = ctx.self.collections[declaredSlug] ?? declaredSlug;
|
|
532
|
+
const result = await ctx.services.collections.listEntries(
|
|
533
|
+
slug,
|
|
534
|
+
{},
|
|
535
|
+
{ as: "user", user: ctx.user ?? void 0 }
|
|
536
|
+
);
|
|
537
|
+
return Response.json({ items: result.data });
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
],
|
|
541
|
+
// Admin UI — the canonical contributes.admin example. Paths
|
|
542
|
+
// are the components form-builder's `/admin` module self-registers (kept
|
|
543
|
+
// as literals so this node entry stays React-free). menu links to
|
|
544
|
+
// the forms collection; settings renders the builder UI at
|
|
545
|
+
// /admin/plugins/<slug>; a custom page is gated by export-submissions;
|
|
546
|
+
// a submissions beforeList view injects the filter above the list.
|
|
547
|
+
admin: {
|
|
548
|
+
menu: [
|
|
549
|
+
{
|
|
550
|
+
label: "Forms",
|
|
551
|
+
to: `/admin/collections/${resolvedConfig.formOverrides.slug}`,
|
|
552
|
+
icon: "file-text",
|
|
553
|
+
order: 50,
|
|
554
|
+
requiredPermission: `read-${resolvedConfig.formOverrides.slug}`
|
|
555
|
+
}
|
|
556
|
+
],
|
|
557
|
+
settings: {
|
|
558
|
+
component: "@nextlyhq/plugin-form-builder/admin#FormBuilderView"
|
|
559
|
+
},
|
|
560
|
+
pages: [
|
|
561
|
+
{
|
|
562
|
+
path: "submissions",
|
|
563
|
+
component: "@nextlyhq/plugin-form-builder/admin#SubmissionsFilter",
|
|
564
|
+
requiredPermission: "export-submissions"
|
|
565
|
+
}
|
|
566
|
+
],
|
|
567
|
+
views: {
|
|
568
|
+
[resolvedConfig.formSubmissionOverrides.slug]: {
|
|
569
|
+
beforeList: "@nextlyhq/plugin-form-builder/admin#SubmissionsFilter"
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
},
|
|
502
574
|
admin: {
|
|
503
575
|
order: 50,
|
|
504
576
|
description: "Create and manage forms with submission tracking"
|
|
505
577
|
},
|
|
506
|
-
// -- Config transformer --------------------------------------------------
|
|
507
|
-
// Automatically adds plugin collections so users don't have to spread them.
|
|
508
|
-
config(config) {
|
|
509
|
-
const existing = config.collections || [];
|
|
510
|
-
const formsSlug = resolvedConfig.formOverrides.slug;
|
|
511
|
-
const submissionsSlug = resolvedConfig.formSubmissionOverrides.slug;
|
|
512
|
-
const toAdd = [];
|
|
513
|
-
if (!existing.some((c) => c.slug === formsSlug))
|
|
514
|
-
toAdd.push(formsCol);
|
|
515
|
-
if (!existing.some((c) => c.slug === submissionsSlug))
|
|
516
|
-
toAdd.push(submissionsCol);
|
|
517
|
-
return { ...config, collections: [...existing, ...toAdd] };
|
|
518
|
-
},
|
|
519
578
|
// -- Init ----------------------------------------------------------------
|
|
520
579
|
// Registers an afterCreate hook on submissions to send email notifications.
|
|
521
580
|
init(nextly) {
|
|
522
|
-
const
|
|
523
|
-
const
|
|
524
|
-
|
|
525
|
-
if (g[guardKey]) return;
|
|
526
|
-
g[guardKey] = true;
|
|
527
|
-
nextly.infra.logger.info("Form Builder plugin initialized", {
|
|
581
|
+
const declaredSubmissionsSlug = resolvedConfig.formSubmissionOverrides.slug;
|
|
582
|
+
const submissionSlug = nextly.self.collections[declaredSubmissionsSlug] ?? declaredSubmissionsSlug;
|
|
583
|
+
nextly.logger.info("Form Builder plugin initialized", {
|
|
528
584
|
formsCollection: resolvedConfig.formOverrides.slug,
|
|
529
585
|
submissionsCollection: submissionSlug,
|
|
530
586
|
enabledFields: Object.entries(resolvedConfig.fields).filter(([, on]) => on).map(([name]) => name)
|
|
531
587
|
});
|
|
532
588
|
nextly.__formBuilderConfig = resolvedConfig;
|
|
589
|
+
if (resolvedConfig.beforeEmail) {
|
|
590
|
+
nextly.filters.add(
|
|
591
|
+
"form-builder.beforeEmail",
|
|
592
|
+
(emails, ctx) => resolvedConfig.beforeEmail({
|
|
593
|
+
emails,
|
|
594
|
+
// Boundary: loose runtime documents → the user's typed contract.
|
|
595
|
+
form: ctx.form,
|
|
596
|
+
submission: ctx.submission
|
|
597
|
+
})
|
|
598
|
+
);
|
|
599
|
+
}
|
|
533
600
|
nextly.hooks.on(
|
|
534
601
|
"afterCreate",
|
|
535
602
|
submissionSlug,
|
|
@@ -538,7 +605,7 @@ function formBuilder(options = {}) {
|
|
|
538
605
|
}
|
|
539
606
|
);
|
|
540
607
|
}
|
|
541
|
-
};
|
|
608
|
+
});
|
|
542
609
|
return {
|
|
543
610
|
plugin,
|
|
544
611
|
collections: [formsCol, submissionsCol],
|
|
@@ -596,42 +663,63 @@ async function handleSubmissionCreated(context, config, nextly) {
|
|
|
596
663
|
const formFields = Array.isArray(form.fields) ? form.fields : [];
|
|
597
664
|
const fileAttachments = collectAttachmentInputs(formFields, submittedData);
|
|
598
665
|
const seen = /* @__PURE__ */ new Set();
|
|
666
|
+
const emails = [];
|
|
599
667
|
for (const notification of notifications) {
|
|
600
668
|
if (!notification.enabled || !notification.templateSlug) continue;
|
|
601
669
|
const key = notification.id || `${notification.to}::${notification.templateSlug}`;
|
|
602
670
|
if (seen.has(key)) continue;
|
|
603
671
|
seen.add(key);
|
|
672
|
+
const to = notification.recipientType === "field" ? resolveFieldRef(notification.to, submittedData) : notification.to;
|
|
673
|
+
if (!to) {
|
|
674
|
+
nextly.logger.warn?.(
|
|
675
|
+
"Form Builder: empty recipient, skipping notification",
|
|
676
|
+
{ notificationId: notification.id, formSlug: form.slug }
|
|
677
|
+
);
|
|
678
|
+
continue;
|
|
679
|
+
}
|
|
680
|
+
const cc = Array.isArray(notification.cc) && notification.cc.length ? notification.cc : void 0;
|
|
681
|
+
const bcc = Array.isArray(notification.bcc) && notification.bcc.length ? notification.bcc : void 0;
|
|
682
|
+
emails.push({
|
|
683
|
+
to,
|
|
684
|
+
templateSlug: notification.templateSlug,
|
|
685
|
+
variables: {
|
|
686
|
+
...submittedData,
|
|
687
|
+
formName: form.name,
|
|
688
|
+
submissionId: submission.id
|
|
689
|
+
},
|
|
690
|
+
providerId: notification.providerId,
|
|
691
|
+
cc,
|
|
692
|
+
bcc,
|
|
693
|
+
notificationId: notification.id
|
|
694
|
+
});
|
|
695
|
+
}
|
|
696
|
+
if (emails.length === 0) return;
|
|
697
|
+
const finalEmails = await nextly.filters.apply("form-builder.beforeEmail", emails, { form, submission });
|
|
698
|
+
for (const email2 of finalEmails) {
|
|
604
699
|
try {
|
|
605
|
-
const to = notification.recipientType === "field" ? resolveFieldRef(notification.to, submittedData) : notification.to;
|
|
606
|
-
if (!to) {
|
|
607
|
-
nextly.infra.logger.warn?.(
|
|
608
|
-
"Form Builder: empty recipient, skipping notification",
|
|
609
|
-
{ notificationId: notification.id, formSlug: form.slug }
|
|
610
|
-
);
|
|
611
|
-
continue;
|
|
612
|
-
}
|
|
613
|
-
const cc = Array.isArray(notification.cc) && notification.cc.length ? notification.cc : void 0;
|
|
614
|
-
const bcc = Array.isArray(notification.bcc) && notification.bcc.length ? notification.bcc : void 0;
|
|
615
700
|
await emailService.sendWithTemplate(
|
|
616
|
-
|
|
617
|
-
to,
|
|
618
|
-
|
|
701
|
+
email2.templateSlug,
|
|
702
|
+
email2.to,
|
|
703
|
+
email2.variables,
|
|
619
704
|
{
|
|
620
|
-
providerId:
|
|
621
|
-
cc,
|
|
622
|
-
bcc,
|
|
705
|
+
providerId: email2.providerId,
|
|
706
|
+
cc: email2.cc,
|
|
707
|
+
bcc: email2.bcc,
|
|
623
708
|
attachments: fileAttachments.length > 0 ? fileAttachments : void 0
|
|
624
709
|
}
|
|
625
710
|
);
|
|
626
|
-
nextly.
|
|
711
|
+
nextly.logger.info?.("Form Builder: notification sent", {
|
|
627
712
|
formSlug: form.slug,
|
|
628
|
-
to,
|
|
629
|
-
templateSlug:
|
|
713
|
+
to: email2.to,
|
|
714
|
+
templateSlug: email2.templateSlug,
|
|
715
|
+
notificationId: email2.notificationId,
|
|
630
716
|
attachmentCount: fileAttachments.length
|
|
631
717
|
});
|
|
632
718
|
} catch (err) {
|
|
633
|
-
nextly.
|
|
634
|
-
|
|
719
|
+
nextly.logger.error?.("Form Builder: notification failed", {
|
|
720
|
+
to: email2.to,
|
|
721
|
+
templateSlug: email2.templateSlug,
|
|
722
|
+
notificationId: email2.notificationId,
|
|
635
723
|
error: err instanceof Error ? err.message : String(err)
|
|
636
724
|
});
|
|
637
725
|
}
|
|
@@ -639,27 +727,14 @@ async function handleSubmissionCreated(context, config, nextly) {
|
|
|
639
727
|
}
|
|
640
728
|
async function fetchParentForm(config, formId, nextly) {
|
|
641
729
|
try {
|
|
642
|
-
const
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
}
|
|
649
|
-
const result = await handler.getEntry({
|
|
650
|
-
collectionName: config.formOverrides.slug,
|
|
651
|
-
entryId: formId,
|
|
652
|
-
overrideAccess: true
|
|
653
|
-
});
|
|
654
|
-
const fromData = result?.data;
|
|
655
|
-
if (fromData) return fromData;
|
|
656
|
-
const fromDoc = result?.doc;
|
|
657
|
-
if (fromDoc && typeof fromDoc === "object") {
|
|
658
|
-
return fromDoc;
|
|
659
|
-
}
|
|
660
|
-
return null;
|
|
730
|
+
const form = await nextly.services.collections.findEntryById(
|
|
731
|
+
config.formOverrides.slug,
|
|
732
|
+
formId,
|
|
733
|
+
{ as: "system" }
|
|
734
|
+
);
|
|
735
|
+
return form;
|
|
661
736
|
} catch (err) {
|
|
662
|
-
nextly.
|
|
737
|
+
nextly.logger.error?.("Form Builder: failed to fetch form", {
|
|
663
738
|
formId,
|
|
664
739
|
error: err instanceof Error ? err.message : String(err)
|
|
665
740
|
});
|
|
@@ -2007,7 +2082,7 @@ async function submitForm(options, context) {
|
|
|
2007
2082
|
const { formSlug, data, metadata } = options;
|
|
2008
2083
|
const { pluginContext, pluginConfig } = context;
|
|
2009
2084
|
const { collections } = pluginContext.services;
|
|
2010
|
-
const { logger } = pluginContext
|
|
2085
|
+
const { logger } = pluginContext;
|
|
2011
2086
|
try {
|
|
2012
2087
|
const form = await fetchFormBySlug(formSlug, pluginConfig, pluginContext);
|
|
2013
2088
|
if (!form) {
|
|
@@ -2076,8 +2151,9 @@ async function submitForm(options, context) {
|
|
|
2076
2151
|
const submission = await collections.createEntry(
|
|
2077
2152
|
pluginConfig.formSubmissionOverrides.slug,
|
|
2078
2153
|
submissionData,
|
|
2079
|
-
|
|
2080
|
-
//
|
|
2154
|
+
// Public form submission — create as system. No ambient user; an
|
|
2155
|
+
// empty context already resolves to system, but be explicit.
|
|
2156
|
+
{ as: "system" }
|
|
2081
2157
|
);
|
|
2082
2158
|
logger.info?.("Form submission created successfully", {
|
|
2083
2159
|
formSlug,
|
|
@@ -2105,19 +2181,10 @@ async function fetchFormBySlug(slug, pluginConfig, pluginContext) {
|
|
|
2105
2181
|
const { collections } = pluginContext.services;
|
|
2106
2182
|
const result = await collections.listEntries(
|
|
2107
2183
|
pluginConfig.formOverrides.slug,
|
|
2108
|
-
{
|
|
2109
|
-
|
|
2110
|
-
// Fetch enough to find the form
|
|
2111
|
-
},
|
|
2112
|
-
{}
|
|
2113
|
-
// Empty context for public access
|
|
2114
|
-
);
|
|
2115
|
-
if (!result.data || result.data.length === 0) {
|
|
2116
|
-
return null;
|
|
2117
|
-
}
|
|
2118
|
-
const form = result.data.find(
|
|
2119
|
-
(entry) => entry.slug === slug
|
|
2184
|
+
{ where: { slug: { equals: slug } }, pagination: { limit: 1 } },
|
|
2185
|
+
{ as: "system" }
|
|
2120
2186
|
);
|
|
2187
|
+
const form = result.data?.[0];
|
|
2121
2188
|
return form ? form : null;
|
|
2122
2189
|
} catch {
|
|
2123
2190
|
return null;
|
|
@@ -2191,33 +2258,28 @@ async function getFormSubmissionStats(formSlug, context) {
|
|
|
2191
2258
|
return null;
|
|
2192
2259
|
}
|
|
2193
2260
|
try {
|
|
2194
|
-
const
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
if (status === "new") stats.new++;
|
|
2217
|
-
else if (status === "read") stats.read++;
|
|
2218
|
-
else if (status === "archived") stats.archived++;
|
|
2219
|
-
}
|
|
2220
|
-
return stats;
|
|
2261
|
+
const submissionsSlug = pluginConfig.formSubmissionOverrides.slug;
|
|
2262
|
+
const sys = { as: "system" };
|
|
2263
|
+
const base = { form: { equals: form.id } };
|
|
2264
|
+
const [total, newCount, read, archived] = await Promise.all([
|
|
2265
|
+
collections.count(submissionsSlug, { where: base }, sys),
|
|
2266
|
+
collections.count(
|
|
2267
|
+
submissionsSlug,
|
|
2268
|
+
{ where: { ...base, status: { equals: "new" } } },
|
|
2269
|
+
sys
|
|
2270
|
+
),
|
|
2271
|
+
collections.count(
|
|
2272
|
+
submissionsSlug,
|
|
2273
|
+
{ where: { ...base, status: { equals: "read" } } },
|
|
2274
|
+
sys
|
|
2275
|
+
),
|
|
2276
|
+
collections.count(
|
|
2277
|
+
submissionsSlug,
|
|
2278
|
+
{ where: { ...base, status: { equals: "archived" } } },
|
|
2279
|
+
sys
|
|
2280
|
+
)
|
|
2281
|
+
]);
|
|
2282
|
+
return { total, new: newCount, read, archived };
|
|
2221
2283
|
} catch {
|
|
2222
2284
|
return null;
|
|
2223
2285
|
}
|