@nextlyhq/plugin-form-builder 0.0.2-alpha.74 → 0.0.2-alpha.76

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
@@ -66,7 +66,8 @@ import "@nextlyhq/plugin-form-builder/admin";
66
66
  - **Admin routes:** `/admin/collections/forms` and `/admin/collections/form-submissions`
67
67
  - **Collections:** `forms`, `form-submissions`
68
68
  - **Form lifecycle states:** Draft, Published, Closed
69
- - **Optional integrations:** email notifications, a `form.submission.created` webhook through Nextly's own webhooks, spam protection (honeypot, rate limit, reCAPTCHA v3)
69
+ - **One response per person:** by email address, browser or network address, refused atomically by a keyed digest (the browser mode needs your form's script to send a token; see the docs)
70
+ - **Optional integrations:** email notifications, a `form.submission.created` webhook through Nextly's own webhooks, spam protection (honeypot, rate limit, and a reCAPTCHA v3 or Cloudflare Turnstile bot check verified on the server)
70
71
 
71
72
  ### Submission privacy
72
73
 
@@ -1,7 +1,7 @@
1
- import { O as FormSettings, S as FormFieldTypeId, T as FormNotification, b as FormField, i as AnyFormField, r as NewSubmissionsData } from "../new-submissions-CzlhVGhH.js";
2
- import { ComponentPath, DocumentLockGates } from "@nextlyhq/plugin-sdk/admin";
1
+ import { D as FormSettings, Z as NewSubmissionsData, t as AnyFormField, w as FormNotification, x as FormFieldTypeId } from "../types-XdBrUfi1.js";
2
+ import { ComponentPath, DocumentLockGates, FieldPickerChoice } from "@nextlyhq/plugin-sdk/admin";
3
3
  import React, { JSX, ReactNode } from "react";
4
- import { FieldTypeCatalogEntry, FormFieldCatalogType } from "nextly/field-catalog";
4
+ import { FormFieldCatalogType } from "nextly/field-catalog";
5
5
  interface FormBuilderViewProps {
6
6
  id?: string;
7
7
  entryId?: string;
@@ -14,6 +14,8 @@ interface FormBuilderViewProps {
14
14
  slug?: string;
15
15
  description?: string;
16
16
  status?: "draft" | "published" | "closed";
17
+ /** When the form first went live: what fixes its ID. */
18
+ wentLiveAt?: string | null;
17
19
  closedMessage?: string;
18
20
  fields?: unknown[];
19
21
  settings?: Record<string, unknown>;
@@ -24,8 +26,15 @@ interface FormBuilderViewProps {
24
26
  */
25
27
  _rev?: string;
26
28
  };
27
- onSave?: (data: unknown) => void;
29
+ /**
30
+ * Saves the form in the host's own way, in place of the builder's request.
31
+ * The builder counts the form saved once this returns, or once the promise
32
+ * it may return settles without failing.
33
+ */
34
+ onSave?: (data: unknown) => void | Promise<void>;
28
35
  onSuccess?: (entry?: Record<string, unknown>) => void;
36
+ /** Called once the form has been deleted from the editor's More menu. */
37
+ onDelete?: () => void;
29
38
  onCancel?: () => void;
30
39
  /**
31
40
  * What a colleague's claim on this form withholds, handed over by the admin
@@ -37,8 +46,20 @@ interface FormBuilderViewProps {
37
46
  * it too.
38
47
  */
39
48
  documentLock?: DocumentLockGates;
49
+ /**
50
+ * The page of the editor the address names (`"settings"` at
51
+ * `/admin/collections/forms/<id>/settings`); absent on the form's edit
52
+ * address, which opens Builder.
53
+ */
54
+ view?: string;
40
55
  }
41
- export declare function FormBuilderView({ id, entryId, collection, collectionSlug, isCreating, initialData, onSave, onSuccess, onCancel, documentLock }: FormBuilderViewProps): JSX.Element;
56
+ export declare function FormBuilderView({ id, entryId, collection, collectionSlug, isCreating, initialData, onSave, onSuccess, onDelete, documentLock, view }: FormBuilderViewProps): JSX.Element;
57
+ /**
58
+ * A submission as the entries API answers it, and the readings of it the
59
+ * list and a submission's page share, so the two cannot read it differently.
60
+ *
61
+ * @module admin/components/submissions/submission-row
62
+ */
42
63
  interface SubmissionRow {
43
64
  id: string;
44
65
  form: string | {
@@ -47,46 +68,64 @@ interface SubmissionRow {
47
68
  };
48
69
  data: Record<string, unknown>;
49
70
  status: string;
71
+ /** Who sent it, as the write seam named it (`utils/submission-title`). */
72
+ title?: string | null;
50
73
  spamReason?: string | null;
51
74
  notes?: string | null;
52
75
  ipAddress?: string | null;
53
76
  userAgent?: string | null;
77
+ /** The page it was sent from: origin and path. */
78
+ page?: string | null;
54
79
  submittedAt?: string | null;
80
+ createdAt?: string | null;
55
81
  editedAt?: string | null;
56
82
  editedBy?: string | null;
57
- /** The submission's version as the list read it; see `submissionPatch`. */
83
+ /** What it was asked, as the write seam stored it. */
84
+ asked?: unknown;
85
+ /** Its version as read, sent back with a change. */
58
86
  _rev?: string;
59
87
  }
60
- interface FormOption {
61
- id: string;
62
- name: string;
63
- fields: FormField[];
64
- }
65
88
  interface SubmissionsViewProps {
66
89
  collectionSlug?: string;
90
+ /**
91
+ * Show only this form's submissions, with no choice of form: the list as a
92
+ * form's own Submissions tab draws it.
93
+ */
94
+ formId?: string;
67
95
  }
68
- export declare function SubmissionsView({ collectionSlug }: SubmissionsViewProps): JSX.Element;
69
- interface SubmissionSheetProps {
70
- submission: SubmissionRow;
71
- form: FormOption;
72
- canUpdate: boolean;
73
- onClose: () => void;
74
- onPrev?: () => void;
75
- onNext?: () => void;
76
- onSave: (changes: Record<string, unknown>) => Promise<void>;
96
+ export declare function SubmissionsView({ collectionSlug, formId }: SubmissionsViewProps): JSX.Element;
97
+ /** A submission as the entries API answers it. */
98
+ type SubmissionDocument = SubmissionRow;
99
+ interface SubmissionPageProps {
100
+ entryId?: string;
101
+ collectionSlug?: string;
102
+ initialData?: Partial<SubmissionDocument>;
103
+ /**
104
+ * What a colleague's claim on this submission withholds, as the entry page
105
+ * hands it over. While either is set the page offers no change and sends
106
+ * none, the automatic Read among them.
107
+ */
108
+ documentLock?: DocumentLockGates;
109
+ /**
110
+ * Tells the entry page the submission was deleted: it stands its leave
111
+ * guard down and returns to the list. Used when unsaved work is on the
112
+ * page; otherwise the page moves to the next submission itself.
113
+ */
114
+ onDelete?: () => void;
77
115
  }
78
- export declare function SubmissionSheet({ submission, form, canUpdate, onClose, onPrev, onNext, onSave }: SubmissionSheetProps): JSX.Element;
79
- /** The card's rows, as many as its size holds. */
80
- export declare function NewSubmissionsCard({ data }: {
116
+ export declare function SubmissionPage({ entryId, collectionSlug, initialData, documentLock, onDelete }: SubmissionPageProps): JSX.Element;
117
+ /** The card's rows: its reader's "Number of entries", the rows its `load` read. */
118
+ export declare function NewSubmissionsCard({ data, settings }: {
81
119
  data: NewSubmissionsData;
120
+ /** The card's settings as its `load` read them; `limit` is its "Number of entries". */
121
+ settings?: Record<string, unknown>;
82
122
  }): JSX.Element;
83
123
  interface FieldCardsProps {
84
124
  /**
85
125
  * Form-surface types the host has NOT excluded (the plugin's field
86
126
  * enable/disable option). `null` while the host configuration is still
87
- * loading — the Add dialog waits rather than flashing the unfiltered set.
88
- * Types outside this set stay renderable on existing fields but cannot be
89
- * added.
127
+ * loading: Add field waits rather than offering the unfiltered set. Types
128
+ * outside this set stay editable on existing fields but cannot be added.
90
129
  */
91
130
  enabledTypes: readonly FormFieldCatalogType[] | null;
92
131
  /**
@@ -95,52 +134,23 @@ interface FieldCardsProps {
95
134
  * contributed types.
96
135
  */
97
136
  disabledFieldTypes?: ReadonlySet<string>;
98
- /** Creates a field of the given type and returns it (context helper). */
99
- onAddField: (type: FormFieldTypeId) => void;
100
- }
101
- export declare function FieldCards({ enabledTypes, disabledFieldTypes, onAddField }: FieldCardsProps): JSX.Element;
102
- interface AddFieldDialogProps {
103
- open: boolean;
104
- onOpenChange: (open: boolean) => void;
105
137
  /**
106
- * The form surface's catalog — built-ins already filtered by the host's
107
- * excludes plus any plugin field types that opted into the forms surface;
108
- * `null` while the host configuration is still loading. `type` is the open
109
- * `FormFieldTypeId` so a plugin entry shares this shape with the built-ins.
138
+ * Creates and selects a field of the chosen type. The picker's whole choice
139
+ * comes second (the label the person typed and the name it gives), so a host
140
+ * written for the type alone keeps working and one that wants the label
141
+ * reads it there. The third is where in the list it goes, when it was added
142
+ * between two rows or below one; absent for the end.
110
143
  */
111
- entries: readonly FieldTypeCatalogEntry<FormFieldTypeId>[] | null;
112
- /** Called with the chosen type; the parent creates and selects the field. */
113
- onAdd: (type: FormFieldTypeId) => void;
114
- }
115
- export declare function AddFieldDialog({ open, onOpenChange, entries, onAdd }: AddFieldDialogProps): JSX.Element;
116
- interface FieldEditorProps {
117
- /** The field being edited (built-in or plugin-contributed). */
118
- field: AnyFormField;
119
- /** All fields in the form (for conditional logic references) */
120
- allFields: AnyFormField[];
121
- /** Callback when field is updated */
122
- onUpdate: (updates: Partial<AnyFormField>) => void;
144
+ onAddField: (type: FormFieldTypeId, choice?: FieldPickerChoice, index?: number) => void;
145
+ /**
146
+ * Where the selected field's settings dock from 1024px: `"beside"` the list
147
+ * (the default, for a list drawn on its own), or by the `"host"` page,
148
+ * which draws `FieldInspector` against the window's edge itself. Below
149
+ * 1024px they open under the row either way.
150
+ */
151
+ dockedSettings?: "beside" | "host";
123
152
  }
124
- /**
125
- * FieldEditor - Field properties configuration panel
126
- *
127
- * Provides a tabbed interface for editing field properties:
128
- * - General: name, label, placeholder, help text, required, width, type-specific options
129
- * - Validation: error messages, min/max length, patterns, etc.
130
- * - Conditional: show/hide logic based on other field values
131
- *
132
- * @example
133
- * ```tsx
134
- * <FieldEditor
135
- * field={selectedField}
136
- * allFields={allFields}
137
- * onUpdate={handleUpdate}
138
- * onDelete={handleDelete}
139
- * onDuplicate={handleDuplicate}
140
- * />
141
- * ```
142
- */
143
- export declare function FieldEditor({ field, allFields, onUpdate }: FieldEditorProps): JSX.Element;
153
+ export declare function FieldCards({ enabledTypes, disabledFieldTypes, onAddField, dockedSettings }: FieldCardsProps): JSX.Element;
144
154
  interface FormPreviewProps {
145
155
  /** Array of form fields to preview (built-in or plugin-contributed). */
146
156
  fields: AnyFormField[];
@@ -152,12 +162,23 @@ interface FormPreviewProps {
152
162
  };
153
163
  }
154
164
  export declare function FormPreview({ fields, formData }: FormPreviewProps): JSX.Element;
165
+ /**
166
+ * Generate a readable unique field name: the type itself for the first
167
+ * field of that type, then a numeric suffix (`email`, `email_2`, ...).
168
+ * Names key submission data, so they should read like keys a developer
169
+ * would have chosen, not timestamps.
170
+ */
171
+ export declare function generateFieldName(type: FormFieldTypeId, existingNames?: readonly string[]): string;
155
172
  interface FormBuilderState {
156
173
  /** Form fields array */
157
174
  fields: AnyFormField[];
158
175
  /** Currently selected field ID (name) */
159
176
  selectedFieldId: string | null;
160
- /** Active tab: builder, preview, settings, or notifications */
177
+ /**
178
+ * Active tab: builder, preview, settings, or notifications.
179
+ * @deprecated The editor shows the page its address names; this is no
180
+ * longer what it shows, and is kept for hosts that read it.
181
+ */
161
182
  activeTab: "builder" | "preview" | "settings" | "notifications";
162
183
  /** Whether form has unsaved changes */
163
184
  isDirty: boolean;
@@ -191,7 +212,10 @@ interface FormBuilderActions {
191
212
  duplicateField: (fieldName: string) => void;
192
213
  /** Select a field */
193
214
  selectField: (fieldName: string | null) => void;
194
- /** Set active tab */
215
+ /**
216
+ * Set active tab.
217
+ * @deprecated The editor's tabs are links to each page's address.
218
+ */
195
219
  setActiveTab: (tab: "builder" | "preview" | "settings" | "notifications") => void;
196
220
  /** Update form metadata */
197
221
  updateFormData: (updates: Partial<FormBuilderState["formData"]>) => void;
@@ -219,6 +243,8 @@ interface FormBuilderProviderProps {
219
243
  slug?: string;
220
244
  description?: string;
221
245
  status?: "draft" | "published" | "closed";
246
+ /** When the form first went live, as the server stamped it. */
247
+ wentLiveAt?: string | null;
222
248
  closedMessage?: string;
223
249
  fields?: AnyFormField[];
224
250
  settings?: Partial<FormSettings>;
@@ -241,14 +267,7 @@ interface FormBuilderProviderProps {
241
267
  /** Default form settings (the canonical defaults from the settings reader) */
242
268
  export declare const DEFAULT_SETTINGS: FormSettings;
243
269
  /** Create a new notification rule with default values */
244
- export declare function createNotification(): FormNotification;
245
- /**
246
- * Generate a readable unique field name: the type itself for the first
247
- * field of that type, then a numeric suffix (`email`, `email_2`, ...).
248
- * Names key submission data, so they should read like keys a developer
249
- * would have chosen, not timestamps.
250
- */
251
- export declare function generateFieldName(type: FormFieldTypeId, existingNames?: readonly string[]): string;
270
+ export declare function createNotification(kind?: "team" | "visitor", fields?: readonly AnyFormField[]): FormNotification;
252
271
  /**
253
272
  * Generate a human-readable label from field type, using the shared catalog
254
273
  * so a new field's default label matches what the picker called the type. A
@@ -310,7 +329,22 @@ export declare function useFormBuilder(): FormBuilderContextValue;
310
329
  * @returns Form builder context value or null
311
330
  */
312
331
  export declare function useOptionalFormBuilder(): FormBuilderContextValue | null;
313
- interface SpamDefaults {
332
+ type BotProvider = "recaptcha" | "turnstile";
333
+ /** What the plugin's options say about bot checks and the rate limit. */
334
+ interface BotCheckSiteDefaults {
335
+ /** The provider "Site default" resolves to, or null for none. */
336
+ botCheckDefault?: BotProvider | null;
337
+ /** Which providers have their keys set. */
338
+ botChecks?: Record<BotProvider, {
339
+ secret: boolean;
340
+ siteKey: boolean;
341
+ }>;
342
+ rateLimit?: {
343
+ maxSubmissions: number;
344
+ windowMs: number;
345
+ } | null;
346
+ }
347
+ interface SpamDefaults extends BotCheckSiteDefaults {
314
348
  honeypot?: boolean;
315
349
  recaptchaEnabled?: boolean;
316
350
  }
@@ -340,16 +374,12 @@ interface FormNotificationsTabProps {
340
374
  /** `null` while the builder-config request is still settling. */
341
375
  defaults: NotificationDefaults | null;
342
376
  }
343
- /**
344
- * FormNotificationsTab - the form's notification rules.
345
- *
346
- * Each rule references an existing email provider and template rather than
347
- * duplicating provider credentials or embedding a second template editor.
348
- */
349
377
  export declare function FormNotificationsTab({ defaults }: FormNotificationsTabProps): JSX.Element;
350
378
  /** Component path for FormBuilderView */
351
379
  export declare const FORM_BUILDER_VIEW_PATH: ComponentPath;
352
380
  /** Component path for SubmissionsView */
353
381
  export declare const SUBMISSIONS_VIEW_PATH: ComponentPath;
354
- export type { AddFieldDialogProps, FieldCardsProps, FieldEditorProps, FormBuilderActions, FormBuilderContextValue, FormBuilderProviderProps, FormBuilderState, FormBuilderViewProps, FormNotification, FormPreviewProps, FormSettings };
382
+ /** Component path for SubmissionPage, a submission's own page */
383
+ export declare const SUBMISSION_PAGE_PATH: ComponentPath;
384
+ export type { FieldCardsProps, FormBuilderActions, FormBuilderContextValue, FormBuilderProviderProps, FormBuilderState, FormBuilderViewProps, FormNotification, FormPreviewProps, FormSettings, SubmissionPageProps };
355
385
  //# sourceMappingURL=index.d.ts.map