@visns-studio/visns-components 5.1.25 → 5.2.0
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
|
@@ -51,6 +51,7 @@
|
|
|
51
51
|
"react-window": "^1.8.11",
|
|
52
52
|
"reactjs-popup": "^2.0.6",
|
|
53
53
|
"style-loader": "^4.0.0",
|
|
54
|
+
"swapy": "^1.0.5",
|
|
54
55
|
"truncate": "^3.0.0",
|
|
55
56
|
"uuid": "^10.0.0",
|
|
56
57
|
"validator": "^13.12.0",
|
|
@@ -77,7 +78,7 @@
|
|
|
77
78
|
"react-dom": "^17.0.0 || ^18.0.0"
|
|
78
79
|
},
|
|
79
80
|
"name": "@visns-studio/visns-components",
|
|
80
|
-
"version": "5.
|
|
81
|
+
"version": "5.2.0",
|
|
81
82
|
"description": "Various packages to assist in the development of our Custom Applications.",
|
|
82
83
|
"main": "src/index.js",
|
|
83
84
|
"files": [
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import '../../styles/global.css';
|
|
2
2
|
|
|
3
|
-
import React, { useState, useEffect } from 'react';
|
|
3
|
+
import React, { useState, useEffect, useRef } from 'react';
|
|
4
4
|
import { Link, useNavigate } from 'react-router-dom';
|
|
5
5
|
import { ResponsiveBar } from '@nivo/bar';
|
|
6
6
|
import { ResponsiveLine } from '@nivo/line';
|
|
@@ -8,6 +8,7 @@ import { ResponsivePie } from '@nivo/pie';
|
|
|
8
8
|
import Popup from 'reactjs-popup';
|
|
9
9
|
import dayjs from 'dayjs';
|
|
10
10
|
import parse from 'html-react-parser';
|
|
11
|
+
import { createSwapy } from 'swapy';
|
|
11
12
|
import { CircleX } from 'akar-icons';
|
|
12
13
|
|
|
13
14
|
import CustomFetch from '../../crm/Fetch';
|
|
@@ -18,6 +19,8 @@ import styles from './styles/GenericDashboard.module.scss';
|
|
|
18
19
|
const toSnakeCase = (str) => str.replace(/[\s-]+/g, '_').toLowerCase();
|
|
19
20
|
|
|
20
21
|
function GenericDashboard({ setting }) {
|
|
22
|
+
const swapy = useRef(null);
|
|
23
|
+
const container = useRef(null);
|
|
21
24
|
const navigate = useNavigate();
|
|
22
25
|
const [data, setData] = useState({});
|
|
23
26
|
const [dropdowns, setDropdowns] = useState({});
|
|
@@ -197,6 +200,23 @@ function GenericDashboard({ setting }) {
|
|
|
197
200
|
fetchData(filters);
|
|
198
201
|
}, [filters]);
|
|
199
202
|
|
|
203
|
+
useEffect(() => {
|
|
204
|
+
// If container element is loaded
|
|
205
|
+
if (container.current) {
|
|
206
|
+
swapy.current = createSwapy(container.current);
|
|
207
|
+
|
|
208
|
+
// Your event listeners
|
|
209
|
+
swapy.current.onSwap((event) => {
|
|
210
|
+
console.log('swap', event);
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
return () => {
|
|
215
|
+
// Destroy the swapy instance on component destroy
|
|
216
|
+
swapy.current?.destroy();
|
|
217
|
+
};
|
|
218
|
+
}, []);
|
|
219
|
+
|
|
200
220
|
const renderFilters = (widget) => {
|
|
201
221
|
if (!widget.filters || widget.filters.length === 0) return null;
|
|
202
222
|
|
|
@@ -407,6 +427,37 @@ function GenericDashboard({ setting }) {
|
|
|
407
427
|
widget.props.keys = widgetData.keys;
|
|
408
428
|
}
|
|
409
429
|
|
|
430
|
+
// Build legendMapping and attach legendLabel and a custom tooltip if a legend is provided.
|
|
431
|
+
let legendMapping = {};
|
|
432
|
+
if (widget.legend) {
|
|
433
|
+
legendMapping = widget.legend.reduce((acc, curr) => {
|
|
434
|
+
acc[curr.id] = curr.label;
|
|
435
|
+
return acc;
|
|
436
|
+
}, {});
|
|
437
|
+
widget.props.legendLabel = (e) =>
|
|
438
|
+
legendMapping[e.id] || e.id;
|
|
439
|
+
|
|
440
|
+
// Define a custom tooltip that uses the legendMapping
|
|
441
|
+
const CustomTooltip = ({ id, value, color }) => {
|
|
442
|
+
const label = legendMapping[id] || id;
|
|
443
|
+
return (
|
|
444
|
+
<div
|
|
445
|
+
style={{
|
|
446
|
+
padding: '6px 9px',
|
|
447
|
+
background: 'white',
|
|
448
|
+
border: `1px solid ${color}`,
|
|
449
|
+
borderRadius: '3px',
|
|
450
|
+
}}
|
|
451
|
+
>
|
|
452
|
+
<strong style={{ color }}>{label}</strong>:{' '}
|
|
453
|
+
{value}
|
|
454
|
+
</div>
|
|
455
|
+
);
|
|
456
|
+
};
|
|
457
|
+
|
|
458
|
+
widget.props.tooltip = CustomTooltip;
|
|
459
|
+
}
|
|
460
|
+
|
|
410
461
|
const barData =
|
|
411
462
|
widgetData.bar?.data || widgetData.data || widgetData;
|
|
412
463
|
|
|
@@ -420,6 +471,7 @@ function GenericDashboard({ setting }) {
|
|
|
420
471
|
</div>
|
|
421
472
|
);
|
|
422
473
|
}
|
|
474
|
+
break;
|
|
423
475
|
case 'line':
|
|
424
476
|
if (widgetData.length > 0) {
|
|
425
477
|
return (
|
|
@@ -733,25 +785,28 @@ function GenericDashboard({ setting }) {
|
|
|
733
785
|
: styles.dashwidget
|
|
734
786
|
: ''
|
|
735
787
|
}`}
|
|
788
|
+
data-swapy-slot={`row-slot-${widget.id}-${rowIndex}`}
|
|
736
789
|
>
|
|
737
|
-
<div
|
|
738
|
-
<
|
|
739
|
-
|
|
740
|
-
{renderFilters(widget)}
|
|
741
|
-
<div>{renderWidget(widget)}</div>
|
|
742
|
-
{widget.modal && (
|
|
743
|
-
<div className={styles.widgetAction}>
|
|
744
|
-
<button
|
|
745
|
-
className={`${styles.btn} ${styles.dmore}`}
|
|
746
|
-
onClick={() => openModal(widget.id)}
|
|
747
|
-
>
|
|
748
|
-
View all{' '}
|
|
749
|
-
<span>
|
|
750
|
-
({data[widget.id]?.length || 0})
|
|
751
|
-
</span>
|
|
752
|
-
</button>
|
|
790
|
+
<div data-swapy-item={`widget-${widget.id}`}>
|
|
791
|
+
<div className={styles.widgetTitle}>
|
|
792
|
+
<h2>{widget.title}</h2>
|
|
753
793
|
</div>
|
|
754
|
-
|
|
794
|
+
{renderFilters(widget)}
|
|
795
|
+
<div>{renderWidget(widget)}</div>
|
|
796
|
+
{widget.modal && (
|
|
797
|
+
<div className={styles.widgetAction}>
|
|
798
|
+
<button
|
|
799
|
+
className={`${styles.btn} ${styles.dmore}`}
|
|
800
|
+
onClick={() => openModal(widget.id)}
|
|
801
|
+
>
|
|
802
|
+
View all{' '}
|
|
803
|
+
<span>
|
|
804
|
+
({data[widget.id]?.length || 0})
|
|
805
|
+
</span>
|
|
806
|
+
</button>
|
|
807
|
+
</div>
|
|
808
|
+
)}
|
|
809
|
+
</div>
|
|
755
810
|
</div>
|
|
756
811
|
))}
|
|
757
812
|
</div>
|
|
@@ -760,7 +815,7 @@ function GenericDashboard({ setting }) {
|
|
|
760
815
|
};
|
|
761
816
|
|
|
762
817
|
return (
|
|
763
|
-
<div className={styles.grid}>
|
|
818
|
+
<div className={styles.grid} ref={container}>
|
|
764
819
|
{renderRows()}
|
|
765
820
|
<Popup open={modalShow} onClose={closeModal}>
|
|
766
821
|
<div className="modalwrap top--modal modalWide">
|
|
@@ -226,18 +226,27 @@ const GenericEditableTable = ({
|
|
|
226
226
|
|
|
227
227
|
dataArray.forEach((entry, index) => {
|
|
228
228
|
const categoryInfo = entry[rows.categoryKey.id];
|
|
229
|
-
const
|
|
229
|
+
const categoryLabel = categoryInfo
|
|
230
230
|
? categoryInfo[rows.categoryKey.label]
|
|
231
231
|
: null;
|
|
232
232
|
const categoryId = categoryInfo ? categoryInfo.id : null;
|
|
233
233
|
const groupKey = categoryId || 'no_category';
|
|
234
|
+
// Use the category's sort_order if provided; otherwise, default to a high number (e.g. 9999)
|
|
235
|
+
|
|
236
|
+
console.info(categoryInfo);
|
|
237
|
+
|
|
238
|
+
const catSortOrder =
|
|
239
|
+
categoryInfo && typeof categoryInfo.sort_order !== 'undefined'
|
|
240
|
+
? categoryInfo.sort_order
|
|
241
|
+
: 9999;
|
|
234
242
|
|
|
235
243
|
if (!grouped[groupKey]) {
|
|
236
244
|
grouped[groupKey] = {
|
|
237
245
|
id: groupKey,
|
|
238
246
|
category:
|
|
239
|
-
|
|
247
|
+
categoryLabel ||
|
|
240
248
|
(groupKey === 'no_category' ? 'No Category' : ''),
|
|
249
|
+
catSortOrder, // assign the sort order (or fallback)
|
|
241
250
|
entries: [],
|
|
242
251
|
};
|
|
243
252
|
}
|
|
@@ -247,15 +256,23 @@ const GenericEditableTable = ({
|
|
|
247
256
|
});
|
|
248
257
|
});
|
|
249
258
|
|
|
259
|
+
// Sort entries within each group by their universal sort_order
|
|
250
260
|
Object.values(grouped).forEach((group) => {
|
|
251
261
|
group.entries.sort((a, b) => a.sort_order - b.sort_order);
|
|
252
262
|
});
|
|
253
263
|
|
|
254
|
-
//
|
|
264
|
+
// Now sort the groups by the cost category's sort_order (catSortOrder)
|
|
255
265
|
const groupedArray = Object.values(grouped);
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
266
|
+
groupedArray.sort((a, b) => {
|
|
267
|
+
// Optionally, place 'no_category' first (or adjust as needed)
|
|
268
|
+
if (a.id === 'no_category') return -1;
|
|
269
|
+
if (b.id === 'no_category') return 1;
|
|
270
|
+
return a.catSortOrder - b.catSortOrder;
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
console.info(groupedArray);
|
|
274
|
+
|
|
275
|
+
return groupedArray;
|
|
259
276
|
};
|
|
260
277
|
|
|
261
278
|
const groupedCategories = useMemo(() => {
|