strapi-plugin-form-builder-cms 1.0.0-alpha.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.
- package/README.md +240 -0
- package/dist/admin/App-By2VKpyh.mjs +1429 -0
- package/dist/admin/App-D5YiNO1j.js +1429 -0
- package/dist/admin/en-B4KWt_jN.js +4 -0
- package/dist/admin/en-Byx4XI2L.mjs +4 -0
- package/dist/admin/index-Cn0nOObD.mjs +66 -0
- package/dist/admin/index-DtKNB8Gg.js +65 -0
- package/dist/admin/index.js +4 -0
- package/dist/admin/index.mjs +4 -0
- package/dist/admin/src/index.d.ts +10 -0
- package/dist/server/index.js +886 -0
- package/dist/server/index.mjs +886 -0
- package/dist/server/src/index.d.ts +248 -0
- package/package.json +92 -0
|
@@ -0,0 +1,886 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toStringTag]: { value: "Module" } });
|
|
3
|
+
const bootstrap = ({ strapi: strapi2 }) => {
|
|
4
|
+
};
|
|
5
|
+
const destroy = ({ strapi: strapi2 }) => {
|
|
6
|
+
};
|
|
7
|
+
const register = ({ strapi: strapi2 }) => {
|
|
8
|
+
};
|
|
9
|
+
const config = {
|
|
10
|
+
default: {},
|
|
11
|
+
validator() {
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
const kind$1 = "collectionType";
|
|
15
|
+
const collectionName$1 = "formcraft_forms";
|
|
16
|
+
const info$1 = { "singularName": "form", "pluralName": "forms", "displayName": "Form", "description": "Dynamic form definitions" };
|
|
17
|
+
const options$1 = { "draftAndPublish": true };
|
|
18
|
+
const pluginOptions$1 = { "content-manager": { "visible": false }, "content-type-builder": { "visible": false } };
|
|
19
|
+
const attributes$1 = { "title": { "type": "string", "required": true, "maxLength": 255 }, "slug": { "type": "uid", "targetField": "title", "required": true }, "description": { "type": "text" }, "fields": { "type": "json", "required": true, "default": [] }, "conditionalLogic": { "type": "json", "default": [] }, "settings": { "type": "json", "default": { "submitButtonText": "Enviar", "successMessage": "Formulario enviado correctamente", "enableHoneypot": true, "enableRateLimit": true, "maxSubmissionsPerHour": 60, "notificationEmails": [], "redirectUrl": "", "customCss": "" } }, "submissions": { "type": "relation", "relation": "oneToMany", "target": "plugin::strapi-plugin-form-builder-cms.form-submission", "mappedBy": "form" } };
|
|
20
|
+
const form$2 = {
|
|
21
|
+
kind: kind$1,
|
|
22
|
+
collectionName: collectionName$1,
|
|
23
|
+
info: info$1,
|
|
24
|
+
options: options$1,
|
|
25
|
+
pluginOptions: pluginOptions$1,
|
|
26
|
+
attributes: attributes$1
|
|
27
|
+
};
|
|
28
|
+
const kind = "collectionType";
|
|
29
|
+
const collectionName = "formcraft_submissions";
|
|
30
|
+
const info = { "singularName": "form-submission", "pluralName": "form-submissions", "displayName": "Form Submission", "description": "Submitted form data" };
|
|
31
|
+
const options = { "draftAndPublish": false };
|
|
32
|
+
const pluginOptions = { "content-manager": { "visible": false }, "content-type-builder": { "visible": false } };
|
|
33
|
+
const attributes = { "form": { "type": "relation", "relation": "manyToOne", "target": "plugin::strapi-plugin-form-builder-cms.form", "inversedBy": "submissions" }, "data": { "type": "json", "required": true }, "metadata": { "type": "json", "default": {} }, "ipAddress": { "type": "string" }, "userAgent": { "type": "string" }, "status": { "type": "enumeration", "enum": ["new", "read", "archived"], "default": "new" } };
|
|
34
|
+
const formSubmission = {
|
|
35
|
+
kind,
|
|
36
|
+
collectionName,
|
|
37
|
+
info,
|
|
38
|
+
options,
|
|
39
|
+
pluginOptions,
|
|
40
|
+
attributes
|
|
41
|
+
};
|
|
42
|
+
const contentTypes = {
|
|
43
|
+
form: { schema: form$2 },
|
|
44
|
+
"form-submission": { schema: formSubmission }
|
|
45
|
+
};
|
|
46
|
+
const PLUGIN_ID$3 = "strapi-plugin-form-builder-cms";
|
|
47
|
+
function publicPageHtml(form2) {
|
|
48
|
+
const title = form2.title || "Form";
|
|
49
|
+
const description = form2.description || "";
|
|
50
|
+
const formId = form2.id;
|
|
51
|
+
return `<!DOCTYPE html>
|
|
52
|
+
<html lang="en">
|
|
53
|
+
<head>
|
|
54
|
+
<meta charset="UTF-8" />
|
|
55
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
56
|
+
<title>${title}</title>
|
|
57
|
+
<style>
|
|
58
|
+
*, *::before, *::after { box-sizing: border-box; }
|
|
59
|
+
body {
|
|
60
|
+
margin: 0;
|
|
61
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
62
|
+
background: #f5f5f9;
|
|
63
|
+
color: #32324d;
|
|
64
|
+
min-height: 100vh;
|
|
65
|
+
display: flex;
|
|
66
|
+
align-items: flex-start;
|
|
67
|
+
justify-content: center;
|
|
68
|
+
padding: 48px 20px;
|
|
69
|
+
}
|
|
70
|
+
.card {
|
|
71
|
+
background: #fff;
|
|
72
|
+
border: 1px solid #dcdce4;
|
|
73
|
+
border-radius: 8px;
|
|
74
|
+
padding: 40px 48px;
|
|
75
|
+
width: 100%;
|
|
76
|
+
max-width: 640px;
|
|
77
|
+
}
|
|
78
|
+
h1 { font-size: 22px; font-weight: 700; margin: 0 0 6px; }
|
|
79
|
+
.desc { font-size: 14px; color: #666687; margin: 0 0 28px; }
|
|
80
|
+
@media (max-width: 600px) { .card { padding: 28px 20px; } }
|
|
81
|
+
</style>
|
|
82
|
+
</head>
|
|
83
|
+
<body>
|
|
84
|
+
<div class="card">
|
|
85
|
+
<h1>${title}</h1>
|
|
86
|
+
${description ? `<p class="desc">${description}</p>` : ""}
|
|
87
|
+
<div id="sfb-form-${formId}"></div>
|
|
88
|
+
</div>
|
|
89
|
+
<script
|
|
90
|
+
src="/api/strapi-plugin-form-builder-cms/embed.js"
|
|
91
|
+
data-form-id="${formId}"
|
|
92
|
+
async
|
|
93
|
+
><\/script>
|
|
94
|
+
</body>
|
|
95
|
+
</html>`;
|
|
96
|
+
}
|
|
97
|
+
function embedScript() {
|
|
98
|
+
return `(function () {
|
|
99
|
+
var PLUGIN = 'strapi-plugin-form-builder-cms';
|
|
100
|
+
|
|
101
|
+
function init() {
|
|
102
|
+
document.querySelectorAll('script[data-form-id]').forEach(function (script) {
|
|
103
|
+
var formId = script.getAttribute('data-form-id');
|
|
104
|
+
var target = document.getElementById('sfb-form-' + formId);
|
|
105
|
+
if (!target) return;
|
|
106
|
+
|
|
107
|
+
var base = new URL(script.src).origin;
|
|
108
|
+
injectStyles();
|
|
109
|
+
|
|
110
|
+
fetch(base + '/api/' + PLUGIN + '/forms/' + formId + '/embed-schema')
|
|
111
|
+
.then(function (r) { return r.json(); })
|
|
112
|
+
.then(function (resp) {
|
|
113
|
+
var form = resp && resp.data;
|
|
114
|
+
if (!form) { target.textContent = 'Form not found.'; return; }
|
|
115
|
+
mount(target, form, base);
|
|
116
|
+
})
|
|
117
|
+
.catch(function () { target.textContent = 'Could not load form.'; });
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function mount(el, form, base) {
|
|
122
|
+
var formEl = document.createElement('form');
|
|
123
|
+
formEl.className = 'sfb-form';
|
|
124
|
+
|
|
125
|
+
var grid = document.createElement('div');
|
|
126
|
+
grid.className = 'sfb-grid';
|
|
127
|
+
(form.fields || []).forEach(function (field) {
|
|
128
|
+
var node = renderField(field);
|
|
129
|
+
if (node) grid.appendChild(node);
|
|
130
|
+
});
|
|
131
|
+
formEl.appendChild(grid);
|
|
132
|
+
|
|
133
|
+
var btn = document.createElement('button');
|
|
134
|
+
btn.type = 'submit';
|
|
135
|
+
btn.className = 'sfb-btn';
|
|
136
|
+
btn.textContent = (form.settings && form.settings.submitButtonText) || 'Submit';
|
|
137
|
+
formEl.appendChild(btn);
|
|
138
|
+
|
|
139
|
+
var submitText = (form.settings && form.settings.submitButtonText) || 'Submit';
|
|
140
|
+
|
|
141
|
+
function clearErrors() {
|
|
142
|
+
formEl.querySelectorAll('.sfb-field-error').forEach(function (el) { el.remove(); });
|
|
143
|
+
formEl.querySelectorAll('.sfb-field').forEach(function (el) { el.classList.remove('sfb-field--error'); });
|
|
144
|
+
var ge = formEl.querySelector('.sfb-error');
|
|
145
|
+
if (ge) ge.remove();
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
function showFieldErrors(errors) {
|
|
149
|
+
Object.keys(errors).forEach(function (name) {
|
|
150
|
+
var fieldEl = formEl.querySelector('[data-field-name="' + name + '"]');
|
|
151
|
+
if (!fieldEl) return;
|
|
152
|
+
fieldEl.classList.add('sfb-field--error');
|
|
153
|
+
var errEl = document.createElement('p');
|
|
154
|
+
errEl.className = 'sfb-field-error';
|
|
155
|
+
errEl.textContent = errors[name][0];
|
|
156
|
+
fieldEl.appendChild(errEl);
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
formEl.addEventListener('submit', function (e) {
|
|
161
|
+
e.preventDefault();
|
|
162
|
+
clearErrors();
|
|
163
|
+
btn.disabled = true;
|
|
164
|
+
btn.textContent = 'Sending…';
|
|
165
|
+
|
|
166
|
+
// collect form data, handling checkbox-group arrays
|
|
167
|
+
var data = {};
|
|
168
|
+
new FormData(formEl).forEach(function (v, k) {
|
|
169
|
+
if (k.endsWith('[]')) {
|
|
170
|
+
var name = k.slice(0, -2);
|
|
171
|
+
if (!data[name]) data[name] = [];
|
|
172
|
+
data[name].push(v);
|
|
173
|
+
} else {
|
|
174
|
+
data[k] = v;
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
fetch(base + '/api/' + PLUGIN + '/forms/' + form.slug + '/submit', {
|
|
179
|
+
method: 'POST',
|
|
180
|
+
headers: { 'Content-Type': 'application/json' },
|
|
181
|
+
body: JSON.stringify({ data: data }),
|
|
182
|
+
})
|
|
183
|
+
.then(function (r) {
|
|
184
|
+
return r.json().then(function (body) {
|
|
185
|
+
if (!r.ok) return Promise.reject(body);
|
|
186
|
+
return body;
|
|
187
|
+
});
|
|
188
|
+
})
|
|
189
|
+
.then(function () {
|
|
190
|
+
var msg = document.createElement('p');
|
|
191
|
+
msg.className = 'sfb-success';
|
|
192
|
+
msg.textContent = (form.settings && form.settings.successMessage) || 'Form submitted successfully';
|
|
193
|
+
el.innerHTML = '';
|
|
194
|
+
el.appendChild(msg);
|
|
195
|
+
})
|
|
196
|
+
.catch(function (body) {
|
|
197
|
+
btn.disabled = false;
|
|
198
|
+
btn.textContent = submitText;
|
|
199
|
+
if (body && body.errors && Object.keys(body.errors).length > 0) {
|
|
200
|
+
showFieldErrors(body.errors);
|
|
201
|
+
} else {
|
|
202
|
+
var errEl = document.createElement('p');
|
|
203
|
+
errEl.className = 'sfb-error';
|
|
204
|
+
errEl.textContent = (body && body.message) ? body.message : 'Something went wrong. Please try again.';
|
|
205
|
+
formEl.insertBefore(errEl, btn);
|
|
206
|
+
}
|
|
207
|
+
});
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
el.innerHTML = '';
|
|
211
|
+
el.appendChild(formEl);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function renderField(field) {
|
|
215
|
+
if (field.type === 'hidden') return null;
|
|
216
|
+
|
|
217
|
+
if (field.type === 'divider') {
|
|
218
|
+
var hr = document.createElement('hr');
|
|
219
|
+
hr.className = 'sfb-divider';
|
|
220
|
+
hr.style.gridColumn = '1 / -1';
|
|
221
|
+
return hr;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
if (field.type === 'heading') {
|
|
225
|
+
var h = document.createElement('p');
|
|
226
|
+
h.className = 'sfb-heading';
|
|
227
|
+
h.style.gridColumn = '1 / -1';
|
|
228
|
+
h.textContent = field.label || '';
|
|
229
|
+
return h;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
if (field.type === 'paragraph') {
|
|
233
|
+
var p = document.createElement('p');
|
|
234
|
+
p.className = 'sfb-paragraph';
|
|
235
|
+
p.style.gridColumn = '1 / -1';
|
|
236
|
+
p.textContent = field.label || '';
|
|
237
|
+
return p;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
var wrapper = document.createElement('div');
|
|
241
|
+
wrapper.className = 'sfb-field';
|
|
242
|
+
wrapper.setAttribute('data-field-name', field.name);
|
|
243
|
+
if (field.width !== 'half') wrapper.style.gridColumn = '1 / -1';
|
|
244
|
+
|
|
245
|
+
if (field.type !== 'checkbox') {
|
|
246
|
+
var label = document.createElement('label');
|
|
247
|
+
label.className = 'sfb-label';
|
|
248
|
+
label.htmlFor = 'sfb-' + field.id;
|
|
249
|
+
label.textContent = field.label || '';
|
|
250
|
+
if (field.required) {
|
|
251
|
+
var req = document.createElement('span');
|
|
252
|
+
req.className = 'sfb-required';
|
|
253
|
+
req.textContent = ' *';
|
|
254
|
+
label.appendChild(req);
|
|
255
|
+
}
|
|
256
|
+
wrapper.appendChild(label);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
var input = buildInput(field);
|
|
260
|
+
if (input) wrapper.appendChild(input);
|
|
261
|
+
|
|
262
|
+
if (field.helpText) {
|
|
263
|
+
var help = document.createElement('p');
|
|
264
|
+
help.className = 'sfb-help';
|
|
265
|
+
help.textContent = field.helpText;
|
|
266
|
+
wrapper.appendChild(help);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
return wrapper;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
function buildInput(field) {
|
|
273
|
+
var id = 'sfb-' + field.id;
|
|
274
|
+
|
|
275
|
+
if (field.type === 'textarea') {
|
|
276
|
+
var ta = document.createElement('textarea');
|
|
277
|
+
ta.id = id; ta.name = field.name; ta.className = 'sfb-input';
|
|
278
|
+
ta.placeholder = field.placeholder || '';
|
|
279
|
+
ta.required = !!field.required;
|
|
280
|
+
ta.rows = 4;
|
|
281
|
+
return ta;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
if (field.type === 'select') {
|
|
285
|
+
var sel = document.createElement('select');
|
|
286
|
+
sel.id = id; sel.name = field.name; sel.className = 'sfb-input';
|
|
287
|
+
sel.required = !!field.required;
|
|
288
|
+
var def = document.createElement('option');
|
|
289
|
+
def.value = ''; def.textContent = field.placeholder || 'Select an option…';
|
|
290
|
+
sel.appendChild(def);
|
|
291
|
+
(field.options || []).forEach(function (o) {
|
|
292
|
+
var opt = document.createElement('option');
|
|
293
|
+
opt.value = o.value; opt.textContent = o.label || o.value;
|
|
294
|
+
sel.appendChild(opt);
|
|
295
|
+
});
|
|
296
|
+
return sel;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
if (field.type === 'radio') {
|
|
300
|
+
var group = document.createElement('div');
|
|
301
|
+
group.className = 'sfb-radio-group';
|
|
302
|
+
(field.options || []).forEach(function (o) {
|
|
303
|
+
var lbl = document.createElement('label');
|
|
304
|
+
lbl.className = 'sfb-radio-label';
|
|
305
|
+
var inp = document.createElement('input');
|
|
306
|
+
inp.type = 'radio'; inp.name = field.name; inp.value = o.value;
|
|
307
|
+
inp.required = !!field.required;
|
|
308
|
+
lbl.appendChild(inp);
|
|
309
|
+
lbl.appendChild(document.createTextNode(o.label || o.value));
|
|
310
|
+
group.appendChild(lbl);
|
|
311
|
+
});
|
|
312
|
+
return group;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
if (field.type === 'checkbox-group') {
|
|
316
|
+
var cgroup = document.createElement('div');
|
|
317
|
+
cgroup.className = 'sfb-radio-group';
|
|
318
|
+
(field.options || []).forEach(function (o) {
|
|
319
|
+
var lbl = document.createElement('label');
|
|
320
|
+
lbl.className = 'sfb-radio-label';
|
|
321
|
+
var inp = document.createElement('input');
|
|
322
|
+
inp.type = 'checkbox'; inp.name = field.name + '[]'; inp.value = o.value;
|
|
323
|
+
lbl.appendChild(inp);
|
|
324
|
+
lbl.appendChild(document.createTextNode(o.label || o.value));
|
|
325
|
+
cgroup.appendChild(lbl);
|
|
326
|
+
});
|
|
327
|
+
return cgroup;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
if (field.type === 'checkbox') {
|
|
331
|
+
var clbl = document.createElement('label');
|
|
332
|
+
clbl.className = 'sfb-checkbox-label';
|
|
333
|
+
var cb = document.createElement('input');
|
|
334
|
+
cb.type = 'checkbox'; cb.id = id; cb.name = field.name; cb.value = '1';
|
|
335
|
+
cb.required = !!field.required;
|
|
336
|
+
clbl.appendChild(cb);
|
|
337
|
+
clbl.appendChild(document.createTextNode(' ' + (field.label || '')));
|
|
338
|
+
return clbl;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
var typeMap = { text:'text', email:'email', number:'number', phone:'tel',
|
|
342
|
+
password:'password', url:'url', date:'date', time:'time' };
|
|
343
|
+
var inp2 = document.createElement('input');
|
|
344
|
+
inp2.type = typeMap[field.type] || 'text';
|
|
345
|
+
inp2.id = id; inp2.name = field.name; inp2.className = 'sfb-input';
|
|
346
|
+
inp2.placeholder = field.placeholder || '';
|
|
347
|
+
inp2.required = !!field.required;
|
|
348
|
+
return inp2;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
function injectStyles() {
|
|
352
|
+
if (document.getElementById('sfb-css')) return;
|
|
353
|
+
var s = document.createElement('style');
|
|
354
|
+
s.id = 'sfb-css';
|
|
355
|
+
s.textContent = [
|
|
356
|
+
'.sfb-form { font-family: inherit; }',
|
|
357
|
+
'.sfb-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; margin-bottom: 20px; }',
|
|
358
|
+
'.sfb-field { display: flex; flex-direction: column; gap: 4px; }',
|
|
359
|
+
'.sfb-label { font-size: 13px; font-weight: 600; color: #32324d; }',
|
|
360
|
+
'.sfb-required { color: #ee5e52; }',
|
|
361
|
+
'.sfb-input { width: 100%; height: 40px; padding: 0 12px; border: 1px solid #dcdce4; border-radius: var(--sfb-radius, 4px); font-size: 14px; font-family: inherit; color: #32324d; background: #fff; box-sizing: border-box; outline: none; transition: border-color .15s; }',
|
|
362
|
+
'.sfb-input:focus { border-color: var(--sfb-accent, #4945ff); box-shadow: 0 0 0 2px rgba(73,69,255,.15); }',
|
|
363
|
+
'textarea.sfb-input { height: auto; padding: 8px 12px; resize: vertical; }',
|
|
364
|
+
'select.sfb-input { appearance: none; cursor: pointer; }',
|
|
365
|
+
'.sfb-radio-group { display: flex; flex-direction: column; gap: 8px; }',
|
|
366
|
+
'.sfb-radio-label, .sfb-checkbox-label { display: flex; align-items: center; gap: 8px; font-size: 14px; color: #32324d; cursor: pointer; }',
|
|
367
|
+
'.sfb-radio-label input, .sfb-checkbox-label input { accent-color: var(--sfb-accent, #4945ff); width: 16px; height: 16px; cursor: pointer; }',
|
|
368
|
+
'.sfb-help { font-size: 12px; color: #666687; margin: 0; }',
|
|
369
|
+
'.sfb-btn { background: var(--sfb-accent, #4945ff); color: #fff; border: none; border-radius: var(--sfb-radius, 4px); padding: 10px 24px; font-size: 14px; font-weight: 600; font-family: inherit; cursor: pointer; transition: opacity .15s; }',
|
|
370
|
+
'.sfb-btn:hover { opacity: .88; }',
|
|
371
|
+
'.sfb-btn:disabled { opacity: .6; cursor: not-allowed; }',
|
|
372
|
+
'.sfb-heading { font-size: 18px; font-weight: 700; color: #32324d; margin: 4px 0; }',
|
|
373
|
+
'.sfb-paragraph { font-size: 14px; color: #666687; margin: 4px 0; line-height: 1.6; }',
|
|
374
|
+
'.sfb-divider { border: none; border-top: 1px solid #dcdce4; margin: 4px 0; }',
|
|
375
|
+
'.sfb-success { font-size: 15px; color: #27ae60; font-weight: 600; }',
|
|
376
|
+
'.sfb-error { font-size: 13px; color: #ee5e52; margin-bottom: 12px; }',
|
|
377
|
+
'.sfb-field-error { font-size: 12px; color: #ee5e52; margin: 4px 0 0; }',
|
|
378
|
+
'.sfb-field--error .sfb-input, .sfb-field--error .sfb-radio-group { border-color: #ee5e52 !important; }',
|
|
379
|
+
'.sfb-field--error > .sfb-label { color: #ee5e52; }',
|
|
380
|
+
].join('\\n');
|
|
381
|
+
document.head.appendChild(s);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
if (document.readyState === 'loading') {
|
|
385
|
+
document.addEventListener('DOMContentLoaded', init);
|
|
386
|
+
} else {
|
|
387
|
+
init();
|
|
388
|
+
}
|
|
389
|
+
})();`;
|
|
390
|
+
}
|
|
391
|
+
const form$1 = {
|
|
392
|
+
async find(ctx) {
|
|
393
|
+
const forms = await strapi.plugin(PLUGIN_ID$3).service("form").find(ctx.query);
|
|
394
|
+
ctx.body = forms;
|
|
395
|
+
},
|
|
396
|
+
async findOne(ctx) {
|
|
397
|
+
const { id } = ctx.params;
|
|
398
|
+
const form2 = await strapi.plugin(PLUGIN_ID$3).service("form").findOne(Number(id));
|
|
399
|
+
if (!form2) return ctx.notFound("Form not found");
|
|
400
|
+
ctx.body = form2;
|
|
401
|
+
},
|
|
402
|
+
async create(ctx) {
|
|
403
|
+
const form2 = await strapi.plugin(PLUGIN_ID$3).service("form").create(ctx.request.body);
|
|
404
|
+
ctx.body = form2;
|
|
405
|
+
},
|
|
406
|
+
async update(ctx) {
|
|
407
|
+
const { id } = ctx.params;
|
|
408
|
+
const form2 = await strapi.plugin(PLUGIN_ID$3).service("form").update(Number(id), ctx.request.body);
|
|
409
|
+
if (!form2) return ctx.notFound("Form not found");
|
|
410
|
+
ctx.body = form2;
|
|
411
|
+
},
|
|
412
|
+
async delete(ctx) {
|
|
413
|
+
const { id } = ctx.params;
|
|
414
|
+
await strapi.plugin(PLUGIN_ID$3).service("form").delete(Number(id));
|
|
415
|
+
ctx.status = 204;
|
|
416
|
+
},
|
|
417
|
+
async duplicate(ctx) {
|
|
418
|
+
const { id } = ctx.params;
|
|
419
|
+
const form2 = await strapi.plugin(PLUGIN_ID$3).service("form").duplicate(Number(id));
|
|
420
|
+
if (!form2) return ctx.notFound("Form not found");
|
|
421
|
+
ctx.body = form2;
|
|
422
|
+
},
|
|
423
|
+
async getPublicSchema(ctx) {
|
|
424
|
+
const { slug } = ctx.params;
|
|
425
|
+
const schema = await strapi.plugin(PLUGIN_ID$3).service("form").getPublicSchema(slug);
|
|
426
|
+
if (!schema) return ctx.notFound("Form not found");
|
|
427
|
+
ctx.body = schema;
|
|
428
|
+
},
|
|
429
|
+
async getPublicSchemaById(ctx) {
|
|
430
|
+
const { id } = ctx.params;
|
|
431
|
+
const schema = await strapi.plugin(PLUGIN_ID$3).service("form").getPublicSchemaById(Number(id));
|
|
432
|
+
if (!schema) return ctx.notFound("Form not found");
|
|
433
|
+
ctx.body = schema;
|
|
434
|
+
},
|
|
435
|
+
async serveEmbed(ctx) {
|
|
436
|
+
ctx.set("Content-Type", "application/javascript; charset=utf-8");
|
|
437
|
+
ctx.set("Cache-Control", "public, max-age=3600");
|
|
438
|
+
ctx.body = embedScript();
|
|
439
|
+
},
|
|
440
|
+
async servePublicPage(ctx) {
|
|
441
|
+
const { slug } = ctx.params;
|
|
442
|
+
const schema = await strapi.plugin(PLUGIN_ID$3).service("form").getPublicSchema(slug);
|
|
443
|
+
if (!schema || !schema.data) return ctx.notFound("Form not found");
|
|
444
|
+
const form2 = schema.data;
|
|
445
|
+
if (!form2.settings?.publicPage) return ctx.notFound("This form does not have a public page enabled");
|
|
446
|
+
ctx.set("Content-Type", "text/html; charset=utf-8");
|
|
447
|
+
ctx.body = publicPageHtml(form2);
|
|
448
|
+
}
|
|
449
|
+
};
|
|
450
|
+
const PLUGIN_ID$2 = "strapi-plugin-form-builder-cms";
|
|
451
|
+
const submission$1 = {
|
|
452
|
+
async submit(ctx) {
|
|
453
|
+
const { slug } = ctx.params;
|
|
454
|
+
try {
|
|
455
|
+
const body = ctx.request.body;
|
|
456
|
+
const raw = body && typeof body === "object" && "data" in body ? body.data : body;
|
|
457
|
+
const data = {};
|
|
458
|
+
for (const [k, v] of Object.entries(raw || {})) {
|
|
459
|
+
if (k.endsWith("[]")) {
|
|
460
|
+
const name = k.slice(0, -2);
|
|
461
|
+
if (!data[name]) data[name] = [];
|
|
462
|
+
data[name].push(v);
|
|
463
|
+
} else {
|
|
464
|
+
data[k] = v;
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
const result = await strapi.plugin(PLUGIN_ID$2).service("submission").submit(
|
|
468
|
+
slug,
|
|
469
|
+
data,
|
|
470
|
+
{
|
|
471
|
+
ip: ctx.request.ip,
|
|
472
|
+
userAgent: ctx.request.headers["user-agent"] || ""
|
|
473
|
+
}
|
|
474
|
+
);
|
|
475
|
+
ctx.body = result;
|
|
476
|
+
} catch (error) {
|
|
477
|
+
if (error.name === "ValidationError") {
|
|
478
|
+
ctx.status = 400;
|
|
479
|
+
ctx.body = { success: false, errors: error.details };
|
|
480
|
+
} else if (error.name === "NotFoundError") {
|
|
481
|
+
return ctx.notFound("Form not found");
|
|
482
|
+
} else {
|
|
483
|
+
console.error("[sfb] unhandled submit error:", error?.message, error?.stack);
|
|
484
|
+
ctx.status = 500;
|
|
485
|
+
ctx.body = { success: false, message: error?.message || "Internal server error" };
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
},
|
|
489
|
+
async find(ctx) {
|
|
490
|
+
const { formId } = ctx.params;
|
|
491
|
+
const submissions = await strapi.plugin(PLUGIN_ID$2).service("submission").find(Number(formId), ctx.query);
|
|
492
|
+
ctx.body = submissions;
|
|
493
|
+
},
|
|
494
|
+
async findOne(ctx) {
|
|
495
|
+
const { id } = ctx.params;
|
|
496
|
+
const submission2 = await strapi.plugin(PLUGIN_ID$2).service("submission").findOne(Number(id));
|
|
497
|
+
if (!submission2) return ctx.notFound("Submission not found");
|
|
498
|
+
ctx.body = submission2;
|
|
499
|
+
},
|
|
500
|
+
async updateStatus(ctx) {
|
|
501
|
+
const { id } = ctx.params;
|
|
502
|
+
const { status } = ctx.request.body;
|
|
503
|
+
const submission2 = await strapi.plugin(PLUGIN_ID$2).service("submission").updateStatus(Number(id), status);
|
|
504
|
+
ctx.body = submission2;
|
|
505
|
+
},
|
|
506
|
+
async delete(ctx) {
|
|
507
|
+
const { id } = ctx.params;
|
|
508
|
+
await strapi.plugin(PLUGIN_ID$2).service("submission").delete(Number(id));
|
|
509
|
+
ctx.status = 204;
|
|
510
|
+
},
|
|
511
|
+
async export(ctx) {
|
|
512
|
+
const { formId } = ctx.params;
|
|
513
|
+
const format = ctx.query.format || "csv";
|
|
514
|
+
const data = await strapi.plugin(PLUGIN_ID$2).service("submission").export(Number(formId), format);
|
|
515
|
+
const ext = format === "xlsx" ? "xlsx" : "csv";
|
|
516
|
+
const mime = format === "xlsx" ? "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" : "text/csv";
|
|
517
|
+
ctx.set("Content-Disposition", `attachment; filename=submissions.${ext}`);
|
|
518
|
+
ctx.set("Content-Type", mime);
|
|
519
|
+
ctx.body = data;
|
|
520
|
+
},
|
|
521
|
+
async stats(ctx) {
|
|
522
|
+
const { formId } = ctx.params;
|
|
523
|
+
const stats = await strapi.plugin(PLUGIN_ID$2).service("submission").getStats(Number(formId));
|
|
524
|
+
ctx.body = stats;
|
|
525
|
+
}
|
|
526
|
+
};
|
|
527
|
+
const controllers = {
|
|
528
|
+
form: form$1,
|
|
529
|
+
submission: submission$1
|
|
530
|
+
};
|
|
531
|
+
const middlewares = {};
|
|
532
|
+
const policies = {};
|
|
533
|
+
const contentAPIRoutes = {
|
|
534
|
+
type: "content-api",
|
|
535
|
+
routes: [
|
|
536
|
+
{
|
|
537
|
+
method: "GET",
|
|
538
|
+
path: "/embed.js",
|
|
539
|
+
handler: "form.serveEmbed",
|
|
540
|
+
config: { auth: false, policies: [] }
|
|
541
|
+
},
|
|
542
|
+
{
|
|
543
|
+
method: "GET",
|
|
544
|
+
path: "/forms/:id/embed-schema",
|
|
545
|
+
handler: "form.getPublicSchemaById",
|
|
546
|
+
config: { auth: false, policies: [] }
|
|
547
|
+
},
|
|
548
|
+
{
|
|
549
|
+
method: "GET",
|
|
550
|
+
path: "/forms/:slug/schema",
|
|
551
|
+
handler: "form.getPublicSchema",
|
|
552
|
+
config: { auth: false, policies: [] }
|
|
553
|
+
},
|
|
554
|
+
{
|
|
555
|
+
method: "GET",
|
|
556
|
+
path: "/page/:slug",
|
|
557
|
+
handler: "form.servePublicPage",
|
|
558
|
+
config: { auth: false, policies: [] }
|
|
559
|
+
},
|
|
560
|
+
{
|
|
561
|
+
method: "POST",
|
|
562
|
+
path: "/forms/:slug/submit",
|
|
563
|
+
handler: "submission.submit",
|
|
564
|
+
config: { auth: false, policies: [] }
|
|
565
|
+
}
|
|
566
|
+
]
|
|
567
|
+
};
|
|
568
|
+
const adminAPIRoutes = {
|
|
569
|
+
type: "admin",
|
|
570
|
+
routes: [
|
|
571
|
+
{ method: "GET", path: "/forms", handler: "form.find" },
|
|
572
|
+
{ method: "GET", path: "/forms/:id", handler: "form.findOne" },
|
|
573
|
+
{ method: "POST", path: "/forms", handler: "form.create" },
|
|
574
|
+
{ method: "PUT", path: "/forms/:id", handler: "form.update" },
|
|
575
|
+
{ method: "DELETE", path: "/forms/:id", handler: "form.delete" },
|
|
576
|
+
{ method: "POST", path: "/forms/:id/duplicate", handler: "form.duplicate" },
|
|
577
|
+
{ method: "GET", path: "/submissions/:formId", handler: "submission.find" },
|
|
578
|
+
{ method: "GET", path: "/submissions/entry/:id", handler: "submission.findOne" },
|
|
579
|
+
{ method: "PUT", path: "/submissions/:id/status", handler: "submission.updateStatus" },
|
|
580
|
+
{ method: "DELETE", path: "/submissions/:id", handler: "submission.delete" },
|
|
581
|
+
{ method: "GET", path: "/submissions/:formId/export", handler: "submission.export" },
|
|
582
|
+
{ method: "GET", path: "/submissions/:formId/stats", handler: "submission.stats" }
|
|
583
|
+
]
|
|
584
|
+
};
|
|
585
|
+
const routes = {
|
|
586
|
+
"content-api": contentAPIRoutes,
|
|
587
|
+
admin: adminAPIRoutes
|
|
588
|
+
};
|
|
589
|
+
const PLUGIN_ID$1 = "strapi-plugin-form-builder-cms";
|
|
590
|
+
const FORM_UID$1 = `plugin::${PLUGIN_ID$1}.form`;
|
|
591
|
+
function generateSlug(title) {
|
|
592
|
+
const base = title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") || "form";
|
|
593
|
+
return `${base}-${Date.now()}`;
|
|
594
|
+
}
|
|
595
|
+
const form = ({ strapi: strapi2 }) => ({
|
|
596
|
+
async find() {
|
|
597
|
+
return strapi2.db.query(FORM_UID$1).findMany({
|
|
598
|
+
orderBy: { createdAt: "desc" }
|
|
599
|
+
});
|
|
600
|
+
},
|
|
601
|
+
async findOne(id) {
|
|
602
|
+
return strapi2.db.query(FORM_UID$1).findOne({
|
|
603
|
+
where: { id },
|
|
604
|
+
orderBy: { updatedAt: "desc" }
|
|
605
|
+
});
|
|
606
|
+
},
|
|
607
|
+
async findBySlug(slug) {
|
|
608
|
+
return strapi2.db.query(FORM_UID$1).findOne({
|
|
609
|
+
where: { slug },
|
|
610
|
+
orderBy: { updatedAt: "desc" }
|
|
611
|
+
});
|
|
612
|
+
},
|
|
613
|
+
async create(data) {
|
|
614
|
+
const slug = generateSlug(data.title || "form");
|
|
615
|
+
return strapi2.db.query(FORM_UID$1).create({ data: { ...data, slug } });
|
|
616
|
+
},
|
|
617
|
+
async update(id, data) {
|
|
618
|
+
return strapi2.db.query(FORM_UID$1).update({ where: { id }, data });
|
|
619
|
+
},
|
|
620
|
+
async delete(id) {
|
|
621
|
+
return strapi2.db.query(FORM_UID$1).delete({ where: { id } });
|
|
622
|
+
},
|
|
623
|
+
async duplicate(id) {
|
|
624
|
+
const original = await strapi2.db.query(FORM_UID$1).findOne({ where: { id } });
|
|
625
|
+
if (!original) return null;
|
|
626
|
+
const { id: _id, slug, createdAt, updatedAt, publishedAt, ...rest } = original;
|
|
627
|
+
return strapi2.db.query(FORM_UID$1).create({
|
|
628
|
+
data: {
|
|
629
|
+
...rest,
|
|
630
|
+
title: `${rest.title} (copy)`,
|
|
631
|
+
slug: generateSlug(`${rest.title} copy`)
|
|
632
|
+
}
|
|
633
|
+
});
|
|
634
|
+
},
|
|
635
|
+
async getPublicSchemaById(id) {
|
|
636
|
+
const form2 = await this.findOne(id);
|
|
637
|
+
if (!form2) return null;
|
|
638
|
+
return {
|
|
639
|
+
data: {
|
|
640
|
+
id: form2.id,
|
|
641
|
+
title: form2.title,
|
|
642
|
+
slug: form2.slug,
|
|
643
|
+
description: form2.description,
|
|
644
|
+
fields: form2.fields || [],
|
|
645
|
+
settings: form2.settings || {}
|
|
646
|
+
}
|
|
647
|
+
};
|
|
648
|
+
},
|
|
649
|
+
async getPublicSchema(slug) {
|
|
650
|
+
const form2 = await this.findBySlug(slug);
|
|
651
|
+
if (!form2) return null;
|
|
652
|
+
return {
|
|
653
|
+
data: {
|
|
654
|
+
id: form2.id,
|
|
655
|
+
title: form2.title,
|
|
656
|
+
slug: form2.slug,
|
|
657
|
+
description: form2.description,
|
|
658
|
+
fields: form2.fields || [],
|
|
659
|
+
conditionalLogic: form2.conditionalLogic || [],
|
|
660
|
+
settings: form2.settings || {}
|
|
661
|
+
}
|
|
662
|
+
};
|
|
663
|
+
}
|
|
664
|
+
});
|
|
665
|
+
const PLUGIN_ID = "strapi-plugin-form-builder-cms";
|
|
666
|
+
const SUBMISSION_UID = `plugin::${PLUGIN_ID}.form-submission`;
|
|
667
|
+
const FORM_UID = `plugin::${PLUGIN_ID}.form`;
|
|
668
|
+
const submission = ({ strapi: strapi2 }) => ({
|
|
669
|
+
async submit(slug, body, meta) {
|
|
670
|
+
const form2 = await strapi2.plugin(PLUGIN_ID).service("form").findBySlug(slug);
|
|
671
|
+
if (!form2) {
|
|
672
|
+
const err = new Error("Form not found");
|
|
673
|
+
err.name = "NotFoundError";
|
|
674
|
+
throw err;
|
|
675
|
+
}
|
|
676
|
+
const validationService = strapi2.plugin(PLUGIN_ID).service("validation");
|
|
677
|
+
const result = validationService.validate(form2.fields || [], body);
|
|
678
|
+
if (!result.valid) {
|
|
679
|
+
const err = new Error("Validation failed");
|
|
680
|
+
err.name = "ValidationError";
|
|
681
|
+
err.details = result.errors;
|
|
682
|
+
throw err;
|
|
683
|
+
}
|
|
684
|
+
if (form2.settings?.enableHoneypot && body._fc_hp) {
|
|
685
|
+
return { success: true, successMessage: form2.settings?.successMessage };
|
|
686
|
+
}
|
|
687
|
+
try {
|
|
688
|
+
await strapi2.db.query(SUBMISSION_UID).create({
|
|
689
|
+
data: {
|
|
690
|
+
form: form2.id,
|
|
691
|
+
data: body,
|
|
692
|
+
ipAddress: meta.ip,
|
|
693
|
+
userAgent: meta.userAgent,
|
|
694
|
+
status: "new"
|
|
695
|
+
}
|
|
696
|
+
});
|
|
697
|
+
} catch (createErr) {
|
|
698
|
+
console.error("[sfb] create error:", createErr?.message, createErr?.stack);
|
|
699
|
+
throw createErr;
|
|
700
|
+
}
|
|
701
|
+
return { success: true, successMessage: form2.settings?.successMessage };
|
|
702
|
+
},
|
|
703
|
+
async find(formId, query = {}) {
|
|
704
|
+
const { page = 1, pageSize = 25, status } = query;
|
|
705
|
+
const where = { form: formId };
|
|
706
|
+
if (status) where.status = status;
|
|
707
|
+
const [results, total] = await Promise.all([
|
|
708
|
+
strapi2.db.query(SUBMISSION_UID).findMany({
|
|
709
|
+
where,
|
|
710
|
+
populate: ["form"],
|
|
711
|
+
orderBy: { createdAt: "desc" },
|
|
712
|
+
limit: Number(pageSize),
|
|
713
|
+
offset: (Number(page) - 1) * Number(pageSize)
|
|
714
|
+
}),
|
|
715
|
+
strapi2.db.query(SUBMISSION_UID).count({ where })
|
|
716
|
+
]);
|
|
717
|
+
return {
|
|
718
|
+
results,
|
|
719
|
+
pagination: {
|
|
720
|
+
page: Number(page),
|
|
721
|
+
pageSize: Number(pageSize),
|
|
722
|
+
total,
|
|
723
|
+
pageCount: Math.ceil(total / Number(pageSize))
|
|
724
|
+
}
|
|
725
|
+
};
|
|
726
|
+
},
|
|
727
|
+
async findOne(id) {
|
|
728
|
+
return strapi2.db.query(SUBMISSION_UID).findOne({
|
|
729
|
+
where: { id },
|
|
730
|
+
populate: ["form"]
|
|
731
|
+
});
|
|
732
|
+
},
|
|
733
|
+
async updateStatus(id, status) {
|
|
734
|
+
return strapi2.db.query(SUBMISSION_UID).update({
|
|
735
|
+
where: { id },
|
|
736
|
+
data: { status }
|
|
737
|
+
});
|
|
738
|
+
},
|
|
739
|
+
async delete(id) {
|
|
740
|
+
return strapi2.db.query(SUBMISSION_UID).delete({ where: { id } });
|
|
741
|
+
},
|
|
742
|
+
async export(formId, format = "csv") {
|
|
743
|
+
const form2 = await strapi2.db.query(FORM_UID).findOne({ where: { id: formId } });
|
|
744
|
+
const submissions = await strapi2.db.query(SUBMISSION_UID).findMany({
|
|
745
|
+
where: { form: formId },
|
|
746
|
+
orderBy: { createdAt: "desc" }
|
|
747
|
+
});
|
|
748
|
+
const fields = form2?.fields || [];
|
|
749
|
+
const fieldNames = fields.filter((f) => !["heading", "paragraph", "divider"].includes(f.type)).map((f) => f.name);
|
|
750
|
+
const headers = ["id", "status", "createdAt", ...fieldNames];
|
|
751
|
+
const rows = submissions.map((s) => [
|
|
752
|
+
s.id,
|
|
753
|
+
s.status,
|
|
754
|
+
s.createdAt,
|
|
755
|
+
...fieldNames.map((name) => s.data?.[name] ?? "")
|
|
756
|
+
]);
|
|
757
|
+
if (format === "csv") {
|
|
758
|
+
const lines = [
|
|
759
|
+
headers.join(","),
|
|
760
|
+
...rows.map(
|
|
761
|
+
(r) => r.map(String).map((v) => `"${v.replace(/"/g, '""')}"`).join(",")
|
|
762
|
+
)
|
|
763
|
+
];
|
|
764
|
+
return lines.join("\n");
|
|
765
|
+
}
|
|
766
|
+
return rows;
|
|
767
|
+
},
|
|
768
|
+
async getStats(formId) {
|
|
769
|
+
const all = await strapi2.db.query(SUBMISSION_UID).findMany({
|
|
770
|
+
where: { form: formId }
|
|
771
|
+
});
|
|
772
|
+
const total = all.length;
|
|
773
|
+
const byStatus = { new: 0, read: 0, archived: 0 };
|
|
774
|
+
for (const s of all) {
|
|
775
|
+
byStatus[s.status] = (byStatus[s.status] || 0) + 1;
|
|
776
|
+
}
|
|
777
|
+
return { total, byStatus };
|
|
778
|
+
}
|
|
779
|
+
});
|
|
780
|
+
const validation$1 = {
|
|
781
|
+
validate(fields, data) {
|
|
782
|
+
const errors = {};
|
|
783
|
+
const decorative = ["heading", "paragraph", "divider", "hidden"];
|
|
784
|
+
for (const field of fields) {
|
|
785
|
+
if (decorative.includes(field.type)) continue;
|
|
786
|
+
const value = data[field.name];
|
|
787
|
+
const fieldErrors = [];
|
|
788
|
+
if (field.required && this.isEmpty(value)) {
|
|
789
|
+
const reqRule = field.validation?.find((v) => v.type === "required");
|
|
790
|
+
fieldErrors.push(reqRule?.message || `${field.label} is required`);
|
|
791
|
+
}
|
|
792
|
+
if (this.isEmpty(value) && !field.required) {
|
|
793
|
+
const hasLengthOrValueRule = (field.validation || []).some(
|
|
794
|
+
(r) => ["minLength", "min"].includes(r.type)
|
|
795
|
+
);
|
|
796
|
+
if (!hasLengthOrValueRule) continue;
|
|
797
|
+
}
|
|
798
|
+
if (!this.isEmpty(value)) {
|
|
799
|
+
if (field.type === "email" && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(String(value))) {
|
|
800
|
+
fieldErrors.push(`${field.label}: enter a valid email address`);
|
|
801
|
+
}
|
|
802
|
+
if (field.type === "url") {
|
|
803
|
+
try {
|
|
804
|
+
new URL(String(value));
|
|
805
|
+
} catch {
|
|
806
|
+
fieldErrors.push(`${field.label}: enter a valid URL`);
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
if (field.type === "number" && isNaN(Number(value))) {
|
|
810
|
+
fieldErrors.push(`${field.label}: enter a valid number`);
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
for (const rule of field.validation || []) {
|
|
814
|
+
if (rule.type === "required") continue;
|
|
815
|
+
const error = this.runRule(rule, value, field, data);
|
|
816
|
+
if (error) fieldErrors.push(error);
|
|
817
|
+
}
|
|
818
|
+
if (fieldErrors.length > 0) {
|
|
819
|
+
errors[field.name] = fieldErrors;
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
return { valid: Object.keys(errors).length === 0, errors };
|
|
823
|
+
},
|
|
824
|
+
runRule(rule, value, field, allData) {
|
|
825
|
+
switch (rule.type) {
|
|
826
|
+
case "minLength":
|
|
827
|
+
if (String(value).length < Number(rule.value))
|
|
828
|
+
return rule.message || `Minimum ${rule.value} characters`;
|
|
829
|
+
break;
|
|
830
|
+
case "maxLength":
|
|
831
|
+
if (String(value).length > Number(rule.value))
|
|
832
|
+
return rule.message || `Maximum ${rule.value} characters`;
|
|
833
|
+
break;
|
|
834
|
+
case "min":
|
|
835
|
+
if (Number(value) < Number(rule.value))
|
|
836
|
+
return rule.message || `Minimum value is ${rule.value}`;
|
|
837
|
+
break;
|
|
838
|
+
case "max":
|
|
839
|
+
if (Number(value) > Number(rule.value))
|
|
840
|
+
return rule.message || `Maximum value is ${rule.value}`;
|
|
841
|
+
break;
|
|
842
|
+
case "pattern":
|
|
843
|
+
if (!new RegExp(rule.value).test(String(value)))
|
|
844
|
+
return rule.message || `Invalid format`;
|
|
845
|
+
break;
|
|
846
|
+
case "email":
|
|
847
|
+
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(String(value)))
|
|
848
|
+
return rule.message || `Enter a valid email address`;
|
|
849
|
+
break;
|
|
850
|
+
case "url":
|
|
851
|
+
try {
|
|
852
|
+
new URL(String(value));
|
|
853
|
+
} catch {
|
|
854
|
+
return rule.message || `Enter a valid URL`;
|
|
855
|
+
}
|
|
856
|
+
break;
|
|
857
|
+
case "matchField":
|
|
858
|
+
if (value !== allData[rule.value])
|
|
859
|
+
return rule.message || `Fields do not match`;
|
|
860
|
+
break;
|
|
861
|
+
}
|
|
862
|
+
return null;
|
|
863
|
+
},
|
|
864
|
+
isEmpty(value) {
|
|
865
|
+
return value === void 0 || value === null || value === "" || Array.isArray(value) && value.length === 0;
|
|
866
|
+
}
|
|
867
|
+
};
|
|
868
|
+
const validation = () => validation$1;
|
|
869
|
+
const services = {
|
|
870
|
+
form,
|
|
871
|
+
submission,
|
|
872
|
+
validation
|
|
873
|
+
};
|
|
874
|
+
const index = {
|
|
875
|
+
register,
|
|
876
|
+
bootstrap,
|
|
877
|
+
destroy,
|
|
878
|
+
config,
|
|
879
|
+
controllers,
|
|
880
|
+
routes,
|
|
881
|
+
services,
|
|
882
|
+
contentTypes,
|
|
883
|
+
policies,
|
|
884
|
+
middlewares
|
|
885
|
+
};
|
|
886
|
+
exports.default = index;
|