groovinads-ui 1.2.68 → 1.2.70

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
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "groovinads-ui",
3
3
  "description": "Groovinads UI is a React component library designed exclusively for Groovinads applications. It provides ready-to-use UI elements styled according to Groovinads design guidelines to facilitate rapid development.",
4
- "version": "1.2.68",
4
+ "version": "1.2.70",
5
5
  "keywords": [
6
6
  "css",
7
7
  "sass",
@@ -18,6 +18,7 @@ const DropdownMultiSelect = ({
18
18
  inputLabel = '',
19
19
  object = false,
20
20
  nameKey,
21
+ customKey,
21
22
  idKey,
22
23
  searchLabel = 'Search',
23
24
  onToggle,
@@ -33,14 +34,22 @@ const DropdownMultiSelect = ({
33
34
  buttonVariant = 'input',
34
35
  nowrap = false,
35
36
  hasId = true,
37
+ validate = false,
38
+ requiredText = 'required',
39
+ errorRequired = false,
40
+ setErrorRequiered,
36
41
  }) => {
42
+ // STATES
37
43
  const [query, setQuery] = useState('');
38
44
  const [innerShow, setInnerShow] = useState(!!show);
39
45
 
40
46
  const dropdownRef = useRef(null);
47
+ const toggleRef = useRef(null);
48
+ const menuRef = useRef(null);
41
49
 
42
50
  const { highlightText } = useTextFormatter();
43
51
 
52
+ // FUNCTIONS
44
53
  const handleCheckbox = (status, value) => {
45
54
  const i = values.findIndex(
46
55
  (item) =>
@@ -68,16 +77,12 @@ const DropdownMultiSelect = ({
68
77
  const valuesFromSearch = () =>
69
78
  values.filter(
70
79
  (item) =>
71
- (object ? `${item[idKey]} ${item[nameKey]}` : item)
80
+ (object ? `${item[idKey]} ${item[nameKey]} ${item[customKey]}` : item)
72
81
  .toLowerCase()
73
82
  .includes(query.toLowerCase()) &&
74
83
  (showStatus ? parseInt(item.status) === parseInt(showStatus) : true),
75
84
  );
76
85
 
77
- useEffect(() => {
78
- setInnerShow(show);
79
- }, [show]);
80
-
81
86
  const handleClickOutside = useCallback((event) => {
82
87
  if (
83
88
  dropdownRef?.current &&
@@ -89,6 +94,19 @@ const DropdownMultiSelect = ({
89
94
  }
90
95
  }, []);
91
96
 
97
+ // VALIDATION REQUIRED
98
+ const validateRequired = () => {
99
+ if (!valuesSelected.length) {
100
+ setTimeout(() => {
101
+ setErrorRequiered((prev) => !prev);
102
+ }, 2000);
103
+ }
104
+ };
105
+
106
+ useEffect(() => {
107
+ setInnerShow(show);
108
+ }, [show]);
109
+
92
110
  useEffect(() => {
93
111
  document.addEventListener('click', handleClickOutside);
94
112
  return () => {
@@ -96,6 +114,36 @@ const DropdownMultiSelect = ({
96
114
  };
97
115
  }, []);
98
116
 
117
+ useEffect(() => {
118
+ if (validate && errorRequired) validateRequired();
119
+ }, [validate, errorRequired]);
120
+
121
+ useEffect(() => {
122
+ if (overflow && menuRef.current && toggleRef.current) {
123
+ menuRef.current.style.width = `${toggleRef.current.offsetWidth}px`;
124
+ }
125
+ }, [innerShow, overflow]);
126
+
127
+ useEffect(() => {
128
+ function updateMenuWidth() {
129
+ if (overflow && menuRef.current && toggleRef.current) {
130
+ const width = toggleRef.current.offsetWidth;
131
+ menuRef.current.style.width = `${width}px`;
132
+ menuRef.current.style.minWidth = `${width}px`;
133
+ menuRef.current.style.maxWidth = `${width}px`;
134
+ }
135
+ }
136
+
137
+ if (innerShow) {
138
+ updateMenuWidth();
139
+ window.addEventListener('resize', updateMenuWidth);
140
+ }
141
+
142
+ return () => {
143
+ window.removeEventListener('resize', updateMenuWidth);
144
+ };
145
+ }, [innerShow, overflow]);
146
+
99
147
  return (
100
148
  <Dropdown
101
149
  className={className}
@@ -104,14 +152,16 @@ const DropdownMultiSelect = ({
104
152
  ref={dropdownRef}
105
153
  onToggle={onToggle}
106
154
  drop={drop}
155
+ data-error={requiredText}
107
156
  >
108
157
  <Dropdown.Toggle
109
158
  as={'div'}
110
- className={`btn btn-${buttonVariant} w-100 ${nowrap ? 'nowrap' : ''}`}
159
+ ref={toggleRef}
160
+ className={`btn btn-${buttonVariant} ${nowrap ? 'nowrap' : ''}${errorRequired && valuesSelected.length === 0 ? 'not-validated' : ''}`}
111
161
  onClick={(e) => {
112
162
  e.stopPropagation(); // Detiene la propagación del clic
113
163
  setInnerShow((prevShow) => !prevShow); // Alterna el estado interno del dropdown
114
- setShow((prevShow) => !prevShow); // Alterna el estado del dropdown en el estado externo también
164
+ // setShow((prevShow) => !prevShow); // Alterna el estado del dropdown en el estado externo también
115
165
  }}
116
166
  >
117
167
  {valuesSelected.length > 0 && buttonVariant === 'input' && (
@@ -127,8 +177,14 @@ const DropdownMultiSelect = ({
127
177
  }}
128
178
  key={'Dropdown.PillComponent' + index}
129
179
  >
130
- {idInPill ? `#${value[idKey]} - ` : ''}
131
- <span>{object ? value[nameKey] : value}</span>
180
+ {customKey
181
+ ? `${value[nameKey]} - ${value[customKey]}`
182
+ : idInPill
183
+ ? `#${value[idKey]}`
184
+ : value[nameKey] || value}
185
+
186
+ {/* {idInPill ? `#${value[idKey]} - ` : ''}
187
+ <span>{object ? value[nameKey] : value}</span> */}
132
188
  </PillComponent>
133
189
  );
134
190
  })}
@@ -145,6 +201,7 @@ const DropdownMultiSelect = ({
145
201
 
146
202
  <Dropdown.Menu
147
203
  className='w-100'
204
+ ref={menuRef}
148
205
  popperConfig={
149
206
  overflow
150
207
  ? {
@@ -188,7 +245,9 @@ const DropdownMultiSelect = ({
188
245
  >
189
246
  {highlightText(
190
247
  object
191
- ? `${hasId ? `#${value[idKey]} - ` : ''}${value[nameKey]}`
248
+ ? customKey
249
+ ? `${value[nameKey]} - ${value[customKey]}`
250
+ : `${hasId ? `#${value[idKey]} - ` : ''}${value[nameKey]}`
192
251
  : value[nameKey] || value,
193
252
  query,
194
253
  )}
@@ -222,6 +281,7 @@ DropdownMultiSelect.propTypes = {
222
281
  setShow: PropTypes.func,
223
282
  object: PropTypes.bool,
224
283
  nameKey: PropTypes.string,
284
+ customKey: PropTypes.string,
225
285
  idKey: PropTypes.string,
226
286
  idInPill: PropTypes.bool,
227
287
  showStatus: PropTypes.string,
@@ -235,6 +295,10 @@ DropdownMultiSelect.propTypes = {
235
295
  ]),
236
296
  nowrap: PropTypes.bool,
237
297
  hasId: PropTypes.bool,
298
+ requiredText: PropTypes.string,
299
+ validate: PropTypes.bool,
300
+ errorRequired: PropTypes.bool,
301
+ setErrorRequiered: PropTypes.func,
238
302
  };
239
303
 
240
304
  export default DropdownMultiSelect;
@@ -149,6 +149,7 @@ Input.propTypes = {
149
149
  autoFocus: PropTypes.bool,
150
150
  min: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
151
151
  max: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
152
+ maxLength: PropTypes.number,
152
153
  };
153
154
 
154
155
  export default Input;
@@ -31,7 +31,6 @@ const InputEmail = ({
31
31
  // GET, Returns a list of emails
32
32
  const fetchNotifications = async () => {
33
33
  const resp = await ComponentsService.getInfo(apiGetEmail);
34
- console.log('resp', resp);
35
34
  setNotifications(resp || []);
36
35
 
37
36
  };
@@ -22,7 +22,6 @@ const DeckDropdown = () => {
22
22
  element.app_icon_sm = element.application_icon;
23
23
  element.app_name = element.application_name;
24
24
  });
25
-
26
25
  setApplications(resp);
27
26
  } catch (error) {
28
27
  console.error('fetchData error', error);
@@ -52,10 +51,8 @@ const DeckDropdown = () => {
52
51
 
53
52
  const favApp = async (identification) => {
54
53
  try {
55
- const resp = await ComponentsService.favApplication(identification);
56
- //console.log('if ---->');
54
+ await ComponentsService.favApplication(identification);
57
55
  setApplications((prev) => {
58
- //console.log('setApplications ---->');
59
56
  const index = prev.findIndex(
60
57
  (element) => element?.id_application === identification,
61
58
  );
@@ -69,141 +66,144 @@ const DeckDropdown = () => {
69
66
 
70
67
  const unfavApp = async (identification) => {
71
68
  try {
72
- const resp = await ComponentsService.unfavApplication(identification);
73
-
74
- if (resp.status === 204) {
75
- setApplications((prev) => {
76
- const index = prev.findIndex(
77
- (element) => element?.id_application === identification,
78
- );
79
- prev[index].favorite = false;
80
- return [...prev];
81
- });
82
- }
69
+ await ComponentsService.unfavApplication(identification);
70
+
71
+ setApplications((prev) => {
72
+ const index = prev.findIndex(
73
+ (element) => element?.id_application === identification,
74
+ );
75
+ prev[index].favorite = false;
76
+ return [...prev];
77
+ });
83
78
  } catch (error) {
84
79
  console.error('unfavApp error', error);
85
80
  }
86
81
  };
87
82
 
88
83
  useEffect(() => {
89
- if (applications?.length === 0) fetchData();
84
+ if (!applications?.length) fetchData();
90
85
  }, []);
91
86
 
92
87
  return (
93
- <Dropdown>
94
- <Dropdown.Toggle className='btn btn-terciary'>
95
- <svg
96
- id='deckApps'
97
- xmlns='http://www.w3.org/2000/svg'
98
- className='icon'
99
- viewBox='0 0 16 16'
100
- >
101
- <g>
102
- <rect x='1' y='1' width='4' height='4' rx='1.25' ry='1.25' />
103
- <rect x='1' y='6' width='4' height='4' rx='1.25' ry='1.25' />
104
- <rect x='1' y='11' width='4' height='4' rx='1.25' ry='1.25' />
105
- <rect x='6' y='1' width='4' height='4' rx='1.25' ry='1.25' />
106
- <rect x='6' y='6' width='4' height='4' rx='1.25' ry='1.25' />
107
- <rect x='6' y='11' width='4' height='4' rx='1.25' ry='1.25' />
108
- <rect x='11' y='1' width='4' height='4' rx='1.25' ry='1.25' />
109
- <rect x='11' y='6' width='4' height='4' rx='1.25' ry='1.25' />
110
- <rect x='11' y='11' width='4' height='4' rx='1.25' ry='1.25' />
111
- </g>
112
- </svg>
113
- </Dropdown.Toggle>
114
-
115
- <Dropdown.Menu as={'ul'}>
116
- {applications?.filter((item) => item.favorite).length > 0 && (
117
- <Dropdown.Item as={'li'}>
118
- <h4 className='dropdown-header'>Favorites</h4>
119
- </Dropdown.Item>
120
- )}
121
-
122
- {applications
123
- .filter((item) => item.favorite)
124
- .map((element, i) => {
125
- return (
126
- <Dropdown.Item key={i} as={'li'}>
127
- <div
128
- onClick={() => {
129
- window.location.replace(
130
- linkToApp(
131
- element.application_url,
132
- element.application_name,
133
- ),
134
- );
135
- }}
136
- className='link-app'
137
- >
138
- <span className='icon-app'>
139
- <Icon
140
- style={'solid'}
141
- iconName={element.app_icon_sm}
142
- scale={1}
143
- />
144
- </span>
145
-
146
- <span>{element.app_name}</span>
147
- </div>
148
- <button
149
- className='deck-fav'
150
- onClick={(e) => {
151
- e.stopPropagation();
152
- unfavApp(element.id_application);
153
- }}
154
- >
155
- <Icon style={'solid'} iconName={'star'} scale={1} />
156
- </button>
88
+ <>
89
+ {applications.length ? (
90
+ <Dropdown>
91
+ <Dropdown.Toggle className='btn btn-terciary'>
92
+ <svg
93
+ id='deckApps'
94
+ xmlns='http://www.w3.org/2000/svg'
95
+ className='icon'
96
+ viewBox='0 0 16 16'
97
+ >
98
+ <g>
99
+ <rect x='1' y='1' width='4' height='4' rx='1.25' ry='1.25' />
100
+ <rect x='1' y='6' width='4' height='4' rx='1.25' ry='1.25' />
101
+ <rect x='1' y='11' width='4' height='4' rx='1.25' ry='1.25' />
102
+ <rect x='6' y='1' width='4' height='4' rx='1.25' ry='1.25' />
103
+ <rect x='6' y='6' width='4' height='4' rx='1.25' ry='1.25' />
104
+ <rect x='6' y='11' width='4' height='4' rx='1.25' ry='1.25' />
105
+ <rect x='11' y='1' width='4' height='4' rx='1.25' ry='1.25' />
106
+ <rect x='11' y='6' width='4' height='4' rx='1.25' ry='1.25' />
107
+ <rect x='11' y='11' width='4' height='4' rx='1.25' ry='1.25' />
108
+ </g>
109
+ </svg>
110
+ </Dropdown.Toggle>
111
+
112
+ <Dropdown.Menu as={'ul'}>
113
+ {applications?.filter((item) => item.favorite).length > 0 && (
114
+ <Dropdown.Item as={'li'}>
115
+ <h4 className='dropdown-header'>Favorites</h4>
157
116
  </Dropdown.Item>
158
- );
159
- })}
160
-
161
- {applications?.filter((item) => item.favorite).length > 0 &&
162
- applications?.filter((item) => !item.favorite).length > 0 && (
163
- <Dropdown.Divider as={'li'} />
164
- )}
165
-
166
- {applications
167
- .filter((item) => !item.favorite)
168
- .map((element, i) => {
169
- return (
170
- <Dropdown.Item key={i} as={'li'}>
171
- <div
172
- onClick={() => {
173
- window.location.replace(
174
- linkToApp(
175
- element.application_url,
176
- element.application_name,
177
- ),
178
- );
179
- }}
180
- className='link-app'
181
- >
182
- <span className='icon-app'>
183
- <Icon
184
- style={'solid'}
185
- iconName={element.app_icon_sm}
186
- scale={1}
187
- />
188
- </span>
189
-
190
- <span>{element.app_name}</span>
191
- </div>
192
- <button
193
- className='deck-fav'
194
- onClick={(e) => {
195
- e.stopPropagation();
196
- favApp(element.id_application);
197
- }}
198
- >
199
- <Icon style={'regular'} iconName={'star'} scale={1} />
200
- </button>
201
- </Dropdown.Item>
202
- );
203
- })}
204
- </Dropdown.Menu>
205
-
206
- </Dropdown>
117
+ )}
118
+
119
+ {applications
120
+ .filter((item) => item.favorite)
121
+ .map((element, i) => {
122
+ return (
123
+ <Dropdown.Item key={i} as={'li'}>
124
+ <div
125
+ onClick={() => {
126
+ window.location.replace(
127
+ linkToApp(
128
+ element.application_url,
129
+ element.application_name,
130
+ ),
131
+ );
132
+ }}
133
+ className='link-app'
134
+ >
135
+ <span className='icon-app'>
136
+ <Icon
137
+ style={'solid'}
138
+ iconName={element.app_icon_sm}
139
+ scale={1}
140
+ />
141
+ </span>
142
+
143
+ <span>{element.app_name}</span>
144
+ </div>
145
+ <button
146
+ className='deck-fav'
147
+ onClick={(e) => {
148
+ e.stopPropagation();
149
+ unfavApp(element.id_application);
150
+ }}
151
+ >
152
+ <Icon style={'solid'} iconName={'star'} scale={1} />
153
+ </button>
154
+ </Dropdown.Item>
155
+ );
156
+ })}
157
+
158
+ {applications?.filter((item) => item.favorite).length > 0 &&
159
+ applications?.filter((item) => !item.favorite).length > 0 && (
160
+ <Dropdown.Divider as={'li'} />
161
+ )}
162
+
163
+ {applications
164
+ .filter((item) => !item.favorite)
165
+ .map((element, i) => {
166
+ return (
167
+ <Dropdown.Item key={i} as={'li'}>
168
+ <div
169
+ onClick={() => {
170
+ window.location.replace(
171
+ linkToApp(
172
+ element.application_url,
173
+ element.application_name,
174
+ ),
175
+ );
176
+ }}
177
+ className='link-app'
178
+ >
179
+ <span className='icon-app'>
180
+ <Icon
181
+ style={'solid'}
182
+ iconName={element.app_icon_sm}
183
+ scale={1}
184
+ />
185
+ </span>
186
+
187
+ <span>{element.app_name}</span>
188
+ </div>
189
+ <button
190
+ className='deck-fav'
191
+ onClick={(e) => {
192
+ e.stopPropagation();
193
+ favApp(element.id_application);
194
+ }}
195
+ >
196
+ <Icon style={'regular'} iconName={'star'} scale={1} />
197
+ </button>
198
+ </Dropdown.Item>
199
+ );
200
+ })}
201
+ </Dropdown.Menu>
202
+ </Dropdown>
203
+ ) : (
204
+ <></>
205
+ )}
206
+ </>
207
207
  );
208
208
  };
209
209
 
@@ -18,7 +18,10 @@ const Navbar = ({
18
18
  children,
19
19
  show,
20
20
  setShow,
21
+ applications,
22
+ setApplications,
21
23
  }) => {
24
+
22
25
  return (
23
26
  <div id='ga-header' className='header fixed-top shadow-2'>
24
27
  <Container fluid className='navbar'>
@@ -50,6 +53,7 @@ const Navbar = ({
50
53
  </svg>
51
54
  </Button>
52
55
  {/* Dropdown apps */}
56
+ {/* TODO: conditional */}
53
57
  {showDeckMenu && <DeckDropdown />}
54
58
 
55
59
  <img
@@ -25,7 +25,9 @@ const Sidebar = ({
25
25
  inModal = false,
26
26
  customUrl,
27
27
  }) => {
28
- const isMobile = inModal ? true : useMediaQuery({ query: '(max-width: 992px)' });
28
+ const isMobile = inModal
29
+ ? true
30
+ : useMediaQuery({ query: '(max-width: 992px)' });
29
31
 
30
32
  const url = customUrl !== undefined ? customUrl : window.location.pathname; // to get current url
31
33
 
@@ -55,7 +57,8 @@ const Sidebar = ({
55
57
 
56
58
  useEffect(() => {
57
59
  if (isMobile && setShow && !inModal) setShow(innerShow);
58
- if (!inModal) setInnerShow(isMobile ? false : firstOpen.current ? defaultOpened : show);
60
+ if (!inModal)
61
+ setInnerShow(isMobile ? false : firstOpen.current ? defaultOpened : show);
59
62
  firstOpen.current = false;
60
63
  }, []);
61
64
 
@@ -80,13 +83,14 @@ const Sidebar = ({
80
83
  <div className='scroll'>
81
84
  {customLinks.map((section, i) => (
82
85
  <div className='sidebar-section' key={`sectionIndex${i}`}>
83
- {section.title ? <h4>{section.title}</h4> : <></> }
86
+ {section.title ? <h4>{section.title}</h4> : <></>}
84
87
 
85
88
  {/* PROPS LINKS */}
86
- {(section.backData ? sidebarLinks : section.links || []).map(
87
- (linkSection, y) => (
88
- <ul className='nav' key={`linksSections${y}`}>
89
- <li className='nav-item'>
89
+
90
+ <ul className='nav'>
91
+ {(section.backData ? sidebarLinks : section.links || []).map(
92
+ (linkSection, y) => (
93
+ <li className='nav-item' key={`linksSections${y}`}>
90
94
  {/* CHILDREN - If has children, the collapse is expanded */}
91
95
  {linkSection.children && linkSection.children.length ? (
92
96
  <>
@@ -121,20 +125,41 @@ const Sidebar = ({
121
125
  </Collapse>
122
126
  </>
123
127
  ) : (
124
- <button
125
- className={`nav-link ${
126
- url === linkSection.url ? 'active' : ''
127
- }`}
128
- onClick={() => onNavigate(linkSection.url)}
129
- >
130
- <Icon iconName={linkSection.icon} />
131
- {linkSection.name}
132
- </button>
128
+ <>
129
+ <button
130
+ className={`nav-link ${
131
+ url === linkSection.url ? 'active' : ''
132
+ }`}
133
+ onClick={() => onNavigate(linkSection.url)}
134
+ >
135
+ <Icon iconName={linkSection.icon} />
136
+
137
+ {linkSection.pendingType ? (
138
+ <div className='link-wrapper'>
139
+ {linkSection.name}
140
+ {linkSection.pendingLength > 0 &&
141
+ (linkSection.pendingType === 'warning' ? (
142
+ <Icon
143
+ iconName='triangle-exclamation'
144
+ className='warning-icon'
145
+ style='duotone'
146
+ />
147
+ ) : linkSection.pendingType === 'badge' ? (
148
+ <span className='badge'>
149
+ {linkSection.pendingLength}
150
+ </span>
151
+ ) : null)}
152
+ </div>
153
+ ) : (
154
+ linkSection.name
155
+ )}
156
+ </button>
157
+ </>
133
158
  )}
134
159
  </li>
135
- </ul>
136
- ),
137
- )}
160
+ ),
161
+ )}
162
+ </ul>
138
163
  </div>
139
164
  ))}
140
165
  </div>
@@ -160,13 +185,14 @@ Sidebar.propTypes = {
160
185
  setShow: PropTypes.func, // Add setShow prop for controlling visibility
161
186
  onNavigate: PropTypes.func.isRequired, // Nueva prop para manejar la navegación
162
187
  selectedClient: PropTypes.object,
163
- showClients:PropTypes.oneOf([true, false, 'single']),
188
+ showClients: PropTypes.oneOf([true, false, 'single']),
164
189
  setGroovinProfile: PropTypes.func,
165
190
  inModal: PropTypes.bool,
166
191
  customUrl: PropTypes.oneOfType([
167
192
  PropTypes.string,
168
- PropTypes.oneOf([undefined])
169
- ]),
193
+ PropTypes.oneOf([undefined]),
194
+ ]),
195
+ pendingType: PropTypes.oneOf(['warning', 'badge']),
170
196
  };
171
197
 
172
198
  export default Sidebar;