@nextlyhq/plugin-form-builder 0.0.2-alpha.4 → 0.0.2-alpha.40

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 (38) hide show
  1. package/README.md +4 -5
  2. package/dist/admin/index.d.ts +123 -331
  3. package/dist/admin/index.js +3809 -3419
  4. package/dist/admin/index.js.map +1 -1
  5. package/dist/chunk-WVI7N3QO.js +559 -0
  6. package/dist/chunk-WVI7N3QO.js.map +1 -0
  7. package/dist/index.d.ts +12 -11
  8. package/dist/index.js +555 -661
  9. package/dist/index.js.map +1 -1
  10. package/dist/{index-C0kBvYjm.d.ts → types-r8uBeB0u.d.ts} +126 -125
  11. package/package.json +28 -30
  12. package/dist/CheckboxFieldEditor-K4T6QX52.js +0 -121
  13. package/dist/CheckboxFieldEditor-K4T6QX52.js.map +0 -1
  14. package/dist/DateFieldEditor-JFJFAVOH.js +0 -187
  15. package/dist/DateFieldEditor-JFJFAVOH.js.map +0 -1
  16. package/dist/EmailFieldEditor-MMOXXL7P.js +0 -138
  17. package/dist/EmailFieldEditor-MMOXXL7P.js.map +0 -1
  18. package/dist/NumberFieldEditor-UISAYR6Z.js +0 -175
  19. package/dist/NumberFieldEditor-UISAYR6Z.js.map +0 -1
  20. package/dist/RadioFieldEditor-6UQFH4WT.js +0 -165
  21. package/dist/RadioFieldEditor-6UQFH4WT.js.map +0 -1
  22. package/dist/SelectFieldEditor-PSO4KHV4.js +0 -213
  23. package/dist/SelectFieldEditor-PSO4KHV4.js.map +0 -1
  24. package/dist/TextFieldEditor-5EU64EA5.js +0 -187
  25. package/dist/TextFieldEditor-5EU64EA5.js.map +0 -1
  26. package/dist/TextareaFieldEditor-773G3LNM.js +0 -190
  27. package/dist/TextareaFieldEditor-773G3LNM.js.map +0 -1
  28. package/dist/admin/index.css +0 -934
  29. package/dist/admin/index.css.map +0 -1
  30. package/dist/chunk-ABNZFTQK.js +0 -586
  31. package/dist/chunk-ABNZFTQK.js.map +0 -1
  32. package/dist/chunk-XTCEPJ7B.js +0 -256
  33. package/dist/chunk-XTCEPJ7B.js.map +0 -1
  34. package/dist/components/index.d.ts +0 -3
  35. package/dist/components/index.js +0 -9
  36. package/dist/components/index.js.map +0 -1
  37. package/dist/styles/form-builder.css +0 -1150
  38. package/dist/styles/submissions-filter.css +0 -75
@@ -0,0 +1,559 @@
1
+ // src/utils/evaluate-conditions.ts
2
+ function isEmptyValue(value) {
3
+ if (value === void 0 || value === null) {
4
+ return true;
5
+ }
6
+ if (typeof value === "string" && value.trim() === "") {
7
+ return true;
8
+ }
9
+ if (Array.isArray(value) && value.length === 0) {
10
+ return true;
11
+ }
12
+ return false;
13
+ }
14
+ function toNumber(value) {
15
+ if (typeof value === "number") {
16
+ return value;
17
+ }
18
+ if (typeof value === "string") {
19
+ const trimmed = value.trim();
20
+ if (trimmed === "") {
21
+ return NaN;
22
+ }
23
+ return Number(trimmed);
24
+ }
25
+ return NaN;
26
+ }
27
+ function toString(value) {
28
+ if (value === void 0 || value === null) {
29
+ return "";
30
+ }
31
+ if (typeof value === "string") {
32
+ return value;
33
+ }
34
+ if (Array.isArray(value)) {
35
+ return value.join(", ");
36
+ }
37
+ if (typeof value === "object") {
38
+ return JSON.stringify(value);
39
+ }
40
+ return String(value);
41
+ }
42
+ function compareValues(operator, fieldValue, conditionValue) {
43
+ switch (operator) {
44
+ case "equals": {
45
+ if (Array.isArray(fieldValue)) {
46
+ if (Array.isArray(conditionValue)) {
47
+ if (fieldValue.length !== conditionValue.length) {
48
+ return false;
49
+ }
50
+ return fieldValue.every((v, i) => v === conditionValue[i]);
51
+ }
52
+ return fieldValue.includes(conditionValue);
53
+ }
54
+ return toString(fieldValue) === toString(conditionValue);
55
+ }
56
+ case "notEquals": {
57
+ if (Array.isArray(fieldValue)) {
58
+ if (Array.isArray(conditionValue)) {
59
+ if (fieldValue.length !== conditionValue.length) {
60
+ return true;
61
+ }
62
+ return !fieldValue.every((v, i) => v === conditionValue[i]);
63
+ }
64
+ return !fieldValue.includes(conditionValue);
65
+ }
66
+ return toString(fieldValue) !== toString(conditionValue);
67
+ }
68
+ case "contains": {
69
+ const fieldStr = toString(fieldValue).toLowerCase();
70
+ const conditionStr = toString(conditionValue).toLowerCase();
71
+ if (Array.isArray(fieldValue)) {
72
+ return fieldValue.some(
73
+ (v) => toString(v).toLowerCase().includes(conditionStr)
74
+ );
75
+ }
76
+ return fieldStr.includes(conditionStr);
77
+ }
78
+ case "isEmpty": {
79
+ return isEmptyValue(fieldValue);
80
+ }
81
+ case "isNotEmpty": {
82
+ return !isEmptyValue(fieldValue);
83
+ }
84
+ case "greaterThan": {
85
+ const fieldNum = toNumber(fieldValue);
86
+ const conditionNum = toNumber(conditionValue);
87
+ if (!Number.isNaN(fieldNum) && !Number.isNaN(conditionNum)) {
88
+ return fieldNum > conditionNum;
89
+ }
90
+ return toString(fieldValue) > toString(conditionValue);
91
+ }
92
+ case "lessThan": {
93
+ const fieldNum = toNumber(fieldValue);
94
+ const conditionNum = toNumber(conditionValue);
95
+ if (!Number.isNaN(fieldNum) && !Number.isNaN(conditionNum)) {
96
+ return fieldNum < conditionNum;
97
+ }
98
+ return toString(fieldValue) < toString(conditionValue);
99
+ }
100
+ default: {
101
+ console.warn(
102
+ `[evaluateConditions] Unknown comparison operator: ${String(operator)}`
103
+ );
104
+ return false;
105
+ }
106
+ }
107
+ }
108
+ function evaluateSingleCondition(condition, formData) {
109
+ const { field, comparison, value: conditionValue } = condition;
110
+ const fieldValue = getNestedValue(formData, field);
111
+ return compareValues(comparison, fieldValue, conditionValue);
112
+ }
113
+ function getNestedValue(obj, path) {
114
+ if (!path) {
115
+ return void 0;
116
+ }
117
+ if (!path.includes(".")) {
118
+ return obj[path];
119
+ }
120
+ const parts = path.split(".");
121
+ let current = obj;
122
+ for (const part of parts) {
123
+ if (current === void 0 || current === null) {
124
+ return void 0;
125
+ }
126
+ if (typeof current !== "object") {
127
+ return void 0;
128
+ }
129
+ current = current[part];
130
+ }
131
+ return current;
132
+ }
133
+ function evaluateConditions(logic, formData) {
134
+ if (!logic || !logic.enabled) {
135
+ return true;
136
+ }
137
+ const { action, operator, conditions } = logic;
138
+ if (!conditions || conditions.length === 0) {
139
+ return true;
140
+ }
141
+ const results = conditions.map(
142
+ (condition) => evaluateSingleCondition(condition, formData)
143
+ );
144
+ let conditionsMet;
145
+ if (operator === "OR") {
146
+ conditionsMet = results.some((result) => result);
147
+ } else {
148
+ conditionsMet = results.every((result) => result);
149
+ }
150
+ if (action === "hide") {
151
+ return !conditionsMet;
152
+ }
153
+ return conditionsMet;
154
+ }
155
+ function isValidComparisonOperator(operator) {
156
+ const validOperators = [
157
+ "equals",
158
+ "notEquals",
159
+ "contains",
160
+ "isEmpty",
161
+ "isNotEmpty",
162
+ "greaterThan",
163
+ "lessThan"
164
+ ];
165
+ return validOperators.includes(operator);
166
+ }
167
+ function getSupportedComparisonOperators() {
168
+ return [
169
+ "equals",
170
+ "notEquals",
171
+ "contains",
172
+ "isEmpty",
173
+ "isNotEmpty",
174
+ "greaterThan",
175
+ "lessThan"
176
+ ];
177
+ }
178
+
179
+ // src/utils/export-formats.ts
180
+ var UTF8_BOM = "\uFEFF";
181
+ function safeString(value) {
182
+ if (typeof value === "object" && value !== null) {
183
+ return JSON.stringify(value);
184
+ }
185
+ return String(value);
186
+ }
187
+ function formatExportValue(value, field) {
188
+ if (value === null || value === void 0) {
189
+ return "";
190
+ }
191
+ if (field) {
192
+ switch (field.type) {
193
+ case "checkbox":
194
+ return value ? "Yes" : "No";
195
+ case "select":
196
+ if (Array.isArray(value)) {
197
+ return value.join(", ");
198
+ }
199
+ return safeString(value);
200
+ case "file":
201
+ if (Array.isArray(value)) {
202
+ return value.map((v) => safeString(v)).join(", ");
203
+ }
204
+ return safeString(value);
205
+ case "date":
206
+ if (value instanceof Date) {
207
+ return value.toISOString().split("T")[0];
208
+ }
209
+ if (typeof value === "string") {
210
+ const parsed = new Date(value);
211
+ if (!isNaN(parsed.getTime())) {
212
+ return parsed.toISOString().split("T")[0];
213
+ }
214
+ }
215
+ return safeString(value);
216
+ case "time":
217
+ return safeString(value);
218
+ case "hidden":
219
+ return safeString(value);
220
+ default:
221
+ break;
222
+ }
223
+ }
224
+ if (Array.isArray(value)) {
225
+ return value.join(", ");
226
+ }
227
+ if (typeof value === "boolean") {
228
+ return value ? "Yes" : "No";
229
+ }
230
+ if (value instanceof Date) {
231
+ return value.toISOString();
232
+ }
233
+ return safeString(value);
234
+ }
235
+ function formatDate(date, format = "iso") {
236
+ if (!date) return "";
237
+ const d = typeof date === "string" ? new Date(date) : date;
238
+ if (isNaN(d.getTime())) {
239
+ return "";
240
+ }
241
+ return format === "iso" ? d.toISOString() : d.toLocaleString();
242
+ }
243
+ function escapeCSVValue(value, delimiter = ",") {
244
+ const isFormulaLike = /^[=+\-@\t\r]/.test(value) && !/^-?\d+(\.\d+)?$/.test(value);
245
+ const escapedFormula = isFormulaLike ? `'${value}` : value;
246
+ const needsQuoting = escapedFormula.includes(delimiter) || escapedFormula.includes('"') || escapedFormula.includes("\n") || escapedFormula.includes("\r");
247
+ if (!needsQuoting) {
248
+ return escapedFormula;
249
+ }
250
+ const escaped = escapedFormula.replace(/"/g, '""');
251
+ return `"${escaped}"`;
252
+ }
253
+ function exportToCSV(submissions, form, options = {}) {
254
+ const {
255
+ includeMetadata = true,
256
+ delimiter = ",",
257
+ includeBOM = true,
258
+ dateFormat = "iso"
259
+ } = options;
260
+ const exportableFields = form.fields;
261
+ const headers = [];
262
+ for (const field of exportableFields) {
263
+ headers.push(field.label);
264
+ }
265
+ if (includeMetadata) {
266
+ headers.push("ID", "Status", "Submitted At", "IP Address", "User Agent");
267
+ }
268
+ const rows = [];
269
+ for (const submission of submissions) {
270
+ const row = [];
271
+ const data = submission.data;
272
+ for (const field of exportableFields) {
273
+ const value = data[field.name];
274
+ const formatted = formatExportValue(value, field);
275
+ row.push(escapeCSVValue(formatted, delimiter));
276
+ }
277
+ if (includeMetadata) {
278
+ row.push(escapeCSVValue(submission.id, delimiter));
279
+ row.push(escapeCSVValue(submission.status, delimiter));
280
+ row.push(
281
+ escapeCSVValue(
282
+ formatDate(submission.submittedAt, dateFormat),
283
+ delimiter
284
+ )
285
+ );
286
+ row.push(escapeCSVValue(submission.ipAddress || "", delimiter));
287
+ row.push(escapeCSVValue(submission.userAgent || "", delimiter));
288
+ }
289
+ rows.push(row);
290
+ }
291
+ const escapedHeaders = headers.map((h) => escapeCSVValue(h, delimiter));
292
+ const lines = [
293
+ escapedHeaders.join(delimiter),
294
+ ...rows.map((row) => row.join(delimiter))
295
+ ];
296
+ const csvContent = lines.join("\r\n");
297
+ return includeBOM ? UTF8_BOM + csvContent : csvContent;
298
+ }
299
+ function exportToJSON(submissions, form, options = {}) {
300
+ const {
301
+ includeMetadata = true,
302
+ includeFormDefinition = true,
303
+ indent = 2
304
+ } = options;
305
+ const exportableFields = form.fields;
306
+ const formDefinition = includeFormDefinition ? {
307
+ id: form.id,
308
+ name: form.name,
309
+ slug: form.slug,
310
+ fields: exportableFields.map((f) => ({
311
+ name: f.name,
312
+ label: f.label,
313
+ type: f.type
314
+ }))
315
+ } : void 0;
316
+ const exportedSubmissions = submissions.map((submission) => {
317
+ const data = submission.data;
318
+ const fieldData = {};
319
+ for (const field of exportableFields) {
320
+ const value = data[field.name];
321
+ fieldData[field.name] = value;
322
+ }
323
+ if (includeMetadata) {
324
+ return {
325
+ ...fieldData,
326
+ _metadata: {
327
+ id: submission.id,
328
+ status: submission.status,
329
+ submittedAt: submission.submittedAt,
330
+ ipAddress: submission.ipAddress || null,
331
+ userAgent: submission.userAgent || null,
332
+ createdAt: submission.createdAt,
333
+ updatedAt: submission.updatedAt
334
+ }
335
+ };
336
+ }
337
+ return fieldData;
338
+ });
339
+ const exportData = {
340
+ exportedAt: (/* @__PURE__ */ new Date()).toISOString(),
341
+ totalSubmissions: submissions.length,
342
+ submissions: exportedSubmissions
343
+ };
344
+ if (formDefinition) {
345
+ exportData.form = formDefinition;
346
+ }
347
+ return JSON.stringify(exportData, null, indent);
348
+ }
349
+ function downloadFile(content, filename, mimeType) {
350
+ if (typeof window === "undefined" || typeof document === "undefined") {
351
+ throw new Error("downloadFile is only available in browser environments");
352
+ }
353
+ const blob = new Blob([content], { type: mimeType });
354
+ const url = URL.createObjectURL(blob);
355
+ const link = document.createElement("a");
356
+ link.href = url;
357
+ link.download = filename;
358
+ document.body.appendChild(link);
359
+ link.click();
360
+ document.body.removeChild(link);
361
+ URL.revokeObjectURL(url);
362
+ }
363
+ function generateExportFilename(formSlug, format) {
364
+ const safeSlug = formSlug.toLowerCase().replace(/[^a-z0-9_-]+/g, "-") || "form";
365
+ const date = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
366
+ return `${safeSlug}-submissions-${date}.${format}`;
367
+ }
368
+ function exportAndDownload(submissions, form, format, options) {
369
+ const filename = generateExportFilename(form.slug, format);
370
+ if (format === "csv") {
371
+ const content = exportToCSV(submissions, form, options);
372
+ downloadFile(content, filename, "text/csv;charset=utf-8");
373
+ } else {
374
+ const content = exportToJSON(submissions, form, options);
375
+ downloadFile(content, filename, "application/json");
376
+ }
377
+ }
378
+
379
+ // src/types.ts
380
+ var BUILT_IN_FORM_FIELD_TYPES = [
381
+ "text",
382
+ "email",
383
+ "number",
384
+ "phone",
385
+ "url",
386
+ "textarea",
387
+ "select",
388
+ "checkbox",
389
+ "radio",
390
+ "file",
391
+ "date",
392
+ "time",
393
+ "hidden"
394
+ ];
395
+ var BUILT_IN_FORM_FIELD_TYPES_SET = new Set(
396
+ BUILT_IN_FORM_FIELD_TYPES
397
+ );
398
+ function isKnownFormField(field) {
399
+ return BUILT_IN_FORM_FIELD_TYPES_SET.has(field.type);
400
+ }
401
+ function isFormField(value) {
402
+ return typeof value === "object" && value !== null && "type" in value && "name" in value && "label" in value;
403
+ }
404
+ function isFormFieldType(field, type) {
405
+ return field.type === type;
406
+ }
407
+ function isTextFormField(field) {
408
+ return field.type === "text";
409
+ }
410
+ function isEmailFormField(field) {
411
+ return field.type === "email";
412
+ }
413
+ function isSelectFormField(field) {
414
+ return field.type === "select";
415
+ }
416
+ function isCheckboxFormField(field) {
417
+ return field.type === "checkbox";
418
+ }
419
+ function isFileFormField(field) {
420
+ return field.type === "file";
421
+ }
422
+
423
+ // src/config/defaults.ts
424
+ var DEFAULT_FORM_SETTINGS = {
425
+ submitButtonText: "Submit",
426
+ confirmationType: "message",
427
+ successMessage: "Thank you for your submission!",
428
+ allowMultipleSubmissions: true
429
+ };
430
+ var DEFAULT_NOTIFICATION_SETTINGS = {
431
+ enabled: true,
432
+ replyTo: true
433
+ };
434
+ function toTitleCase(str) {
435
+ return str.replace(/[-_]/g, " ").replace(/\b\w/g, (char) => char.toUpperCase());
436
+ }
437
+ function pluralize(singular) {
438
+ if (singular.endsWith("y") && !/[aeiou]y$/i.test(singular)) {
439
+ return singular.slice(0, -1) + "ies";
440
+ }
441
+ if (singular.endsWith("s") || singular.endsWith("x") || singular.endsWith("z") || singular.endsWith("ch") || singular.endsWith("sh")) {
442
+ return singular + "es";
443
+ }
444
+ return singular + "s";
445
+ }
446
+ function applyFormDefaults(config) {
447
+ const singularLabel = config.labels?.singular || toTitleCase(config.slug);
448
+ return {
449
+ ...config,
450
+ // Generate labels from slug if not provided
451
+ labels: {
452
+ singular: singularLabel,
453
+ plural: config.labels?.plural || pluralize(singularLabel)
454
+ },
455
+ // Apply settings defaults
456
+ settings: {
457
+ submitButtonText: config.settings?.submitButtonText || DEFAULT_FORM_SETTINGS.submitButtonText,
458
+ confirmationType: config.settings?.confirmationType || DEFAULT_FORM_SETTINGS.confirmationType,
459
+ successMessage: config.settings?.successMessage || DEFAULT_FORM_SETTINGS.successMessage,
460
+ allowMultipleSubmissions: config.settings?.allowMultipleSubmissions ?? DEFAULT_FORM_SETTINGS.allowMultipleSubmissions,
461
+ // Preserve other settings that don't have defaults
462
+ redirectUrl: config.settings?.redirectUrl,
463
+ redirectRelation: config.settings?.redirectRelation,
464
+ honeypotEnabled: config.settings?.honeypotEnabled,
465
+ captchaEnabled: config.settings?.captchaEnabled,
466
+ captchaSiteKey: config.settings?.captchaSiteKey
467
+ },
468
+ // Apply notification defaults if notifications are configured
469
+ notifications: config.notifications ? {
470
+ enabled: config.notifications.enabled ?? DEFAULT_NOTIFICATION_SETTINGS.enabled,
471
+ recipients: config.notifications.recipients || [],
472
+ subject: config.notifications.subject || `New submission for ${singularLabel}`,
473
+ replyTo: config.notifications.replyTo ?? DEFAULT_NOTIFICATION_SETTINGS.replyTo,
474
+ template: config.notifications.template
475
+ } : void 0,
476
+ // Apply access defaults
477
+ access: {
478
+ // Default: anyone can submit the form
479
+ submit: config.access?.submit || (() => true),
480
+ // Default: only authenticated users can view submissions
481
+ read: config.access?.read || (({ req }) => !!req.user)
482
+ }
483
+ };
484
+ }
485
+ function applyFieldDefaults(field) {
486
+ const label = field.label || (field.name ? toTitleCase(field.name.replace(/([A-Z])/g, " $1").trim()) : "");
487
+ return {
488
+ ...field,
489
+ label,
490
+ required: field.required ?? false
491
+ };
492
+ }
493
+ function createFormConfig(slug, fields, options) {
494
+ return applyFormDefaults({
495
+ slug,
496
+ fields,
497
+ ...options
498
+ });
499
+ }
500
+
501
+ // src/utils/form-settings.ts
502
+ function asOptionalBoolean(value) {
503
+ return typeof value === "boolean" ? value : void 0;
504
+ }
505
+ function asOptionalString(value) {
506
+ return typeof value === "string" && value !== "" ? value : void 0;
507
+ }
508
+ function normalizeFormSettings(raw) {
509
+ if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
510
+ return { ...DEFAULT_FORM_SETTINGS };
511
+ }
512
+ const source = raw;
513
+ const legacyMessage = asOptionalString(source.confirmationMessage);
514
+ const legacyCaptcha = source.captcha && typeof source.captcha === "object" ? source.captcha : void 0;
515
+ const confirmationType = source.confirmationType === "redirect" || source.confirmationType === "relationship" ? source.confirmationType : "message";
516
+ return {
517
+ submitButtonText: asOptionalString(source.submitButtonText) ?? DEFAULT_FORM_SETTINGS.submitButtonText,
518
+ confirmationType,
519
+ successMessage: asOptionalString(source.successMessage) ?? legacyMessage ?? DEFAULT_FORM_SETTINGS.successMessage,
520
+ redirectUrl: asOptionalString(source.redirectUrl),
521
+ redirectPage: source.redirectPage ?? void 0,
522
+ redirectRelation: source.redirectRelation && typeof source.redirectRelation === "object" ? source.redirectRelation : void 0,
523
+ allowMultipleSubmissions: asOptionalBoolean(source.allowMultipleSubmissions) ?? DEFAULT_FORM_SETTINGS.allowMultipleSubmissions,
524
+ honeypotEnabled: asOptionalBoolean(source.honeypotEnabled),
525
+ captchaEnabled: asOptionalBoolean(source.captchaEnabled) ?? asOptionalBoolean(legacyCaptcha?.enabled),
526
+ captchaSiteKey: asOptionalString(source.captchaSiteKey) ?? asOptionalString(legacyCaptcha?.siteKey)
527
+ };
528
+ }
529
+
530
+ export {
531
+ evaluateSingleCondition,
532
+ evaluateConditions,
533
+ isValidComparisonOperator,
534
+ getSupportedComparisonOperators,
535
+ formatExportValue,
536
+ exportToCSV,
537
+ exportToJSON,
538
+ downloadFile,
539
+ generateExportFilename,
540
+ exportAndDownload,
541
+ BUILT_IN_FORM_FIELD_TYPES,
542
+ isKnownFormField,
543
+ isFormField,
544
+ isFormFieldType,
545
+ isTextFormField,
546
+ isEmailFormField,
547
+ isSelectFormField,
548
+ isCheckboxFormField,
549
+ isFileFormField,
550
+ DEFAULT_FORM_SETTINGS,
551
+ DEFAULT_NOTIFICATION_SETTINGS,
552
+ toTitleCase,
553
+ pluralize,
554
+ applyFormDefaults,
555
+ applyFieldDefaults,
556
+ createFormConfig,
557
+ normalizeFormSettings
558
+ };
559
+ //# sourceMappingURL=chunk-WVI7N3QO.js.map