@visns-studio/visns-components 4.10.48 → 5.0.1-8.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/package.json +29 -29
- package/src/components/cms/Field.jsx +39 -63
- package/src/components/crm/AsyncSelect.jsx +121 -15
- package/src/components/crm/Breadcrumb.jsx +2 -18
- package/src/components/crm/DataGrid.jsx +51 -424
- package/src/components/crm/Field.jsx +185 -358
- package/src/components/crm/Form.jsx +104 -92
- package/src/components/crm/MultiSelect.jsx +66 -45
- package/src/components/crm/Navigation.jsx +9 -92
- package/src/components/crm/QuickAction.jsx +1 -0
- package/src/components/crm/auth/Login.jsx +9 -1
- package/src/components/crm/auth/Profile.jsx +4 -2
- package/src/components/crm/auth/styles/Login.module.scss +52 -82
- package/src/components/crm/auth/styles/Profile.module.scss +150 -44
- package/src/components/crm/auth/styles/Reset.module.scss +55 -37
- package/src/components/crm/auth/styles/Verify.module.scss +55 -76
- package/src/components/crm/generic/GenericAuth.jsx +12 -5
- package/src/components/crm/generic/GenericDashboard.jsx +30 -340
- package/src/components/crm/generic/GenericDetail.jsx +534 -86
- package/src/components/crm/generic/GenericDynamic.jsx +76 -94
- package/src/components/crm/generic/GenericEditableTable.jsx +407 -0
- package/src/components/crm/generic/GenericFormBuilder.jsx +19 -79
- package/src/components/crm/generic/GenericIndex.jsx +1 -2
- package/src/components/crm/generic/styles/AuditLog.module.scss +4 -37
- package/src/components/crm/generic/styles/AuditLogs.module.scss +0 -51
- package/src/components/crm/generic/styles/GenericDetail.module.scss +263 -244
- package/src/components/crm/generic/styles/GenericDynamic.module.scss +100 -198
- package/src/components/crm/generic/styles/GenericEditableTable.module.scss +178 -0
- package/src/components/crm/generic/styles/GenericFormBuilder.module.scss +63 -101
- package/src/components/crm/generic/styles/GenericIndex.module.scss +46 -66
- package/src/components/crm/generic/styles/GenericMain.module.scss +7 -7
- package/src/components/crm/generic/styles/GenericSort.module.scss +0 -36
- package/src/components/crm/generic/styles/NotificationList.module.scss +1 -32
- package/src/components/crm/styles/Autocomplete.module.scss +12 -20
- package/src/components/crm/styles/DataGrid.module.scss +28 -86
- package/src/components/crm/styles/Field.module.scss +72 -252
- package/src/components/crm/styles/Form.module.scss +141 -450
- package/src/components/crm/styles/Navigation.module.scss +356 -507
- package/src/components/crm/styles/Notification.module.scss +1 -0
- package/src/components/crm/styles/QrCode.module.scss +4 -18
- package/src/components/crm/styles/SwitchAccount.module.scss +0 -1
- package/src/components/crm/styles/TableFilter.module.scss +2 -1
- package/src/components/styles/global.css +164 -34
- package/src/index.js +0 -8
- package/src/components/crm/generic/styles/GenericDashboard.module.scss +0 -325
- package/src/components/crm/sketch/SketchField.jsx +0 -395
- package/src/components/crm/sketch/arrow.jsx +0 -108
- package/src/components/crm/sketch/circle.jsx +0 -75
- package/src/components/crm/sketch/default-tool.jsx +0 -16
- package/src/components/crm/sketch/fabrictool.jsx +0 -22
- package/src/components/crm/sketch/history.jsx +0 -144
- package/src/components/crm/sketch/json/config.json +0 -14
- package/src/components/crm/sketch/line.jsx +0 -64
- package/src/components/crm/sketch/pan.jsx +0 -48
- package/src/components/crm/sketch/pencil.jsx +0 -36
- package/src/components/crm/sketch/rectangle-label-object.jsx +0 -69
- package/src/components/crm/sketch/rectangle-label.jsx +0 -93
- package/src/components/crm/sketch/rectangle.jsx +0 -76
- package/src/components/crm/sketch/select.jsx +0 -16
- package/src/components/crm/sketch/tools.jsx +0 -11
- package/src/components/crm/sketch/utils.jsx +0 -67
|
@@ -31,8 +31,6 @@ const updateField = (fields, name, value) => {
|
|
|
31
31
|
|
|
32
32
|
const renderValue = (field, data) => {
|
|
33
33
|
switch (field.type) {
|
|
34
|
-
case 'canvas':
|
|
35
|
-
return field.value || '';
|
|
36
34
|
case 'datetime':
|
|
37
35
|
case 'date':
|
|
38
36
|
return field.value && moment(field.value).isValid()
|
|
@@ -105,7 +103,6 @@ const GenericDynamic = ({
|
|
|
105
103
|
}) => {
|
|
106
104
|
const [activeForm, setActiveForm] = useState({});
|
|
107
105
|
const [activeFormKey, setActiveFormKey] = useState(0);
|
|
108
|
-
const [canvasSaveCounter, setCanvasSaveCounter] = useState(0);
|
|
109
106
|
|
|
110
107
|
const handleChange = (e) => {
|
|
111
108
|
const {
|
|
@@ -177,35 +174,6 @@ const GenericDynamic = ({
|
|
|
177
174
|
}
|
|
178
175
|
};
|
|
179
176
|
|
|
180
|
-
const handleChangeCanvas = (fieldSetting, value, imageData) => {
|
|
181
|
-
// Check if activeForm has keys
|
|
182
|
-
if (activeForm && typeof activeForm === 'object') {
|
|
183
|
-
// Iterate over the keys in activeForm to find the matching id
|
|
184
|
-
for (const key in activeForm) {
|
|
185
|
-
if (activeForm[key].id === fieldSetting.id) {
|
|
186
|
-
// If a matching id is found, update the corresponding field
|
|
187
|
-
setActiveForm((prevState) => ({
|
|
188
|
-
...prevState,
|
|
189
|
-
[key]: {
|
|
190
|
-
...prevState[key],
|
|
191
|
-
value, // Update the value for the matching id
|
|
192
|
-
imageData,
|
|
193
|
-
},
|
|
194
|
-
}));
|
|
195
|
-
|
|
196
|
-
setCanvasSaveCounter((prevState) => prevState + 1);
|
|
197
|
-
return; // Exit after updating to prevent further iterations
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
};
|
|
202
|
-
|
|
203
|
-
useEffect(() => {
|
|
204
|
-
if (canvasSaveCounter > 0) {
|
|
205
|
-
handleSaveForm(false);
|
|
206
|
-
}
|
|
207
|
-
}, [canvasSaveCounter]);
|
|
208
|
-
|
|
209
177
|
const handleChangeCheckbox = (e) => {
|
|
210
178
|
const { checked, name, value } = e.target;
|
|
211
179
|
|
|
@@ -279,20 +247,33 @@ const GenericDynamic = ({
|
|
|
279
247
|
return prevActiveForm;
|
|
280
248
|
}
|
|
281
249
|
|
|
282
|
-
// Create a
|
|
250
|
+
// Create a new rows array with updated value properties based on input type
|
|
283
251
|
const updatedRows = prevActiveForm[formKey].rows.map((row) => {
|
|
284
252
|
if (row.id === rowId) {
|
|
285
|
-
//
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
[columnId]:
|
|
253
|
+
// Determine new value based on input type
|
|
254
|
+
let newValue;
|
|
255
|
+
if (type === 'checkbox') {
|
|
256
|
+
newValue = { ...row.value, [columnId]: checked };
|
|
257
|
+
} else if (
|
|
258
|
+
type === 'date' ||
|
|
259
|
+
type === 'time' ||
|
|
260
|
+
type === 'text'
|
|
261
|
+
) {
|
|
262
|
+
newValue = { ...row.value, [columnId]: value };
|
|
263
|
+
} else {
|
|
264
|
+
// For other input types, use the value directly
|
|
265
|
+
newValue = { ...row.value, [columnId]: value };
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
return {
|
|
269
|
+
...row,
|
|
270
|
+
value: newValue,
|
|
289
271
|
};
|
|
290
|
-
return { ...row, value: newValue }; // Return the updated row
|
|
291
272
|
}
|
|
292
|
-
return row;
|
|
273
|
+
return row;
|
|
293
274
|
});
|
|
294
275
|
|
|
295
|
-
//
|
|
276
|
+
// Return the updated activeForm state
|
|
296
277
|
return {
|
|
297
278
|
...prevActiveForm,
|
|
298
279
|
[formKey]: {
|
|
@@ -582,29 +563,27 @@ const GenericDynamic = ({
|
|
|
582
563
|
});
|
|
583
564
|
};
|
|
584
565
|
|
|
585
|
-
const handleSaveForm = async (
|
|
566
|
+
const handleSaveForm = async () => {
|
|
586
567
|
try {
|
|
587
568
|
let error = '';
|
|
588
569
|
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
];
|
|
570
|
+
const excludedTypes = [
|
|
571
|
+
'dynamicdata',
|
|
572
|
+
'plaintext',
|
|
573
|
+
'plaintextheading',
|
|
574
|
+
'section',
|
|
575
|
+
'table_radio',
|
|
576
|
+
];
|
|
597
577
|
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
}
|
|
578
|
+
Object.entries(activeForm).forEach(([key, field]) => {
|
|
579
|
+
if (
|
|
580
|
+
field.required === true &&
|
|
581
|
+
!excludedTypes.includes(field.type) &&
|
|
582
|
+
(field.value === null || field.value === '')
|
|
583
|
+
) {
|
|
584
|
+
error += `<p>${field.label} is required</p>`;
|
|
585
|
+
}
|
|
586
|
+
});
|
|
608
587
|
|
|
609
588
|
if (error === '') {
|
|
610
589
|
const res = await CustomFetch(setting.save, 'POST', {
|
|
@@ -629,34 +608,47 @@ const GenericDynamic = ({
|
|
|
629
608
|
};
|
|
630
609
|
|
|
631
610
|
useEffect(() => {
|
|
632
|
-
if (
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
611
|
+
if (data[type] && data[type].length > 0) {
|
|
612
|
+
if (multiple) {
|
|
613
|
+
if (templateStatus) {
|
|
614
|
+
let lastForm = data[type][data[type].length - 1];
|
|
615
|
+
|
|
616
|
+
if (lastForm && typeof lastForm === 'object') {
|
|
617
|
+
Object.entries(lastForm).forEach(([key, field]) => {
|
|
618
|
+
if (field && field.required) {
|
|
619
|
+
if (
|
|
620
|
+
field.required === 'yes' ||
|
|
621
|
+
field.required === 'no'
|
|
622
|
+
) {
|
|
623
|
+
lastForm[key].required =
|
|
624
|
+
field.required === 'yes';
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
});
|
|
636
628
|
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
? data[type][0] || data[type]
|
|
641
|
-
: data[type];
|
|
642
|
-
|
|
643
|
-
if (
|
|
644
|
-
multiple &&
|
|
645
|
-
templateStatus &&
|
|
646
|
-
formData &&
|
|
647
|
-
typeof formData === 'object'
|
|
648
|
-
) {
|
|
649
|
-
// Process the last form for required fields
|
|
650
|
-
Object.entries(formData).forEach(([key, field]) => {
|
|
651
|
-
if (field?.required) {
|
|
652
|
-
field.required = field.required === 'yes';
|
|
629
|
+
setActiveForm(lastForm);
|
|
630
|
+
setActiveFormKey(data[type].length - 1);
|
|
631
|
+
}
|
|
653
632
|
}
|
|
654
|
-
}
|
|
633
|
+
} else {
|
|
634
|
+
if (data[type].hasOwnProperty('id')) {
|
|
635
|
+
if (
|
|
636
|
+
Object.keys(activeForm).length === 0 &&
|
|
637
|
+
templateStatus &&
|
|
638
|
+
data[type][0]
|
|
639
|
+
) {
|
|
640
|
+
setActiveForm(data[type][0]);
|
|
641
|
+
setActiveFormKey(0);
|
|
642
|
+
}
|
|
643
|
+
} else {
|
|
644
|
+
setActiveForm(data[type]);
|
|
645
|
+
setActiveFormKey(0);
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
} else {
|
|
649
|
+
setActiveForm({});
|
|
655
650
|
}
|
|
656
|
-
|
|
657
|
-
setActiveForm(formData);
|
|
658
|
-
setActiveFormKey(0);
|
|
659
|
-
}, [data, type, multiple, templateStatus]);
|
|
651
|
+
}, [data, type]);
|
|
660
652
|
|
|
661
653
|
useEffect(() => {
|
|
662
654
|
if (data[type] && data[type].length > 0) {
|
|
@@ -824,7 +816,6 @@ const GenericDynamic = ({
|
|
|
824
816
|
? Object.entries(activeForm)
|
|
825
817
|
.filter(
|
|
826
818
|
([key, value]) =>
|
|
827
|
-
value && // Add this check to ensure value is not null
|
|
828
819
|
typeof value === 'object' &&
|
|
829
820
|
[
|
|
830
821
|
'id',
|
|
@@ -851,9 +842,6 @@ const GenericDynamic = ({
|
|
|
851
842
|
onChange={
|
|
852
843
|
handleChange
|
|
853
844
|
}
|
|
854
|
-
onChangeCanvas={
|
|
855
|
-
handleChangeCanvas
|
|
856
|
-
}
|
|
857
845
|
onChangeCheckbox={
|
|
858
846
|
handleChangeCheckbox
|
|
859
847
|
}
|
|
@@ -902,18 +890,12 @@ const GenericDynamic = ({
|
|
|
902
890
|
originalData
|
|
903
891
|
)}
|
|
904
892
|
onChange={handleChange}
|
|
905
|
-
onChangeCanvas={
|
|
906
|
-
handleChangeCanvas
|
|
907
|
-
}
|
|
908
893
|
onChangeCheckbox={
|
|
909
894
|
handleChangeCheckbox
|
|
910
895
|
}
|
|
911
896
|
onChangeDate={
|
|
912
897
|
handleChangeDate
|
|
913
898
|
}
|
|
914
|
-
onChangeNumberFormat={
|
|
915
|
-
handleChangeNumberFormat
|
|
916
|
-
}
|
|
917
899
|
onChangeTableRadio={
|
|
918
900
|
handleChangeTableRadio
|
|
919
901
|
}
|
|
@@ -0,0 +1,407 @@
|
|
|
1
|
+
import React, { useEffect, useMemo, useState } from 'react';
|
|
2
|
+
import debounce from 'lodash.debounce';
|
|
3
|
+
import { toast } from 'react-toastify';
|
|
4
|
+
import Popup from 'reactjs-popup';
|
|
5
|
+
import { confirmAlert } from 'react-confirm-alert';
|
|
6
|
+
import { TrashCan, ArrowUp, ArrowDown } from 'akar-icons';
|
|
7
|
+
|
|
8
|
+
import CustomFetch from '../Fetch';
|
|
9
|
+
import Form from '../Form';
|
|
10
|
+
import MultiSelect from '../MultiSelect';
|
|
11
|
+
|
|
12
|
+
import 'react-confirm-alert/src/react-confirm-alert.css';
|
|
13
|
+
import styles from './styles/GenericEditableTable.module.scss';
|
|
14
|
+
|
|
15
|
+
const GenericEditableTable = ({
|
|
16
|
+
schedulingConfig,
|
|
17
|
+
data,
|
|
18
|
+
dataId,
|
|
19
|
+
modalOpen,
|
|
20
|
+
preloadedOptions,
|
|
21
|
+
}) => {
|
|
22
|
+
const { columns, rows, form } = schedulingConfig;
|
|
23
|
+
|
|
24
|
+
const [localData, setLocalData] = useState([]);
|
|
25
|
+
const [groupedCategories, setGroupedCategories] = useState([]);
|
|
26
|
+
|
|
27
|
+
useEffect(() => {
|
|
28
|
+
const enrichedData = data[rows.key]?.map((entry, index) => ({
|
|
29
|
+
...entry,
|
|
30
|
+
sort_order: entry.sort_order ?? index + 1,
|
|
31
|
+
}));
|
|
32
|
+
setLocalData(enrichedData || []);
|
|
33
|
+
}, [data, rows.key]);
|
|
34
|
+
|
|
35
|
+
const debouncedUpdate = useMemo(
|
|
36
|
+
() =>
|
|
37
|
+
debounce(async (updatedData) => {
|
|
38
|
+
try {
|
|
39
|
+
const res = await CustomFetch(
|
|
40
|
+
`${form.url}/${dataId}`,
|
|
41
|
+
'PUT',
|
|
42
|
+
updatedData
|
|
43
|
+
);
|
|
44
|
+
if (!res.error) {
|
|
45
|
+
toast.success('Template updated successfully');
|
|
46
|
+
} else {
|
|
47
|
+
toast.error(res.error);
|
|
48
|
+
}
|
|
49
|
+
} catch (error) {
|
|
50
|
+
console.error('Error updating field:', error);
|
|
51
|
+
}
|
|
52
|
+
}, 1000),
|
|
53
|
+
[]
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
const groupCategoriesByType = (data) => {
|
|
57
|
+
const grouped = {};
|
|
58
|
+
|
|
59
|
+
data.forEach((category, categoryKey) => {
|
|
60
|
+
const categoryType =
|
|
61
|
+
category[rows.categoryKey.id]?.[rows.categoryKey.label] || null;
|
|
62
|
+
|
|
63
|
+
const categoryId = category[rows.categoryKey.id]?.id || null;
|
|
64
|
+
|
|
65
|
+
const groupKey = categoryId || 'no_category';
|
|
66
|
+
|
|
67
|
+
if (!grouped[groupKey]) {
|
|
68
|
+
grouped[groupKey] = {
|
|
69
|
+
id: categoryId || 'no_category',
|
|
70
|
+
category: categoryType || '',
|
|
71
|
+
entries: [],
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
grouped[groupKey].entries.push({
|
|
75
|
+
...category,
|
|
76
|
+
keyCounter: categoryKey,
|
|
77
|
+
});
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
Object.values(grouped).forEach((group) => {
|
|
81
|
+
group.entries.sort((a, b) => a.sort_order - b.sort_order);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
// Ensure 'no_category' group is at the top
|
|
85
|
+
const groupedArray = Object.values(grouped);
|
|
86
|
+
return groupedArray.sort((a, b) =>
|
|
87
|
+
a.id === 'no_category' ? -1 : b.id === 'no_category' ? 1 : 0
|
|
88
|
+
);
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
const handleFieldChange = (value, fieldId, keyCounter) => {
|
|
92
|
+
setLocalData((prev) => {
|
|
93
|
+
const updatedDetail = [...prev];
|
|
94
|
+
updatedDetail[keyCounter] = {
|
|
95
|
+
...updatedDetail[keyCounter],
|
|
96
|
+
[fieldId]: value,
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
debouncedUpdate({ [rows.key]: updatedDetail });
|
|
100
|
+
|
|
101
|
+
return updatedDetail;
|
|
102
|
+
});
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
const handleSortChange = (groupId, entryId, direction) => {
|
|
106
|
+
setLocalData((prev) => {
|
|
107
|
+
const updatedDetail = [...prev];
|
|
108
|
+
const group = groupCategoriesByType(updatedDetail).find(
|
|
109
|
+
(group) => group.id === groupId
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
if (group) {
|
|
113
|
+
const index = group.entries.findIndex(
|
|
114
|
+
(entry) => entry.id === entryId
|
|
115
|
+
);
|
|
116
|
+
|
|
117
|
+
if (
|
|
118
|
+
(direction === 'up' && index > 0) ||
|
|
119
|
+
(direction === 'down' && index < group.entries.length - 1)
|
|
120
|
+
) {
|
|
121
|
+
const swapIndex =
|
|
122
|
+
direction === 'up' ? index - 1 : index + 1;
|
|
123
|
+
[
|
|
124
|
+
group.entries[index].sort_order,
|
|
125
|
+
group.entries[swapIndex].sort_order,
|
|
126
|
+
] = [
|
|
127
|
+
group.entries[swapIndex].sort_order,
|
|
128
|
+
group.entries[index].sort_order,
|
|
129
|
+
];
|
|
130
|
+
|
|
131
|
+
const reordered = updatedDetail.map((item) => {
|
|
132
|
+
const entry = group.entries.find(
|
|
133
|
+
(e) => e.id === item.id
|
|
134
|
+
);
|
|
135
|
+
return entry
|
|
136
|
+
? { ...item, sort_order: entry.sort_order }
|
|
137
|
+
: item;
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
debouncedUpdate({ [rows.key]: reordered });
|
|
141
|
+
return reordered;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return updatedDetail;
|
|
146
|
+
});
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
const handleDeleteRow = (entry) => {
|
|
150
|
+
confirmAlert({
|
|
151
|
+
title: 'Confirm to Delete',
|
|
152
|
+
message: 'Are you sure you want to delete this entry?',
|
|
153
|
+
buttons: [
|
|
154
|
+
{
|
|
155
|
+
label: 'Yes',
|
|
156
|
+
onClick: async () => {
|
|
157
|
+
setLocalData((prev) => {
|
|
158
|
+
const updatedDetail = prev.filter(
|
|
159
|
+
(item) => item.id !== entry.id
|
|
160
|
+
);
|
|
161
|
+
debouncedUpdate({ [rows.key]: updatedDetail });
|
|
162
|
+
return updatedDetail;
|
|
163
|
+
});
|
|
164
|
+
},
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
label: 'No',
|
|
168
|
+
},
|
|
169
|
+
],
|
|
170
|
+
});
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
const renderScheduledTableField = (entry, column, keyCounter) => {
|
|
174
|
+
if (!entry || !column) return null;
|
|
175
|
+
|
|
176
|
+
switch (column.type) {
|
|
177
|
+
case 'currency':
|
|
178
|
+
case 'number':
|
|
179
|
+
return (
|
|
180
|
+
<input
|
|
181
|
+
type="text"
|
|
182
|
+
value={entry[column.id] || ''}
|
|
183
|
+
onChange={(e) =>
|
|
184
|
+
handleFieldChange(
|
|
185
|
+
e.target.value,
|
|
186
|
+
column.id,
|
|
187
|
+
keyCounter
|
|
188
|
+
)
|
|
189
|
+
}
|
|
190
|
+
style={{ textAlign: 'right' }}
|
|
191
|
+
/>
|
|
192
|
+
);
|
|
193
|
+
case 'date':
|
|
194
|
+
return (
|
|
195
|
+
<input
|
|
196
|
+
type="date"
|
|
197
|
+
value={entry[column.id] || ''}
|
|
198
|
+
onChange={(e) =>
|
|
199
|
+
handleFieldChange(
|
|
200
|
+
e.target.value,
|
|
201
|
+
column.id,
|
|
202
|
+
keyCounter
|
|
203
|
+
)
|
|
204
|
+
}
|
|
205
|
+
style={{ textAlign: 'right' }}
|
|
206
|
+
/>
|
|
207
|
+
);
|
|
208
|
+
case 'dropdown':
|
|
209
|
+
return (
|
|
210
|
+
<select
|
|
211
|
+
value={entry[column.id] || ''}
|
|
212
|
+
onChange={(e) =>
|
|
213
|
+
handleFieldChange(
|
|
214
|
+
e.target.value,
|
|
215
|
+
column.id,
|
|
216
|
+
keyCounter
|
|
217
|
+
)
|
|
218
|
+
}
|
|
219
|
+
>
|
|
220
|
+
{column.options.map((option) => (
|
|
221
|
+
<option key={option.id} value={option.id}>
|
|
222
|
+
{option.label}
|
|
223
|
+
</option>
|
|
224
|
+
))}
|
|
225
|
+
</select>
|
|
226
|
+
);
|
|
227
|
+
case 'dropdown-ajax':
|
|
228
|
+
const options = preloadedOptions[column.id] || [];
|
|
229
|
+
return (
|
|
230
|
+
<select
|
|
231
|
+
value={entry[column.id] || ''}
|
|
232
|
+
onChange={(e) =>
|
|
233
|
+
handleFieldChange(
|
|
234
|
+
e.target.value,
|
|
235
|
+
column.id,
|
|
236
|
+
keyCounter
|
|
237
|
+
)
|
|
238
|
+
}
|
|
239
|
+
>
|
|
240
|
+
<option>Select an Option</option>
|
|
241
|
+
{options.map((option) => (
|
|
242
|
+
<option key={option.id} value={option.id}>
|
|
243
|
+
{option.label}
|
|
244
|
+
</option>
|
|
245
|
+
))}
|
|
246
|
+
</select>
|
|
247
|
+
);
|
|
248
|
+
case 'multi-dropdown-ajax':
|
|
249
|
+
const multiOptions = preloadedOptions[column.id] || [];
|
|
250
|
+
return (
|
|
251
|
+
<MultiSelect
|
|
252
|
+
settings={{
|
|
253
|
+
id: column.id,
|
|
254
|
+
}}
|
|
255
|
+
className={styles.selectFullWidth}
|
|
256
|
+
multi={false}
|
|
257
|
+
onChange={(value) =>
|
|
258
|
+
handleFieldChange(value, column.id, keyCounter)
|
|
259
|
+
}
|
|
260
|
+
inputValue={entry[column.id] || ''}
|
|
261
|
+
options={multiOptions}
|
|
262
|
+
/>
|
|
263
|
+
);
|
|
264
|
+
default:
|
|
265
|
+
return (
|
|
266
|
+
<input
|
|
267
|
+
type="text"
|
|
268
|
+
defaultValue={entry[column.id] || ''}
|
|
269
|
+
onChange={(e) =>
|
|
270
|
+
handleFieldChange(
|
|
271
|
+
e.target.value,
|
|
272
|
+
column.id,
|
|
273
|
+
keyCounter
|
|
274
|
+
)
|
|
275
|
+
}
|
|
276
|
+
/>
|
|
277
|
+
);
|
|
278
|
+
}
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
useEffect(() => {
|
|
282
|
+
setGroupedCategories(groupCategoriesByType(localData));
|
|
283
|
+
}, [localData]);
|
|
284
|
+
|
|
285
|
+
return (
|
|
286
|
+
<div className={styles.gridtxt}>
|
|
287
|
+
<div className={styles.gridtxt__header}>
|
|
288
|
+
<span>{schedulingConfig.title || 'Estimation Schedule'}</span>
|
|
289
|
+
</div>
|
|
290
|
+
{groupedCategories.length > 0 ? (
|
|
291
|
+
<table className={styles.schedulingTable}>
|
|
292
|
+
<thead>
|
|
293
|
+
<tr>
|
|
294
|
+
{columns.map((column) => (
|
|
295
|
+
<th
|
|
296
|
+
key={column.id}
|
|
297
|
+
style={{ width: column.width || 'auto' }}
|
|
298
|
+
>
|
|
299
|
+
{column.label}
|
|
300
|
+
</th>
|
|
301
|
+
))}
|
|
302
|
+
<th style={{ width: '5%' }}></th>
|
|
303
|
+
</tr>
|
|
304
|
+
</thead>
|
|
305
|
+
<tbody>
|
|
306
|
+
{groupedCategories.map((group) => (
|
|
307
|
+
<React.Fragment key={group.id}>
|
|
308
|
+
<tr className={styles.categoryRow}>
|
|
309
|
+
<td colSpan={columns.length + 1}>
|
|
310
|
+
{group.category}
|
|
311
|
+
</td>
|
|
312
|
+
</tr>
|
|
313
|
+
{group.entries.map((entry, idx) => (
|
|
314
|
+
<tr key={entry.id}>
|
|
315
|
+
{columns.map((column) => (
|
|
316
|
+
<td
|
|
317
|
+
key={column.id}
|
|
318
|
+
style={{
|
|
319
|
+
width:
|
|
320
|
+
column.width || 'auto',
|
|
321
|
+
textAlign:
|
|
322
|
+
column.type ===
|
|
323
|
+
'currency'
|
|
324
|
+
? 'right'
|
|
325
|
+
: 'left',
|
|
326
|
+
}}
|
|
327
|
+
>
|
|
328
|
+
{renderScheduledTableField(
|
|
329
|
+
entry,
|
|
330
|
+
column,
|
|
331
|
+
entry.keyCounter
|
|
332
|
+
)}
|
|
333
|
+
</td>
|
|
334
|
+
))}
|
|
335
|
+
<td>
|
|
336
|
+
<div className={styles.tdactions}>
|
|
337
|
+
{idx > 0 && (
|
|
338
|
+
<ArrowUp
|
|
339
|
+
size={18}
|
|
340
|
+
onClick={() =>
|
|
341
|
+
handleSortChange(
|
|
342
|
+
group.id,
|
|
343
|
+
entry.id,
|
|
344
|
+
'up'
|
|
345
|
+
)
|
|
346
|
+
}
|
|
347
|
+
style={{
|
|
348
|
+
cursor: 'pointer',
|
|
349
|
+
}}
|
|
350
|
+
/>
|
|
351
|
+
)}
|
|
352
|
+
{idx <
|
|
353
|
+
group.entries.length -
|
|
354
|
+
1 && (
|
|
355
|
+
<ArrowDown
|
|
356
|
+
size={18}
|
|
357
|
+
onClick={() =>
|
|
358
|
+
handleSortChange(
|
|
359
|
+
group.id,
|
|
360
|
+
entry.id,
|
|
361
|
+
'down'
|
|
362
|
+
)
|
|
363
|
+
}
|
|
364
|
+
style={{
|
|
365
|
+
cursor: 'pointer',
|
|
366
|
+
}}
|
|
367
|
+
/>
|
|
368
|
+
)}
|
|
369
|
+
<TrashCan
|
|
370
|
+
size={18}
|
|
371
|
+
onClick={() =>
|
|
372
|
+
handleDeleteRow(entry)
|
|
373
|
+
}
|
|
374
|
+
style={{
|
|
375
|
+
cursor: 'pointer',
|
|
376
|
+
}}
|
|
377
|
+
/>
|
|
378
|
+
</div>
|
|
379
|
+
</td>
|
|
380
|
+
</tr>
|
|
381
|
+
))}
|
|
382
|
+
</React.Fragment>
|
|
383
|
+
))}
|
|
384
|
+
</tbody>
|
|
385
|
+
</table>
|
|
386
|
+
) : (
|
|
387
|
+
<div className={styles.noDataMessage}>
|
|
388
|
+
No data available. Please check your filters or try
|
|
389
|
+
reloading the data.
|
|
390
|
+
</div>
|
|
391
|
+
)}
|
|
392
|
+
|
|
393
|
+
<div className={styles.polActions}>
|
|
394
|
+
<button
|
|
395
|
+
className={styles.btn}
|
|
396
|
+
onClick={() => {
|
|
397
|
+
modalOpen('create', 0);
|
|
398
|
+
}}
|
|
399
|
+
>
|
|
400
|
+
Add
|
|
401
|
+
</button>
|
|
402
|
+
</div>
|
|
403
|
+
</div>
|
|
404
|
+
);
|
|
405
|
+
};
|
|
406
|
+
|
|
407
|
+
export default GenericEditableTable;
|