@visns-studio/visns-components 5.6.8 → 5.6.9

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
@@ -84,7 +84,7 @@
84
84
  "react-dom": "^17.0.0 || ^18.0.0"
85
85
  },
86
86
  "name": "@visns-studio/visns-components",
87
- "version": "5.6.8",
87
+ "version": "5.6.9",
88
88
  "description": "Various packages to assist in the development of our Custom Applications.",
89
89
  "main": "src/index.js",
90
90
  "files": [
@@ -2,31 +2,86 @@ import React, { useState, useEffect, useRef } from 'react';
2
2
  import styles from './styles/TableFilter.module.scss';
3
3
 
4
4
  function TableFilter({
5
- collapsible = false,
6
5
  filters,
7
6
  setFilters,
8
7
  setSettings,
9
8
  type,
9
+ onFilterChange,
10
10
  }) {
11
- const [visibleChildren, setVisibleChildren] = useState(null);
11
+ const [visibleChildren, setVisibleChildren] = useState({});
12
12
  const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
13
13
  const isFirstRender = useRef(true);
14
14
 
15
+ // Initialize visible children for all parent categories
15
16
  useEffect(() => {
16
- if (
17
- isFirstRender.current &&
18
- filters.length > 0 &&
19
- filters[0].children &&
20
- filters[0].children.length > 0
21
- ) {
22
- setVisibleChildren(filters[0].id);
17
+ if (isFirstRender.current && filters.length > 0) {
18
+ const initialVisibleState = {};
19
+
20
+ // Find the first parent with isParent=true and set only it to visible
21
+ let foundFirstParent = false;
22
+
23
+ for (const filter of filters) {
24
+ if (
25
+ filter.isParent &&
26
+ filter.children &&
27
+ filter.children.length > 0
28
+ ) {
29
+ // Only show the first parent's children by default
30
+ // and ensure all others are explicitly set to false
31
+ initialVisibleState[filter.id] = !foundFirstParent;
32
+
33
+ if (!foundFirstParent) {
34
+ foundFirstParent = true;
35
+ }
36
+ }
37
+ }
38
+
39
+ setVisibleChildren(initialVisibleState);
23
40
  isFirstRender.current = false;
24
41
  }
25
42
  }, [filters]);
26
43
 
44
+ // Toggle visibility of children when clicking on a parent
45
+ // Only one parent can be open at a time
46
+ const toggleChildren = (parentId) => {
47
+ setVisibleChildren((prev) => {
48
+ // If this parent is already open, close it
49
+ if (prev[parentId]) {
50
+ return {
51
+ ...prev,
52
+ [parentId]: false,
53
+ };
54
+ }
55
+
56
+ // Otherwise, close all parents and open this one
57
+ const newState = {};
58
+ Object.keys(prev).forEach((key) => {
59
+ newState[key] = false;
60
+ });
61
+
62
+ return {
63
+ ...newState,
64
+ [parentId]: true,
65
+ };
66
+ });
67
+ };
68
+
27
69
  const filterTable = (e) => {
70
+ e.preventDefault();
28
71
  const { id, parent, filtertype, url, value } = e.target.dataset;
29
72
 
73
+ // If clicking on a parent category, toggle its children visibility
74
+ if (filtertype === 'parent') {
75
+ const clickedFilter = filters.find((f) => f.id === id);
76
+ if (clickedFilter && clickedFilter.isParent) {
77
+ toggleChildren(id);
78
+
79
+ // Don't activate the parent as a filter, but keep processing to update settings
80
+ // This allows the parent to expand/collapse while still triggering content updates
81
+ }
82
+ }
83
+
84
+ // Handle filter selection (both parent and child)
30
85
  const updateFilter = (filter) => {
31
86
  let isActive = false;
32
87
 
@@ -39,83 +94,171 @@ function TableFilter({
39
94
  const isParent =
40
95
  parent && filtertype === 'children' && filter.id === parent;
41
96
 
42
- const updatedChildren = isParent
43
- ? filter.children.map((child) => ({
44
- ...child,
45
- class: child.id === id ? 'subactivechildren' : '',
46
- show: child.id === id ? true : false,
47
- }))
48
- : filter.children
49
- ? filter.children.map((child, childKey) => ({
50
- ...child,
51
- class:
52
- childKey === 0 && isActive ? 'subactivechildren' : '',
53
- show: childKey === 0 && isActive ? true : false,
54
- }))
55
- : [];
97
+ // For parent filters
98
+ if (filter.isParent) {
99
+ // If this is the clicked parent, expand/collapse it
100
+ if (filter.id === id && filtertype === 'parent') {
101
+ // Update children - don't change their active state
102
+ const updatedChildren = filter.children
103
+ ? filter.children.map((child) => ({
104
+ ...child,
105
+ // Preserve existing active state
106
+ class: child.class,
107
+ show: child.show,
108
+ }))
109
+ : [];
56
110
 
57
- return {
58
- ...filter,
59
- class:
60
- isActive || isParent
111
+ return {
112
+ ...filter,
113
+ // Don't change parent's active state when just expanding/collapsing
114
+ class: filter.class,
115
+ show: filter.show,
116
+ children: updatedChildren,
117
+ };
118
+ }
119
+ // If this is a parent of a clicked child
120
+ else if (isParent) {
121
+ // Update children - set the clicked child as active
122
+ const updatedChildren = filter.children
123
+ ? filter.children.map((child) => {
124
+ const isActiveChild = child.id === id;
125
+ return {
126
+ ...child,
127
+ class: isActiveChild
128
+ ? 'subactivechildren'
129
+ : '',
130
+ show: isActiveChild,
131
+ // Make sure the child has the active state set
132
+ active: isActiveChild,
133
+ };
134
+ })
135
+ : [];
136
+
137
+ return {
138
+ ...filter,
139
+ // Parent becomes active when its child is selected
140
+ class: 'subactive',
141
+ show: true,
142
+ active: true,
143
+ children: updatedChildren,
144
+ };
145
+ }
146
+ // Other parents - deactivate them and their children
147
+ else {
148
+ const updatedChildren = filter.children
149
+ ? filter.children.map((child) => ({
150
+ ...child,
151
+ class: '',
152
+ show: false,
153
+ active: false,
154
+ }))
155
+ : [];
156
+
157
+ return {
158
+ ...filter,
159
+ class: '',
160
+ show: false,
161
+ active: false,
162
+ children: updatedChildren,
163
+ };
164
+ }
165
+ }
166
+ // For non-parent filters (backward compatibility)
167
+ else {
168
+ return {
169
+ ...filter,
170
+ class: isActive
61
171
  ? type === 'simple'
62
172
  ? 'activetab'
63
173
  : 'subactive'
64
174
  : '',
65
- show: isActive || isParent,
66
- children: updatedChildren,
67
- };
175
+ show: isActive,
176
+ active: isActive,
177
+ };
178
+ }
68
179
  };
69
180
 
70
- setFilters((prevFilters) => prevFilters.map(updateFilter));
181
+ // Calculate the updated filters
182
+ const updatedFilters = filters.map(updateFilter);
71
183
 
72
- if (url && url !== '') {
184
+ // Only navigate to URL if it's provided and not a parent toggle action
185
+ if (
186
+ url &&
187
+ url !== '' &&
188
+ !(
189
+ filtertype === 'parent' &&
190
+ filters.find(
191
+ (f) => f.id === id && f.isParent && f.children?.length > 0
192
+ )
193
+ )
194
+ ) {
73
195
  window.open(url);
74
- } else {
75
- if (setSettings) {
76
- setSettings((prevState) => {
77
- if (prevState.ajaxSetting) {
78
- let _where = prevState.ajaxSetting.where.reduce(
79
- (acc, a) => {
80
- if (
81
- a.id === id &&
82
- !acc.some((b) => b.id === id)
83
- ) {
84
- acc.push({ id: id, value: value });
85
- } else {
86
- acc.push(a);
87
- }
88
- return acc;
89
- },
90
- []
91
- );
92
-
93
- return {
94
- ...prevState,
95
- ajaxSetting: {
96
- ...prevState.ajaxSetting,
97
- where: _where,
98
- },
99
- };
100
- } else {
101
- let w = prevState.where
102
- ? prevState.where.map((a) => {
103
- if (id === a.id) {
104
- return { ...a, value: value };
105
- }
106
- return a;
107
- })
108
- : [];
109
-
110
- return { ...prevState, where: w };
111
- }
112
- });
196
+ }
197
+
198
+ // Calculate the updated settings
199
+ let updatedSettings = null;
200
+
201
+ if (setSettings) {
202
+ // For child items or non-parent items, we need to update the filter value
203
+ if (
204
+ filtertype === 'children' ||
205
+ (filtertype === 'parent' &&
206
+ !filters.find((f) => f.id === id && f.isParent))
207
+ ) {
208
+ // For child filters, we need to update the filter with the child's ID
209
+ const filterId = id;
210
+ const filterValue = value || '';
211
+
212
+ // Start with the current settings
213
+ updatedSettings = { ...setSettings._currentValue };
214
+
215
+ if (updatedSettings.ajaxSetting) {
216
+ // First, remove any existing filters that might conflict
217
+ let _where = [...updatedSettings.ajaxSetting.where].filter(
218
+ (item) => {
219
+ // Keep items that don't match this filter ID
220
+ return item.id !== filterId;
221
+ }
222
+ );
223
+
224
+ // Add the new filter
225
+ _where.push({ id: filterId, value: filterValue });
226
+
227
+ updatedSettings = {
228
+ ...updatedSettings,
229
+ ajaxSetting: {
230
+ ...updatedSettings.ajaxSetting,
231
+ where: _where,
232
+ },
233
+ };
234
+ } else if (updatedSettings.where) {
235
+ // Handle the case where we're using 'where' directly instead of ajaxSetting.where
236
+ let w = updatedSettings.where
237
+ ? [...updatedSettings.where]
238
+ : [];
239
+
240
+ // Remove any existing filters that might conflict
241
+ w = w.filter((item) => item.id !== filterId);
242
+
243
+ // Add the new filter
244
+ w.push({ id: filterId, value: filterValue });
245
+
246
+ updatedSettings = { ...updatedSettings, where: w };
247
+ }
113
248
  }
114
249
  }
115
250
 
116
- // Handle collapsible functionality
117
- if (collapsible && filtertype === 'parent') {
118
- setVisibleChildren(id);
251
+ // Use the onFilterChange prop if available, otherwise use the individual setters
252
+ if (onFilterChange) {
253
+ onFilterChange(updatedFilters, updatedSettings);
254
+ } else {
255
+ // Update the filters
256
+ setFilters(updatedFilters);
257
+
258
+ // Update the settings if needed
259
+ if (updatedSettings && setSettings) {
260
+ setSettings(updatedSettings);
261
+ }
119
262
  }
120
263
 
121
264
  // Close mobile menu after selection on small screens
@@ -123,9 +266,15 @@ function TableFilter({
123
266
  };
124
267
 
125
268
  const renderContent = (d) => {
126
- const parentClass = `${styles.link} ${styles[d.class] || ''}`;
269
+ // Determine if this is a parent category
270
+ const isParent = d.isParent === true;
271
+
272
+ // Set appropriate classes
273
+ const parentClass = `${isParent ? styles.parentLink : styles.link} ${
274
+ styles[d.class] || ''
275
+ }`;
127
276
  const childClass = (child) =>
128
- `${styles.link} ${styles[child.class] || ''}`;
277
+ `${styles.childLink} ${styles[child.class] || ''}`;
129
278
 
130
279
  if (type === 'simple') {
131
280
  return (
@@ -143,8 +292,9 @@ function TableFilter({
143
292
  );
144
293
  } else {
145
294
  return (
146
- <li>
295
+ <li className={isParent ? styles.parentItem : ''}>
147
296
  <a
297
+ href="#"
148
298
  data-id={d.id}
149
299
  data-filtertype="parent"
150
300
  data-url={d.url || ''}
@@ -153,35 +303,62 @@ function TableFilter({
153
303
  className={parentClass}
154
304
  >
155
305
  {d.label}
306
+ {isParent && d.children && d.children.length > 0 && (
307
+ <span
308
+ className={`${styles.arrow} ${
309
+ visibleChildren[d.id]
310
+ ? styles.arrowDown
311
+ : ''
312
+ }`}
313
+ >
314
+
315
+ </span>
316
+ )}
156
317
  </a>
157
- {d.children && d.children.length > 0 && (
158
- <ul
318
+ {isParent && d.children && d.children.length > 0 && (
319
+ <div
159
320
  className={`${styles.collapsibleMenu} ${
160
- visibleChildren === d.id
161
- ? styles.visibleMenu
162
- : ''
321
+ visibleChildren[d.id] ? styles.visibleMenu : ''
163
322
  }`}
164
323
  >
165
- {d.children.map((child, key) => (
166
- <li key={`table-filter-child-${d.id}-${key}`}>
167
- <a
168
- className={childClass(child)}
169
- data-id={child.id}
170
- data-value={
171
- child.hasOwnProperty('value')
172
- ? child.value
173
- : ''
174
- }
175
- data-url={child.url || ''}
176
- data-filtertype="children"
177
- data-parent={d.id}
178
- onClick={filterTable}
324
+ <ul
325
+ style={{
326
+ listStyle: 'none',
327
+ padding: 0,
328
+ margin: 0,
329
+ }}
330
+ >
331
+ {d.children.map((child, key) => (
332
+ <li
333
+ key={`table-filter-child-${d.id}-${key}`}
334
+ className={styles.childItem}
179
335
  >
180
- {child.label}
181
- </a>
182
- </li>
183
- ))}
184
- </ul>
336
+ <a
337
+ href="#"
338
+ className={childClass(child)}
339
+ data-id={child.id}
340
+ data-value={
341
+ child.hasOwnProperty('value')
342
+ ? child.value
343
+ : ''
344
+ }
345
+ data-url={child.url || ''}
346
+ data-filtertype="children"
347
+ data-parent={d.id}
348
+ onClick={(e) => {
349
+ // Make sure the parent is expanded when a child is clicked
350
+ if (!visibleChildren[d.id]) {
351
+ toggleChildren(d.id);
352
+ }
353
+ filterTable(e);
354
+ }}
355
+ >
356
+ {child.label}
357
+ </a>
358
+ </li>
359
+ ))}
360
+ </ul>
361
+ </div>
185
362
  )}
186
363
  </li>
187
364
  );
@@ -193,7 +370,15 @@ function TableFilter({
193
370
  };
194
371
 
195
372
  // Find active filter for mobile display
196
- const activeFilter = filters.find((filter) => filter.show === true);
373
+ const activeFilter = filters.find((filter) => {
374
+ if (filter.isParent) {
375
+ return (
376
+ filter.children && filter.children.some((child) => child.show)
377
+ );
378
+ }
379
+ return filter.show === true;
380
+ });
381
+
197
382
  const activeLabel = activeFilter ? activeFilter.label : 'Select Option';
198
383
 
199
384
  return (
@@ -265,15 +265,11 @@ const TwoFactorAuth = ({ logo, setSystemAuth, setUserProfile }) => {
265
265
  alt="CRM"
266
266
  className={styles.loginlogo}
267
267
  />
268
- <div
269
- className={`${styles.formItem} ${styles.fwItem}`}
270
- >
271
- <h2>Two-Factor Authentication</h2>
272
- <p className={styles.instructions}>
273
- Please enter the 6-digit code from your
274
- authenticator app.
275
- </p>
276
- </div>
268
+ <h2>Two-Factor Authentication</h2>
269
+ <p className={styles.instructions}>
270
+ Please enter the 6-digit code from your
271
+ authenticator app.
272
+ </p>
277
273
  <div
278
274
  className={`${styles.formItem} ${styles.fwItem}`}
279
275
  >
@@ -308,6 +304,7 @@ const TwoFactorAuth = ({ logo, setSystemAuth, setUserProfile }) => {
308
304
  className={styles.btn}
309
305
  type="submit"
310
306
  disabled={isLoading}
307
+ tabIndex="2"
311
308
  >
312
309
  {isLoading ? 'Verifying...' : 'Verify'}
313
310
  </button>
@@ -330,6 +327,7 @@ const TwoFactorAuth = ({ logo, setSystemAuth, setUserProfile }) => {
330
327
  e.preventDefault();
331
328
  navigate('/login');
332
329
  }}
330
+ tabIndex="3"
333
331
  >
334
332
  Back to Login
335
333
  </a>