@nextlyhq/plugin-form-builder 0.0.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.
Files changed (37) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +122 -0
  3. package/dist/CheckboxFieldEditor-K4T6QX52.js +121 -0
  4. package/dist/CheckboxFieldEditor-K4T6QX52.js.map +1 -0
  5. package/dist/DateFieldEditor-JFJFAVOH.js +187 -0
  6. package/dist/DateFieldEditor-JFJFAVOH.js.map +1 -0
  7. package/dist/EmailFieldEditor-MMOXXL7P.js +138 -0
  8. package/dist/EmailFieldEditor-MMOXXL7P.js.map +1 -0
  9. package/dist/NumberFieldEditor-UISAYR6Z.js +175 -0
  10. package/dist/NumberFieldEditor-UISAYR6Z.js.map +1 -0
  11. package/dist/RadioFieldEditor-6UQFH4WT.js +165 -0
  12. package/dist/RadioFieldEditor-6UQFH4WT.js.map +1 -0
  13. package/dist/SelectFieldEditor-PSO4KHV4.js +213 -0
  14. package/dist/SelectFieldEditor-PSO4KHV4.js.map +1 -0
  15. package/dist/TextFieldEditor-5EU64EA5.js +187 -0
  16. package/dist/TextFieldEditor-5EU64EA5.js.map +1 -0
  17. package/dist/TextareaFieldEditor-773G3LNM.js +190 -0
  18. package/dist/TextareaFieldEditor-773G3LNM.js.map +1 -0
  19. package/dist/admin/index.css +934 -0
  20. package/dist/admin/index.css.map +1 -0
  21. package/dist/admin/index.d.ts +547 -0
  22. package/dist/admin/index.js +4030 -0
  23. package/dist/admin/index.js.map +1 -0
  24. package/dist/chunk-ABNZFTQK.js +586 -0
  25. package/dist/chunk-ABNZFTQK.js.map +1 -0
  26. package/dist/chunk-XTCEPJ7B.js +256 -0
  27. package/dist/chunk-XTCEPJ7B.js.map +1 -0
  28. package/dist/components/index.d.ts +3 -0
  29. package/dist/components/index.js +9 -0
  30. package/dist/components/index.js.map +1 -0
  31. package/dist/index-C0kBvYjm.d.ts +1012 -0
  32. package/dist/index.d.ts +1663 -0
  33. package/dist/index.js +2470 -0
  34. package/dist/index.js.map +1 -0
  35. package/dist/styles/form-builder.css +1150 -0
  36. package/dist/styles/submissions-filter.css +75 -0
  37. package/package.json +96 -0
@@ -0,0 +1,1012 @@
1
+ import React, { ComponentType } from 'react';
2
+ import { RequestContext, CollectionConfig, FieldDefinition } from 'nextly';
3
+
4
+ /**
5
+ * Form Builder Plugin Types
6
+ *
7
+ * Defines configuration options and types for the form builder plugin.
8
+ *
9
+ * @packageDocumentation
10
+ */
11
+
12
+ /**
13
+ * Configuration for customizing field block behavior.
14
+ */
15
+ interface FieldBlockConfig {
16
+ /** Default label for this field type */
17
+ label?: string;
18
+ /** Default validation settings */
19
+ validation?: Record<string, unknown>;
20
+ /** Custom component to use for this field type */
21
+ component?: ComponentType<unknown>;
22
+ }
23
+ /**
24
+ * Email configuration for notifications.
25
+ */
26
+ interface EmailConfig {
27
+ to: string | string[];
28
+ from?: string;
29
+ subject: string;
30
+ html?: string;
31
+ text?: string;
32
+ replyTo?: string;
33
+ cc?: string | string[];
34
+ bcc?: string | string[];
35
+ }
36
+ /**
37
+ * A single email notification integration stored on a form.
38
+ * Matches the shape saved by the form builder admin UI.
39
+ */
40
+ interface FormNotificationItem {
41
+ /** Unique ID for this notification */
42
+ id: string;
43
+ /** Display name for this integration */
44
+ name: string;
45
+ /** Whether this notification is active */
46
+ enabled: boolean;
47
+ /** ID of the email provider to use; undefined = system default */
48
+ providerId?: string;
49
+ /** Sender email override; undefined = use provider's configured address */
50
+ senderEmail?: string;
51
+ /** How the recipient address is determined */
52
+ recipientType: "static" | "field";
53
+ /** Recipient: static email address or a {{fieldName}} reference */
54
+ to: string;
55
+ /** CC email addresses */
56
+ cc: string[];
57
+ /** BCC email addresses */
58
+ bcc: string[];
59
+ /** Slug of the email template to use */
60
+ templateSlug?: string;
61
+ }
62
+ /**
63
+ * Form document structure (stored in database).
64
+ */
65
+ interface FormDocument {
66
+ id: string;
67
+ slug: string;
68
+ name: string;
69
+ fields: FormField[];
70
+ settings: FormSettings;
71
+ notifications: FormNotificationItem[];
72
+ webhooks?: WebhookConfig[];
73
+ status: "draft" | "published" | "closed";
74
+ createdAt: Date;
75
+ updatedAt: Date;
76
+ }
77
+ /**
78
+ * Submission document structure (stored in database).
79
+ */
80
+ interface SubmissionDocument {
81
+ id: string;
82
+ form: string | FormDocument;
83
+ data: Record<string, unknown>;
84
+ status: "new" | "read" | "archived";
85
+ ipAddress?: string;
86
+ userAgent?: string;
87
+ submittedAt: Date;
88
+ createdAt: Date;
89
+ updatedAt: Date;
90
+ }
91
+ /**
92
+ * Configuration options for the form builder plugin.
93
+ */
94
+ interface FormBuilderPluginOptions {
95
+ /**
96
+ * Override the default Forms collection configuration.
97
+ *
98
+ * Allows customizing the slug, labels, fields, access control, and other
99
+ * collection options.
100
+ *
101
+ * The `fields` option can be:
102
+ * - An array of fields (merged with defaults)
103
+ * - A function receiving `{ defaultFields }` for full control
104
+ *
105
+ * @example
106
+ * ```typescript
107
+ * // Option 1: Array (fields merged with defaults)
108
+ * formOverrides: {
109
+ * slug: 'my-forms',
110
+ * labels: { singular: 'My Form', plural: 'My Forms' },
111
+ * access: {
112
+ * read: () => true,
113
+ * create: ({ roles }) => roles.includes('admin'),
114
+ * },
115
+ * fields: [
116
+ * text({ name: 'internalNotes', label: 'Internal Notes' }),
117
+ * ],
118
+ * }
119
+ *
120
+ * // Option 2: Function (full control over fields)
121
+ * formOverrides: {
122
+ * slug: 'contact-forms',
123
+ * access: {
124
+ * read: ({ user }) => !!user,
125
+ * update: () => false,
126
+ * },
127
+ * fields: ({ defaultFields }) => {
128
+ * return [
129
+ * ...defaultFields,
130
+ * { name: 'custom', type: 'text' },
131
+ * ];
132
+ * },
133
+ * }
134
+ * ```
135
+ */
136
+ formOverrides?: Partial<CollectionConfig> & {
137
+ slug?: string;
138
+ labels?: {
139
+ singular?: string;
140
+ plural?: string;
141
+ };
142
+ /** Fields can be an array or a function receiving defaultFields */
143
+ fields?: FieldDefinition[] | ((args: {
144
+ defaultFields: FieldDefinition[];
145
+ }) => FieldDefinition[]);
146
+ };
147
+ /**
148
+ * Override the default Form Submissions collection configuration.
149
+ *
150
+ * Allows customizing the slug, labels, fields, access control, and other
151
+ * collection options.
152
+ *
153
+ * The `fields` option can be:
154
+ * - An array of fields (merged with defaults)
155
+ * - A function receiving `{ defaultFields }` for full control
156
+ *
157
+ * @example
158
+ * ```typescript
159
+ * // Option 1: Array (fields merged with defaults)
160
+ * formSubmissionOverrides: {
161
+ * slug: 'my-submissions',
162
+ * labels: { singular: 'Response', plural: 'Responses' },
163
+ * access: {
164
+ * read: ({ roles }) => roles.includes('admin'),
165
+ * },
166
+ * fields: [
167
+ * select({
168
+ * name: 'priority',
169
+ * label: 'Priority',
170
+ * options: [
171
+ * { label: 'Low', value: 'low' },
172
+ * { label: 'High', value: 'high' },
173
+ * ],
174
+ * }),
175
+ * ],
176
+ * }
177
+ *
178
+ * // Option 2: Function (full control over fields)
179
+ * formSubmissionOverrides: {
180
+ * fields: ({ defaultFields }) => {
181
+ * // Add field after 'form' field
182
+ * const formIndex = defaultFields.findIndex(f => f.name === 'form');
183
+ * return [
184
+ * ...defaultFields.slice(0, formIndex + 1),
185
+ * { name: 'source', type: 'text' },
186
+ * ...defaultFields.slice(formIndex + 1),
187
+ * ];
188
+ * },
189
+ * }
190
+ * ```
191
+ */
192
+ formSubmissionOverrides?: Partial<CollectionConfig> & {
193
+ slug?: string;
194
+ labels?: {
195
+ singular?: string;
196
+ plural?: string;
197
+ };
198
+ /** Fields can be an array or a function receiving defaultFields */
199
+ fields?: FieldDefinition[] | ((args: {
200
+ defaultFields: FieldDefinition[];
201
+ }) => FieldDefinition[]);
202
+ };
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
+ * Configure which field types are available in the form builder.
225
+ *
226
+ * Set to `false` to disable a field type, or provide partial configuration
227
+ * to customize default behavior.
228
+ *
229
+ * @example
230
+ * ```typescript
231
+ * fields: {
232
+ * // Disable payment field
233
+ * payment: false,
234
+ *
235
+ * // Customize text field defaults
236
+ * text: {
237
+ * maxLength: 500,
238
+ * },
239
+ *
240
+ * // Disable file uploads
241
+ * file: false,
242
+ * }
243
+ * ```
244
+ */
245
+ fields?: {
246
+ text?: boolean | Partial<FieldBlockConfig>;
247
+ email?: boolean | Partial<FieldBlockConfig>;
248
+ number?: boolean | Partial<FieldBlockConfig>;
249
+ phone?: boolean | Partial<FieldBlockConfig>;
250
+ url?: boolean | Partial<FieldBlockConfig>;
251
+ textarea?: boolean | Partial<FieldBlockConfig>;
252
+ select?: boolean | Partial<FieldBlockConfig>;
253
+ checkbox?: boolean | Partial<FieldBlockConfig>;
254
+ radio?: boolean | Partial<FieldBlockConfig>;
255
+ file?: boolean | Partial<FieldBlockConfig>;
256
+ date?: boolean | Partial<FieldBlockConfig>;
257
+ time?: boolean | Partial<FieldBlockConfig>;
258
+ hidden?: boolean | Partial<FieldBlockConfig>;
259
+ };
260
+ /**
261
+ * Collections that can be used as redirect targets after form submission.
262
+ *
263
+ * When specified, the form builder will allow selecting documents from
264
+ * these collections as redirect destinations.
265
+ *
266
+ * @example
267
+ * ```typescript
268
+ * redirectRelationships: ['pages', 'posts', 'landing-pages']
269
+ * ```
270
+ */
271
+ redirectRelationships?: string[];
272
+ /**
273
+ * Hook called before sending email notifications.
274
+ *
275
+ * Allows modifying, adding, or filtering emails before they are sent.
276
+ * Return the modified emails array.
277
+ *
278
+ * @example
279
+ * ```typescript
280
+ * beforeEmail: async ({ emails, form, submission }) => {
281
+ * // Add BCC to all emails
282
+ * return emails.map(email => ({
283
+ * ...email,
284
+ * bcc: ['archive@example.com'],
285
+ * }));
286
+ * }
287
+ * ```
288
+ */
289
+ beforeEmail?: (args: {
290
+ emails: EmailConfig[];
291
+ form: FormDocument;
292
+ submission: SubmissionDocument;
293
+ }) => Promise<EmailConfig[]> | EmailConfig[];
294
+ /**
295
+ * Email notification settings.
296
+ */
297
+ notifications?: {
298
+ /**
299
+ * Default "from" email address.
300
+ */
301
+ defaultFrom?: string;
302
+ /**
303
+ * Default "to" email address for all form submissions.
304
+ *
305
+ * If specified, this email will receive all form submissions
306
+ * regardless of form-specific notification settings.
307
+ * @example
308
+ * ```typescript
309
+ * notifications: {
310
+ * defaultToEmail: 'submissions@example.com',
311
+ * }
312
+ * ```
313
+ */
314
+ defaultToEmail?: string;
315
+ /**
316
+ * Enable/disable email notifications globally.
317
+ * @default true
318
+ */
319
+ enabled?: boolean;
320
+ };
321
+ /**
322
+ * Spam protection configuration.
323
+ */
324
+ spamProtection?: {
325
+ /**
326
+ * Enable honeypot field for spam detection.
327
+ * @default true
328
+ */
329
+ honeypot?: boolean;
330
+ /**
331
+ * Enable reCAPTCHA v3.
332
+ * @default false
333
+ */
334
+ recaptcha?: {
335
+ enabled: boolean;
336
+ siteKey?: string;
337
+ secretKey?: string;
338
+ scoreThreshold?: number;
339
+ };
340
+ /**
341
+ * Rate limiting configuration.
342
+ */
343
+ rateLimit?: {
344
+ /**
345
+ * Maximum submissions per window.
346
+ * @default 10
347
+ */
348
+ maxSubmissions?: number;
349
+ /**
350
+ * Time window in milliseconds.
351
+ * @default 60000 (1 minute)
352
+ */
353
+ windowMs?: number;
354
+ };
355
+ };
356
+ /**
357
+ * File upload settings.
358
+ */
359
+ uploads?: {
360
+ /**
361
+ * Maximum file size in bytes.
362
+ * @default 10485760 (10MB)
363
+ */
364
+ maxFileSize?: number;
365
+ /**
366
+ * Allowed file types (MIME types).
367
+ * @default ['image/*', 'application/pdf', 'text/*']
368
+ */
369
+ allowedMimeTypes?: string[];
370
+ /**
371
+ * Collection to store uploaded files.
372
+ * @default 'media'
373
+ */
374
+ uploadCollection?: string;
375
+ };
376
+ /**
377
+ * Enable/disable features.
378
+ */
379
+ features?: {
380
+ /**
381
+ * Enable visual form builder UI.
382
+ * @default true
383
+ */
384
+ builder?: boolean;
385
+ /**
386
+ * Enable conditional logic.
387
+ * @default true
388
+ */
389
+ conditionalLogic?: boolean;
390
+ /**
391
+ * Enable file uploads.
392
+ * @default true
393
+ */
394
+ fileUploads?: boolean;
395
+ };
396
+ }
397
+ /**
398
+ * Form configuration interface.
399
+ * Similar to CollectionConfig but tailored for forms.
400
+ */
401
+ interface FormConfig {
402
+ /**
403
+ * Unique identifier for the form.
404
+ */
405
+ slug: string;
406
+ /**
407
+ * Display labels for the form.
408
+ */
409
+ labels?: {
410
+ singular?: string;
411
+ plural?: string;
412
+ };
413
+ /**
414
+ * Form fields definition.
415
+ */
416
+ fields: FormField[];
417
+ /**
418
+ * Form settings and behavior.
419
+ */
420
+ settings?: FormSettings;
421
+ /**
422
+ * Email notification configuration.
423
+ */
424
+ notifications?: FormNotifications;
425
+ /**
426
+ * Access control for form submission and viewing.
427
+ */
428
+ access?: FormAccess;
429
+ /**
430
+ * Lifecycle hooks for form submissions.
431
+ */
432
+ hooks?: FormHooks;
433
+ /**
434
+ * Custom metadata.
435
+ */
436
+ custom?: Record<string, unknown>;
437
+ }
438
+ /**
439
+ * Form field type (discriminated union).
440
+ * Uses the `type` property as the discriminant.
441
+ */
442
+ type FormField = TextFormField | EmailFormField | NumberFormField | PhoneFormField | UrlFormField | TextareaFormField | SelectFormField | CheckboxFormField | RadioFormField | FileFormField | DateFormField | TimeFormField | HiddenFormField;
443
+ /**
444
+ * All available form field type identifiers.
445
+ */
446
+ type FormFieldType = "text" | "email" | "number" | "phone" | "url" | "textarea" | "select" | "checkbox" | "radio" | "file" | "date" | "time" | "hidden";
447
+ /**
448
+ * Base form field properties.
449
+ * All form field types extend this interface.
450
+ */
451
+ interface BaseFormField {
452
+ /** Field type identifier (discriminant) */
453
+ type: FormFieldType;
454
+ /** Field name (used as key in submission data) */
455
+ name: string;
456
+ /** Display label */
457
+ label: string;
458
+ /** Placeholder text */
459
+ placeholder?: string;
460
+ /** Help text shown below field */
461
+ helpText?: string;
462
+ /** Default value */
463
+ defaultValue?: unknown;
464
+ /** Whether field is required */
465
+ required?: boolean;
466
+ /** Validation rules */
467
+ validation?: ValidationRules;
468
+ /** Conditional logic configuration */
469
+ conditionalLogic?: ConditionalLogic;
470
+ /** Admin UI configuration */
471
+ admin?: {
472
+ /** CSS classes */
473
+ className?: string;
474
+ /** Field width */
475
+ width?: "25%" | "33%" | "50%" | "66%" | "75%" | "100%";
476
+ };
477
+ }
478
+ /**
479
+ * Text input field.
480
+ */
481
+ interface TextFormField extends Omit<BaseFormField, "type"> {
482
+ type: "text";
483
+ defaultValue?: string;
484
+ validation?: ValidationRules & {
485
+ minLength?: number;
486
+ maxLength?: number;
487
+ pattern?: string;
488
+ };
489
+ }
490
+ /**
491
+ * Email input field.
492
+ */
493
+ interface EmailFormField extends Omit<BaseFormField, "type"> {
494
+ type: "email";
495
+ defaultValue?: string;
496
+ validation?: ValidationRules & {
497
+ pattern?: string;
498
+ };
499
+ }
500
+ /**
501
+ * Number input field.
502
+ */
503
+ interface NumberFormField extends Omit<BaseFormField, "type"> {
504
+ type: "number";
505
+ defaultValue?: number;
506
+ validation?: ValidationRules & {
507
+ min?: number;
508
+ max?: number;
509
+ step?: number;
510
+ };
511
+ }
512
+ /**
513
+ * Phone input field.
514
+ */
515
+ interface PhoneFormField extends Omit<BaseFormField, "type"> {
516
+ type: "phone";
517
+ defaultValue?: string;
518
+ validation?: ValidationRules & {
519
+ pattern?: string;
520
+ };
521
+ }
522
+ /**
523
+ * URL input field.
524
+ */
525
+ interface UrlFormField extends Omit<BaseFormField, "type"> {
526
+ type: "url";
527
+ defaultValue?: string;
528
+ validation?: ValidationRules & {
529
+ pattern?: string;
530
+ };
531
+ }
532
+ /**
533
+ * Textarea field.
534
+ */
535
+ interface TextareaFormField extends Omit<BaseFormField, "type"> {
536
+ type: "textarea";
537
+ defaultValue?: string;
538
+ rows?: number;
539
+ validation?: ValidationRules & {
540
+ minLength?: number;
541
+ maxLength?: number;
542
+ };
543
+ }
544
+ /**
545
+ * Select dropdown field.
546
+ */
547
+ interface SelectFormField extends Omit<BaseFormField, "type"> {
548
+ type: "select";
549
+ options: Array<{
550
+ label: string;
551
+ value: string;
552
+ }>;
553
+ defaultValue?: string | string[];
554
+ allowMultiple?: boolean;
555
+ }
556
+ /**
557
+ * Single checkbox field.
558
+ */
559
+ interface CheckboxFormField extends Omit<BaseFormField, "type"> {
560
+ type: "checkbox";
561
+ defaultValue?: boolean;
562
+ }
563
+ /**
564
+ * Radio group field.
565
+ */
566
+ interface RadioFormField extends Omit<BaseFormField, "type"> {
567
+ type: "radio";
568
+ options: Array<{
569
+ label: string;
570
+ value: string;
571
+ }>;
572
+ defaultValue?: string;
573
+ }
574
+ /**
575
+ * File upload field.
576
+ */
577
+ interface FileFormField extends Omit<BaseFormField, "type"> {
578
+ type: "file";
579
+ accept?: string;
580
+ multiple?: boolean;
581
+ maxFileSize?: number;
582
+ /**
583
+ * When true, files uploaded through this field are attached to all
584
+ * notification emails for this form. Default false.
585
+ */
586
+ attachToEmail?: boolean;
587
+ }
588
+ /**
589
+ * Date picker field.
590
+ */
591
+ interface DateFormField extends Omit<BaseFormField, "type"> {
592
+ type: "date";
593
+ defaultValue?: string;
594
+ min?: string;
595
+ max?: string;
596
+ }
597
+ /**
598
+ * Time picker field.
599
+ */
600
+ interface TimeFormField extends Omit<BaseFormField, "type"> {
601
+ type: "time";
602
+ defaultValue?: string;
603
+ }
604
+ /**
605
+ * Hidden field.
606
+ */
607
+ interface HiddenFormField extends Omit<BaseFormField, "type"> {
608
+ type: "hidden";
609
+ defaultValue?: string;
610
+ }
611
+ /**
612
+ * Validation rules for form fields.
613
+ */
614
+ interface ValidationRules {
615
+ /** Custom error message */
616
+ errorMessage?: string;
617
+ /** Custom validation function */
618
+ validate?: (value: unknown, formData: Record<string, unknown>) => boolean | string | Promise<boolean | string>;
619
+ }
620
+ /**
621
+ * Conditional logic configuration.
622
+ */
623
+ interface ConditionalLogic {
624
+ /** Whether conditional logic is enabled */
625
+ enabled: boolean;
626
+ /** Action to perform when conditions are met */
627
+ action: "show" | "hide";
628
+ /** Logical operator for multiple conditions */
629
+ operator: "AND" | "OR";
630
+ /** Conditions to evaluate */
631
+ conditions: ConditionalLogicCondition[];
632
+ }
633
+ /**
634
+ * A single conditional logic condition.
635
+ */
636
+ interface ConditionalLogicCondition {
637
+ /** Field name to check */
638
+ field: string;
639
+ /** Comparison operator */
640
+ comparison: "equals" | "notEquals" | "contains" | "isEmpty" | "isNotEmpty" | "greaterThan" | "lessThan";
641
+ /** Value to compare against (not used for isEmpty/isNotEmpty) */
642
+ value?: unknown;
643
+ }
644
+ /**
645
+ * Form settings and behavior.
646
+ */
647
+ interface FormSettings {
648
+ /** Submit button text */
649
+ submitButtonText?: string;
650
+ /**
651
+ * Confirmation type after successful submission.
652
+ * - "message": Display a success message
653
+ * - "redirect": Redirect to a URL or linked document
654
+ *
655
+ * @default "message"
656
+ */
657
+ confirmationType?: "message" | "redirect";
658
+ /** Success message (supports HTML) - used when confirmationType is "message" */
659
+ successMessage?: string;
660
+ /** Redirect URL after successful submission - used when confirmationType is "redirect" */
661
+ redirectUrl?: string;
662
+ /**
663
+ * Relationship to redirect document - used when confirmationType is "redirect"
664
+ * and redirectRelationships are configured in plugin options.
665
+ */
666
+ redirectRelation?: {
667
+ relationTo: string;
668
+ value: string;
669
+ };
670
+ /** Allow multiple submissions from same user/IP */
671
+ allowMultipleSubmissions?: boolean;
672
+ /** reCAPTCHA configuration */
673
+ captcha?: {
674
+ enabled: boolean;
675
+ siteKey?: string;
676
+ };
677
+ }
678
+ /**
679
+ * Email notification configuration for a form.
680
+ */
681
+ interface FormNotifications {
682
+ /** Enable email notifications */
683
+ enabled: boolean;
684
+ /** Recipient email addresses */
685
+ recipients: Array<{
686
+ email: string;
687
+ name?: string;
688
+ }>;
689
+ /** Email subject line */
690
+ subject?: string;
691
+ /** Use submitter's email as reply-to */
692
+ replyTo?: boolean;
693
+ /** Custom email template */
694
+ template?: string;
695
+ /**
696
+ * Webhook endpoints to notify on form events.
697
+ *
698
+ * Webhooks are fired asynchronously and do not block form submission.
699
+ * Failed webhooks are logged but do not cause submission failures.
700
+ *
701
+ * @example
702
+ * ```typescript
703
+ * webhooks: [
704
+ * {
705
+ * url: 'https://api.example.com/webhooks/form-submissions',
706
+ * events: ['submission.created'],
707
+ * secret: 'your-webhook-secret', // Optional HMAC signature
708
+ * },
709
+ * {
710
+ * url: 'https://crm.example.com/api/leads',
711
+ * events: ['submission.created', 'submission.updated'],
712
+ * headers: { 'X-API-Key': 'abc123' },
713
+ * includeData: true,
714
+ * },
715
+ * ]
716
+ * ```
717
+ */
718
+ webhooks?: WebhookConfig[];
719
+ }
720
+ /**
721
+ * Webhook event types.
722
+ */
723
+ type WebhookEvent = "submission.created" | "submission.updated" | "submission.deleted";
724
+ /**
725
+ * Configuration for a webhook endpoint.
726
+ */
727
+ interface WebhookConfig {
728
+ /** Webhook URL to send POST requests to */
729
+ url: string;
730
+ /**
731
+ * HTTP method for the webhook request.
732
+ * @default "POST"
733
+ */
734
+ method?: "POST" | "PUT";
735
+ /**
736
+ * Custom headers to include in the webhook request.
737
+ * Useful for authentication tokens or API keys.
738
+ */
739
+ headers?: Record<string, string>;
740
+ /**
741
+ * Events that trigger this webhook.
742
+ * Must include at least one event.
743
+ */
744
+ events: WebhookEvent[];
745
+ /**
746
+ * Whether to include full submission data in the webhook payload.
747
+ * If false, only submission ID and timestamp are included.
748
+ * @default true
749
+ */
750
+ includeData?: boolean;
751
+ /**
752
+ * Secret key for HMAC-SHA256 signature verification.
753
+ *
754
+ * When provided, the webhook request will include an
755
+ * `X-Webhook-Signature` header containing the signature:
756
+ * `sha256=<hex-encoded-hmac>`
757
+ *
758
+ * Recipients can verify the signature to ensure the webhook
759
+ * originated from your server and wasn't tampered with.
760
+ *
761
+ * @example
762
+ * ```typescript
763
+ * // Verifying the signature on the receiving end (Node.js):
764
+ * const crypto = require('crypto');
765
+ * const expectedSignature = req.headers['x-webhook-signature'];
766
+ * const payload = JSON.stringify(req.body);
767
+ * const signature = 'sha256=' + crypto
768
+ * .createHmac('sha256', secret)
769
+ * .update(payload)
770
+ * .digest('hex');
771
+ * const isValid = crypto.timingSafeEqual(
772
+ * Buffer.from(expectedSignature),
773
+ * Buffer.from(signature)
774
+ * );
775
+ * ```
776
+ */
777
+ secret?: string;
778
+ }
779
+ /**
780
+ * Form access control.
781
+ */
782
+ interface FormAccess {
783
+ /** Who can submit the form */
784
+ submit?: (context: {
785
+ req: RequestContext;
786
+ }) => boolean | Promise<boolean>;
787
+ /** Who can view submissions */
788
+ read?: (context: {
789
+ req: RequestContext;
790
+ }) => boolean | Promise<boolean>;
791
+ }
792
+ /**
793
+ * Form lifecycle hooks.
794
+ */
795
+ interface FormHooks {
796
+ /** Before submission validation */
797
+ beforeValidate?: Array<(context: FormHookContext) => void | Promise<void>>;
798
+ /** Before submission is saved */
799
+ beforeSubmit?: Array<(context: FormHookContext) => void | Promise<void>>;
800
+ /** After submission is saved */
801
+ afterSubmit?: Array<(context: FormHookContext) => void | Promise<void>>;
802
+ }
803
+ /**
804
+ * Hook context for form hooks.
805
+ */
806
+ interface FormHookContext {
807
+ /** Form configuration */
808
+ form: FormConfig;
809
+ /** Submission data */
810
+ data: Record<string, unknown>;
811
+ /** Request context */
812
+ req: RequestContext;
813
+ /** Submission metadata */
814
+ metadata?: {
815
+ ipAddress?: string;
816
+ userAgent?: string;
817
+ };
818
+ }
819
+ /**
820
+ * Form submission data structure.
821
+ */
822
+ interface FormSubmission {
823
+ /** Submission ID */
824
+ id: string;
825
+ /** Form slug */
826
+ formSlug: string;
827
+ /** Submission data (JSON) */
828
+ data: Record<string, unknown>;
829
+ /** Submission status */
830
+ status: "new" | "read" | "archived";
831
+ /** Internal notes (admin only) */
832
+ notes?: string;
833
+ /** Metadata */
834
+ ipAddress?: string;
835
+ userAgent?: string;
836
+ submittedAt: Date;
837
+ /** Created/updated timestamps */
838
+ createdAt: Date;
839
+ updatedAt: Date;
840
+ }
841
+ /**
842
+ * Resolved form builder configuration after applying defaults.
843
+ * Used internally by the plugin.
844
+ */
845
+ interface ResolvedFormBuilderConfig {
846
+ formOverrides: {
847
+ slug: string;
848
+ labels: {
849
+ singular: string;
850
+ plural: string;
851
+ };
852
+ } & Partial<CollectionConfig>;
853
+ formSubmissionOverrides: {
854
+ slug: string;
855
+ labels: {
856
+ singular: string;
857
+ plural: string;
858
+ };
859
+ } & Partial<CollectionConfig>;
860
+ fields: {
861
+ text: boolean | Partial<FieldBlockConfig>;
862
+ email: boolean | Partial<FieldBlockConfig>;
863
+ number: boolean | Partial<FieldBlockConfig>;
864
+ phone: boolean | Partial<FieldBlockConfig>;
865
+ url: boolean | Partial<FieldBlockConfig>;
866
+ textarea: boolean | Partial<FieldBlockConfig>;
867
+ select: boolean | Partial<FieldBlockConfig>;
868
+ checkbox: boolean | Partial<FieldBlockConfig>;
869
+ radio: boolean | Partial<FieldBlockConfig>;
870
+ file: boolean | Partial<FieldBlockConfig>;
871
+ date: boolean | Partial<FieldBlockConfig>;
872
+ time: boolean | Partial<FieldBlockConfig>;
873
+ hidden: boolean | Partial<FieldBlockConfig>;
874
+ };
875
+ redirectRelationships: string[];
876
+ beforeEmail?: FormBuilderPluginOptions["beforeEmail"];
877
+ notifications: {
878
+ defaultFrom?: string;
879
+ defaultToEmail?: string;
880
+ enabled: boolean;
881
+ };
882
+ spamProtection: {
883
+ honeypot: boolean;
884
+ recaptcha?: {
885
+ enabled: boolean;
886
+ siteKey?: string;
887
+ secretKey?: string;
888
+ scoreThreshold?: number;
889
+ };
890
+ rateLimit?: {
891
+ maxSubmissions: number;
892
+ windowMs: number;
893
+ };
894
+ };
895
+ uploads: {
896
+ maxFileSize: number;
897
+ allowedMimeTypes: string[];
898
+ uploadCollection: string;
899
+ };
900
+ features: {
901
+ builder: boolean;
902
+ conditionalLogic: boolean;
903
+ fileUploads: boolean;
904
+ };
905
+ }
906
+ /**
907
+ * Check if a value is a FormField.
908
+ */
909
+ declare function isFormField(value: unknown): value is FormField;
910
+ /**
911
+ * Check if a FormField is a specific type.
912
+ */
913
+ declare function isFormFieldType<T extends FormField>(field: FormField, type: T["type"]): field is T;
914
+ /**
915
+ * Type guard for text field.
916
+ */
917
+ declare function isTextFormField(field: FormField): field is TextFormField;
918
+ /**
919
+ * Type guard for email field.
920
+ */
921
+ declare function isEmailFormField(field: FormField): field is EmailFormField;
922
+ /**
923
+ * Type guard for select field.
924
+ */
925
+ declare function isSelectFormField(field: FormField): field is SelectFormField;
926
+ /**
927
+ * Type guard for checkbox field.
928
+ */
929
+ declare function isCheckboxFormField(field: FormField): field is CheckboxFormField;
930
+ /**
931
+ * Type guard for file field.
932
+ */
933
+ declare function isFileFormField(field: FormField): field is FileFormField;
934
+
935
+ /**
936
+ * Props for the SubmissionList component.
937
+ */
938
+ interface SubmissionListProps {
939
+ /** Form document containing field definitions */
940
+ form: FormDocument;
941
+ /** Array of submission documents to display */
942
+ submissions: SubmissionDocument[];
943
+ /** Whether data is currently loading */
944
+ isLoading?: boolean;
945
+ /** Callback when submission status is changed */
946
+ onStatusChange?: (id: string, status: "new" | "read" | "archived") => Promise<void>;
947
+ /** Callback when submission is deleted */
948
+ onDelete?: (id: string) => Promise<void>;
949
+ /** Callback when bulk delete is triggered */
950
+ onBulkDelete?: (ids: string[]) => Promise<void>;
951
+ /** Callback when bulk status change is triggered */
952
+ onBulkStatusChange?: (ids: string[], status: "new" | "read" | "archived") => Promise<void>;
953
+ /** Callback when export is triggered */
954
+ onExport?: (format: "csv" | "json") => void;
955
+ /** Callback when a submission is clicked for detail view */
956
+ onViewDetail?: (submission: SubmissionDocument) => void;
957
+ }
958
+ /**
959
+ * Submission List Component
960
+ *
961
+ * Displays form submissions in a table with filtering, selection, and actions.
962
+ *
963
+ * @example
964
+ * ```tsx
965
+ * <SubmissionList
966
+ * form={form}
967
+ * submissions={submissions}
968
+ * onStatusChange={handleStatusChange}
969
+ * onDelete={handleDelete}
970
+ * onExport={handleExport}
971
+ * onViewDetail={handleViewDetail}
972
+ * />
973
+ * ```
974
+ */
975
+ declare function SubmissionList({ form, submissions, isLoading, onStatusChange, onDelete, onBulkDelete, onBulkStatusChange, onExport, onViewDetail, }: SubmissionListProps): React.ReactElement;
976
+
977
+ /**
978
+ * Props for the SubmissionDetail component.
979
+ */
980
+ interface SubmissionDetailProps {
981
+ /** Form document containing field definitions */
982
+ form: FormDocument;
983
+ /** Submission document to display */
984
+ submission: SubmissionDocument;
985
+ /** Callback when status is changed */
986
+ onStatusChange?: (status: "new" | "read" | "archived") => Promise<void>;
987
+ /** Callback when notes are updated */
988
+ onNotesUpdate?: (notes: string) => Promise<void>;
989
+ /** Callback when back button is clicked */
990
+ onBack?: () => void;
991
+ /** Callback when delete is triggered */
992
+ onDelete?: () => Promise<void>;
993
+ }
994
+ /**
995
+ * Submission Detail Component
996
+ *
997
+ * Displays full submission data with status management and notes.
998
+ *
999
+ * @example
1000
+ * ```tsx
1001
+ * <SubmissionDetail
1002
+ * form={form}
1003
+ * submission={submission}
1004
+ * onStatusChange={handleStatusChange}
1005
+ * onNotesUpdate={handleNotesUpdate}
1006
+ * onBack={handleBack}
1007
+ * />
1008
+ * ```
1009
+ */
1010
+ declare function SubmissionDetail({ form, submission, onStatusChange, onNotesUpdate, onBack, onDelete, }: SubmissionDetailProps): React.ReactElement;
1011
+
1012
+ export { isFormFieldType as A, type BaseFormField as B, type CheckboxFormField as C, type DateFormField as D, type EmailFormField as E, type FormBuilderPluginOptions as F, isSelectFormField as G, type HiddenFormField as H, isTextFormField as I, SubmissionDetail as J, SubmissionList as K, type NumberFormField as N, type PhoneFormField as P, type ResolvedFormBuilderConfig as R, type SelectFormField as S, type TextFormField as T, type UrlFormField as U, type ValidationRules as V, type WebhookEvent as W, type FormConfig as a, type FormSettings as b, type FormNotifications as c, type FileFormField as d, type RadioFormField as e, type TextareaFormField as f, type TimeFormField as g, type ConditionalLogic as h, type FormField as i, type SubmissionDocument as j, type FormDocument as k, type ConditionalLogicCondition as l, type EmailConfig as m, type FieldBlockConfig as n, type FormAccess as o, type FormFieldType as p, type FormHookContext as q, type FormHooks as r, type FormSubmission as s, type SubmissionDetailProps as t, type SubmissionListProps as u, type WebhookConfig as v, isCheckboxFormField as w, isEmailFormField as x, isFileFormField as y, isFormField as z };