@visns-studio/visns-components 3.4.12 → 3.5.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.
@@ -0,0 +1,1093 @@
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 (
334
+ <label className="fi__label">
335
+ <span className="fi__spancheckbox">
336
+ {field.label}{' '}
337
+ {field.required === 'yes' ? '*' : null}
338
+ </span>
339
+ {field.options.length > 0 ? (
340
+ <div className="fi__optionscontainer">
341
+ {field.options.map((option, index) => (
342
+ <div
343
+ key={`${field.id}-checkbox-option-${index}`}
344
+ className="fi__checkboxcontainer"
345
+ >
346
+ <input
347
+ type="checkbox"
348
+ className="fi__customcheckbox"
349
+ />
350
+ <label className="fi__checkboxlabel">
351
+ {option.label}
352
+ </label>{' '}
353
+ </div>
354
+ ))}
355
+ </div>
356
+ ) : null}
357
+ </label>
358
+ );
359
+ case 'date':
360
+ return (
361
+ <label className="fi__label">
362
+ <input type="date" />
363
+ <span className="fi__span">
364
+ {field.label}{' '}
365
+ {field.required === 'yes' ? '*' : null}
366
+ </span>
367
+ </label>
368
+ );
369
+ case 'datetime':
370
+ return (
371
+ <label className="fi__label">
372
+ <input type="datetime-local" />
373
+ <span className="fi__span">
374
+ {field.label}{' '}
375
+ {field.required === 'yes' ? '*' : null}
376
+ </span>
377
+ </label>
378
+ );
379
+ case 'dropdown':
380
+ return (
381
+ <label className="fi__label">
382
+ <select>
383
+ <option>Please Select an Option</option>
384
+ </select>
385
+ <span className="fi__span">
386
+ {field.label}{' '}
387
+ {field.required === 'yes' ? '*' : null}
388
+ </span>
389
+ </label>
390
+ );
391
+ case 'dynamicdata':
392
+ return (
393
+ <label className="fi__label">
394
+ <strong>{field.label}</strong> [Dynamic Data]
395
+ </label>
396
+ );
397
+ case 'file':
398
+ return (
399
+ <label className="fi__label">
400
+ <input type="file" />
401
+ <span className="fi__span">
402
+ {field.label}{' '}
403
+ {field.required === 'yes' ? '*' : null}
404
+ </span>
405
+ </label>
406
+ );
407
+ case 'image':
408
+ return (
409
+ <label className="fi__label">
410
+ <input type="file" />
411
+ <span className="fi__span">
412
+ {field.label}{' '}
413
+ {field.required === 'yes' ? '*' : null}
414
+ </span>
415
+ </label>
416
+ );
417
+ case 'plaintextheading':
418
+ return (
419
+ <label className="fi__label">
420
+ <h2>{field.label}</h2>
421
+ </label>
422
+ );
423
+ case 'plaintext':
424
+ return (
425
+ <label className="fi__label">
426
+ {field.description ? parse(field.description) : null}
427
+ </label>
428
+ );
429
+ case 'number':
430
+ return (
431
+ <label className="fi__label">
432
+ <input type="number" />
433
+ <span className="fi__span">
434
+ {field.label}{' '}
435
+ {field.required === 'yes' ? '*' : null}
436
+ </span>
437
+ </label>
438
+ );
439
+ case 'textarea':
440
+ return (
441
+ <label className="fi__label">
442
+ <textarea rows="5"></textarea>
443
+ <span className="fi__span">
444
+ {field.label}{' '}
445
+ {field.required === 'yes' ? '*' : null}
446
+ </span>
447
+ </label>
448
+ );
449
+ case 'time':
450
+ return (
451
+ <label className="fi__label">
452
+ <input type="time" />
453
+ <span className="fi__span">
454
+ {field.label}{' '}
455
+ {field.required === 'yes' ? '*' : null}
456
+ </span>
457
+ </label>
458
+ );
459
+ case 'toggle':
460
+ return (
461
+ <div>
462
+ <Toggle />
463
+ <span className="fi__toggletxt">
464
+ {field.label}{' '}
465
+ {field.required === 'yes' ? '*' : null}
466
+ </span>
467
+ </div>
468
+ );
469
+ default:
470
+ return (
471
+ <label className="fi__label">
472
+ <input type="text" />
473
+ <span className="fi__span">
474
+ {field.label}{' '}
475
+ {field.required === 'yes' ? '*' : null}
476
+ </span>
477
+ </label>
478
+ );
479
+ }
480
+ };
481
+
482
+ const handleCloseModalForm = () => {
483
+ setModalFormShow(false);
484
+ };
485
+
486
+ const handleOpenModalForm = () => {
487
+ setModalFormShow(true);
488
+ };
489
+
490
+ const handleChange = (e) => {
491
+ if (e) {
492
+ const { name, value } = e.target;
493
+
494
+ setData((prevState) => ({
495
+ ...prevState,
496
+ [name]: value,
497
+ }));
498
+ }
499
+ };
500
+
501
+ const handleSubmit = async (e) => {
502
+ try {
503
+ if (e) {
504
+ e.preventDefault();
505
+
506
+ let _error = '';
507
+
508
+ if (data.label === '') {
509
+ _error += 'Please a label for the form.<br />';
510
+ }
511
+
512
+ if (_error === '') {
513
+ const res = await CustomFetch(
514
+ `/ajax/forms/${dataId}`,
515
+ 'PUT',
516
+ {
517
+ ...data,
518
+ }
519
+ );
520
+
521
+ if (res.data.error === '') {
522
+ toast.success(
523
+ "You have successfully updated the form's detail."
524
+ );
525
+
526
+ handleCloseModalForm();
527
+ } else {
528
+ toast.error(String(res.data.error));
529
+ }
530
+ } else {
531
+ toast.error(<div>{parse(_error)}</div>);
532
+ }
533
+ }
534
+ } catch (err) {
535
+ toast.error(`Error: ${err}`);
536
+ }
537
+ };
538
+
539
+ const fetchData = async () => {
540
+ try {
541
+ const res = await CustomFetch(
542
+ `${fetchUrl}/${routeParams[urlParam]}`,
543
+ 'GET',
544
+ {}
545
+ );
546
+
547
+ setData(res.data);
548
+ } catch (err) {
549
+ toast.error(`Error: ${err}`);
550
+ }
551
+ };
552
+
553
+ const saveOnSort = async () => {
554
+ try {
555
+ const res = await CustomFetch(
556
+ `/ajax/forms/sort/${dataId}`,
557
+ 'POST',
558
+ {
559
+ detail: data.detail,
560
+ }
561
+ );
562
+
563
+ if (res.data.error !== '') {
564
+ toast.error(String(res.data.error));
565
+ }
566
+ } catch (err) {
567
+ toast.error(`Error: ${err}`);
568
+ }
569
+ };
570
+
571
+ useEffect(() => {
572
+ if (data.detail && data.detail.length > 0) {
573
+ saveOnSort();
574
+ }
575
+ }, [data.detail]);
576
+
577
+ useEffect(() => {
578
+ fetchData();
579
+ }, []);
580
+
581
+ return (
582
+ <div>
583
+ <div className="grid">
584
+ <div className="grid__row">
585
+ <div className="grid__full crmtitle">
586
+ <h1>
587
+ <Link to="/forms">Form</Link>{' '}
588
+ <CircleChevronRight strokeWidth={2} size={18} />{' '}
589
+ {data.label}
590
+ </h1>
591
+ </div>
592
+ </div>
593
+ </div>
594
+ <div className="grid">
595
+ <div className="grid__subrow">
596
+ <div className="grid__subnav">
597
+ {data.detail && data.detail.length > 0 ? (
598
+ <SortableList
599
+ handleClick={handleEdit}
600
+ handleDelete={handleDeleteField}
601
+ items={data.detail}
602
+ onSortEnd={onSortEnd}
603
+ axis="xy"
604
+ helperClass="dragitem"
605
+ transitionDuration={250}
606
+ showImage={false}
607
+ useDragHandle
608
+ />
609
+ ) : null}
610
+ </div>
611
+ <div className="grid__subcontent">
612
+ <div className="formSplit">
613
+ <div className="gridtxt__header">
614
+ <span>Form Preview</span>
615
+ </div>
616
+ {data.detail && data.detail.length > 0 ? (
617
+ <div className="modal__content">
618
+ <div className="formcontainer">
619
+ <form className="modalForm">
620
+ {data.detail.map((a, b) => (
621
+ <div
622
+ key={`form-preview-field-${b}`}
623
+ className={renderClassName(
624
+ a
625
+ )}
626
+ >
627
+ {renderField(a)}
628
+ </div>
629
+ ))}
630
+ </form>
631
+ </div>
632
+ </div>
633
+ ) : null}
634
+ </div>
635
+ <div className="polActions">
636
+ <button className="btn" onClick={handleOpenModal}>
637
+ Add Field
638
+ </button>
639
+ <button
640
+ className="btn"
641
+ onClick={handleOpenModalForm}
642
+ >
643
+ Edit Form
644
+ </button>
645
+ </div>
646
+ </div>
647
+ </div>
648
+ </div>
649
+
650
+ <Popup
651
+ open={modalShow}
652
+ onClose={handleCloseModal}
653
+ closeOnDocumentClick={false}
654
+ >
655
+ <div className="modalwrap top--modal">
656
+ <div className="modal">
657
+ <div className="modal__header">
658
+ <h1>
659
+ {modalType.type === 'create'
660
+ ? 'Add new Field'
661
+ : 'Update existing Field'}
662
+ </h1>
663
+ <button
664
+ className="modal__close"
665
+ onClick={handleCloseModal}
666
+ >
667
+ <CircleX strokeWidth={1} size={24} />
668
+ </button>
669
+ </div>
670
+ <div className="modal__content">
671
+ <div className="formcontainer">
672
+ <form className="modalForm">
673
+ <div className="formItem">
674
+ <label className="fi__label">
675
+ <select
676
+ name="type"
677
+ value={dataField.type}
678
+ onChange={handleChangeForm}
679
+ >
680
+ <option>
681
+ Please Select an Option
682
+ </option>
683
+ <option value="checkbox">
684
+ Checkbox
685
+ </option>
686
+ <option value="date">
687
+ Date
688
+ </option>
689
+ <option value="datetime">
690
+ Date & Time
691
+ </option>
692
+ <option value="dropdown">
693
+ Dropdown
694
+ </option>
695
+ <option value="dynamicdata">
696
+ Dynamic Data
697
+ </option>
698
+ <option value="file">
699
+ File
700
+ </option>
701
+ <option value="plaintextheading">
702
+ Heading
703
+ </option>
704
+ <option value="image">
705
+ Image
706
+ </option>
707
+ <option value="number">
708
+ Number
709
+ </option>
710
+ <option value="plaintext">
711
+ Plain Text
712
+ </option>
713
+ <option value="text">
714
+ Text
715
+ </option>
716
+ <option value="textarea">
717
+ Textarea
718
+ </option>
719
+ <option value="time">
720
+ Time
721
+ </option>
722
+ <option value="toggle">
723
+ Toggle
724
+ </option>
725
+ </select>
726
+ <span className="fi__span">
727
+ Type *
728
+ </span>
729
+ </label>
730
+ </div>
731
+ <div className="formItem">
732
+ <label className="fi__label">
733
+ <input
734
+ type="text"
735
+ name="label"
736
+ value={
737
+ dataField.label
738
+ ? dataField.label
739
+ : ''
740
+ }
741
+ onChange={handleChangeForm}
742
+ />
743
+ <span className="fi__span">
744
+ Label *
745
+ </span>
746
+ </label>
747
+ </div>
748
+ {dataField.type === 'dynamicdata' && (
749
+ <div className="formItem fwItem">
750
+ <label className="fi__label">
751
+ <select
752
+ name="dynamicfield"
753
+ value={
754
+ dataField.dynamicfield
755
+ }
756
+ onChange={handleChangeForm}
757
+ >
758
+ <option value="">
759
+ Please select an option
760
+ </option>
761
+ {fields.map((category) => (
762
+ <optgroup
763
+ label={
764
+ category.label
765
+ }
766
+ key={`dynamic-field-category-${category.id}`}
767
+ >
768
+ {category.data.map(
769
+ (field) => (
770
+ <option
771
+ value={`${category.id}::${field.value}`}
772
+ key={`dynamic-field-category-${category.id}-${field.value}`}
773
+ >
774
+ {
775
+ field.label
776
+ }
777
+ </option>
778
+ )
779
+ )}
780
+ </optgroup>
781
+ ))}
782
+ </select>
783
+ <span className="fi__span">
784
+ Dynamic Field *
785
+ </span>
786
+ </label>
787
+ </div>
788
+ )}
789
+ <div className="formItem">
790
+ <label className="fi__label">
791
+ <select
792
+ name="size"
793
+ value={dataField.size}
794
+ onChange={handleChangeForm}
795
+ >
796
+ <option>
797
+ Please Select an Option
798
+ </option>
799
+ <option value="quarter">
800
+ Quarter
801
+ </option>
802
+ <option value="half">
803
+ Half
804
+ </option>
805
+ <option value="full">
806
+ Full
807
+ </option>
808
+ </select>
809
+ <span className="fi__span">
810
+ Size *
811
+ </span>
812
+ </label>
813
+ </div>
814
+ <div className="formItem">
815
+ <label className="fi__label">
816
+ <select
817
+ name="required"
818
+ value={dataField.required}
819
+ onChange={handleChangeForm}
820
+ >
821
+ <option>
822
+ Please Select an Option
823
+ </option>
824
+ <option value="no">No</option>
825
+ <option value="yes">Yes</option>
826
+ </select>
827
+ <span className="fi__span">
828
+ Required? *
829
+ </span>
830
+ </label>
831
+ </div>
832
+ {dataField.type === 'plaintext' && (
833
+ <div className="formItem fwItem">
834
+ <label className="fi__label">
835
+ <Editor
836
+ apiKey={
837
+ userProfile.settings.api
838
+ .tinymce
839
+ }
840
+ onEditorChange={(
841
+ value,
842
+ e
843
+ ) => {
844
+ handleChangeFormRichEditor(
845
+ e,
846
+ value,
847
+ 'description'
848
+ );
849
+ }}
850
+ onInit={(evt, editor) =>
851
+ (editorRef.current =
852
+ editor)
853
+ }
854
+ value={
855
+ dataField.description ||
856
+ ''
857
+ }
858
+ init={{
859
+ branding: false,
860
+ height: 500,
861
+ menubar: false,
862
+ plugins: [
863
+ 'advlist',
864
+ 'autolink',
865
+ 'lists',
866
+ 'link',
867
+ 'image',
868
+ 'charmap',
869
+ 'preview',
870
+ 'anchor',
871
+ 'searchreplace',
872
+ 'visualblocks',
873
+ 'code',
874
+ 'fullscreen',
875
+ 'insertdatetime',
876
+ 'media',
877
+ 'table',
878
+ 'code',
879
+ ],
880
+ toolbar:
881
+ 'undo redo | blocks | ' +
882
+ 'bold italic forecolor | alignleft aligncenter ' +
883
+ 'alignright alignjustify | bullist numlist outdent indent | ' +
884
+ 'removeformat | table',
885
+ content_style:
886
+ 'body { font-family:Helvetica,Arial,sans-serif; font-size:14px }',
887
+ }}
888
+ />
889
+ <span className="fi__span prefocus">
890
+ Description
891
+ </span>
892
+ </label>
893
+ </div>
894
+ )}
895
+
896
+ {dataField.type === 'checkbox' ||
897
+ dataField.type === 'dropdown' ? (
898
+ <div className="formItem fwItem">
899
+ <table className="content-table">
900
+ <thead className="grid-headers">
901
+ <tr>
902
+ <th
903
+ style={{
904
+ width: '450px',
905
+ }}
906
+ >
907
+ Label
908
+ </th>
909
+ <th></th>
910
+ </tr>
911
+ </thead>
912
+ <tbody>
913
+ {dataField.options.length >
914
+ 0 ? (
915
+ dataField.options.map(
916
+ (
917
+ option,
918
+ optionKey
919
+ ) => (
920
+ <tr
921
+ key={`option-${optionKey}`}
922
+ >
923
+ <td>
924
+ {
925
+ option.label
926
+ }
927
+ </td>
928
+ <td
929
+ style={{
930
+ textAlign:
931
+ 'center',
932
+ }}
933
+ >
934
+ <button
935
+ className="btn"
936
+ style={{
937
+ padding:
938
+ '5px 20px',
939
+ }}
940
+ onClick={
941
+ handleDeleteOption
942
+ }
943
+ data-counter={
944
+ optionKey
945
+ }
946
+ data-tooltip-id="system-tooltip"
947
+ data-tooltip-content={
948
+ 'Delete Option'
949
+ }
950
+ >
951
+ <TrashCan
952
+ strokeWidth={
953
+ 2
954
+ }
955
+ size={
956
+ 12
957
+ }
958
+ />
959
+ </button>
960
+ </td>
961
+ </tr>
962
+ )
963
+ )
964
+ ) : (
965
+ <tr>
966
+ <td colSpan="2">
967
+ No{' '}
968
+ {dataField.type}{' '}
969
+ options have
970
+ been added
971
+ </td>
972
+ </tr>
973
+ )}
974
+ </tbody>
975
+ <tfoot>
976
+ <tr>
977
+ <td>
978
+ <input
979
+ type="text"
980
+ style={{
981
+ padding:
982
+ '5px',
983
+ }}
984
+ name="label"
985
+ onChange={
986
+ handleDataOption
987
+ }
988
+ value={
989
+ dataOption.label
990
+ }
991
+ />
992
+ </td>
993
+ <td
994
+ style={{
995
+ textAlign:
996
+ 'center',
997
+ }}
998
+ >
999
+ <button
1000
+ className="btn"
1001
+ style={{
1002
+ padding:
1003
+ '5px 20px',
1004
+ }}
1005
+ onClick={
1006
+ handleAddOption
1007
+ }
1008
+ data-tooltip-id="system-tooltip"
1009
+ data-tooltip-content={
1010
+ 'Add Option'
1011
+ }
1012
+ >
1013
+ <CirclePlus
1014
+ strokeWidth={
1015
+ 2
1016
+ }
1017
+ size={12}
1018
+ />
1019
+ </button>
1020
+ </td>
1021
+ </tr>
1022
+ </tfoot>
1023
+ </table>
1024
+ </div>
1025
+ ) : null}
1026
+ <div className="formItem fwItem lastItem">
1027
+ <button
1028
+ className="btn modalsave"
1029
+ onClick={handleSaveField}
1030
+ >
1031
+ Save
1032
+ </button>
1033
+ </div>
1034
+ </form>
1035
+ </div>
1036
+ </div>
1037
+ </div>
1038
+ </div>
1039
+ </Popup>
1040
+
1041
+ <Popup
1042
+ open={modalFormShow}
1043
+ onClose={handleCloseModalForm}
1044
+ closeOnDocumentClick={false}
1045
+ >
1046
+ <div className="modalwrap top--modal">
1047
+ <div className="modal">
1048
+ <div className="modal__header">
1049
+ <h1>Update Form Detail</h1>
1050
+ <button
1051
+ className="modal__close"
1052
+ onClick={handleCloseModalForm}
1053
+ >
1054
+ <CircleX strokeWidth={1} size={24} />
1055
+ </button>
1056
+ </div>
1057
+ <div className="modal__content">
1058
+ <div className="formcontainer">
1059
+ <form className="modalForm">
1060
+ <div className="formItem">
1061
+ <label className="fi__label">
1062
+ <input
1063
+ type="text"
1064
+ name="label"
1065
+ value={
1066
+ data.label ? data.label : ''
1067
+ }
1068
+ onChange={handleChange}
1069
+ />
1070
+ <span className="fi__span">
1071
+ Label *
1072
+ </span>
1073
+ </label>
1074
+ </div>
1075
+ <div className="formItem fwItem lastItem">
1076
+ <button
1077
+ className="btn modalsave"
1078
+ onClick={handleSubmit}
1079
+ >
1080
+ Save
1081
+ </button>
1082
+ </div>
1083
+ </form>
1084
+ </div>
1085
+ </div>
1086
+ </div>
1087
+ </div>
1088
+ </Popup>
1089
+ </div>
1090
+ );
1091
+ }
1092
+
1093
+ export default GenericFormBuilder;