@visns-studio/visns-components 5.12.11 → 5.12.13
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.
|
@@ -10,6 +10,64 @@ import CustomFetch from '../Fetch';
|
|
|
10
10
|
import MultiSelect from '../MultiSelect';
|
|
11
11
|
import styles from '../styles/GenericEditableTable.module.scss';
|
|
12
12
|
|
|
13
|
+
// Simple Tooltip Component
|
|
14
|
+
const Tooltip = ({ children, text, position = 'top' }) => {
|
|
15
|
+
const [isVisible, setIsVisible] = useState(false);
|
|
16
|
+
const [coords, setCoords] = useState({ x: 0, y: 0 });
|
|
17
|
+
const elementRef = useRef(null);
|
|
18
|
+
|
|
19
|
+
const handleMouseEnter = () => {
|
|
20
|
+
if (!elementRef.current) return;
|
|
21
|
+
const rect = elementRef.current.getBoundingClientRect();
|
|
22
|
+
const scrollLeft =
|
|
23
|
+
window.pageXOffset || document.documentElement.scrollLeft;
|
|
24
|
+
const scrollTop =
|
|
25
|
+
window.pageYOffset || document.documentElement.scrollTop;
|
|
26
|
+
|
|
27
|
+
let x = rect.left + scrollLeft + rect.width / 2;
|
|
28
|
+
let y = rect.top + scrollTop;
|
|
29
|
+
|
|
30
|
+
if (position === 'bottom') {
|
|
31
|
+
y = rect.bottom + scrollTop + 5;
|
|
32
|
+
} else if (position === 'top') {
|
|
33
|
+
y = y - 5;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
setCoords({ x, y });
|
|
37
|
+
setIsVisible(true);
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const handleMouseLeave = () => {
|
|
41
|
+
setIsVisible(false);
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
return (
|
|
45
|
+
<span
|
|
46
|
+
ref={elementRef}
|
|
47
|
+
onMouseEnter={handleMouseEnter}
|
|
48
|
+
onMouseLeave={handleMouseLeave}
|
|
49
|
+
className={styles.tooltipContainer}
|
|
50
|
+
>
|
|
51
|
+
{children}
|
|
52
|
+
{isVisible &&
|
|
53
|
+
ReactDOM.createPortal(
|
|
54
|
+
<div
|
|
55
|
+
className={`${styles.tooltip} ${
|
|
56
|
+
styles[`tooltip-${position}`]
|
|
57
|
+
}`}
|
|
58
|
+
style={{
|
|
59
|
+
left: coords.x,
|
|
60
|
+
top: coords.y,
|
|
61
|
+
}}
|
|
62
|
+
>
|
|
63
|
+
{text}
|
|
64
|
+
</div>,
|
|
65
|
+
document.body
|
|
66
|
+
)}
|
|
67
|
+
</span>
|
|
68
|
+
);
|
|
69
|
+
};
|
|
70
|
+
|
|
13
71
|
// Updated ColourPicker with an expanded pastel palette
|
|
14
72
|
const ColourPicker = ({ value, columnId, keyCounter, onChange }) => {
|
|
15
73
|
const [colorPickerShow, setColorPickerShow] = useState(false);
|
|
@@ -59,35 +117,42 @@ const ColourPicker = ({ value, columnId, keyCounter, onChange }) => {
|
|
|
59
117
|
<div className={styles.cpicker__btn}>
|
|
60
118
|
<button
|
|
61
119
|
ref={buttonRef}
|
|
62
|
-
style={
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
120
|
+
style={
|
|
121
|
+
value
|
|
122
|
+
? {
|
|
123
|
+
background: value,
|
|
124
|
+
border: '1px solid rgba(0,0,0,0.2)',
|
|
125
|
+
borderRadius: '3px',
|
|
126
|
+
width: '20px',
|
|
127
|
+
height: '20px',
|
|
128
|
+
padding: '0',
|
|
129
|
+
cursor: 'pointer',
|
|
130
|
+
display: 'flex',
|
|
131
|
+
alignItems: 'center',
|
|
132
|
+
justifyContent: 'center',
|
|
133
|
+
}
|
|
134
|
+
: {
|
|
135
|
+
background: 'transparent',
|
|
136
|
+
border: '1px solid rgba(0,0,0,0.2)',
|
|
137
|
+
borderRadius: '3px',
|
|
138
|
+
width: '20px',
|
|
139
|
+
height: '20px',
|
|
140
|
+
padding: '0',
|
|
141
|
+
cursor: 'pointer',
|
|
142
|
+
display: 'flex',
|
|
143
|
+
alignItems: 'center',
|
|
144
|
+
justifyContent: 'center',
|
|
145
|
+
}
|
|
146
|
+
}
|
|
85
147
|
onClick={(e) => {
|
|
86
148
|
e.preventDefault();
|
|
87
149
|
setColorPickerShow(!colorPickerShow);
|
|
88
150
|
}}
|
|
89
151
|
>
|
|
90
|
-
<Droplets
|
|
152
|
+
<Droplets
|
|
153
|
+
size={18}
|
|
154
|
+
style={{ color: value ? 'white' : 'black' }}
|
|
155
|
+
/>
|
|
91
156
|
</button>
|
|
92
157
|
|
|
93
158
|
{colorPickerShow &&
|
|
@@ -146,6 +211,7 @@ const GenericEditableTable = ({
|
|
|
146
211
|
const isInitialLoad = useRef(true);
|
|
147
212
|
const { columns, rows, form, title } = schedulingConfig;
|
|
148
213
|
const hasCategory = rows && rows.categoryKey; // Only group if defined
|
|
214
|
+
const hasColourColumn = columns.some((col) => col.id === 'colours');
|
|
149
215
|
|
|
150
216
|
// dataSets: each item => { created_at: string, data: array of rows }
|
|
151
217
|
const [dataSets, setDataSets] = useState([]);
|
|
@@ -157,6 +223,9 @@ const GenericEditableTable = ({
|
|
|
157
223
|
const [newEntries, setNewEntries] = useState({});
|
|
158
224
|
const [newEntry, setNewEntry] = useState({});
|
|
159
225
|
|
|
226
|
+
// Row selection state for copy functionality
|
|
227
|
+
const [selectedRows, setSelectedRows] = useState(new Set());
|
|
228
|
+
|
|
160
229
|
// On mount or when 'data' changes, read from data[schedulingConfig.rows.key] and handle both new & old formats
|
|
161
230
|
useEffect(() => {
|
|
162
231
|
const raw = data && data[schedulingConfig.rows.key];
|
|
@@ -219,15 +288,19 @@ const GenericEditableTable = ({
|
|
|
219
288
|
} else {
|
|
220
289
|
setLocalData([]);
|
|
221
290
|
}
|
|
291
|
+
// Clear selected rows when dataset changes
|
|
292
|
+
setSelectedRows(new Set());
|
|
222
293
|
}, [dataSets, activeDatasetIndex]);
|
|
223
294
|
|
|
224
295
|
// Helper to sort by sort_order
|
|
225
296
|
const sortBySortOrder = (dataArray) =>
|
|
226
297
|
[...dataArray].sort((a, b) => a.sort_order - b.sort_order);
|
|
227
298
|
|
|
228
|
-
// Return background color from row_colour field
|
|
299
|
+
// Return background color from row_colour field or colours column
|
|
229
300
|
const getRowBackground = (entry) => {
|
|
230
|
-
|
|
301
|
+
// Check if there's a colour picked from the colours column
|
|
302
|
+
const coloursValue = entry.colours || entry.row_colour;
|
|
303
|
+
return coloursValue ? `${coloursValue}40` : undefined; // Add 40% opacity
|
|
231
304
|
};
|
|
232
305
|
|
|
233
306
|
// Calculate total for columns of type 'total'
|
|
@@ -501,11 +574,50 @@ const GenericEditableTable = ({
|
|
|
501
574
|
return hasCategory ? groupCategoriesByType(localData) : [];
|
|
502
575
|
}, [localData, hasCategory]);
|
|
503
576
|
|
|
504
|
-
// 5)
|
|
577
|
+
// 5) Row selection handlers
|
|
578
|
+
const handleRowSelection = (rowId, isChecked) => {
|
|
579
|
+
setSelectedRows((prev) => {
|
|
580
|
+
const newSelected = new Set(prev);
|
|
581
|
+
if (isChecked) {
|
|
582
|
+
newSelected.add(rowId);
|
|
583
|
+
} else {
|
|
584
|
+
newSelected.delete(rowId);
|
|
585
|
+
}
|
|
586
|
+
return newSelected;
|
|
587
|
+
});
|
|
588
|
+
};
|
|
589
|
+
|
|
590
|
+
const handleSelectAll = (isChecked, dataToSelect = null) => {
|
|
591
|
+
if (isChecked) {
|
|
592
|
+
const rowIds = dataToSelect
|
|
593
|
+
? dataToSelect.map((entry) => entry.id)
|
|
594
|
+
: localData.map((entry) => entry.id);
|
|
595
|
+
setSelectedRows(new Set(rowIds));
|
|
596
|
+
} else {
|
|
597
|
+
setSelectedRows(new Set());
|
|
598
|
+
}
|
|
599
|
+
};
|
|
600
|
+
|
|
601
|
+
const isAllSelected = (dataToCheck = null) => {
|
|
602
|
+
const dataSet = dataToCheck || localData;
|
|
603
|
+
return (
|
|
604
|
+
dataSet.length > 0 &&
|
|
605
|
+
dataSet.every((entry) => selectedRows.has(entry.id))
|
|
606
|
+
);
|
|
607
|
+
};
|
|
608
|
+
|
|
609
|
+
// 6) Enhanced column copy with row selection
|
|
505
610
|
const handleColumnCopy = (column, group = null) => {
|
|
506
|
-
let
|
|
507
|
-
|
|
508
|
-
|
|
611
|
+
let dataToProcess = group ? group.entries : localData;
|
|
612
|
+
|
|
613
|
+
// Filter by selected rows if any are selected
|
|
614
|
+
if (selectedRows.size > 0) {
|
|
615
|
+
dataToProcess = dataToProcess.filter((entry) =>
|
|
616
|
+
selectedRows.has(entry.id)
|
|
617
|
+
);
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
let values = dataToProcess.map((entry) => entry[column.id]);
|
|
509
621
|
|
|
510
622
|
const textToCopy = values
|
|
511
623
|
.filter((val) => val !== undefined && val !== null)
|
|
@@ -513,19 +625,23 @@ const GenericEditableTable = ({
|
|
|
513
625
|
|
|
514
626
|
navigator.clipboard
|
|
515
627
|
.writeText(textToCopy)
|
|
516
|
-
.then(() =>
|
|
628
|
+
.then(() => {
|
|
629
|
+
const selectionInfo =
|
|
630
|
+
selectedRows.size > 0
|
|
631
|
+
? ` (${selectedRows.size} selected rows)`
|
|
632
|
+
: ' (all rows)';
|
|
517
633
|
toast.success(
|
|
518
634
|
`Copied ${values.length} value${
|
|
519
635
|
values.length !== 1 ? 's' : ''
|
|
520
636
|
} from "${column.label}"${
|
|
521
637
|
group ? ` (Group: ${group.category})` : ''
|
|
522
|
-
}`
|
|
523
|
-
)
|
|
524
|
-
)
|
|
638
|
+
}${selectionInfo}`
|
|
639
|
+
);
|
|
640
|
+
})
|
|
525
641
|
.catch(() => toast.error('Failed to copy to clipboard'));
|
|
526
642
|
};
|
|
527
643
|
|
|
528
|
-
//
|
|
644
|
+
// 7) Sorting: grouped
|
|
529
645
|
const handleSortChangeGrouped = (groupId, entryId, direction) => {
|
|
530
646
|
if (!hasCategory) return;
|
|
531
647
|
setLocalData((prev) => {
|
|
@@ -577,7 +693,7 @@ const GenericEditableTable = ({
|
|
|
577
693
|
});
|
|
578
694
|
};
|
|
579
695
|
|
|
580
|
-
//
|
|
696
|
+
// 8) Sorting: ungrouped
|
|
581
697
|
const handleSortChangeUngrouped = (entryId, direction) => {
|
|
582
698
|
setLocalData((prev) => {
|
|
583
699
|
const sortedData = sortBySortOrder(prev);
|
|
@@ -608,7 +724,7 @@ const GenericEditableTable = ({
|
|
|
608
724
|
});
|
|
609
725
|
};
|
|
610
726
|
|
|
611
|
-
//
|
|
727
|
+
// 9) Deletion
|
|
612
728
|
const handleDeleteRow = (entry) => {
|
|
613
729
|
confirmDialog({
|
|
614
730
|
title: 'Confirm to Delete',
|
|
@@ -642,9 +758,25 @@ const GenericEditableTable = ({
|
|
|
642
758
|
const renderScheduledTableField = (entry, column, keyCounter) => {
|
|
643
759
|
if (!entry || !column) return null;
|
|
644
760
|
|
|
761
|
+
// Special handling for colours column regardless of type
|
|
762
|
+
if (column.id === 'colours') {
|
|
763
|
+
return (
|
|
764
|
+
<ColourPicker
|
|
765
|
+
value={entry[column.id] || ''}
|
|
766
|
+
columnId={column.id}
|
|
767
|
+
keyCounter={keyCounter}
|
|
768
|
+
onChange={(value, fieldId, keyCounter) => {
|
|
769
|
+
// Update both the colours field and row_colour for row highlighting
|
|
770
|
+
handleFieldChange(value, fieldId, keyCounter);
|
|
771
|
+
handleFieldChange(value, 'row_colour', keyCounter);
|
|
772
|
+
}}
|
|
773
|
+
/>
|
|
774
|
+
);
|
|
775
|
+
}
|
|
776
|
+
|
|
645
777
|
switch (column.type) {
|
|
646
778
|
case 'colour':
|
|
647
|
-
//
|
|
779
|
+
// For other colour columns, picker is rendered in the action column
|
|
648
780
|
return null;
|
|
649
781
|
case 'currency':
|
|
650
782
|
case 'number':
|
|
@@ -765,9 +897,26 @@ const GenericEditableTable = ({
|
|
|
765
897
|
// Render new row field
|
|
766
898
|
const renderNewRowField = (newRowData, column, groupId = null) => {
|
|
767
899
|
const value = newRowData ? newRowData[column.id] : '';
|
|
900
|
+
|
|
901
|
+
// Special handling for colours column regardless of type
|
|
902
|
+
if (column.id === 'colours') {
|
|
903
|
+
return (
|
|
904
|
+
<ColourPicker
|
|
905
|
+
value={value || ''}
|
|
906
|
+
columnId={column.id}
|
|
907
|
+
keyCounter={0}
|
|
908
|
+
onChange={(val, colId) => {
|
|
909
|
+
// Update both the colours field and row_colour for row highlighting
|
|
910
|
+
handleNewFieldChange(val, colId, groupId);
|
|
911
|
+
handleNewFieldChange(val, 'row_colour', groupId);
|
|
912
|
+
}}
|
|
913
|
+
/>
|
|
914
|
+
);
|
|
915
|
+
}
|
|
916
|
+
|
|
768
917
|
switch (column.type) {
|
|
769
918
|
case 'colour':
|
|
770
|
-
//
|
|
919
|
+
// For other colour columns, picker is rendered in the action column
|
|
771
920
|
return null;
|
|
772
921
|
case 'currency':
|
|
773
922
|
case 'number':
|
|
@@ -885,6 +1034,7 @@ const GenericEditableTable = ({
|
|
|
885
1034
|
const newRowData = hasCategory ? newEntries[groupId] || {} : newEntry;
|
|
886
1035
|
return (
|
|
887
1036
|
<tr className={styles.newRow}>
|
|
1037
|
+
<td className={styles.checkboxColumn}></td>
|
|
888
1038
|
{columns.map((column) => (
|
|
889
1039
|
<td
|
|
890
1040
|
key={column.id}
|
|
@@ -910,6 +1060,51 @@ const GenericEditableTable = ({
|
|
|
910
1060
|
);
|
|
911
1061
|
};
|
|
912
1062
|
|
|
1063
|
+
// Checkbox column header
|
|
1064
|
+
const renderSelectAllCheckbox = (group = null) => (
|
|
1065
|
+
<th
|
|
1066
|
+
key="select-all"
|
|
1067
|
+
className={styles.checkboxColumn}
|
|
1068
|
+
onClick={() => {
|
|
1069
|
+
const currentlyAllSelected = isAllSelected(group?.entries);
|
|
1070
|
+
handleSelectAll(!currentlyAllSelected, group?.entries);
|
|
1071
|
+
}}
|
|
1072
|
+
style={{ cursor: 'pointer' }}
|
|
1073
|
+
title={
|
|
1074
|
+
group ? `Select all in ${group.category}` : 'Select all rows'
|
|
1075
|
+
}
|
|
1076
|
+
>
|
|
1077
|
+
<input
|
|
1078
|
+
type="checkbox"
|
|
1079
|
+
checked={isAllSelected(group?.entries)}
|
|
1080
|
+
onChange={() => {}} // Empty handler since we handle click on the cell
|
|
1081
|
+
style={{ cursor: 'pointer', pointerEvents: 'none' }}
|
|
1082
|
+
tabIndex={-1}
|
|
1083
|
+
/>
|
|
1084
|
+
</th>
|
|
1085
|
+
);
|
|
1086
|
+
|
|
1087
|
+
// Row checkbox cell
|
|
1088
|
+
const renderRowCheckbox = (entry) => (
|
|
1089
|
+
<td
|
|
1090
|
+
key={`checkbox-${entry.id}`}
|
|
1091
|
+
className={styles.checkboxColumn}
|
|
1092
|
+
onClick={() => {
|
|
1093
|
+
const isCurrentlySelected = selectedRows.has(entry.id);
|
|
1094
|
+
handleRowSelection(entry.id, !isCurrentlySelected);
|
|
1095
|
+
}}
|
|
1096
|
+
style={{ cursor: 'pointer' }}
|
|
1097
|
+
>
|
|
1098
|
+
<input
|
|
1099
|
+
type="checkbox"
|
|
1100
|
+
checked={selectedRows.has(entry.id)}
|
|
1101
|
+
onChange={() => {}} // Empty handler since we handle click on the cell
|
|
1102
|
+
style={{ cursor: 'pointer', pointerEvents: 'none' }}
|
|
1103
|
+
tabIndex={-1}
|
|
1104
|
+
/>
|
|
1105
|
+
</td>
|
|
1106
|
+
);
|
|
1107
|
+
|
|
913
1108
|
// Header cell with optional copy icon
|
|
914
1109
|
const renderHeaderCell = (column, group = null) => (
|
|
915
1110
|
<th
|
|
@@ -932,7 +1127,7 @@ const GenericEditableTable = ({
|
|
|
932
1127
|
}}
|
|
933
1128
|
style={{ cursor: 'pointer' }}
|
|
934
1129
|
>
|
|
935
|
-
<Copy size={
|
|
1130
|
+
<Copy size={20} />
|
|
936
1131
|
</span>
|
|
937
1132
|
)}
|
|
938
1133
|
</div>
|
|
@@ -991,12 +1186,13 @@ const GenericEditableTable = ({
|
|
|
991
1186
|
{groupedCategories.map((group) => (
|
|
992
1187
|
<React.Fragment key={group.id}>
|
|
993
1188
|
<tr className={styles.categoryRow}>
|
|
994
|
-
<td colSpan={columns.length +
|
|
1189
|
+
<td colSpan={columns.length + 2}>
|
|
995
1190
|
{group.category}
|
|
996
1191
|
</td>
|
|
997
1192
|
</tr>
|
|
998
1193
|
<tr>
|
|
999
|
-
{
|
|
1194
|
+
{renderSelectAllCheckbox(group)}
|
|
1195
|
+
{columns.map((column) =>
|
|
1000
1196
|
renderHeaderCell(column, group)
|
|
1001
1197
|
)}
|
|
1002
1198
|
<th style={{ width: '5%' }}></th>
|
|
@@ -1010,6 +1206,7 @@ const GenericEditableTable = ({
|
|
|
1010
1206
|
getRowBackground(entry),
|
|
1011
1207
|
}}
|
|
1012
1208
|
>
|
|
1209
|
+
{renderRowCheckbox(entry)}
|
|
1013
1210
|
{columns.map((column) => (
|
|
1014
1211
|
<td
|
|
1015
1212
|
key={column.id}
|
|
@@ -1032,55 +1229,72 @@ const GenericEditableTable = ({
|
|
|
1032
1229
|
<div
|
|
1033
1230
|
className={styles.tdactions}
|
|
1034
1231
|
>
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
)
|
|
1232
|
+
{!hasColourColumn && (
|
|
1233
|
+
<ColourPicker
|
|
1234
|
+
value={
|
|
1235
|
+
entry.row_colour ||
|
|
1236
|
+
''
|
|
1237
|
+
}
|
|
1238
|
+
columnId={
|
|
1239
|
+
'row_colour'
|
|
1240
|
+
}
|
|
1241
|
+
keyCounter={
|
|
1242
|
+
entry.keyCounter
|
|
1243
|
+
}
|
|
1244
|
+
onChange={
|
|
1245
|
+
handleFieldChange
|
|
1050
1246
|
}
|
|
1051
|
-
style={{
|
|
1052
|
-
cursor: 'pointer',
|
|
1053
|
-
}}
|
|
1054
1247
|
/>
|
|
1055
1248
|
)}
|
|
1249
|
+
{idx > 0 && (
|
|
1250
|
+
<Tooltip text="Move up">
|
|
1251
|
+
<ArrowUp
|
|
1252
|
+
size={16}
|
|
1253
|
+
onClick={() =>
|
|
1254
|
+
handleSortChangeGrouped(
|
|
1255
|
+
group.id,
|
|
1256
|
+
entry.id,
|
|
1257
|
+
'up'
|
|
1258
|
+
)
|
|
1259
|
+
}
|
|
1260
|
+
style={{
|
|
1261
|
+
cursor: 'pointer',
|
|
1262
|
+
}}
|
|
1263
|
+
/>
|
|
1264
|
+
</Tooltip>
|
|
1265
|
+
)}
|
|
1056
1266
|
{idx <
|
|
1057
1267
|
group.entries.length -
|
|
1058
1268
|
1 && (
|
|
1059
|
-
<
|
|
1269
|
+
<Tooltip text="Move down">
|
|
1270
|
+
<ArrowDown
|
|
1271
|
+
size={16}
|
|
1272
|
+
onClick={() =>
|
|
1273
|
+
handleSortChangeGrouped(
|
|
1274
|
+
group.id,
|
|
1275
|
+
entry.id,
|
|
1276
|
+
'down'
|
|
1277
|
+
)
|
|
1278
|
+
}
|
|
1279
|
+
style={{
|
|
1280
|
+
cursor: 'pointer',
|
|
1281
|
+
}}
|
|
1282
|
+
/>
|
|
1283
|
+
</Tooltip>
|
|
1284
|
+
)}
|
|
1285
|
+
<Tooltip text="Delete row">
|
|
1286
|
+
<Trash2
|
|
1060
1287
|
size={16}
|
|
1061
1288
|
onClick={() =>
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
entry.id,
|
|
1065
|
-
'down'
|
|
1289
|
+
handleDeleteRow(
|
|
1290
|
+
entry
|
|
1066
1291
|
)
|
|
1067
1292
|
}
|
|
1068
1293
|
style={{
|
|
1069
1294
|
cursor: 'pointer',
|
|
1070
1295
|
}}
|
|
1071
1296
|
/>
|
|
1072
|
-
|
|
1073
|
-
<Trash2
|
|
1074
|
-
size={16}
|
|
1075
|
-
onClick={() =>
|
|
1076
|
-
handleDeleteRow(
|
|
1077
|
-
entry
|
|
1078
|
-
)
|
|
1079
|
-
}
|
|
1080
|
-
style={{
|
|
1081
|
-
cursor: 'pointer',
|
|
1082
|
-
}}
|
|
1083
|
-
/>
|
|
1297
|
+
</Tooltip>
|
|
1084
1298
|
</div>
|
|
1085
1299
|
</td>
|
|
1086
1300
|
</tr>
|
|
@@ -1092,9 +1306,10 @@ const GenericEditableTable = ({
|
|
|
1092
1306
|
(col) => col.type === 'total'
|
|
1093
1307
|
) !== -1 && (
|
|
1094
1308
|
<tr className={styles.subtotalRow}>
|
|
1095
|
-
|
|
1309
|
+
<td></td>
|
|
1310
|
+
{columns.map((col, index) => {
|
|
1096
1311
|
const totalColIndex =
|
|
1097
|
-
columns.
|
|
1312
|
+
columns.findIndex(
|
|
1098
1313
|
(c) =>
|
|
1099
1314
|
c.type === 'total'
|
|
1100
1315
|
);
|
|
@@ -1116,7 +1331,7 @@ const GenericEditableTable = ({
|
|
|
1116
1331
|
sum +
|
|
1117
1332
|
calculateTotal(
|
|
1118
1333
|
entry,
|
|
1119
|
-
columns
|
|
1334
|
+
columns[
|
|
1120
1335
|
totalColIndex
|
|
1121
1336
|
].keys
|
|
1122
1337
|
),
|
|
@@ -1154,124 +1369,153 @@ const GenericEditableTable = ({
|
|
|
1154
1369
|
<table className={styles.schedulingTable}>
|
|
1155
1370
|
<thead>
|
|
1156
1371
|
<tr>
|
|
1157
|
-
{
|
|
1372
|
+
{renderSelectAllCheckbox()}
|
|
1373
|
+
{columns.map((column) => renderHeaderCell(column))}
|
|
1158
1374
|
<th style={{ width: '5%' }}></th>
|
|
1159
1375
|
</tr>
|
|
1160
1376
|
</thead>
|
|
1161
1377
|
<tbody>
|
|
1162
|
-
{localData.length > 0 &&
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1378
|
+
{localData.length > 0 &&
|
|
1379
|
+
sortBySortOrder(localData).map((entry, idx) => (
|
|
1380
|
+
<tr
|
|
1381
|
+
key={entry.id}
|
|
1382
|
+
style={{
|
|
1383
|
+
background: getRowBackground(entry),
|
|
1384
|
+
}}
|
|
1385
|
+
>
|
|
1386
|
+
{renderRowCheckbox(entry)}
|
|
1387
|
+
{columns.map((column) => (
|
|
1388
|
+
<td
|
|
1389
|
+
key={column.id}
|
|
1390
|
+
style={{
|
|
1391
|
+
width: column.width || 'auto',
|
|
1392
|
+
textAlign:
|
|
1393
|
+
column.type === 'currency'
|
|
1394
|
+
? 'right'
|
|
1395
|
+
: 'left',
|
|
1396
|
+
}}
|
|
1397
|
+
>
|
|
1398
|
+
{renderScheduledTableField(
|
|
1399
|
+
entry,
|
|
1400
|
+
column,
|
|
1401
|
+
idx
|
|
1402
|
+
)}
|
|
1403
|
+
</td>
|
|
1404
|
+
))}
|
|
1405
|
+
<td>
|
|
1406
|
+
<div className={styles.tdactions}>
|
|
1407
|
+
{!hasColourColumn && (
|
|
1408
|
+
<ColourPicker
|
|
1409
|
+
value={
|
|
1410
|
+
entry.row_colour || ''
|
|
1411
|
+
}
|
|
1412
|
+
columnId={'row_colour'}
|
|
1413
|
+
keyCounter={idx}
|
|
1414
|
+
onChange={handleFieldChange}
|
|
1415
|
+
/>
|
|
1416
|
+
)}
|
|
1417
|
+
{idx > 0 && (
|
|
1418
|
+
<Tooltip text="Move up">
|
|
1419
|
+
<ArrowUp
|
|
1420
|
+
size={16}
|
|
1421
|
+
onClick={() =>
|
|
1422
|
+
handleSortChangeUngrouped(
|
|
1423
|
+
entry.id,
|
|
1424
|
+
'up'
|
|
1425
|
+
)
|
|
1426
|
+
}
|
|
1427
|
+
style={{
|
|
1428
|
+
cursor: 'pointer',
|
|
1429
|
+
}}
|
|
1430
|
+
/>
|
|
1431
|
+
</Tooltip>
|
|
1432
|
+
)}
|
|
1433
|
+
{idx <
|
|
1434
|
+
sortBySortOrder(localData)
|
|
1435
|
+
.length -
|
|
1436
|
+
1 && (
|
|
1437
|
+
<Tooltip text="Move down">
|
|
1438
|
+
<ArrowDown
|
|
1439
|
+
size={16}
|
|
1440
|
+
onClick={() =>
|
|
1441
|
+
handleSortChangeUngrouped(
|
|
1442
|
+
entry.id,
|
|
1443
|
+
'down'
|
|
1444
|
+
)
|
|
1445
|
+
}
|
|
1446
|
+
style={{
|
|
1447
|
+
cursor: 'pointer',
|
|
1448
|
+
}}
|
|
1449
|
+
/>
|
|
1450
|
+
</Tooltip>
|
|
1451
|
+
)}
|
|
1452
|
+
<Tooltip text="Delete row">
|
|
1453
|
+
<Trash2
|
|
1454
|
+
size={16}
|
|
1455
|
+
onClick={() =>
|
|
1456
|
+
handleDeleteRow(entry)
|
|
1457
|
+
}
|
|
1458
|
+
style={{
|
|
1459
|
+
cursor: 'pointer',
|
|
1460
|
+
}}
|
|
1461
|
+
/>
|
|
1462
|
+
</Tooltip>
|
|
1463
|
+
</div>
|
|
1183
1464
|
</td>
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
<div className={styles.tdactions}>
|
|
1187
|
-
<ColourPicker
|
|
1188
|
-
value={entry.row_colour || ''}
|
|
1189
|
-
columnId={'row_colour'}
|
|
1190
|
-
keyCounter={idx}
|
|
1191
|
-
onChange={handleFieldChange}
|
|
1192
|
-
/>
|
|
1193
|
-
{idx > 0 && (
|
|
1194
|
-
<ArrowUp
|
|
1195
|
-
size={16}
|
|
1196
|
-
onClick={() =>
|
|
1197
|
-
handleSortChangeUngrouped(
|
|
1198
|
-
entry.id,
|
|
1199
|
-
'up'
|
|
1200
|
-
)
|
|
1201
|
-
}
|
|
1202
|
-
style={{ cursor: 'pointer' }}
|
|
1203
|
-
/>
|
|
1204
|
-
)}
|
|
1205
|
-
{idx <
|
|
1206
|
-
sortBySortOrder(localData).length -
|
|
1207
|
-
1 && (
|
|
1208
|
-
<ArrowDown
|
|
1209
|
-
size={16}
|
|
1210
|
-
onClick={() =>
|
|
1211
|
-
handleSortChangeUngrouped(
|
|
1212
|
-
entry.id,
|
|
1213
|
-
'down'
|
|
1214
|
-
)
|
|
1215
|
-
}
|
|
1216
|
-
style={{ cursor: 'pointer' }}
|
|
1217
|
-
/>
|
|
1218
|
-
)}
|
|
1219
|
-
<Trash2
|
|
1220
|
-
size={16}
|
|
1221
|
-
onClick={() =>
|
|
1222
|
-
handleDeleteRow(entry)
|
|
1223
|
-
}
|
|
1224
|
-
style={{ cursor: 'pointer' }}
|
|
1225
|
-
/>
|
|
1226
|
-
</div>
|
|
1227
|
-
</td>
|
|
1228
|
-
</tr>
|
|
1229
|
-
))}
|
|
1465
|
+
</tr>
|
|
1466
|
+
))}
|
|
1230
1467
|
|
|
1231
1468
|
{renderNewRowForm()}
|
|
1232
1469
|
|
|
1233
|
-
{localData.length > 0 &&
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
{
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
index === totalColIndex - 1 &&
|
|
1242
|
-
index > 0
|
|
1243
|
-
) {
|
|
1244
|
-
return <td key={col.id}>Subtotal</td>;
|
|
1245
|
-
}
|
|
1246
|
-
if (index === totalColIndex) {
|
|
1247
|
-
const overallTotal = sortBySortOrder(
|
|
1248
|
-
localData
|
|
1249
|
-
).reduce(
|
|
1250
|
-
(sum, entry) =>
|
|
1251
|
-
sum +
|
|
1252
|
-
calculateTotal(
|
|
1253
|
-
entry,
|
|
1254
|
-
columns.filter(col => col.type !== 'colour')[totalColIndex].keys
|
|
1255
|
-
),
|
|
1256
|
-
0
|
|
1257
|
-
);
|
|
1258
|
-
return (
|
|
1259
|
-
<td key={col.id}>
|
|
1260
|
-
{overallTotal.toLocaleString(
|
|
1261
|
-
'en-AU',
|
|
1262
|
-
{
|
|
1263
|
-
style: 'currency',
|
|
1264
|
-
currency: 'AUD',
|
|
1265
|
-
}
|
|
1266
|
-
)}
|
|
1267
|
-
</td>
|
|
1470
|
+
{localData.length > 0 &&
|
|
1471
|
+
columns.findIndex((col) => col.type === 'total') !==
|
|
1472
|
+
-1 && (
|
|
1473
|
+
<tr className={styles.subtotalRow}>
|
|
1474
|
+
<td></td>
|
|
1475
|
+
{columns.map((col, index) => {
|
|
1476
|
+
const totalColIndex = columns.findIndex(
|
|
1477
|
+
(c) => c.type === 'total'
|
|
1268
1478
|
);
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1479
|
+
if (
|
|
1480
|
+
index === totalColIndex - 1 &&
|
|
1481
|
+
index > 0
|
|
1482
|
+
) {
|
|
1483
|
+
return (
|
|
1484
|
+
<td key={col.id}>Subtotal</td>
|
|
1485
|
+
);
|
|
1486
|
+
}
|
|
1487
|
+
if (index === totalColIndex) {
|
|
1488
|
+
const overallTotal =
|
|
1489
|
+
sortBySortOrder(
|
|
1490
|
+
localData
|
|
1491
|
+
).reduce(
|
|
1492
|
+
(sum, entry) =>
|
|
1493
|
+
sum +
|
|
1494
|
+
calculateTotal(
|
|
1495
|
+
entry,
|
|
1496
|
+
columns[
|
|
1497
|
+
totalColIndex
|
|
1498
|
+
].keys
|
|
1499
|
+
),
|
|
1500
|
+
0
|
|
1501
|
+
);
|
|
1502
|
+
return (
|
|
1503
|
+
<td key={col.id}>
|
|
1504
|
+
{overallTotal.toLocaleString(
|
|
1505
|
+
'en-AU',
|
|
1506
|
+
{
|
|
1507
|
+
style: 'currency',
|
|
1508
|
+
currency: 'AUD',
|
|
1509
|
+
}
|
|
1510
|
+
)}
|
|
1511
|
+
</td>
|
|
1512
|
+
);
|
|
1513
|
+
}
|
|
1514
|
+
return <td key={col.id} />;
|
|
1515
|
+
})}
|
|
1516
|
+
<td></td>
|
|
1517
|
+
</tr>
|
|
1518
|
+
)}
|
|
1275
1519
|
</tbody>
|
|
1276
1520
|
</table>
|
|
1277
1521
|
)}
|