@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 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.7.1",
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.5",
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.5",
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.2",
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.20",
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 style={{ fontSize: '0.8rem' }}>
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 style={{ fontSize: '0.8rem' }}>
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={{ display: 'flex', alignItems: 'center', width: '100%' }}>
36
+ <div style={{ width: '100%', minWidth: '150px' }}>
37
37
  <div
38
38
  style={{
39
- width: '100px',
40
- height: '20px',
39
+ width: '100%',
40
+ height: '24px',
41
41
  backgroundColor: '#e9ecef',
42
- borderRadius: '10px',
42
+ borderRadius: '12px',
43
43
  overflow: 'hidden',
44
- marginRight: showPercentage ? '8px' : '0'
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: '10px',
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 renderCellContent = (value, column, widget) => {
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>
@@ -63,6 +63,9 @@
63
63
  height: auto;
64
64
  padding: 0;
65
65
  margin-bottom: 0.85rem;
66
+ display: flex;
67
+ justify-content: space-between;
68
+ align-items: center;
66
69
  }
67
70
  .widgetTitle h2 {
68
71
  font-size: 1rem;
@@ -417,10 +417,8 @@
417
417
  }
418
418
 
419
419
  .cpicker-value {
420
- font-size: 0.8rem;
421
420
  font-weight: 500;
422
421
  color: #495057;
423
- font-family: 'Courier New', monospace;
424
422
  background: rgba(255, 255, 255, 0.9);
425
423
  padding: 3px 6px;
426
424
  border-radius: 3px;
@@ -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;