@visns-studio/visns-components 5.1.21 → 5.1.23
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
CHANGED
|
@@ -77,7 +77,7 @@
|
|
|
77
77
|
"react-dom": "^17.0.0 || ^18.0.0"
|
|
78
78
|
},
|
|
79
79
|
"name": "@visns-studio/visns-components",
|
|
80
|
-
"version": "5.1.
|
|
80
|
+
"version": "5.1.23",
|
|
81
81
|
"description": "Various packages to assist in the development of our Custom Applications.",
|
|
82
82
|
"main": "src/index.js",
|
|
83
83
|
"files": [
|
|
@@ -116,6 +116,9 @@ const GenericEditableTable = ({
|
|
|
116
116
|
|
|
117
117
|
const [localData, setLocalData] = useState([]);
|
|
118
118
|
|
|
119
|
+
// Determine if a total column exists and its index.
|
|
120
|
+
const totalColumnIndex = columns.findIndex((col) => col.type === 'total');
|
|
121
|
+
|
|
119
122
|
// Helper to sort by sort_order without mutating the original array
|
|
120
123
|
const sortBySortOrder = (dataArray) =>
|
|
121
124
|
[...dataArray].sort((a, b) => a.sort_order - b.sort_order);
|
|
@@ -128,6 +131,46 @@ const GenericEditableTable = ({
|
|
|
128
131
|
: undefined;
|
|
129
132
|
};
|
|
130
133
|
|
|
134
|
+
// New helper to calculate total using the updated keys structure
|
|
135
|
+
const calculateTotal = (entry, keys) => {
|
|
136
|
+
let total = 0;
|
|
137
|
+
if (Array.isArray(keys)) {
|
|
138
|
+
// Legacy format: simply sum up the values.
|
|
139
|
+
total = keys.reduce(
|
|
140
|
+
(sum, key) => sum + (parseFloat(entry[key]) || 0),
|
|
141
|
+
0
|
|
142
|
+
);
|
|
143
|
+
} else if (
|
|
144
|
+
keys &&
|
|
145
|
+
typeof keys === 'object' &&
|
|
146
|
+
keys.price &&
|
|
147
|
+
keys.unit
|
|
148
|
+
) {
|
|
149
|
+
const priceKeys = keys.price;
|
|
150
|
+
const unitKeys = keys.unit;
|
|
151
|
+
// If there's one unit key, use its value for all price keys.
|
|
152
|
+
if (unitKeys.length === 1) {
|
|
153
|
+
const unitValue = parseFloat(entry[unitKeys[0]]) || 0;
|
|
154
|
+
total = priceKeys.reduce(
|
|
155
|
+
(sum, key) =>
|
|
156
|
+
sum + (parseFloat(entry[key]) || 0) * unitValue,
|
|
157
|
+
0
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
// Otherwise, if both arrays have the same length, multiply pair‑wise.
|
|
161
|
+
else if (priceKeys.length === unitKeys.length) {
|
|
162
|
+
total = priceKeys.reduce(
|
|
163
|
+
(sum, key, index) =>
|
|
164
|
+
sum +
|
|
165
|
+
(parseFloat(entry[key]) || 0) *
|
|
166
|
+
(parseFloat(entry[unitKeys[index]]) || 0),
|
|
167
|
+
0
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
return total;
|
|
172
|
+
};
|
|
173
|
+
|
|
131
174
|
// On load, enrich the data and sort by sort_order
|
|
132
175
|
useEffect(() => {
|
|
133
176
|
const enrichedData = data[rows.key]?.map((entry, index) => ({
|
|
@@ -441,10 +484,7 @@ const GenericEditableTable = ({
|
|
|
441
484
|
);
|
|
442
485
|
}
|
|
443
486
|
case 'total': {
|
|
444
|
-
const subtotal = column.keys
|
|
445
|
-
(sum, key) => sum + (parseFloat(entry[key]) || 0),
|
|
446
|
-
0
|
|
447
|
-
);
|
|
487
|
+
const subtotal = calculateTotal(entry, column.keys);
|
|
448
488
|
return new Intl.NumberFormat('en-AU', {
|
|
449
489
|
style: 'currency',
|
|
450
490
|
currency: 'AUD',
|
|
@@ -595,6 +635,54 @@ const GenericEditableTable = ({
|
|
|
595
635
|
</td>
|
|
596
636
|
</tr>
|
|
597
637
|
))}
|
|
638
|
+
{/* Render subtotal row for group only if a total column exists */}
|
|
639
|
+
{totalColumnIndex !== -1 && (
|
|
640
|
+
<tr className={styles.subtotalRow}>
|
|
641
|
+
{columns.map((col, index) => {
|
|
642
|
+
if (
|
|
643
|
+
index ===
|
|
644
|
+
totalColumnIndex - 1 &&
|
|
645
|
+
totalColumnIndex > 0
|
|
646
|
+
) {
|
|
647
|
+
return (
|
|
648
|
+
<td key={col.id}>
|
|
649
|
+
Subtotal
|
|
650
|
+
</td>
|
|
651
|
+
);
|
|
652
|
+
}
|
|
653
|
+
if (
|
|
654
|
+
index === totalColumnIndex
|
|
655
|
+
) {
|
|
656
|
+
const groupTotal =
|
|
657
|
+
group.entries.reduce(
|
|
658
|
+
(sum, entry) =>
|
|
659
|
+
sum +
|
|
660
|
+
calculateTotal(
|
|
661
|
+
entry,
|
|
662
|
+
columns[
|
|
663
|
+
totalColumnIndex
|
|
664
|
+
].keys
|
|
665
|
+
),
|
|
666
|
+
0
|
|
667
|
+
);
|
|
668
|
+
return (
|
|
669
|
+
<td key={col.id}>
|
|
670
|
+
{groupTotal.toLocaleString(
|
|
671
|
+
'en-AU',
|
|
672
|
+
{
|
|
673
|
+
style: 'currency',
|
|
674
|
+
currency:
|
|
675
|
+
'AUD',
|
|
676
|
+
}
|
|
677
|
+
)}
|
|
678
|
+
</td>
|
|
679
|
+
);
|
|
680
|
+
}
|
|
681
|
+
return <td key={col.id} />;
|
|
682
|
+
})}
|
|
683
|
+
<td></td>
|
|
684
|
+
</tr>
|
|
685
|
+
)}
|
|
598
686
|
</React.Fragment>
|
|
599
687
|
))}
|
|
600
688
|
</tbody>
|
|
@@ -676,38 +764,46 @@ const GenericEditableTable = ({
|
|
|
676
764
|
</td>
|
|
677
765
|
</tr>
|
|
678
766
|
))}
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
767
|
+
{/* Render overall subtotal row only if a total column exists */}
|
|
768
|
+
{totalColumnIndex !== -1 && (
|
|
769
|
+
<tr className={styles.subtotalRow}>
|
|
770
|
+
{columns.map((col, index) => {
|
|
771
|
+
if (
|
|
772
|
+
index === totalColumnIndex - 1 &&
|
|
773
|
+
totalColumnIndex > 0
|
|
774
|
+
) {
|
|
775
|
+
return <td key={col.id}>Subtotal</td>;
|
|
776
|
+
}
|
|
777
|
+
if (index === totalColumnIndex) {
|
|
778
|
+
const overallTotal = sortBySortOrder(
|
|
779
|
+
localData
|
|
780
|
+
).reduce(
|
|
781
|
+
(sum, entry) =>
|
|
782
|
+
sum +
|
|
783
|
+
calculateTotal(
|
|
784
|
+
entry,
|
|
785
|
+
columns[totalColumnIndex]
|
|
786
|
+
.keys
|
|
787
|
+
),
|
|
788
|
+
0
|
|
789
|
+
);
|
|
790
|
+
return (
|
|
791
|
+
<td key={col.id}>
|
|
792
|
+
{overallTotal.toLocaleString(
|
|
793
|
+
'en-AU',
|
|
794
|
+
{
|
|
795
|
+
style: 'currency',
|
|
796
|
+
currency: 'AUD',
|
|
797
|
+
}
|
|
798
|
+
)}
|
|
799
|
+
</td>
|
|
800
|
+
);
|
|
801
|
+
}
|
|
802
|
+
return <td key={col.id} />;
|
|
803
|
+
})}
|
|
804
|
+
<td></td>
|
|
805
|
+
</tr>
|
|
806
|
+
)}
|
|
711
807
|
</tbody>
|
|
712
808
|
</table>
|
|
713
809
|
) : (
|
|
@@ -22,6 +22,18 @@
|
|
|
22
22
|
td {
|
|
23
23
|
background-color: rgba(var(--primary-rgb), 0.05);
|
|
24
24
|
}
|
|
25
|
+
|
|
26
|
+
/* Compact styling for native input and select elements */
|
|
27
|
+
input,
|
|
28
|
+
select {
|
|
29
|
+
padding: 0.25rem 0.5rem;
|
|
30
|
+
margin: 0;
|
|
31
|
+
font-size: 0.85rem;
|
|
32
|
+
height: 28px;
|
|
33
|
+
line-height: 1;
|
|
34
|
+
border: 1px solid rgba(var(--primary-rgb), 0.1);
|
|
35
|
+
box-sizing: border-box;
|
|
36
|
+
}
|
|
25
37
|
}
|
|
26
38
|
|
|
27
39
|
.categoryRow {
|
|
@@ -249,18 +261,6 @@
|
|
|
249
261
|
}
|
|
250
262
|
}
|
|
251
263
|
|
|
252
|
-
/* Compact styling for native input and select elements */
|
|
253
|
-
input,
|
|
254
|
-
select {
|
|
255
|
-
padding: 0.25rem 0.5rem;
|
|
256
|
-
margin: 0;
|
|
257
|
-
font-size: 0.85rem;
|
|
258
|
-
height: 28px;
|
|
259
|
-
line-height: 1;
|
|
260
|
-
border: 1px solid rgba(var(--primary-rgb), 0.1);
|
|
261
|
-
box-sizing: border-box;
|
|
262
|
-
}
|
|
263
|
-
|
|
264
264
|
/* Compact styling for react-select components */
|
|
265
265
|
.react-select__control {
|
|
266
266
|
min-height: 28px;
|