ui-soxo-bootstrap-core 2.4.25-dev.34 → 2.4.25-dev.35

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.
@@ -39,40 +39,54 @@ function CollapsedIconMenu({ menu, collapsed, icon, caption }) {
39
39
  // Import t and i18n from useTranslation
40
40
  const { t, i18n } = useTranslation();
41
41
  const { state } = useContext(GlobalContext);
42
+ const [isMobile, setIsMobile] = useState(false);
43
+
44
+ useEffect(() => {
45
+ const handleResize = () => {
46
+ setIsMobile(window.innerWidth < 768); // Common breakpoint for mobile
47
+ };
48
+
49
+ handleResize(); // Set initial value
50
+ window.addEventListener('resize', handleResize);
51
+
52
+ return () => window.removeEventListener('resize', handleResize);
53
+ }, []);
54
+
42
55
  const menuText = t(caption);
43
- return (
56
+ const menuContent = (
44
57
  <>
45
- <Popover content={menuText} placement="right">
46
- {/* If value of collapsed is false show caption & icon else hiding caption and showing only icon*/}
47
- {!collapsed ? (
48
- <div className="menu-collapsed">
49
- <div>
50
- {menu && menu.image_path ? <img style={{ width: '25px' }} src={menu.image_path}></img> : <i className={`fa-solid fas ${icon}`} />}
51
- </div>
52
-
53
- <div style={{ color: state.theme.colors.leftSectionColor }}>
54
- <span className="caption">
55
- {/* {caption} */}
56
- {/* <Trans i18nKey="Appointments"></Trans> */}
57
- {t(`${caption}`)}
58
- </span>
59
- </div>
58
+ {/* If value of collapsed is false show caption & icon else hiding caption and showing only icon*/}
59
+ {!collapsed ? (
60
+ <div className="menu-collapsed">
61
+ <div>
62
+ {menu && menu.image_path ? <img style={{ width: '25px' }} src={menu.image_path}></img> : <i className={`fa-solid fas ${icon}`} />}
60
63
  </div>
61
- ) : (
62
- <div className="menu-collapsed">
63
- <span className="anticon">
64
- {menu && menu.image_path ? <img style={{ width: '25px' }} src={menu.image_path}></img> : <i className={`fa-solid fas ${icon}`} />}
65
- </span>
66
64
 
67
- <span style={{ color: state.theme.colors.colorPrimaryText, paddingLeft: '6px' }}>
68
- {/* <>{caption}</> */}
65
+ <div style={{ color: state.theme.colors.leftSectionColor }}>
66
+ <span className="caption">
67
+ {/* {caption} */}
68
+ {/* <Trans i18nKey="Appointments"></Trans> */}
69
69
  {t(`${caption}`)}
70
70
  </span>
71
71
  </div>
72
- )}
73
- </Popover>
72
+ </div>
73
+ ) : (
74
+ <div className="menu-collapsed">
75
+ <span className="anticon">
76
+ {menu && menu.image_path ? <img style={{ width: '25px' }} src={menu.image_path}></img> : <i className={`fa-solid fas ${icon}`} />}
77
+ </span>
78
+
79
+ <span style={{ color: state.theme.colors.colorPrimaryText, paddingLeft: '6px' }}>
80
+ {/* <>{caption}</> */}
81
+ {t(`${caption}`)}
82
+ </span>
83
+ </div>
84
+ )}
74
85
  </>
75
86
  );
87
+
88
+ // On mobile, or when the menu is collapsed (based on original logic), don't show the popover tooltip.
89
+ return isMobile || collapsed ? menuContent : <Popover content={menuText} placement="right">{menuContent}</Popover>;
76
90
  }
77
91
 
78
92
  export default function SideMenu({ loading, modules = [], callback, appSettings, collapsed }) {
@@ -45,6 +45,8 @@ export default function AssignRole() {
45
45
  const [loadingUser, setLoadingUser] = useState(false);
46
46
  const [loadingRoles, setLoadingRoles] = useState(false);
47
47
  const [loadingMenus, setLoadingMenus] = useState(false);
48
+ // for save
49
+ const [saving, setSaving] = useState(false);
48
50
 
49
51
  const [selectedMenus, setSelectedMenus] = useState([]);
50
52
  const [search, setSearch] = useState('');
@@ -133,13 +135,9 @@ export default function AssignRole() {
133
135
  * @returns {Array<Object>}
134
136
  */
135
137
 
136
- const filterAndSortMenus = (menus, allowedIds) => {
137
- return menus
138
- .filter((m) => allowedIds.includes(m.id))
139
- .map((m) => ({
140
- ...m,
141
- sub_menus: filterAndSortMenus(m.sub_menus || [], allowedIds),
142
- }));
138
+ const filterAndSortMenus = (menus, allowedIds = []) => {
139
+ if (!Array.isArray(menus) || !Array.isArray(allowedIds)) return [];
140
+ return menus.filter((m) => allowedIds.includes(m.id)).map((m) => ({ ...m, sub_menus: filterAndSortMenus(m.sub_menus || [], allowedIds) }));
143
141
  };
144
142
 
145
143
  /**
@@ -180,7 +178,7 @@ export default function AssignRole() {
180
178
  /** Filtered roles */
181
179
  const filteredRoles = useMemo(() => {
182
180
  if (!search) return roles;
183
- return roles.filter((r) => r.name.toLowerCase().includes(search.toLowerCase()));
181
+ return roles.filter((r) => r?.name?.toLowerCase().includes(search.toLowerCase()));
184
182
  }, [roles, search]);
185
183
 
186
184
  /**
@@ -203,28 +201,30 @@ export default function AssignRole() {
203
201
  */
204
202
  const handleSaveUserRole = async () => {
205
203
  if (!id || !selectedRoles.length) {
206
- message.warning('User or roles missing');
204
+ message.warning('No roles selected to save');
207
205
  return;
208
206
  }
207
+ // start button spinner
208
+ setSaving(true);
209
209
 
210
210
  try {
211
- message.loading({ key: 'save', content: 'Saving user roles...' });
212
-
211
+ // Save all roles in parallel
213
212
  await Promise.all(
214
213
  selectedRoles.map((roleId) =>
215
214
  UserRolesAPI.addUserRole({
216
- values: {
217
- user_id: id,
218
- role_id: roleId,
219
- },
215
+ values: { user_id: id, role_id: roleId },
220
216
  })
221
217
  )
222
218
  );
223
219
 
224
- message.success({ key: 'save', content: 'User roles saved successfully' });
220
+ // Only show success AFTER all saves
221
+ message.success('User roles saved successfully');
225
222
  } catch (err) {
226
223
  console.error(err);
227
- message.error({ key: 'save', content: 'Failed to save user roles' });
224
+ message.error('Failed to save user roles');
225
+ } finally {
226
+ // stop button spinner
227
+ setSaving(false);
228
228
  }
229
229
  };
230
230
 
@@ -233,7 +233,7 @@ export default function AssignRole() {
233
233
  {/* LEFT PANEL */}
234
234
  <Card className="left-panel" bodyStyle={{ padding: 12 }}>
235
235
  <div size="small" className="user-card">
236
- <Avatar size={40}>{user?.name?.[0] ||''}</Avatar>
236
+ <Avatar size={40}>{user?.name?.[0] || ''}</Avatar>
237
237
 
238
238
  <div className="user-info">
239
239
  <div>{user?.name || '--'}</div>
@@ -288,7 +288,7 @@ export default function AssignRole() {
288
288
  </div>
289
289
 
290
290
  <div className="footer-actions">
291
- <Button type="primary" onClick={handleSaveUserRole}>
291
+ <Button type="primary" onClick={handleSaveUserRole} loading={saving}>
292
292
  Save
293
293
  </Button>
294
294
  </div>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ui-soxo-bootstrap-core",
3
- "version": "2.4.25-dev.34",
3
+ "version": "2.4.25-dev.35",
4
4
  "description": "All the Core Components for you to start",
5
5
  "keywords": [
6
6
  "all in one"