@visns-studio/visns-components 5.1.22 → 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.22",
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.reduce(
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
- <tr className={styles.subtotalRow}>
680
- {columns.map((column, index) => (
681
- <td key={column.id}>
682
- {index === columns.length - 1
683
- ? sortBySortOrder(localData)
684
- .reduce((sum, entry) => {
685
- const subtotal =
686
- Array.isArray(column.keys)
687
- ? column.keys.reduce(
688
- (rowSum, key) =>
689
- rowSum +
690
- (parseFloat(
691
- entry[
692
- key
693
- ]
694
- ) || 0),
695
- 0
696
- )
697
- : 0;
698
- return sum + subtotal;
699
- }, 0)
700
- .toLocaleString('en-AU', {
701
- style: 'currency',
702
- currency: 'AUD',
703
- })
704
- : index === columns.length - 2
705
- ? 'Subtotal'
706
- : ''}
707
- </td>
708
- ))}
709
- <td></td>
710
- </tr>
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
  ) : (