@visns-studio/visns-components 5.13.20 → 5.13.22
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 +5 -5
- package/src/components/columns/ColumnRenderers.jsx +2 -11
- package/src/components/generic/GenericDashboard.jsx +125 -13
- package/src/components/styles/GenericDashboard.module.scss +3 -0
- package/src/components/styles/GenericEditableTable.module.scss +0 -2
- package/src/components/styles/global.css +0 -4
package/package.json
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"@nivo/pie": "^0.99.0",
|
|
14
14
|
"@tinymce/miniature": "^6.0.0",
|
|
15
15
|
"@tinymce/tinymce-react": "^6.2.1",
|
|
16
|
-
"@uiw/react-color": "^2.
|
|
16
|
+
"@uiw/react-color": "^2.6.0",
|
|
17
17
|
"@visns-studio/visns-datagrid-community": "1.1.0",
|
|
18
18
|
"@visns-studio/visns-datagrid-enterprise": "1.1.0",
|
|
19
19
|
"@vitejs/plugin-react": "^4.6.0",
|
|
@@ -24,13 +24,13 @@
|
|
|
24
24
|
"dayjs": "^1.11.13",
|
|
25
25
|
"fabric": "^6.7.0",
|
|
26
26
|
"file-saver": "^2.0.5",
|
|
27
|
-
"framer-motion": "^12.23.
|
|
27
|
+
"framer-motion": "^12.23.1",
|
|
28
28
|
"html-react-parser": "^5.2.5",
|
|
29
29
|
"lodash": "^4.17.21",
|
|
30
30
|
"lodash.debounce": "^4.0.8",
|
|
31
31
|
"lucide-react": "^0.525.0",
|
|
32
32
|
"moment": "^2.30.1",
|
|
33
|
-
"motion": "^12.23.
|
|
33
|
+
"motion": "^12.23.1",
|
|
34
34
|
"numeral": "^2.0.6",
|
|
35
35
|
"pluralize": "^8.0.0",
|
|
36
36
|
"qrcode.react": "^4.2.0",
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
"react-quill": "^2.0.0",
|
|
48
48
|
"react-reveal": "^1.2.2",
|
|
49
49
|
"react-router-dom": "^6.30.1",
|
|
50
|
-
"react-select": "^5.10.
|
|
50
|
+
"react-select": "^5.10.1",
|
|
51
51
|
"react-signature-pad-wrapper": "^4.1.1",
|
|
52
52
|
"react-slugify": "^4.0.1",
|
|
53
53
|
"react-sortable-hoc": "^2.0.0",
|
|
@@ -88,7 +88,7 @@
|
|
|
88
88
|
"react-dom": "^17.0.0 || ^18.0.0"
|
|
89
89
|
},
|
|
90
90
|
"name": "@visns-studio/visns-components",
|
|
91
|
-
"version": "5.13.
|
|
91
|
+
"version": "5.13.22",
|
|
92
92
|
"description": "Various packages to assist in the development of our Custom Applications.",
|
|
93
93
|
"main": "src/index.js",
|
|
94
94
|
"files": [
|
|
@@ -961,13 +961,12 @@ export const renderJsonColumn = ({ column, commonProps }) => {
|
|
|
961
961
|
: 'N/A';
|
|
962
962
|
|
|
963
963
|
displayContent = (
|
|
964
|
-
<div
|
|
964
|
+
<div>
|
|
965
965
|
<div
|
|
966
966
|
style={{
|
|
967
967
|
fontWeight: '600',
|
|
968
968
|
color: 'var(--primary-color)',
|
|
969
969
|
marginBottom: '0.1rem',
|
|
970
|
-
fontSize: '0.75rem',
|
|
971
970
|
}}
|
|
972
971
|
>
|
|
973
972
|
{question.length > 50
|
|
@@ -977,7 +976,6 @@ export const renderJsonColumn = ({ column, commonProps }) => {
|
|
|
977
976
|
<div
|
|
978
977
|
style={{
|
|
979
978
|
color: 'var(--paragraph-color)',
|
|
980
|
-
fontSize: '0.75rem',
|
|
981
979
|
}}
|
|
982
980
|
>
|
|
983
981
|
{answer}
|
|
@@ -988,7 +986,6 @@ export const renderJsonColumn = ({ column, commonProps }) => {
|
|
|
988
986
|
displayContent = (
|
|
989
987
|
<div
|
|
990
988
|
style={{
|
|
991
|
-
fontSize: '0.75rem',
|
|
992
989
|
color: 'var(--paragraph-color)',
|
|
993
990
|
}}
|
|
994
991
|
>
|
|
@@ -1007,7 +1004,7 @@ export const renderJsonColumn = ({ column, commonProps }) => {
|
|
|
1007
1004
|
const entries = Object.entries(jsonData);
|
|
1008
1005
|
if (entries.length <= 2) {
|
|
1009
1006
|
displayContent = (
|
|
1010
|
-
<div
|
|
1007
|
+
<div>
|
|
1011
1008
|
{entries.map(([key, val]) => (
|
|
1012
1009
|
<div
|
|
1013
1010
|
key={key}
|
|
@@ -1020,7 +1017,6 @@ export const renderJsonColumn = ({ column, commonProps }) => {
|
|
|
1020
1017
|
<strong
|
|
1021
1018
|
style={{
|
|
1022
1019
|
color: 'var(--primary-color)',
|
|
1023
|
-
fontSize: '0.7rem',
|
|
1024
1020
|
}}
|
|
1025
1021
|
>
|
|
1026
1022
|
{key
|
|
@@ -1033,7 +1029,6 @@ export const renderJsonColumn = ({ column, commonProps }) => {
|
|
|
1033
1029
|
<span
|
|
1034
1030
|
style={{
|
|
1035
1031
|
color: 'var(--paragraph-color)',
|
|
1036
|
-
fontSize: '0.7rem',
|
|
1037
1032
|
}}
|
|
1038
1033
|
>
|
|
1039
1034
|
{val !== null &&
|
|
@@ -1056,7 +1051,6 @@ export const renderJsonColumn = ({ column, commonProps }) => {
|
|
|
1056
1051
|
displayContent = (
|
|
1057
1052
|
<div
|
|
1058
1053
|
style={{
|
|
1059
|
-
fontSize: '0.75rem',
|
|
1060
1054
|
color: 'var(--paragraph-color)',
|
|
1061
1055
|
}}
|
|
1062
1056
|
>
|
|
@@ -1072,7 +1066,6 @@ export const renderJsonColumn = ({ column, commonProps }) => {
|
|
|
1072
1066
|
displayContent = (
|
|
1073
1067
|
<div
|
|
1074
1068
|
style={{
|
|
1075
|
-
fontSize: '0.75rem',
|
|
1076
1069
|
color: 'var(--paragraph-color)',
|
|
1077
1070
|
fontStyle: 'italic',
|
|
1078
1071
|
}}
|
|
@@ -1092,8 +1085,6 @@ export const renderJsonColumn = ({ column, commonProps }) => {
|
|
|
1092
1085
|
displayContent = (
|
|
1093
1086
|
<span
|
|
1094
1087
|
style={{
|
|
1095
|
-
fontSize: '0.75rem',
|
|
1096
|
-
fontFamily: 'monospace',
|
|
1097
1088
|
color: 'var(--paragraph-color)',
|
|
1098
1089
|
}}
|
|
1099
1090
|
>
|
|
@@ -33,15 +33,15 @@ const renderProgressBar = (value, options = {}) => {
|
|
|
33
33
|
const backgroundColor = colorMap[color] || colorMap.primary;
|
|
34
34
|
|
|
35
35
|
return (
|
|
36
|
-
<div style={{
|
|
36
|
+
<div style={{ width: '100%', minWidth: '150px' }}>
|
|
37
37
|
<div
|
|
38
38
|
style={{
|
|
39
|
-
width: '
|
|
40
|
-
height: '
|
|
39
|
+
width: '100%',
|
|
40
|
+
height: '24px',
|
|
41
41
|
backgroundColor: '#e9ecef',
|
|
42
|
-
borderRadius: '
|
|
42
|
+
borderRadius: '12px',
|
|
43
43
|
overflow: 'hidden',
|
|
44
|
-
|
|
44
|
+
position: 'relative'
|
|
45
45
|
}}
|
|
46
46
|
>
|
|
47
47
|
<div
|
|
@@ -49,21 +49,66 @@ const renderProgressBar = (value, options = {}) => {
|
|
|
49
49
|
width: `${Math.min(Math.max(percentage, 0), 100)}%`,
|
|
50
50
|
height: '100%',
|
|
51
51
|
backgroundColor: backgroundColor,
|
|
52
|
-
borderRadius: '
|
|
52
|
+
borderRadius: '12px',
|
|
53
53
|
transition: 'width 0.3s ease'
|
|
54
54
|
}}
|
|
55
55
|
/>
|
|
56
|
+
{showPercentage && (
|
|
57
|
+
<span style={{
|
|
58
|
+
position: 'absolute',
|
|
59
|
+
top: '50%',
|
|
60
|
+
left: '50%',
|
|
61
|
+
transform: 'translate(-50%, -50%)',
|
|
62
|
+
fontSize: '12px',
|
|
63
|
+
fontWeight: '600',
|
|
64
|
+
color: percentage > 50 ? '#fff' : '#333',
|
|
65
|
+
textShadow: percentage > 50 ? '0 1px 2px rgba(0,0,0,0.3)' : '0 1px 2px rgba(255,255,255,0.8)',
|
|
66
|
+
zIndex: 1
|
|
67
|
+
}}>
|
|
68
|
+
{percentage.toFixed(1)}%
|
|
69
|
+
</span>
|
|
70
|
+
)}
|
|
56
71
|
</div>
|
|
57
|
-
{showPercentage && (
|
|
58
|
-
<span style={{ fontSize: '12px', color: '#6c757d', minWidth: '35px' }}>
|
|
59
|
-
{percentage.toFixed(1)}%
|
|
60
|
-
</span>
|
|
61
|
-
)}
|
|
62
72
|
</div>
|
|
63
73
|
);
|
|
64
74
|
};
|
|
65
75
|
|
|
66
|
-
const
|
|
76
|
+
const renderStatusMessage = (status, messages) => {
|
|
77
|
+
const message = messages[status] || messages.default || 'Status not available';
|
|
78
|
+
return (
|
|
79
|
+
<div style={{
|
|
80
|
+
fontSize: '12px',
|
|
81
|
+
color: '#6c757d',
|
|
82
|
+
fontStyle: 'italic',
|
|
83
|
+
padding: '4px 8px',
|
|
84
|
+
backgroundColor: '#f8f9fa',
|
|
85
|
+
borderRadius: '4px',
|
|
86
|
+
border: '1px solid #e9ecef'
|
|
87
|
+
}}>
|
|
88
|
+
{message}
|
|
89
|
+
</div>
|
|
90
|
+
);
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
const checkCondition = (rowData, condition) => {
|
|
94
|
+
if (!condition || !rowData) return false;
|
|
95
|
+
|
|
96
|
+
const { field, value, operator = 'equals' } = condition;
|
|
97
|
+
const fieldValue = rowData[field];
|
|
98
|
+
|
|
99
|
+
switch (operator) {
|
|
100
|
+
case 'equals':
|
|
101
|
+
return fieldValue === value;
|
|
102
|
+
case 'not_equals':
|
|
103
|
+
return fieldValue !== value;
|
|
104
|
+
case 'contains':
|
|
105
|
+
return fieldValue && fieldValue.toString().includes(value);
|
|
106
|
+
default:
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
const renderCellContent = (value, column, widget, rowData = {}) => {
|
|
67
112
|
const columnFormatting = widget.columnFormatting || {};
|
|
68
113
|
const formatting = columnFormatting[column];
|
|
69
114
|
|
|
@@ -71,6 +116,17 @@ const renderCellContent = (value, column, widget) => {
|
|
|
71
116
|
return renderProgressBar(value, formatting);
|
|
72
117
|
}
|
|
73
118
|
|
|
119
|
+
if (formatting && formatting.type === 'conditionalProgressBar') {
|
|
120
|
+
const conditionMet = checkCondition(rowData, formatting.condition);
|
|
121
|
+
|
|
122
|
+
if (conditionMet) {
|
|
123
|
+
return renderProgressBar(value, formatting);
|
|
124
|
+
} else if (formatting.fallback && formatting.fallback.type === 'statusMessage') {
|
|
125
|
+
const status = rowData[formatting.condition.field] || 'default';
|
|
126
|
+
return renderStatusMessage(status, formatting.fallback.messages);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
74
130
|
return value;
|
|
75
131
|
};
|
|
76
132
|
|
|
@@ -94,6 +150,9 @@ function GenericDashboard({ setting, userProfile, dynamicDashboard = false }) {
|
|
|
94
150
|
const [widgetAdded, setWidgetAdded] = useState(false);
|
|
95
151
|
// State to track viewport width
|
|
96
152
|
const [viewportWidth, setViewportWidth] = useState(window.innerWidth);
|
|
153
|
+
// Auto-refresh state
|
|
154
|
+
const [autoRefreshEnabled, setAutoRefreshEnabled] = useState(false);
|
|
155
|
+
const autoRefreshIntervalRef = useRef(null);
|
|
97
156
|
|
|
98
157
|
// Compute available widgets from original settings that are not yet added.
|
|
99
158
|
const availableWidgets = setting.widgets.filter(
|
|
@@ -260,6 +319,39 @@ function GenericDashboard({ setting, userProfile, dynamicDashboard = false }) {
|
|
|
260
319
|
};
|
|
261
320
|
}, []);
|
|
262
321
|
|
|
322
|
+
// Auto-refresh functionality
|
|
323
|
+
useEffect(() => {
|
|
324
|
+
const widgets = dashboardSetting.widgets || [];
|
|
325
|
+
const autoRefreshWidget = widgets.find(widget => widget.autoRefresh?.enabled);
|
|
326
|
+
|
|
327
|
+
if (autoRefreshWidget && autoRefreshEnabled) {
|
|
328
|
+
const interval = autoRefreshWidget.autoRefresh.interval || 30000; // Default 30 seconds
|
|
329
|
+
|
|
330
|
+
autoRefreshIntervalRef.current = setInterval(() => {
|
|
331
|
+
if (Object.keys(filters).length > 0) {
|
|
332
|
+
fetchData(filters);
|
|
333
|
+
}
|
|
334
|
+
}, interval);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
return () => {
|
|
338
|
+
if (autoRefreshIntervalRef.current) {
|
|
339
|
+
clearInterval(autoRefreshIntervalRef.current);
|
|
340
|
+
autoRefreshIntervalRef.current = null;
|
|
341
|
+
}
|
|
342
|
+
};
|
|
343
|
+
}, [autoRefreshEnabled, filters, dashboardSetting]);
|
|
344
|
+
|
|
345
|
+
// Initialize auto-refresh from widget settings
|
|
346
|
+
useEffect(() => {
|
|
347
|
+
const widgets = dashboardSetting.widgets || [];
|
|
348
|
+
const autoRefreshWidget = widgets.find(widget => widget.autoRefresh?.enabled);
|
|
349
|
+
|
|
350
|
+
if (autoRefreshWidget) {
|
|
351
|
+
setAutoRefreshEnabled(true);
|
|
352
|
+
}
|
|
353
|
+
}, [dashboardSetting]);
|
|
354
|
+
|
|
263
355
|
const openModal = (widgetId) => {
|
|
264
356
|
setModalContent(widgetId);
|
|
265
357
|
setModalShow(true);
|
|
@@ -1119,7 +1211,8 @@ function GenericDashboard({ setting, userProfile, dynamicDashboard = false }) {
|
|
|
1119
1211
|
{renderCellContent(
|
|
1120
1212
|
row[column],
|
|
1121
1213
|
column,
|
|
1122
|
-
widget
|
|
1214
|
+
widget,
|
|
1215
|
+
row
|
|
1123
1216
|
)}
|
|
1124
1217
|
</td>
|
|
1125
1218
|
)
|
|
@@ -1333,6 +1426,25 @@ function GenericDashboard({ setting, userProfile, dynamicDashboard = false }) {
|
|
|
1333
1426
|
>
|
|
1334
1427
|
<div className={styles.widgetTitle}>
|
|
1335
1428
|
<h2>{widget.title}</h2>
|
|
1429
|
+
{widget.autoRefresh?.enabled && widget.autoRefresh?.showControls && (
|
|
1430
|
+
<div className={styles.autoRefreshControl}>
|
|
1431
|
+
<label style={{
|
|
1432
|
+
display: 'flex',
|
|
1433
|
+
alignItems: 'center',
|
|
1434
|
+
fontSize: '12px',
|
|
1435
|
+
color: '#6c757d',
|
|
1436
|
+
marginLeft: '10px'
|
|
1437
|
+
}}>
|
|
1438
|
+
<input
|
|
1439
|
+
type="checkbox"
|
|
1440
|
+
checked={autoRefreshEnabled}
|
|
1441
|
+
onChange={(e) => setAutoRefreshEnabled(e.target.checked)}
|
|
1442
|
+
style={{ marginRight: '5px' }}
|
|
1443
|
+
/>
|
|
1444
|
+
Auto-refresh ({Math.round((widget.autoRefresh.interval || 30000) / 1000)}s)
|
|
1445
|
+
</label>
|
|
1446
|
+
</div>
|
|
1447
|
+
)}
|
|
1336
1448
|
</div>
|
|
1337
1449
|
{renderFilters(widget)}
|
|
1338
1450
|
<div>{renderWidget(widget)}</div>
|
|
@@ -641,10 +641,8 @@ form div:has(.react-toggle) input[type='checkbox'] {
|
|
|
641
641
|
}
|
|
642
642
|
|
|
643
643
|
.relation-color-text {
|
|
644
|
-
font-size: 0.875rem;
|
|
645
644
|
font-weight: 500;
|
|
646
645
|
color: #495057;
|
|
647
|
-
font-family: 'Courier New', monospace;
|
|
648
646
|
user-select: all;
|
|
649
647
|
}
|
|
650
648
|
|
|
@@ -720,10 +718,8 @@ form div:has(.react-toggle) input[type='checkbox'] {
|
|
|
720
718
|
}
|
|
721
719
|
|
|
722
720
|
.cpicker-value {
|
|
723
|
-
font-size: 0.8rem;
|
|
724
721
|
font-weight: 500;
|
|
725
722
|
color: #495057;
|
|
726
|
-
font-family: 'Courier New', monospace;
|
|
727
723
|
background: rgba(255, 255, 255, 0.9);
|
|
728
724
|
padding: 3px 6px;
|
|
729
725
|
border-radius: 3px;
|