@visns-studio/visns-components 5.6.7 → 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.7",
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 (
@@ -28,20 +28,11 @@ const Login = ({ logo, providers, setSystemAuth, setUserProfile }) => {
28
28
  });
29
29
 
30
30
  const renderSsoButton = () => {
31
- let content =
32
- providers && providers.length > 0 ? (
33
- <div className="formItem fwItem lastItem forgotten">
34
- <span
35
- style={{
36
- display: 'block',
37
- textAlign: 'center',
38
- width: '100%',
39
- }}
40
- >
41
- OR
42
- </span>
43
- </div>
44
- ) : null;
31
+ if (!providers || providers.length === 0) {
32
+ return null;
33
+ }
34
+
35
+ let content = <div className={styles.divider}>OR</div>;
45
36
 
46
37
  if (providers) {
47
38
  providers.forEach((provider) => {
@@ -51,13 +42,12 @@ const Login = ({ logo, providers, setSystemAuth, setUserProfile }) => {
51
42
  <>
52
43
  {content}
53
44
  <div
54
- className={`${styles.formItem} ${styles.fwItem} ${styles.lastItem} ${styles.forgotten}`}
45
+ className={`${styles.formItem} ${styles.fwItem} ${styles.lastItem}`}
55
46
  >
56
47
  <button
57
- className={styles.btn}
48
+ className={styles.ssoButton}
58
49
  onClick={(e) => {
59
50
  e.preventDefault();
60
-
61
51
  window.location.href =
62
52
  '/auth/azure';
63
53
  }}
@@ -66,10 +56,11 @@ const Login = ({ logo, providers, setSystemAuth, setUserProfile }) => {
66
56
  src={
67
57
  'https://d16lktya8ojp5z.cloudfront.net/generic/images/microsoft.png'
68
58
  }
69
- width="13px"
70
- height="13px"
71
- />{' '}
72
- Microsoft
59
+ width="18px"
60
+ height="18px"
61
+ alt="Microsoft logo"
62
+ />
63
+ Sign in with Microsoft
73
64
  </button>
74
65
  </div>
75
66
  </>
@@ -182,6 +173,17 @@ const Login = ({ logo, providers, setSystemAuth, setUserProfile }) => {
182
173
  alt="CRM"
183
174
  className={styles.loginlogo}
184
175
  />
176
+ <h2
177
+ style={{
178
+ textAlign: 'center',
179
+ marginBottom: '1.5rem',
180
+ color: '#111827',
181
+ fontWeight: '600',
182
+ fontSize: '1.5rem',
183
+ }}
184
+ >
185
+ Sign in to your account
186
+ </h2>
185
187
  <div
186
188
  className={`${styles.formItem} ${styles.fwItem}`}
187
189
  >
@@ -232,9 +234,10 @@ const Login = ({ logo, providers, setSystemAuth, setUserProfile }) => {
232
234
  >
233
235
  <div className={styles.rememberMeContainer}>
234
236
  <div
235
- className={
236
- styles.rememberMeCheckboxWrapper
237
- }
237
+ style={{
238
+ display: 'flex',
239
+ alignItems: 'center',
240
+ }}
238
241
  >
239
242
  <input
240
243
  type="checkbox"
@@ -246,13 +249,28 @@ const Login = ({ logo, providers, setSystemAuth, setUserProfile }) => {
246
249
  styles.rememberMeCheckbox
247
250
  }
248
251
  />
252
+ <label
253
+ htmlFor="rememberMe"
254
+ className={
255
+ styles.rememberMeLabel
256
+ }
257
+ >
258
+ Remember me
259
+ </label>
260
+ </div>
261
+ <div>
262
+ <Link
263
+ to="/reset"
264
+ style={{
265
+ color: 'var(--primary-color, #4f46e5)',
266
+ textDecoration: 'none',
267
+ fontWeight: '500',
268
+ fontSize: '0.875rem',
269
+ }}
270
+ >
271
+ Forgot password?
272
+ </Link>
249
273
  </div>
250
- <label
251
- htmlFor="rememberMe"
252
- className={styles.rememberMeLabel}
253
- >
254
- Remember me
255
- </label>
256
274
  </div>
257
275
  </div>
258
276
  <div
@@ -263,17 +281,11 @@ const Login = ({ logo, providers, setSystemAuth, setUserProfile }) => {
263
281
  type="submit"
264
282
  disabled={isLoading}
265
283
  >
266
- {isLoading ? 'Logging in...' : 'Login'}
284
+ {isLoading
285
+ ? 'Signing in...'
286
+ : 'Sign in'}
267
287
  </button>
268
288
  </div>
269
- <div
270
- className={`${styles.formItem} ${styles.fwItem} ${styles.lastItem} ${styles.forgotten}`}
271
- >
272
- <span>
273
- Forgot your password?{' '}
274
- <Link to="/reset">Click Here</Link>
275
- </span>
276
- </div>
277
289
  {renderSsoButton()}
278
290
  </div>
279
291
  </Reveal>
@@ -53,11 +53,17 @@ const Reset = ({ logo }) => {
53
53
  alt="CRM"
54
54
  className={styles.loginlogo}
55
55
  />
56
- <div
57
- className={`${styles.formItem} ${styles.fwItem}`}
56
+ <h2
57
+ style={{
58
+ textAlign: 'center',
59
+ marginBottom: '1.5rem',
60
+ color: '#111827',
61
+ fontWeight: '600',
62
+ fontSize: '1.5rem',
63
+ }}
58
64
  >
59
- <h1>Forgot your password?</h1>
60
- </div>
65
+ Forgot your password?
66
+ </h2>
61
67
  <div
62
68
  className={`${styles.formItem} ${styles.fwItem}`}
63
69
  >
@@ -89,7 +95,7 @@ const Reset = ({ logo }) => {
89
95
  type="submit"
90
96
  tabIndex="2"
91
97
  >
92
- Reset Password
98
+ Send Reset Link
93
99
  </button>
94
100
  </div>
95
101
  </div>