@visns-studio/visns-components 5.12.10 → 5.12.12

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
@@ -87,7 +87,7 @@
87
87
  "react-dom": "^17.0.0 || ^18.0.0"
88
88
  },
89
89
  "name": "@visns-studio/visns-components",
90
- "version": "5.12.10",
90
+ "version": "5.12.12",
91
91
  "description": "Various packages to assist in the development of our Custom Applications.",
92
92
  "main": "src/index.js",
93
93
  "files": [
@@ -1928,6 +1928,20 @@ const DataGrid = forwardRef(
1928
1928
  let iconStyle = {};
1929
1929
  let allow = true;
1930
1930
 
1931
+ // Check role-based permissions if roles are specified
1932
+ if (s.roles && Array.isArray(s.roles) && s.roles.length > 0) {
1933
+ // Check if user has any of the required roles
1934
+ const hasRequiredRole = s.roles.some((requiredRole) =>
1935
+ userProfile?.roles?.some(
1936
+ (userRole) => userRole.name === requiredRole
1937
+ )
1938
+ );
1939
+
1940
+ if (!hasRequiredRole) {
1941
+ allow = false;
1942
+ }
1943
+ }
1944
+
1931
1945
  if (s.active) {
1932
1946
  // Helper function to get nested values for main active condition (searches for target value)
1933
1947
  const getNestedValueForMain = (data, path, targetValue) => {
@@ -2836,7 +2850,25 @@ const DataGrid = forwardRef(
2836
2850
 
2837
2851
  const newColumns = columns.map(renderColumn);
2838
2852
 
2839
- if (settings.length > 0) {
2853
+ // Function to check if any settings would be visible for any row
2854
+ const hasVisibleSettings = () => {
2855
+ if (settings.length === 0) return false;
2856
+
2857
+ // Check if any setting would be visible based on role permissions
2858
+ return settings.some(setting => {
2859
+ // Check role-based permissions
2860
+ if (setting.roles && Array.isArray(setting.roles) && setting.roles.length > 0) {
2861
+ const hasRequiredRole = setting.roles.some(requiredRole =>
2862
+ userProfile?.roles?.some(userRole => userRole.name === requiredRole)
2863
+ );
2864
+ return hasRequiredRole;
2865
+ }
2866
+ // If no roles specified, setting is visible
2867
+ return true;
2868
+ });
2869
+ };
2870
+
2871
+ if (hasVisibleSettings()) {
2840
2872
  newColumns.push({
2841
2873
  name: 'setting',
2842
2874
  header: 'Action',
@@ -59,13 +59,35 @@ const ColourPicker = ({ value, columnId, keyCounter, onChange }) => {
59
59
  <div className={styles.cpicker__btn}>
60
60
  <button
61
61
  ref={buttonRef}
62
- style={value ? { background: value } : {}}
62
+ style={value ? {
63
+ background: value,
64
+ border: '1px solid rgba(0,0,0,0.2)',
65
+ borderRadius: '3px',
66
+ width: '20px',
67
+ height: '20px',
68
+ padding: '0',
69
+ cursor: 'pointer',
70
+ display: 'flex',
71
+ alignItems: 'center',
72
+ justifyContent: 'center'
73
+ } : {
74
+ background: 'transparent',
75
+ border: '1px solid rgba(0,0,0,0.2)',
76
+ borderRadius: '3px',
77
+ width: '20px',
78
+ height: '20px',
79
+ padding: '0',
80
+ cursor: 'pointer',
81
+ display: 'flex',
82
+ alignItems: 'center',
83
+ justifyContent: 'center'
84
+ }}
63
85
  onClick={(e) => {
64
86
  e.preventDefault();
65
87
  setColorPickerShow(!colorPickerShow);
66
88
  }}
67
89
  >
68
- <Droplets size={20} style={{ color: 'black' }} />
90
+ <Droplets size={16} style={{ color: value ? 'white' : 'black' }} />
69
91
  </button>
70
92
 
71
93
  {colorPickerShow &&
@@ -124,6 +146,7 @@ const GenericEditableTable = ({
124
146
  const isInitialLoad = useRef(true);
125
147
  const { columns, rows, form, title } = schedulingConfig;
126
148
  const hasCategory = rows && rows.categoryKey; // Only group if defined
149
+ const hasColourColumn = columns.some(col => col.id === 'colours');
127
150
 
128
151
  // dataSets: each item => { created_at: string, data: array of rows }
129
152
  const [dataSets, setDataSets] = useState([]);
@@ -203,12 +226,11 @@ const GenericEditableTable = ({
203
226
  const sortBySortOrder = (dataArray) =>
204
227
  [...dataArray].sort((a, b) => a.sort_order - b.sort_order);
205
228
 
206
- // Return background color if there's a column of type 'colour'
229
+ // Return background color from row_colour field or colours column
207
230
  const getRowBackground = (entry) => {
208
- const colourColumn = columns.find((col) => col.type === 'colour');
209
- return colourColumn && entry[colourColumn.id]
210
- ? entry[colourColumn.id]
211
- : undefined;
231
+ // Check if there's a colour picked from the colours column
232
+ const coloursValue = entry.colours || entry.row_colour;
233
+ return coloursValue ? `${coloursValue}40` : undefined; // Add 40% opacity
212
234
  };
213
235
 
214
236
  // Calculate total for columns of type 'total'
@@ -623,16 +645,26 @@ const GenericEditableTable = ({
623
645
  const renderScheduledTableField = (entry, column, keyCounter) => {
624
646
  if (!entry || !column) return null;
625
647
 
648
+ // Special handling for colours column regardless of type
649
+ if (column.id === 'colours') {
650
+ return (
651
+ <ColourPicker
652
+ value={entry[column.id] || ''}
653
+ columnId={column.id}
654
+ keyCounter={keyCounter}
655
+ onChange={(value, fieldId, keyCounter) => {
656
+ // Update both the colours field and row_colour for row highlighting
657
+ handleFieldChange(value, fieldId, keyCounter);
658
+ handleFieldChange(value, 'row_colour', keyCounter);
659
+ }}
660
+ />
661
+ );
662
+ }
663
+
626
664
  switch (column.type) {
627
665
  case 'colour':
628
- return (
629
- <ColourPicker
630
- value={entry[column.id] || ''}
631
- columnId={column.id}
632
- keyCounter={keyCounter}
633
- onChange={handleFieldChange}
634
- />
635
- );
666
+ // For other colour columns, picker is rendered in the action column
667
+ return null;
636
668
  case 'currency':
637
669
  case 'number':
638
670
  return (
@@ -752,18 +784,27 @@ const GenericEditableTable = ({
752
784
  // Render new row field
753
785
  const renderNewRowField = (newRowData, column, groupId = null) => {
754
786
  const value = newRowData ? newRowData[column.id] : '';
787
+
788
+ // Special handling for colours column regardless of type
789
+ if (column.id === 'colours') {
790
+ return (
791
+ <ColourPicker
792
+ value={value || ''}
793
+ columnId={column.id}
794
+ keyCounter={0}
795
+ onChange={(val, colId) => {
796
+ // Update both the colours field and row_colour for row highlighting
797
+ handleNewFieldChange(val, colId, groupId);
798
+ handleNewFieldChange(val, 'row_colour', groupId);
799
+ }}
800
+ />
801
+ );
802
+ }
803
+
755
804
  switch (column.type) {
756
805
  case 'colour':
757
- return (
758
- <ColourPicker
759
- value={value}
760
- columnId={column.id}
761
- keyCounter={0}
762
- onChange={(val) =>
763
- handleNewFieldChange(val, column.id, groupId)
764
- }
765
- />
766
- );
806
+ // For other colour columns, picker is rendered in the action column
807
+ return null;
767
808
  case 'currency':
768
809
  case 'number':
769
810
  return (
@@ -1027,6 +1068,14 @@ const GenericEditableTable = ({
1027
1068
  <div
1028
1069
  className={styles.tdactions}
1029
1070
  >
1071
+ {!hasColourColumn && (
1072
+ <ColourPicker
1073
+ value={entry.row_colour || ''}
1074
+ columnId={'row_colour'}
1075
+ keyCounter={entry.keyCounter}
1076
+ onChange={handleFieldChange}
1077
+ />
1078
+ )}
1030
1079
  {idx > 0 && (
1031
1080
  <ArrowUp
1032
1081
  size={16}
@@ -1173,6 +1222,14 @@ const GenericEditableTable = ({
1173
1222
  ))}
1174
1223
  <td>
1175
1224
  <div className={styles.tdactions}>
1225
+ {!hasColourColumn && (
1226
+ <ColourPicker
1227
+ value={entry.row_colour || ''}
1228
+ columnId={'row_colour'}
1229
+ keyCounter={idx}
1230
+ onChange={handleFieldChange}
1231
+ />
1232
+ )}
1176
1233
  {idx > 0 && (
1177
1234
  <ArrowUp
1178
1235
  size={16}
@@ -1,47 +1,124 @@
1
1
  .schedulingTable {
2
2
  width: 100%;
3
3
  border-collapse: collapse;
4
- margin: 0.5em 0; // Reduced margin
4
+ margin: 0.1em 0; // Minimal margin
5
5
 
6
6
  th,
7
7
  td {
8
- padding: 0.2rem; // Reduced padding for cells
8
+ padding: 0.08rem 0.25rem; // Ultra-compact padding for cells
9
9
  text-align: left;
10
10
  border: 1px solid rgba(var(--primary-rgb), 0.1);
11
+ line-height: 1; // Minimal line height
12
+ vertical-align: middle; // Ensure vertical alignment
13
+ height: 22px; // Fixed row height
14
+ }
15
+
16
+ // Additional high-specificity selectors to override any global styles
17
+ td input,
18
+ td select,
19
+ th input,
20
+ th select {
21
+ padding: 0.05rem 0.2rem !important;
22
+ margin: 0 !important;
23
+ font-size: 0.7rem !important;
24
+ height: 18px !important;
25
+ line-height: 1 !important;
26
+ border: 1px solid rgba(var(--primary-rgb), 0.1) !important;
27
+ box-sizing: border-box !important;
28
+ border-radius: 2px !important;
29
+ width: 100% !important;
30
+ appearance: none !important;
31
+ background: white !important;
32
+ }
33
+
34
+ td select,
35
+ th select {
36
+ background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12"><path d="M6 9L1 4h10z" fill="%23666"/></svg>') !important;
37
+ background-repeat: no-repeat !important;
38
+ background-position: right 0.2rem center !important;
39
+ background-size: 8px !important;
40
+ padding: 0.05rem 1rem 0.05rem 0.2rem !important;
41
+ cursor: pointer !important;
42
+
43
+ &::-ms-expand {
44
+ display: none !important;
45
+ }
11
46
  }
12
47
 
13
48
  th {
14
- padding: 0.25rem 0.5rem; // Less padding than before
15
- font-size: 0.9rem; // Slightly smaller font size
49
+ padding: 0.1rem 0.25rem; // Ultra-compact header padding
50
+ font-size: 0.75rem; // Smaller font size
16
51
  font-weight: bold;
17
52
  background-color: var(--primary-color);
18
53
  color: white;
19
54
  text-align: center;
55
+ height: 24px; // Fixed header height
20
56
  }
21
57
 
22
58
  td {
23
59
  background-color: rgba(var(--primary-rgb), 0.05);
60
+
61
+ /* Ensure action column cells don't inherit row background */
62
+ &:has(.tdactions) {
63
+ background: rgba(var(--primary-rgb), 0.05) !important;
64
+ }
24
65
  }
25
66
 
26
- /* Compact styling for native input and select elements */
67
+ /* Ultra-compact styling for native input and select elements - override global styles */
27
68
  input,
28
69
  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;
70
+ padding: 0.05rem 0.2rem !important; // Force minimal padding, override global styles
71
+ margin: 0 !important;
72
+ font-size: 0.7rem !important; // Smaller font
73
+ height: 18px !important; // Minimal height
74
+ line-height: 1 !important;
75
+ border: 1px solid rgba(var(--primary-rgb), 0.1) !important;
76
+ box-sizing: border-box !important;
77
+ border-radius: 2px !important; // Smaller border radius
78
+ width: 100% !important;
79
+ appearance: none !important; // Remove default dropdown arrow for consistency
80
+ background: white !important;
81
+
82
+ // Custom dropdown arrow for select elements
83
+ &[type="select"],
84
+ &:not([type]) {
85
+ background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12"><path d="M6 9L1 4h10z" fill="%23666"/></svg>') !important;
86
+ background-repeat: no-repeat !important;
87
+ background-position: right 0.2rem center !important;
88
+ background-size: 8px !important;
89
+ padding-right: 1rem !important;
90
+ }
91
+ }
92
+
93
+ // Specific styling for select elements to match inputs exactly - with !important to override globals
94
+ select {
95
+ appearance: none !important;
96
+ background: white !important;
97
+ background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12"><path d="M6 9L1 4h10z" fill="%23666"/></svg>') !important;
98
+ background-repeat: no-repeat !important;
99
+ background-position: right 0.2rem center !important;
100
+ background-size: 8px !important;
101
+ padding: 0.05rem 1rem 0.05rem 0.2rem !important; // Force specific padding values
102
+ cursor: pointer !important;
103
+
104
+ // Remove default arrow on different browsers
105
+ &::-ms-expand {
106
+ display: none !important;
107
+ }
108
+
109
+ &:focus {
110
+ outline: none !important;
111
+ border-color: var(--primary-color) !important;
112
+ }
36
113
  }
37
114
  }
38
115
 
39
116
  .categoryRow {
40
117
  background-color: rgba(var(--primary-rgb), 0.15);
41
- font-size: 1rem; // Reduced font size
118
+ font-size: 0.9rem; // Further reduced font size
42
119
  font-weight: bold;
43
120
  text-align: left;
44
- padding: 0.5rem 0.75rem; // Reduced padding
121
+ padding: 0.35rem 0.5rem; // Further reduced padding
45
122
  border-bottom: 2px solid var(--primary-color);
46
123
  color: var(--primary-color);
47
124
  }
@@ -156,17 +233,22 @@
156
233
  width: max-content;
157
234
  display: inline-block;
158
235
  position: relative;
159
- padding: 0.4rem 0.75rem; /* Reduced padding for a compact look */
236
+ padding: 0.2rem 0.4rem; /* Ultra-minimal padding */
160
237
  cursor: pointer;
161
- font-size: 1rem; /* Slightly smaller font size */
238
+ font-size: 0.75rem; /* Even smaller font size */
162
239
  color: var(--tertiary-color);
163
240
  text-decoration: none;
164
241
  overflow: hidden;
165
242
  background: var(--primary-color);
166
243
  border: 1px solid rgba(var(--primary-color--rgb), 1.1);
167
- border-radius: var(--br);
244
+ border-radius: 2px; /* Smaller border radius */
168
245
  outline: none;
169
246
  transition: all 0.2s cubic-bezier(0.85, 0, 0.15, 1) 0s;
247
+ height: 20px; /* Match input height */
248
+ line-height: 1;
249
+ display: flex;
250
+ align-items: center;
251
+ justify-content: center;
170
252
 
171
253
  &:hover {
172
254
  color: var(--primary-color);
@@ -181,18 +263,48 @@
181
263
  flex-wrap: nowrap;
182
264
  justify-content: flex-start;
183
265
  align-items: center;
184
- gap: 0.1rem;
266
+ gap: 0.05rem; // Reduced gap
267
+ background: transparent !important; /* Ensure no background color interference */
185
268
 
186
269
  span {
187
270
  display: flex;
188
271
  align-items: center;
189
272
  position: relative;
190
- width: 16px;
191
- height: 16px;
273
+ width: 14px; // Smaller icons
274
+ height: 14px;
192
275
 
193
276
  svg {
194
- width: 16px;
277
+ width: 14px;
278
+ height: 14px;
279
+ }
280
+ }
281
+ }
282
+
283
+ /* Color picker specific styling */
284
+ .cpicker {
285
+ display: flex;
286
+ align-items: center;
287
+
288
+ &__btn {
289
+ display: flex;
290
+ align-items: center;
291
+
292
+ button {
293
+ margin: 0;
294
+ outline: none;
295
+ transition: all 0.2s ease;
296
+ width: 16px; // Smaller color picker button
195
297
  height: 16px;
298
+
299
+ &:hover {
300
+ opacity: 0.8;
301
+ transform: scale(1.05);
302
+ }
303
+
304
+ svg {
305
+ width: 12px; // Smaller icon
306
+ height: 12px;
307
+ }
196
308
  }
197
309
  }
198
310
  }
@@ -221,8 +333,8 @@
221
333
  background-color: rgba(var(--secondary-rgb), 0.15);
222
334
  font-weight: bold;
223
335
  text-align: right;
224
- padding: 0.5em; // Reduced padding
225
- font-size: 0.8rem; // Reduced font size
336
+ padding: 0.3em; // Further reduced padding
337
+ font-size: 0.75rem; // Further reduced font size
226
338
  color: var(--primary-color);
227
339
  border-top: 2px solid var(--primary-color);
228
340
  }
@@ -403,23 +515,53 @@
403
515
  }
404
516
  }
405
517
 
406
- /* Compact styling for react-select components */
518
+ /* Ultra-compact styling for react-select components to match native elements */
407
519
  .react-select__control {
408
- min-height: 28px;
520
+ min-height: 18px; // Match native input height exactly
409
521
  padding: 0;
410
- font-size: 0.85rem;
522
+ font-size: 0.7rem; // Match native input font size
411
523
  border: 1px solid rgba(var(--primary-rgb), 0.1);
412
524
  box-sizing: border-box;
525
+ border-radius: 2px;
526
+ background: white;
527
+
528
+ &:hover {
529
+ border-color: rgba(var(--primary-rgb), 0.2);
530
+ }
531
+
532
+ &--is-focused {
533
+ border-color: var(--primary-color);
534
+ box-shadow: none;
535
+ }
413
536
  }
414
537
 
415
538
  .react-select__value-container {
416
- padding: 0 0.5rem;
539
+ padding: 0.05rem 0.2rem; // Match native input padding exactly
417
540
  margin: 0;
418
541
  }
419
542
 
420
543
  .react-select__input {
421
544
  margin: 0;
422
545
  padding: 0;
546
+
547
+ input {
548
+ font-size: 0.7rem !important;
549
+ height: 16px;
550
+ padding: 0;
551
+ margin: 0;
552
+ }
553
+ }
554
+
555
+ .react-select__single-value {
556
+ font-size: 0.7rem;
557
+ margin: 0;
558
+ color: inherit;
559
+ }
560
+
561
+ .react-select__placeholder {
562
+ font-size: 0.7rem;
563
+ margin: 0;
564
+ color: #999;
423
565
  }
424
566
 
425
567
  .react-select__indicator-separator {
@@ -427,7 +569,92 @@
427
569
  }
428
570
 
429
571
  .react-select__indicators {
430
- padding: 0 0.25rem;
572
+ padding: 0 0.2rem; // Match native select padding
573
+ height: 16px;
574
+
575
+ svg {
576
+ width: 8px; // Match native select arrow size
577
+ height: 8px;
578
+ color: #666;
579
+ }
580
+ }
581
+
582
+ .react-select__dropdown-indicator {
583
+ padding: 0;
584
+ }
585
+
586
+ /* Ultra-compact styling for MultiSelect components to match native inputs */
587
+ .selectFullWidth {
588
+ width: 100%;
589
+
590
+ .react-select__control {
591
+ min-height: 18px; // Match native input height
592
+ height: 18px;
593
+ font-size: 0.7rem; // Match native input font size
594
+ border: 1px solid rgba(var(--primary-rgb), 0.1);
595
+ border-radius: 2px;
596
+ background: white;
597
+
598
+ &:hover {
599
+ border-color: rgba(var(--primary-rgb), 0.2);
600
+ }
601
+
602
+ &--is-focused {
603
+ border-color: var(--primary-color);
604
+ box-shadow: none;
605
+ }
606
+ }
607
+
608
+ .react-select__value-container {
609
+ padding: 0.05rem 0.2rem; // Match native input padding
610
+ height: 16px;
611
+ display: flex;
612
+ align-items: center;
613
+ }
614
+
615
+ .react-select__single-value {
616
+ font-size: 0.7rem; // Match native input font size
617
+ margin: 0;
618
+ color: inherit;
619
+ }
620
+
621
+ .react-select__placeholder {
622
+ font-size: 0.7rem; // Match native input font size
623
+ margin: 0;
624
+ color: #999;
625
+ }
626
+
627
+ .react-select__input-container {
628
+ height: 16px;
629
+ padding: 0;
630
+ margin: 0;
631
+
632
+ input {
633
+ font-size: 0.7rem !important;
634
+ height: 16px;
635
+ padding: 0;
636
+ margin: 0;
637
+ }
638
+ }
639
+
640
+ .react-select__indicators {
641
+ height: 16px;
642
+ padding: 0 0.2rem; // Match native select padding
643
+ }
644
+
645
+ .react-select__dropdown-indicator {
646
+ padding: 0;
647
+
648
+ svg {
649
+ width: 8px; // Match native select arrow size
650
+ height: 8px;
651
+ color: #666;
652
+ }
653
+ }
654
+
655
+ .react-select__indicator-separator {
656
+ display: none;
657
+ }
431
658
  }
432
659
 
433
660
  .newRow {
@@ -437,6 +664,47 @@
437
664
  ); /* Slightly lighter background */
438
665
  border-top: 1px dashed rgba(var(--primary-rgb), 0.3);
439
666
  border-bottom: 1px dashed rgba(var(--primary-rgb), 0.3);
667
+
668
+ /* Ultra-compact styling for new row inputs and selects - force override globals */
669
+ input,
670
+ select {
671
+ height: 18px !important; // Consistent height
672
+ padding: 0.05rem 0.2rem !important; // Force override global padding
673
+ font-size: 0.7rem !important;
674
+ appearance: none !important;
675
+ background: white !important;
676
+ margin: 0 !important;
677
+ line-height: 1 !important;
678
+ border: 1px solid rgba(var(--primary-rgb), 0.1) !important;
679
+ box-sizing: border-box !important;
680
+ border-radius: 2px !important;
681
+ width: 100% !important;
682
+
683
+ // Ensure selects have dropdown arrow
684
+ &[type="select"],
685
+ &:not([type]) {
686
+ background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12"><path d="M6 9L1 4h10z" fill="%23666"/></svg>') !important;
687
+ background-repeat: no-repeat !important;
688
+ background-position: right 0.2rem center !important;
689
+ background-size: 8px !important;
690
+ padding-right: 1rem !important;
691
+ }
692
+ }
693
+
694
+ select {
695
+ appearance: none !important;
696
+ background: white !important;
697
+ background-image: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12"><path d="M6 9L1 4h10z" fill="%23666"/></svg>') !important;
698
+ background-repeat: no-repeat !important;
699
+ background-position: right 0.2rem center !important;
700
+ background-size: 8px !important;
701
+ padding: 0.05rem 1rem 0.05rem 0.2rem !important; // Force specific padding
702
+ cursor: pointer !important;
703
+
704
+ &::-ms-expand {
705
+ display: none !important;
706
+ }
707
+ }
440
708
  }
441
709
 
442
710
  .datasetSelector {