@visns-studio/visns-components 3.4.12 → 3.5.0
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.
|
@@ -0,0 +1,1013 @@
|
|
|
1
|
+
import React, { useEffect, useRef, useState } from 'react';
|
|
2
|
+
import { useParams, Link } from 'react-router-dom';
|
|
3
|
+
import Popup from 'reactjs-popup';
|
|
4
|
+
import { arrayMoveImmutable } from 'array-move';
|
|
5
|
+
import { confirmAlert } from 'react-confirm-alert';
|
|
6
|
+
import parse from 'html-react-parser';
|
|
7
|
+
import Toggle from 'react-toggle';
|
|
8
|
+
import { toast } from 'react-toastify';
|
|
9
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
10
|
+
import { Editor } from '@tinymce/tinymce-react';
|
|
11
|
+
import { CircleChevronRight, CirclePlus, CircleX, TrashCan } from 'akar-icons';
|
|
12
|
+
|
|
13
|
+
import 'react-toggle/style.css';
|
|
14
|
+
import 'react-datepicker/dist/react-datepicker.css';
|
|
15
|
+
import 'react-confirm-alert/src/react-confirm-alert.css';
|
|
16
|
+
|
|
17
|
+
import CustomFetch from '../Fetch';
|
|
18
|
+
import SortableList from '../../../components/cms/sorting/List';
|
|
19
|
+
|
|
20
|
+
function GenericFormBuilder({ setting, urlParam, userProfile }) {
|
|
21
|
+
const editorRef = useRef(null);
|
|
22
|
+
const routeParams = useParams();
|
|
23
|
+
|
|
24
|
+
const { fetchUrl, fields } = setting;
|
|
25
|
+
|
|
26
|
+
const { dataId } = useParams();
|
|
27
|
+
const [data, setData] = useState({});
|
|
28
|
+
const [dataField, setDataField] = useState({
|
|
29
|
+
id: '',
|
|
30
|
+
label: '',
|
|
31
|
+
description: '',
|
|
32
|
+
type: '',
|
|
33
|
+
options: [],
|
|
34
|
+
size: 'half',
|
|
35
|
+
required: 'no',
|
|
36
|
+
});
|
|
37
|
+
const [dataOption, setDataOption] = useState({
|
|
38
|
+
id: '',
|
|
39
|
+
label: '',
|
|
40
|
+
});
|
|
41
|
+
const [modalShow, setModalShow] = useState(false);
|
|
42
|
+
const [modalType, setModalType] = useState({
|
|
43
|
+
type: 'create',
|
|
44
|
+
key: '',
|
|
45
|
+
});
|
|
46
|
+
const [modalFormShow, setModalFormShow] = useState(false);
|
|
47
|
+
|
|
48
|
+
/** Data Option Functionalities */
|
|
49
|
+
const handleDataOption = (e) => {
|
|
50
|
+
const { name, value } = e.target;
|
|
51
|
+
|
|
52
|
+
setDataOption((items) => ({
|
|
53
|
+
...items,
|
|
54
|
+
[name]: value,
|
|
55
|
+
id: `${slugify(value)}-`,
|
|
56
|
+
}));
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const handleAddOption = (e) => {
|
|
60
|
+
e.preventDefault();
|
|
61
|
+
|
|
62
|
+
if (dataOption.label !== '') {
|
|
63
|
+
// Check if the label is already present in the options array
|
|
64
|
+
const isLabelUnique = !dataField.options.some(
|
|
65
|
+
(option) => option.label === dataOption.label
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
if (isLabelUnique) {
|
|
69
|
+
setDataField((items) => ({
|
|
70
|
+
...items,
|
|
71
|
+
options: [...items.options, dataOption],
|
|
72
|
+
}));
|
|
73
|
+
|
|
74
|
+
setDataOption(() => ({
|
|
75
|
+
id: '',
|
|
76
|
+
label: '',
|
|
77
|
+
}));
|
|
78
|
+
} else {
|
|
79
|
+
toast.warn('Option label must be unique.');
|
|
80
|
+
}
|
|
81
|
+
} else {
|
|
82
|
+
toast.warn('Please enter a label for the option.');
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
const handleDeleteOption = (e) => {
|
|
87
|
+
e.preventDefault();
|
|
88
|
+
|
|
89
|
+
const { counter } = e.target.dataset;
|
|
90
|
+
|
|
91
|
+
setDataField((prevState) => {
|
|
92
|
+
const options = prevState.options.slice(); // Create a copy of the options array
|
|
93
|
+
options.splice(counter, 1); // Remove the option at the specified index
|
|
94
|
+
|
|
95
|
+
return {
|
|
96
|
+
...prevState,
|
|
97
|
+
options: options, // Update the options array in the state
|
|
98
|
+
};
|
|
99
|
+
});
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
/** Field Functionalities */
|
|
103
|
+
const onSortEnd = ({ oldIndex, newIndex }) => {
|
|
104
|
+
const newData = arrayMoveImmutable(data.detail, oldIndex, newIndex);
|
|
105
|
+
|
|
106
|
+
setData((items) => ({
|
|
107
|
+
...items,
|
|
108
|
+
detail: newData,
|
|
109
|
+
}));
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
const handleCloseModal = () => {
|
|
113
|
+
setModalShow(false);
|
|
114
|
+
setDataField(() => ({
|
|
115
|
+
label: '',
|
|
116
|
+
type: '',
|
|
117
|
+
options: [],
|
|
118
|
+
size: 'quarter',
|
|
119
|
+
required: 'no',
|
|
120
|
+
}));
|
|
121
|
+
setModalType(() => ({
|
|
122
|
+
type: 'create',
|
|
123
|
+
key: '',
|
|
124
|
+
}));
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
const handleOpenModal = () => {
|
|
128
|
+
setModalShow(true);
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
const slugify = (text) => {
|
|
132
|
+
// Implement your slugification logic here
|
|
133
|
+
// Example: Convert spaces to hyphens and lowercase the text
|
|
134
|
+
return text.replace(/\s+/g, '-').toLowerCase();
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
const handleChangeForm = (e) => {
|
|
138
|
+
if (e) {
|
|
139
|
+
const { name, value } = e.target;
|
|
140
|
+
const updatedValue = name === 'id' ? slugify(value) : value;
|
|
141
|
+
|
|
142
|
+
setDataField((items) => ({
|
|
143
|
+
...items,
|
|
144
|
+
[name]: updatedValue,
|
|
145
|
+
}));
|
|
146
|
+
|
|
147
|
+
if (name === 'label') {
|
|
148
|
+
setDataField((items) => ({
|
|
149
|
+
...items,
|
|
150
|
+
id: slugify(value),
|
|
151
|
+
}));
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
const handleChangeFormRichEditor = (e, value, name) => {
|
|
157
|
+
setDataField((items) => ({
|
|
158
|
+
...items,
|
|
159
|
+
[name]: value,
|
|
160
|
+
}));
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
const handleEdit = (key) => {
|
|
164
|
+
const { detail } = data;
|
|
165
|
+
|
|
166
|
+
setDataField(() => ({
|
|
167
|
+
...detail[key],
|
|
168
|
+
}));
|
|
169
|
+
|
|
170
|
+
setModalType(() => ({
|
|
171
|
+
type: 'update',
|
|
172
|
+
key: key,
|
|
173
|
+
}));
|
|
174
|
+
|
|
175
|
+
handleOpenModal();
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
const handleDeleteField = (key, label) => {
|
|
179
|
+
confirmAlert({
|
|
180
|
+
title: `Delete "${label}" Field`,
|
|
181
|
+
message: 'Are you sure you want to delete this field?',
|
|
182
|
+
buttons: [
|
|
183
|
+
{
|
|
184
|
+
label: 'Yes',
|
|
185
|
+
onClick: () => {
|
|
186
|
+
let newDetail = data.detail;
|
|
187
|
+
newDetail.splice(key, 1);
|
|
188
|
+
|
|
189
|
+
setData((items) => ({
|
|
190
|
+
...items,
|
|
191
|
+
detail: [...newDetail],
|
|
192
|
+
}));
|
|
193
|
+
|
|
194
|
+
handleCloseModal();
|
|
195
|
+
},
|
|
196
|
+
},
|
|
197
|
+
{
|
|
198
|
+
label: 'No',
|
|
199
|
+
onClick: () => close(),
|
|
200
|
+
},
|
|
201
|
+
],
|
|
202
|
+
});
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
const handleSaveField = (e) => {
|
|
206
|
+
if (e) {
|
|
207
|
+
e.preventDefault();
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
let field = dataField;
|
|
211
|
+
|
|
212
|
+
// Ensure the field ID is unique and formatted
|
|
213
|
+
field.id = formatFieldId(field.id);
|
|
214
|
+
|
|
215
|
+
let errorMessage = validateField(field);
|
|
216
|
+
|
|
217
|
+
if (errorMessage === '') {
|
|
218
|
+
saveFieldData(field);
|
|
219
|
+
handleCloseModal();
|
|
220
|
+
} else {
|
|
221
|
+
displayErrorMessage(errorMessage);
|
|
222
|
+
}
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
const formatFieldId = (id) => {
|
|
226
|
+
return id.includes('::') ? id : `${id}::${uuidv4()}`;
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
const validateField = (field) => {
|
|
230
|
+
let errors = [];
|
|
231
|
+
|
|
232
|
+
// Validate ID
|
|
233
|
+
if (!isIdValid(field.id)) {
|
|
234
|
+
errors.push('Please enter a unique id for the field.');
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// Modify Validation for Label and Description based on Type
|
|
238
|
+
if (field.type === 'plaintext' || field.type === 'plaintextheading') {
|
|
239
|
+
// For plaintext or plaintextheading, description is required
|
|
240
|
+
if (!field.description || field.description === '') {
|
|
241
|
+
errors.push('Please enter a description for the field.');
|
|
242
|
+
}
|
|
243
|
+
} else {
|
|
244
|
+
// For other types, label is required
|
|
245
|
+
if (!field.label || field.label === '') {
|
|
246
|
+
errors.push('Please enter a label for the field.');
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// Validate Type and Options for Dropdown
|
|
251
|
+
if (field.type === '') {
|
|
252
|
+
errors.push('Please select a type for the field.');
|
|
253
|
+
} else if (
|
|
254
|
+
field.type === 'dropdown' &&
|
|
255
|
+
(!field.options || field.options.length === 0)
|
|
256
|
+
) {
|
|
257
|
+
errors.push('Please add options for the dropdown.');
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// Validate Size
|
|
261
|
+
if (!field.size || field.size === '') {
|
|
262
|
+
errors.push('Please select a size for the field.');
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// Validate Required
|
|
266
|
+
if (field.required === '') {
|
|
267
|
+
errors.push('Is the field a required field?');
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
return errors.join('<br />');
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
const isIdValid = (id) => {
|
|
274
|
+
if (id === '') {
|
|
275
|
+
return false;
|
|
276
|
+
}
|
|
277
|
+
if (
|
|
278
|
+
modalType.type === 'create' ||
|
|
279
|
+
id !== data.detail[modalType.key]?.id
|
|
280
|
+
) {
|
|
281
|
+
return data.detail
|
|
282
|
+
? data.detail.every((field) => field.id !== id)
|
|
283
|
+
: true;
|
|
284
|
+
}
|
|
285
|
+
return true;
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
const saveFieldData = (field) => {
|
|
289
|
+
let newDetail =
|
|
290
|
+
modalType.type === 'create'
|
|
291
|
+
? [...(data.detail || []), field]
|
|
292
|
+
: data.detail.map((f, index) =>
|
|
293
|
+
index === modalType.key ? field : f
|
|
294
|
+
);
|
|
295
|
+
|
|
296
|
+
setData((items) => ({
|
|
297
|
+
...items,
|
|
298
|
+
detail: newDetail,
|
|
299
|
+
}));
|
|
300
|
+
};
|
|
301
|
+
|
|
302
|
+
const displayErrorMessage = (errorMessage) => {
|
|
303
|
+
toast.error(<div>{parse(errorMessage)}</div>);
|
|
304
|
+
};
|
|
305
|
+
|
|
306
|
+
/** Form Functionalities */
|
|
307
|
+
const renderClassName = (d) => {
|
|
308
|
+
const sizeClassMap = {
|
|
309
|
+
full: 'formBuilderItem fwBuilderItem',
|
|
310
|
+
half: 'formBuilderItem halfBuilderItem',
|
|
311
|
+
quarter: 'formBuilderItem qtrBuilderItem',
|
|
312
|
+
};
|
|
313
|
+
|
|
314
|
+
return sizeClassMap[d.size] || 'formBuilderItem halfBuilderItem';
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
const renderLabel = (field, children) => (
|
|
318
|
+
<label className="fi__label">
|
|
319
|
+
{children}
|
|
320
|
+
<span
|
|
321
|
+
className={
|
|
322
|
+
field.type === 'checkbox' ? 'fi__spancheckbox' : 'fi__span'
|
|
323
|
+
}
|
|
324
|
+
>
|
|
325
|
+
{field.label} {field.required === 'yes' ? '*' : null}
|
|
326
|
+
</span>
|
|
327
|
+
</label>
|
|
328
|
+
);
|
|
329
|
+
|
|
330
|
+
const renderField = (field) => {
|
|
331
|
+
switch (field.type) {
|
|
332
|
+
case 'checkbox':
|
|
333
|
+
return renderLabel(
|
|
334
|
+
field,
|
|
335
|
+
field.options.length > 0 && (
|
|
336
|
+
<div className="fi__optionscontainer">
|
|
337
|
+
{field.options.map((option, index) => (
|
|
338
|
+
<div
|
|
339
|
+
key={`${field.id}-checkbox-option-${index}`}
|
|
340
|
+
className="fi__checkboxcontainer"
|
|
341
|
+
>
|
|
342
|
+
<input
|
|
343
|
+
type="checkbox"
|
|
344
|
+
className="fi__customcheckbox"
|
|
345
|
+
/>
|
|
346
|
+
<label className="fi__checkboxlabel">
|
|
347
|
+
{option.label}
|
|
348
|
+
</label>{' '}
|
|
349
|
+
</div>
|
|
350
|
+
))}
|
|
351
|
+
</div>
|
|
352
|
+
)
|
|
353
|
+
);
|
|
354
|
+
case 'dynamicdata':
|
|
355
|
+
return (
|
|
356
|
+
<label className="fi__label">
|
|
357
|
+
<strong>{field.label}</strong> [Dynamic Data]
|
|
358
|
+
</label>
|
|
359
|
+
);
|
|
360
|
+
case 'plaintextheading':
|
|
361
|
+
return (
|
|
362
|
+
<label className="fi__label">
|
|
363
|
+
<h2>{field.label}</h2>
|
|
364
|
+
</label>
|
|
365
|
+
);
|
|
366
|
+
case 'plaintext':
|
|
367
|
+
return (
|
|
368
|
+
<label className="fi__label">
|
|
369
|
+
{field.description ? parse(field.description) : null}
|
|
370
|
+
</label>
|
|
371
|
+
);
|
|
372
|
+
case 'toggle':
|
|
373
|
+
return (
|
|
374
|
+
<div>
|
|
375
|
+
<Toggle />
|
|
376
|
+
<span className="fi__toggletxt">
|
|
377
|
+
{field.label}{' '}
|
|
378
|
+
{field.required === 'yes' ? '*' : null}
|
|
379
|
+
</span>
|
|
380
|
+
</div>
|
|
381
|
+
);
|
|
382
|
+
default:
|
|
383
|
+
const inputType =
|
|
384
|
+
field.type === 'dropdown' ? (
|
|
385
|
+
<select>
|
|
386
|
+
<option>Please Select an Option</option>
|
|
387
|
+
</select>
|
|
388
|
+
) : (
|
|
389
|
+
<input
|
|
390
|
+
type={
|
|
391
|
+
field.type === 'datetime'
|
|
392
|
+
? 'datetime-local'
|
|
393
|
+
: field.type
|
|
394
|
+
}
|
|
395
|
+
/>
|
|
396
|
+
);
|
|
397
|
+
|
|
398
|
+
return renderLabel(field, inputType);
|
|
399
|
+
}
|
|
400
|
+
};
|
|
401
|
+
|
|
402
|
+
const handleCloseModalForm = () => {
|
|
403
|
+
setModalFormShow(false);
|
|
404
|
+
};
|
|
405
|
+
|
|
406
|
+
const handleOpenModalForm = () => {
|
|
407
|
+
setModalFormShow(true);
|
|
408
|
+
};
|
|
409
|
+
|
|
410
|
+
const handleChange = (e) => {
|
|
411
|
+
if (e) {
|
|
412
|
+
const { name, value } = e.target;
|
|
413
|
+
|
|
414
|
+
setData((prevState) => ({
|
|
415
|
+
...prevState,
|
|
416
|
+
[name]: value,
|
|
417
|
+
}));
|
|
418
|
+
}
|
|
419
|
+
};
|
|
420
|
+
|
|
421
|
+
const handleSubmit = async (e) => {
|
|
422
|
+
try {
|
|
423
|
+
if (e) {
|
|
424
|
+
e.preventDefault();
|
|
425
|
+
|
|
426
|
+
let _error = '';
|
|
427
|
+
|
|
428
|
+
if (data.label === '') {
|
|
429
|
+
_error += 'Please a label for the form.<br />';
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
if (_error === '') {
|
|
433
|
+
const res = await CustomFetch(
|
|
434
|
+
`/ajax/forms/${dataId}`,
|
|
435
|
+
'PUT',
|
|
436
|
+
{
|
|
437
|
+
...data,
|
|
438
|
+
}
|
|
439
|
+
);
|
|
440
|
+
|
|
441
|
+
if (res.data.error === '') {
|
|
442
|
+
toast.success(
|
|
443
|
+
"You have successfully updated the form's detail."
|
|
444
|
+
);
|
|
445
|
+
|
|
446
|
+
handleCloseModalForm();
|
|
447
|
+
} else {
|
|
448
|
+
toast.error(String(res.data.error));
|
|
449
|
+
}
|
|
450
|
+
} else {
|
|
451
|
+
toast.error(<div>{parse(_error)}</div>);
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
} catch (err) {
|
|
455
|
+
toast.error(`Error: ${err}`);
|
|
456
|
+
}
|
|
457
|
+
};
|
|
458
|
+
|
|
459
|
+
const fetchData = async () => {
|
|
460
|
+
try {
|
|
461
|
+
const res = await CustomFetch(
|
|
462
|
+
`${fetchUrl}/${routeParams[urlParam]}`,
|
|
463
|
+
'GET',
|
|
464
|
+
{}
|
|
465
|
+
);
|
|
466
|
+
|
|
467
|
+
setData(res.data);
|
|
468
|
+
} catch (err) {
|
|
469
|
+
toast.error(`Error: ${err}`);
|
|
470
|
+
}
|
|
471
|
+
};
|
|
472
|
+
|
|
473
|
+
const saveOnSort = async () => {
|
|
474
|
+
try {
|
|
475
|
+
const res = await CustomFetch(
|
|
476
|
+
`/ajax/forms/sort/${dataId}`,
|
|
477
|
+
'POST',
|
|
478
|
+
{
|
|
479
|
+
detail: data.detail,
|
|
480
|
+
}
|
|
481
|
+
);
|
|
482
|
+
|
|
483
|
+
if (res.data.error !== '') {
|
|
484
|
+
toast.error(String(res.data.error));
|
|
485
|
+
}
|
|
486
|
+
} catch (err) {
|
|
487
|
+
toast.error(`Error: ${err}`);
|
|
488
|
+
}
|
|
489
|
+
};
|
|
490
|
+
|
|
491
|
+
useEffect(() => {
|
|
492
|
+
if (data.detail && data.detail.length > 0) {
|
|
493
|
+
saveOnSort();
|
|
494
|
+
}
|
|
495
|
+
}, [data.detail]);
|
|
496
|
+
|
|
497
|
+
useEffect(() => {
|
|
498
|
+
fetchData();
|
|
499
|
+
}, []);
|
|
500
|
+
|
|
501
|
+
return (
|
|
502
|
+
<div>
|
|
503
|
+
<div className="grid">
|
|
504
|
+
<div className="grid__row">
|
|
505
|
+
<div className="grid__full crmtitle">
|
|
506
|
+
<h1>
|
|
507
|
+
<Link to="/forms">Form</Link>{' '}
|
|
508
|
+
<CircleChevronRight strokeWidth={2} size={18} />{' '}
|
|
509
|
+
{data.label}
|
|
510
|
+
</h1>
|
|
511
|
+
</div>
|
|
512
|
+
</div>
|
|
513
|
+
</div>
|
|
514
|
+
<div className="grid">
|
|
515
|
+
<div className="grid__subrow">
|
|
516
|
+
<div className="grid__subnav">
|
|
517
|
+
{data.detail && data.detail.length > 0 ? (
|
|
518
|
+
<SortableList
|
|
519
|
+
handleClick={handleEdit}
|
|
520
|
+
handleDelete={handleDeleteField}
|
|
521
|
+
items={data.detail}
|
|
522
|
+
onSortEnd={onSortEnd}
|
|
523
|
+
axis="xy"
|
|
524
|
+
helperClass="dragitem"
|
|
525
|
+
transitionDuration={250}
|
|
526
|
+
showImage={false}
|
|
527
|
+
useDragHandle
|
|
528
|
+
/>
|
|
529
|
+
) : null}
|
|
530
|
+
</div>
|
|
531
|
+
<div className="grid__subcontent">
|
|
532
|
+
<div className="formSplit">
|
|
533
|
+
<div className="gridtxt__header">
|
|
534
|
+
<span>Form Preview</span>
|
|
535
|
+
</div>
|
|
536
|
+
{data.detail && data.detail.length > 0 ? (
|
|
537
|
+
<div className="modal__content">
|
|
538
|
+
<div className="formcontainer">
|
|
539
|
+
<form className="modalForm">
|
|
540
|
+
{data.detail.map((a, b) => (
|
|
541
|
+
<div
|
|
542
|
+
key={`form-preview-field-${b}`}
|
|
543
|
+
className={renderClassName(
|
|
544
|
+
a
|
|
545
|
+
)}
|
|
546
|
+
>
|
|
547
|
+
{renderField(a)}
|
|
548
|
+
</div>
|
|
549
|
+
))}
|
|
550
|
+
</form>
|
|
551
|
+
</div>
|
|
552
|
+
</div>
|
|
553
|
+
) : null}
|
|
554
|
+
</div>
|
|
555
|
+
<div className="polActions">
|
|
556
|
+
<button className="btn" onClick={handleOpenModal}>
|
|
557
|
+
Add Field
|
|
558
|
+
</button>
|
|
559
|
+
<button
|
|
560
|
+
className="btn"
|
|
561
|
+
onClick={handleOpenModalForm}
|
|
562
|
+
>
|
|
563
|
+
Edit Form
|
|
564
|
+
</button>
|
|
565
|
+
</div>
|
|
566
|
+
</div>
|
|
567
|
+
</div>
|
|
568
|
+
</div>
|
|
569
|
+
|
|
570
|
+
<Popup
|
|
571
|
+
open={modalShow}
|
|
572
|
+
onClose={handleCloseModal}
|
|
573
|
+
closeOnDocumentClick={false}
|
|
574
|
+
>
|
|
575
|
+
<div className="modalwrap top--modal">
|
|
576
|
+
<div className="modal">
|
|
577
|
+
<div className="modal__header">
|
|
578
|
+
<h1>
|
|
579
|
+
{modalType.type === 'create'
|
|
580
|
+
? 'Add new Field'
|
|
581
|
+
: 'Update existing Field'}
|
|
582
|
+
</h1>
|
|
583
|
+
<button
|
|
584
|
+
className="modal__close"
|
|
585
|
+
onClick={handleCloseModal}
|
|
586
|
+
>
|
|
587
|
+
<CircleX strokeWidth={1} size={24} />
|
|
588
|
+
</button>
|
|
589
|
+
</div>
|
|
590
|
+
<div className="modal__content">
|
|
591
|
+
<div className="formcontainer">
|
|
592
|
+
<form className="modalForm">
|
|
593
|
+
<div className="formItem">
|
|
594
|
+
<label className="fi__label">
|
|
595
|
+
<select
|
|
596
|
+
name="type"
|
|
597
|
+
value={dataField.type}
|
|
598
|
+
onChange={handleChangeForm}
|
|
599
|
+
>
|
|
600
|
+
<option>
|
|
601
|
+
Please Select an Option
|
|
602
|
+
</option>
|
|
603
|
+
<option value="checkbox">
|
|
604
|
+
Checkbox
|
|
605
|
+
</option>
|
|
606
|
+
<option value="date">
|
|
607
|
+
Date
|
|
608
|
+
</option>
|
|
609
|
+
<option value="datetime">
|
|
610
|
+
Date & Time
|
|
611
|
+
</option>
|
|
612
|
+
<option value="dropdown">
|
|
613
|
+
Dropdown
|
|
614
|
+
</option>
|
|
615
|
+
<option value="dynamicdata">
|
|
616
|
+
Dynamic Data
|
|
617
|
+
</option>
|
|
618
|
+
<option value="file">
|
|
619
|
+
File
|
|
620
|
+
</option>
|
|
621
|
+
<option value="plaintextheading">
|
|
622
|
+
Heading
|
|
623
|
+
</option>
|
|
624
|
+
<option value="image">
|
|
625
|
+
Image
|
|
626
|
+
</option>
|
|
627
|
+
<option value="number">
|
|
628
|
+
Number
|
|
629
|
+
</option>
|
|
630
|
+
<option value="plaintext">
|
|
631
|
+
Plain Text
|
|
632
|
+
</option>
|
|
633
|
+
<option value="text">
|
|
634
|
+
Text
|
|
635
|
+
</option>
|
|
636
|
+
<option value="textarea">
|
|
637
|
+
Textarea
|
|
638
|
+
</option>
|
|
639
|
+
<option value="time">
|
|
640
|
+
Time
|
|
641
|
+
</option>
|
|
642
|
+
<option value="toggle">
|
|
643
|
+
Toggle
|
|
644
|
+
</option>
|
|
645
|
+
</select>
|
|
646
|
+
<span className="fi__span">
|
|
647
|
+
Type *
|
|
648
|
+
</span>
|
|
649
|
+
</label>
|
|
650
|
+
</div>
|
|
651
|
+
<div className="formItem">
|
|
652
|
+
<label className="fi__label">
|
|
653
|
+
<input
|
|
654
|
+
type="text"
|
|
655
|
+
name="label"
|
|
656
|
+
value={
|
|
657
|
+
dataField.label
|
|
658
|
+
? dataField.label
|
|
659
|
+
: ''
|
|
660
|
+
}
|
|
661
|
+
onChange={handleChangeForm}
|
|
662
|
+
/>
|
|
663
|
+
<span className="fi__span">
|
|
664
|
+
Label *
|
|
665
|
+
</span>
|
|
666
|
+
</label>
|
|
667
|
+
</div>
|
|
668
|
+
{dataField.type === 'dynamicdata' && (
|
|
669
|
+
<div className="formItem fwItem">
|
|
670
|
+
<label className="fi__label">
|
|
671
|
+
<select
|
|
672
|
+
name="dynamicfield"
|
|
673
|
+
value={
|
|
674
|
+
dataField.dynamicfield
|
|
675
|
+
}
|
|
676
|
+
onChange={handleChangeForm}
|
|
677
|
+
>
|
|
678
|
+
<option value="">
|
|
679
|
+
Please select an option
|
|
680
|
+
</option>
|
|
681
|
+
{fields.map((category) => (
|
|
682
|
+
<optgroup
|
|
683
|
+
label={
|
|
684
|
+
category.label
|
|
685
|
+
}
|
|
686
|
+
key={`dynamic-field-category-${category.id}`}
|
|
687
|
+
>
|
|
688
|
+
{category.data.map(
|
|
689
|
+
(field) => (
|
|
690
|
+
<option
|
|
691
|
+
value={`${category.id}::${field.value}`}
|
|
692
|
+
key={`dynamic-field-category-${category.id}-${field.value}`}
|
|
693
|
+
>
|
|
694
|
+
{
|
|
695
|
+
field.label
|
|
696
|
+
}
|
|
697
|
+
</option>
|
|
698
|
+
)
|
|
699
|
+
)}
|
|
700
|
+
</optgroup>
|
|
701
|
+
))}
|
|
702
|
+
</select>
|
|
703
|
+
<span className="fi__span">
|
|
704
|
+
Dynamic Field *
|
|
705
|
+
</span>
|
|
706
|
+
</label>
|
|
707
|
+
</div>
|
|
708
|
+
)}
|
|
709
|
+
<div className="formItem">
|
|
710
|
+
<label className="fi__label">
|
|
711
|
+
<select
|
|
712
|
+
name="size"
|
|
713
|
+
value={dataField.size}
|
|
714
|
+
onChange={handleChangeForm}
|
|
715
|
+
>
|
|
716
|
+
<option>
|
|
717
|
+
Please Select an Option
|
|
718
|
+
</option>
|
|
719
|
+
<option value="quarter">
|
|
720
|
+
Quarter
|
|
721
|
+
</option>
|
|
722
|
+
<option value="half">
|
|
723
|
+
Half
|
|
724
|
+
</option>
|
|
725
|
+
<option value="full">
|
|
726
|
+
Full
|
|
727
|
+
</option>
|
|
728
|
+
</select>
|
|
729
|
+
<span className="fi__span">
|
|
730
|
+
Size *
|
|
731
|
+
</span>
|
|
732
|
+
</label>
|
|
733
|
+
</div>
|
|
734
|
+
<div className="formItem">
|
|
735
|
+
<label className="fi__label">
|
|
736
|
+
<select
|
|
737
|
+
name="required"
|
|
738
|
+
value={dataField.required}
|
|
739
|
+
onChange={handleChangeForm}
|
|
740
|
+
>
|
|
741
|
+
<option>
|
|
742
|
+
Please Select an Option
|
|
743
|
+
</option>
|
|
744
|
+
<option value="no">No</option>
|
|
745
|
+
<option value="yes">Yes</option>
|
|
746
|
+
</select>
|
|
747
|
+
<span className="fi__span">
|
|
748
|
+
Required? *
|
|
749
|
+
</span>
|
|
750
|
+
</label>
|
|
751
|
+
</div>
|
|
752
|
+
{dataField.type === 'plaintext' && (
|
|
753
|
+
<div className="formItem fwItem">
|
|
754
|
+
<label className="fi__label">
|
|
755
|
+
<Editor
|
|
756
|
+
apiKey={
|
|
757
|
+
userProfile.settings.api
|
|
758
|
+
.tinymce
|
|
759
|
+
}
|
|
760
|
+
onEditorChange={(
|
|
761
|
+
value,
|
|
762
|
+
e
|
|
763
|
+
) => {
|
|
764
|
+
handleChangeFormRichEditor(
|
|
765
|
+
e,
|
|
766
|
+
value,
|
|
767
|
+
'description'
|
|
768
|
+
);
|
|
769
|
+
}}
|
|
770
|
+
onInit={(evt, editor) =>
|
|
771
|
+
(editorRef.current =
|
|
772
|
+
editor)
|
|
773
|
+
}
|
|
774
|
+
value={
|
|
775
|
+
dataField.description ||
|
|
776
|
+
''
|
|
777
|
+
}
|
|
778
|
+
init={{
|
|
779
|
+
branding: false,
|
|
780
|
+
height: 500,
|
|
781
|
+
menubar: false,
|
|
782
|
+
plugins: [
|
|
783
|
+
'advlist',
|
|
784
|
+
'autolink',
|
|
785
|
+
'lists',
|
|
786
|
+
'link',
|
|
787
|
+
'image',
|
|
788
|
+
'charmap',
|
|
789
|
+
'preview',
|
|
790
|
+
'anchor',
|
|
791
|
+
'searchreplace',
|
|
792
|
+
'visualblocks',
|
|
793
|
+
'code',
|
|
794
|
+
'fullscreen',
|
|
795
|
+
'insertdatetime',
|
|
796
|
+
'media',
|
|
797
|
+
'table',
|
|
798
|
+
'code',
|
|
799
|
+
],
|
|
800
|
+
toolbar:
|
|
801
|
+
'undo redo | blocks | ' +
|
|
802
|
+
'bold italic forecolor | alignleft aligncenter ' +
|
|
803
|
+
'alignright alignjustify | bullist numlist outdent indent | ' +
|
|
804
|
+
'removeformat | table',
|
|
805
|
+
content_style:
|
|
806
|
+
'body { font-family:Helvetica,Arial,sans-serif; font-size:14px }',
|
|
807
|
+
}}
|
|
808
|
+
/>
|
|
809
|
+
<span className="fi__span prefocus">
|
|
810
|
+
Description
|
|
811
|
+
</span>
|
|
812
|
+
</label>
|
|
813
|
+
</div>
|
|
814
|
+
)}
|
|
815
|
+
|
|
816
|
+
{dataField.type === 'checkbox' ||
|
|
817
|
+
dataField.type === 'dropdown' ? (
|
|
818
|
+
<div className="formItem fwItem">
|
|
819
|
+
<table className="content-table">
|
|
820
|
+
<thead className="grid-headers">
|
|
821
|
+
<tr>
|
|
822
|
+
<th
|
|
823
|
+
style={{
|
|
824
|
+
width: '450px',
|
|
825
|
+
}}
|
|
826
|
+
>
|
|
827
|
+
Label
|
|
828
|
+
</th>
|
|
829
|
+
<th></th>
|
|
830
|
+
</tr>
|
|
831
|
+
</thead>
|
|
832
|
+
<tbody>
|
|
833
|
+
{dataField.options.length >
|
|
834
|
+
0 ? (
|
|
835
|
+
dataField.options.map(
|
|
836
|
+
(
|
|
837
|
+
option,
|
|
838
|
+
optionKey
|
|
839
|
+
) => (
|
|
840
|
+
<tr
|
|
841
|
+
key={`option-${optionKey}`}
|
|
842
|
+
>
|
|
843
|
+
<td>
|
|
844
|
+
{
|
|
845
|
+
option.label
|
|
846
|
+
}
|
|
847
|
+
</td>
|
|
848
|
+
<td
|
|
849
|
+
style={{
|
|
850
|
+
textAlign:
|
|
851
|
+
'center',
|
|
852
|
+
}}
|
|
853
|
+
>
|
|
854
|
+
<button
|
|
855
|
+
className="btn"
|
|
856
|
+
style={{
|
|
857
|
+
padding:
|
|
858
|
+
'5px 20px',
|
|
859
|
+
}}
|
|
860
|
+
onClick={
|
|
861
|
+
handleDeleteOption
|
|
862
|
+
}
|
|
863
|
+
data-counter={
|
|
864
|
+
optionKey
|
|
865
|
+
}
|
|
866
|
+
data-tooltip-id="system-tooltip"
|
|
867
|
+
data-tooltip-content={
|
|
868
|
+
'Delete Option'
|
|
869
|
+
}
|
|
870
|
+
>
|
|
871
|
+
<TrashCan
|
|
872
|
+
strokeWidth={
|
|
873
|
+
2
|
|
874
|
+
}
|
|
875
|
+
size={
|
|
876
|
+
12
|
|
877
|
+
}
|
|
878
|
+
/>
|
|
879
|
+
</button>
|
|
880
|
+
</td>
|
|
881
|
+
</tr>
|
|
882
|
+
)
|
|
883
|
+
)
|
|
884
|
+
) : (
|
|
885
|
+
<tr>
|
|
886
|
+
<td colSpan="2">
|
|
887
|
+
No{' '}
|
|
888
|
+
{dataField.type}{' '}
|
|
889
|
+
options have
|
|
890
|
+
been added
|
|
891
|
+
</td>
|
|
892
|
+
</tr>
|
|
893
|
+
)}
|
|
894
|
+
</tbody>
|
|
895
|
+
<tfoot>
|
|
896
|
+
<tr>
|
|
897
|
+
<td>
|
|
898
|
+
<input
|
|
899
|
+
type="text"
|
|
900
|
+
style={{
|
|
901
|
+
padding:
|
|
902
|
+
'5px',
|
|
903
|
+
}}
|
|
904
|
+
name="label"
|
|
905
|
+
onChange={
|
|
906
|
+
handleDataOption
|
|
907
|
+
}
|
|
908
|
+
value={
|
|
909
|
+
dataOption.label
|
|
910
|
+
}
|
|
911
|
+
/>
|
|
912
|
+
</td>
|
|
913
|
+
<td
|
|
914
|
+
style={{
|
|
915
|
+
textAlign:
|
|
916
|
+
'center',
|
|
917
|
+
}}
|
|
918
|
+
>
|
|
919
|
+
<button
|
|
920
|
+
className="btn"
|
|
921
|
+
style={{
|
|
922
|
+
padding:
|
|
923
|
+
'5px 20px',
|
|
924
|
+
}}
|
|
925
|
+
onClick={
|
|
926
|
+
handleAddOption
|
|
927
|
+
}
|
|
928
|
+
data-tooltip-id="system-tooltip"
|
|
929
|
+
data-tooltip-content={
|
|
930
|
+
'Add Option'
|
|
931
|
+
}
|
|
932
|
+
>
|
|
933
|
+
<CirclePlus
|
|
934
|
+
strokeWidth={
|
|
935
|
+
2
|
|
936
|
+
}
|
|
937
|
+
size={12}
|
|
938
|
+
/>
|
|
939
|
+
</button>
|
|
940
|
+
</td>
|
|
941
|
+
</tr>
|
|
942
|
+
</tfoot>
|
|
943
|
+
</table>
|
|
944
|
+
</div>
|
|
945
|
+
) : null}
|
|
946
|
+
<div className="formItem fwItem lastItem">
|
|
947
|
+
<button
|
|
948
|
+
className="btn modalsave"
|
|
949
|
+
onClick={handleSaveField}
|
|
950
|
+
>
|
|
951
|
+
Save
|
|
952
|
+
</button>
|
|
953
|
+
</div>
|
|
954
|
+
</form>
|
|
955
|
+
</div>
|
|
956
|
+
</div>
|
|
957
|
+
</div>
|
|
958
|
+
</div>
|
|
959
|
+
</Popup>
|
|
960
|
+
|
|
961
|
+
<Popup
|
|
962
|
+
open={modalFormShow}
|
|
963
|
+
onClose={handleCloseModalForm}
|
|
964
|
+
closeOnDocumentClick={false}
|
|
965
|
+
>
|
|
966
|
+
<div className="modalwrap top--modal">
|
|
967
|
+
<div className="modal">
|
|
968
|
+
<div className="modal__header">
|
|
969
|
+
<h1>Update Form Detail</h1>
|
|
970
|
+
<button
|
|
971
|
+
className="modal__close"
|
|
972
|
+
onClick={handleCloseModalForm}
|
|
973
|
+
>
|
|
974
|
+
<CircleX strokeWidth={1} size={24} />
|
|
975
|
+
</button>
|
|
976
|
+
</div>
|
|
977
|
+
<div className="modal__content">
|
|
978
|
+
<div className="formcontainer">
|
|
979
|
+
<form className="modalForm">
|
|
980
|
+
<div className="formItem">
|
|
981
|
+
<label className="fi__label">
|
|
982
|
+
<input
|
|
983
|
+
type="text"
|
|
984
|
+
name="label"
|
|
985
|
+
value={
|
|
986
|
+
data.label ? data.label : ''
|
|
987
|
+
}
|
|
988
|
+
onChange={handleChange}
|
|
989
|
+
/>
|
|
990
|
+
<span className="fi__span">
|
|
991
|
+
Label *
|
|
992
|
+
</span>
|
|
993
|
+
</label>
|
|
994
|
+
</div>
|
|
995
|
+
<div className="formItem fwItem lastItem">
|
|
996
|
+
<button
|
|
997
|
+
className="btn modalsave"
|
|
998
|
+
onClick={handleSubmit}
|
|
999
|
+
>
|
|
1000
|
+
Save
|
|
1001
|
+
</button>
|
|
1002
|
+
</div>
|
|
1003
|
+
</form>
|
|
1004
|
+
</div>
|
|
1005
|
+
</div>
|
|
1006
|
+
</div>
|
|
1007
|
+
</div>
|
|
1008
|
+
</Popup>
|
|
1009
|
+
</div>
|
|
1010
|
+
);
|
|
1011
|
+
}
|
|
1012
|
+
|
|
1013
|
+
export default GenericFormBuilder;
|