ui-soxo-bootstrap-core 2.6.40-dev.17 → 2.6.40

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.
Files changed (27) hide show
  1. package/DEVELOPER_GUIDE.md +9 -38
  2. package/core/components/index.js +11 -2
  3. package/core/components/landing-api/landing-api.js +5 -165
  4. package/core/lib/components/global-header/global-header.js +77 -20
  5. package/core/lib/components/index.js +2 -2
  6. package/core/lib/components/sidemenu/sidemenu.js +147 -66
  7. package/core/lib/elements/basic/dragabble-wrapper/draggable-wrapper.js +24 -91
  8. package/core/lib/models/menus/components/menu-add/menu-add.js +2 -2
  9. package/core/lib/modules/generic/generic-list/ExportReactCSV.js +8 -334
  10. package/core/lib/modules/generic/generic-list/generic-list.scss +0 -34
  11. package/core/models/core-scripts/core-scripts.js +1 -22
  12. package/core/models/menus/components/menu-add/menu-add.js +4 -31
  13. package/core/models/menus/components/menu-lists/menu-lists.js +12 -75
  14. package/core/models/menus/menus.js +1 -29
  15. package/core/models/roles/components/role-add/role-add.js +169 -54
  16. package/core/models/roles/components/role-list/role-list.js +0 -20
  17. package/core/models/roles/roles.js +0 -3
  18. package/core/models/users/components/assign-role/assign-role.js +8 -23
  19. package/core/modules/reporting/components/reporting-dashboard/display-columns/display-cell-renderer.js +5 -202
  20. package/core/modules/reporting/components/reporting-dashboard/display-columns/display-cell-renderer.test.js +0 -73
  21. package/core/modules/reporting/components/reporting-dashboard/reporting-dashboard.js +35 -56
  22. package/core/modules/reporting/components/reporting-dashboard/reporting-dashboard.scss +0 -1
  23. package/package.json +1 -1
  24. package/core/components/license-management/license-alert.js +0 -97
  25. package/core/models/roles/components/role-add/menu-label.js +0 -14
  26. package/core/models/roles/components/role-add/menu-tree.js +0 -127
  27. package/core/modules/reporting/components/reporting-dashboard/reporting-table.js +0 -519
@@ -30,9 +30,6 @@ const MenuLists = ({ model, match, relativeAdd = false, additional_queries = [],
30
30
  const [query, setQuery] = useState('');
31
31
  const [dragMode, setDragMode] = useState(false);
32
32
  const [orderChanged, setOrderChanged] = useState(false);
33
- // keys of currently-expanded top-level panels. Seeded from the search results
34
- // when the query changes, but the user can freely toggle panels afterwards.
35
- const [openKeys, setOpenKeys] = useState([]);
36
33
 
37
34
  const [nextId, setNextId] = useState(10000);
38
35
 
@@ -184,16 +181,6 @@ const MenuLists = ({ model, match, relativeAdd = false, additional_queries = [],
184
181
  return;
185
182
  }
186
183
 
187
- // Prevent moving a menu into one of its own descendants
188
- // (e.g. A > B, C — A cannot be dropped under B or C).
189
- if (targetParentId) {
190
- const draggedNode = findMenuById(records, draggedItem.id);
191
- if (draggedNode && findMenuById(draggedNode.sub_menus || [], targetParentId)) {
192
- message.warning('Cannot move a menu into its own sub menu');
193
- return;
194
- }
195
- }
196
-
197
184
  // Remove item from original location
198
185
  const { newItems: itemsAfterRemove, foundItem } = findAndRemoveItem(records, draggedItem.id);
199
186
 
@@ -207,7 +194,7 @@ const MenuLists = ({ model, match, relativeAdd = false, additional_queries = [],
207
194
 
208
195
  setRecords(finalItems);
209
196
  setOrderChanged(true);
210
- message.success(`Moved "${foundItem.caption}" to level ${targetLevel}`);
197
+ message.success(`Moved "${foundItem.name}" to level ${targetLevel}`);
211
198
  },
212
199
  [records]
213
200
  );
@@ -216,42 +203,9 @@ const MenuLists = ({ model, match, relativeAdd = false, additional_queries = [],
216
203
  model.delete(rec).then(loadMenus);
217
204
  };
218
205
 
219
- // Recursively filter the menu tree by caption. A menu is kept if its own
220
- // caption matches OR any descendant matches. When the menu itself matches we
221
- // keep its full subtree; when only a descendant matches we keep just the
222
- // pruned path so the matching item stays reachable.
223
- const filterMenus = (menus, q) => {
224
- if (!q) return menus || [];
225
- const lower = q.toLowerCase();
226
-
227
- return (menus || []).reduce((acc, menu) => {
228
- const selfMatch = (menu.caption || '').toLowerCase().includes(lower);
229
- if (selfMatch) {
230
- acc.push({ ...menu });
231
- return acc;
232
- }
233
- const subFiltered = filterMenus(menu.sub_menus || [], q);
234
- if (subFiltered.length) {
235
- acc.push({ ...menu, sub_menus: subFiltered });
236
- }
237
- return acc;
238
- }, []);
239
- };
240
-
241
- // Ids of items (at one level) that have sub_menus — used to auto-expand
242
- // every branch leading to a search match.
243
- const expandableKeys = (items) => (items || []).filter((m) => m.sub_menus && m.sub_menus.length > 0).map((m) => String(m.id));
206
+ const filtered = records.filter((r) => r.caption?.toUpperCase().includes(query.toUpperCase()));
244
207
 
245
- const searchActive = !!query;
246
- const visibleItems = filterMenus(records, query);
247
-
248
- // When the query changes, seed the open panels from the search results (every
249
- // branch on the path to a match) so the matched sub-menu is revealed. After
250
- // that the user can collapse/expand freely via the Collapse onChange below.
251
- useEffect(() => {
252
- setOpenKeys(query ? expandableKeys(filterMenus(records, query)) : []);
253
- // eslint-disable-next-line react-hooks/exhaustive-deps
254
- }, [query]);
208
+ const visibleItems = filtered;
255
209
 
256
210
  const onSearch = (event) => {
257
211
  setQuery(event.target.value);
@@ -351,12 +305,12 @@ const MenuLists = ({ model, match, relativeAdd = false, additional_queries = [],
351
305
  <Card className="generic-list">
352
306
  <div style={{ marginBottom: 16 }}>
353
307
  <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 16 }}>
354
- <Search placeholder="Search menus & sub-menus…" allowClear style={{ width: 300, marginBottom: '0px' }} onChange={onSearch} />
308
+ <Search placeholder="Enter Search Value" allowClear style={{ width: 300, marginBottom: '0px' }} onChange={onSearch} />
355
309
 
356
310
  <Space size="small">
357
- {/* <Button onClick={getRecords} size={'small'} type="default">
311
+ <Button onClick={getRecords} size={'small'} type="default">
358
312
  <ReloadOutlined />
359
- </Button> */}
313
+ </Button>
360
314
 
361
315
  <Switch checked={dragMode} onChange={toggleDragMode} checkedChildren="Order On" unCheckedChildren="Order Off" />
362
316
 
@@ -407,11 +361,7 @@ const MenuLists = ({ model, match, relativeAdd = false, additional_queries = [],
407
361
  {/* {!view ? ( */}
408
362
  <Card>
409
363
  <DndProvider backend={HTML5Backend}>
410
- <Collapse
411
- accordion={!searchActive}
412
- activeKey={openKeys}
413
- onChange={(keys) => setOpenKeys(Array.isArray(keys) ? keys : keys ? [keys] : [])}
414
- >
364
+ <Collapse accordion>
415
365
  {visibleItems && visibleItems.length > 0 ? (
416
366
  visibleItems.map((item, index) => (
417
367
  <Panel
@@ -439,8 +389,6 @@ const MenuLists = ({ model, match, relativeAdd = false, additional_queries = [],
439
389
  items={item.sub_menus || []}
440
390
  model={model}
441
391
  dragMode={dragMode}
442
- searchActive={searchActive}
443
- query={query}
444
392
  setSelectedRecord={setSelectedRecord}
445
393
  setDrawerTitle={setDrawerTitle}
446
394
  setDrawerVisible={setDrawerVisible}
@@ -489,7 +437,7 @@ const MenuLists = ({ model, match, relativeAdd = false, additional_queries = [],
489
437
  );
490
438
  };
491
439
  /* -----------------------------------------------------------------------
492
- PANEL ACTIONS
440
+ PANEL ACTIONS
493
441
  ------------------------------------------------------------------------ */
494
442
  function panelActions(item, model, setSelectedRecord, setDrawerTitle, setDrawerVisible, deleteRecord) {
495
443
  return (
@@ -552,8 +500,6 @@ function NestedMenu({
552
500
  items,
553
501
  model,
554
502
  dragMode,
555
- searchActive,
556
- query,
557
503
  setSelectedRecord,
558
504
  setDrawerTitle,
559
505
  setDrawerVisible,
@@ -562,22 +508,15 @@ function NestedMenu({
562
508
  onCrossLevelMove,
563
509
  onChange,
564
510
  }) {
511
+ // do not render Collapse
512
+ if (!items || items.length === 0) return null;
513
+
565
514
  const [localItems, setLocalItems] = useState(items);
566
- // keys of currently-expanded sub-panels. Seeded from the search results when
567
- // the query changes, but the user can freely toggle panels afterwards.
568
- const [openKeys, setOpenKeys] = useState([]);
569
515
 
570
516
  useEffect(() => {
571
517
  setLocalItems(items);
572
518
  }, [items]);
573
519
 
574
- // Seed the open sub-panels from the search path when the query changes; keep
575
- // the user's toggles otherwise so a search-opened panel can be closed.
576
- useEffect(() => {
577
- setOpenKeys(searchActive ? (items || []).filter((c) => c.sub_menus && c.sub_menus.length > 0).map((c) => String(c.id)) : []);
578
- // eslint-disable-next-line react-hooks/exhaustive-deps
579
- }, [query, searchActive]);
580
-
581
520
  const moveSubMenu = useCallback(
582
521
  (from, to) => {
583
522
  if (!dragMode || from === to) return;
@@ -597,7 +536,7 @@ function NestedMenu({
597
536
  }
598
537
 
599
538
  return (
600
- <Collapse accordion={!searchActive} activeKey={openKeys} onChange={(keys) => setOpenKeys(Array.isArray(keys) ? keys : keys ? [keys] : [])}>
539
+ <Collapse accordion>
601
540
  {localItems.map((child, index) => (
602
541
  <Panel
603
542
  key={child.id}
@@ -624,8 +563,6 @@ function NestedMenu({
624
563
  step={step + 1}
625
564
  items={child.sub_menus || []}
626
565
  dragMode={dragMode}
627
- searchActive={searchActive}
628
- query={query}
629
566
  setSelectedRecord={setSelectedRecord}
630
567
  setDrawerTitle={setDrawerTitle}
631
568
  setDrawerVisible={setDrawerVisible}
@@ -160,29 +160,6 @@ class MenusAPI extends Base {
160
160
  });
161
161
  };
162
162
 
163
- getBranches = () => {
164
- return ApiUtils.get({
165
- url: 'branches',
166
- });
167
- };
168
-
169
- switchBranch = (formBody, dbPtr) => {
170
- return ApiUtils.post({
171
- url: `auth/switch-branch`,
172
- headers: { db_ptr: dbPtr },
173
- formBody,
174
- });
175
- };
176
-
177
- getProfile = (token) => {
178
- return ApiUtils.get({
179
- url: 'auth/profile',
180
- headers: {
181
- Authorization: `Bearer ${token}`,
182
- },
183
- });
184
- };
185
-
186
163
  /**
187
164
  * create menu
188
165
  */
@@ -208,6 +185,7 @@ class MenusAPI extends Base {
208
185
  : 'menus/get-menus'; // NURA
209
186
 
210
187
  if (!dbPtr) dbPtr = localStorage.db_ptr;
188
+
211
189
  return this.get({
212
190
  url,
213
191
  config,
@@ -327,12 +305,6 @@ class MenusAPI extends Base {
327
305
  // }
328
306
  ];
329
307
  };
330
- // license summary api call
331
- getSummary = () => {
332
- return ApiUtils.get({
333
- url: 'license/summary',
334
- });
335
- };
336
308
  }
337
309
 
338
310
  export default MenusAPI;
@@ -1,14 +1,15 @@
1
1
  import React, { useState, useEffect, useContext } from 'react';
2
2
  import { useLocation, useParams } from 'react-router-dom';
3
- import { Skeleton, Typography, message, Form, Input } from 'antd';
3
+ import { Skeleton, Typography, message, Form, Input, Collapse, Checkbox, Tag } from 'antd';
4
4
  import { GlobalContext } from './../../../../lib';
5
+ import { Button } from './../../../../lib';
5
6
  import { ModelsAPI, PagesAPI, RolesAPI, MenusAPI } from '../../..';
6
- import MenuTree from './menu-tree';
7
7
  import './role-add.scss';
8
8
 
9
9
  const { Title } = Typography;
10
+ const { Panel } = Collapse;
10
11
 
11
- const RoleAdd = ({ model, callback, edit, formContent = {}, match, additional_queries = [], onSubmittingChange, formRef }) => {
12
+ const RoleAdd = ({ model, callback, edit, formContent = {}, match, additional_queries = [] }) => {
12
13
  let mode = 'Add';
13
14
  if (formContent.id) mode = 'Edit';
14
15
  else if (formContent.copy) mode = 'copy';
@@ -30,10 +31,8 @@ const RoleAdd = ({ model, callback, edit, formContent = {}, match, additional_qu
30
31
 
31
32
  const isEdit = !!(formContent.id || formContent.copy);
32
33
  const [initialLoading, setInitialLoading] = useState(isEdit);
34
+ const [loading, setLoading] = useState(false);
33
35
  const [form] = Form.useForm();
34
-
35
- // expose the form instance so the Drawer footer Save button can submit it
36
- if (formRef) formRef.current = form;
37
36
  const [pages, setPages] = useState([]);
38
37
  const [models, setModels] = useState([]);
39
38
  const [menuList, setMenuList] = useState([]);
@@ -44,9 +43,6 @@ const RoleAdd = ({ model, callback, edit, formContent = {}, match, additional_qu
44
43
  // Selected menus (array of IDs)
45
44
  const [selectedMenus, setSelectedMenus] = useState([]);
46
45
 
47
- // Search term for filtering the menu list
48
- const [menuSearch, setMenuSearch] = useState('');
49
-
50
46
  const location = useLocation();
51
47
  const query = new URLSearchParams(location.search);
52
48
  let step = parseInt(query.get('step')) || 1;
@@ -101,28 +97,40 @@ const RoleAdd = ({ model, callback, edit, formContent = {}, match, additional_qu
101
97
  setSelectedMenus((prev) => (checked ? [...prev, id] : prev.filter((m) => m !== id)));
102
98
  };
103
99
 
104
- // Recursively filter menus by caption. A menu is kept if its caption matches
105
- // the search term, or if any of its descendants match (ancestors are kept so
106
- // the matching child stays reachable).
107
- const filterMenus = (menus, term) => {
108
- if (!term) return menus;
109
- const lower = term.toLowerCase();
110
-
111
- return menus.reduce((acc, menu) => {
112
- const children = filterMenus(menu.sub_menus || [], term);
113
- const selfMatch = (menu.caption || '').toLowerCase().includes(lower);
114
-
115
- if (selfMatch || children.length > 0) {
116
- acc.push({ ...menu, sub_menus: children });
117
- }
118
- return acc;
119
- }, []);
120
- };
121
-
122
- const filteredMenuList = filterMenus(menuList, menuSearch);
123
-
100
+ // Submit handler
101
+ // const onSubmit = (values) => {
102
+ // setLoading(true);
103
+
104
+ // const payload = {
105
+ // ...values,
106
+ // menu_ids
107
+ // : selectedMenus,
108
+ // attributes: JSON.stringify(values.attributes || {}),
109
+ // };
110
+
111
+ // if (formContent.id) {
112
+ // RolesAPI.updateRole({ id: formContent.id, formBody: payload })
113
+ // .then(() => {
114
+ // message.success('Role Updated');
115
+ // setLoading(false);
116
+ // callback();
117
+ // })
118
+ // .catch(() => setLoading(false));
119
+ // } else {
120
+ // additional_queries.forEach(({ field, value }) => {
121
+ // payload[field] = value;
122
+ // });
123
+ // RolesAPI.createRole(payload)
124
+ // .then(() => {
125
+ // message.success('Role Added');
126
+ // setLoading(false);
127
+ // callback();
128
+ // })
129
+ // .catch(() => setLoading(false));
130
+ // }
131
+ // };
124
132
  const onSubmit = async (values) => {
125
- onSubmittingChange?.(true);
133
+ setLoading(true);
126
134
 
127
135
  // Find menus that were originally selected but now deselected
128
136
  const deselectedMenus = originalMenus.filter((id) => !selectedMenus.includes(id));
@@ -145,21 +153,13 @@ const RoleAdd = ({ model, callback, edit, formContent = {}, match, additional_qu
145
153
  });
146
154
 
147
155
  try {
148
- const res = await RolesAPI.createRole(payload); // single API
149
-
150
- // it via success: false — show that message as a warning.
151
- if (res?.success === false) {
152
- message.warning(res.message || 'Failed to save role');
153
- return;
154
- }
155
-
156
+ await RolesAPI.createRole(payload); // single API
156
157
  message.success(formContent?.id ? 'Role Updated' : 'Role Added');
157
158
  callback();
158
159
  } catch (err) {
159
- // Surface any unexpected backend error message as a warning.
160
- message.warning(err?.message || err?.result?.message || 'Failed to save role');
160
+ // message.error('Something went wrong');
161
161
  } finally {
162
- onSubmittingChange?.(false);
162
+ setLoading(false);
163
163
  }
164
164
  };
165
165
 
@@ -194,7 +194,6 @@ const RoleAdd = ({ model, callback, edit, formContent = {}, match, additional_qu
194
194
  display: 'flex',
195
195
  justifyContent: 'space-between',
196
196
  alignItems: 'center',
197
- gap: 16,
198
197
  marginBottom: 16,
199
198
  }}
200
199
  >
@@ -205,20 +204,14 @@ const RoleAdd = ({ model, callback, edit, formContent = {}, match, additional_qu
205
204
  <p style={{ color: '#999', marginBottom: 0 }}>Choose menus and set permissions</p>
206
205
  </div>
207
206
 
208
- <Input.Search
209
- allowClear
210
- placeholder="Search menus"
211
- value={menuSearch}
212
- onChange={(e) => setMenuSearch(e.target.value)}
213
- style={{ maxWidth: 260 }}
214
- />
207
+ <Form.Item style={{ marginBottom: 0 }}>
208
+ <Button loading={loading} htmlType="submit" type="primary">
209
+ Save
210
+ </Button>
211
+ </Form.Item>
215
212
  </div>
216
213
 
217
- {filteredMenuList.length > 0 ? (
218
- <MenuTree menus={filteredMenuList} selectedMenus={selectedMenus} toggleMenu={toggleMenu} searchActive={!!menuSearch} />
219
- ) : (
220
- <p style={{ color: '#999', textAlign: 'center', padding: '16px 0' }}>No menus found</p>
221
- )}
214
+ <MenuTree menus={menuList} selectedMenus={selectedMenus} toggleMenu={toggleMenu} />
222
215
  </div>
223
216
  )}
224
217
  </Form>
@@ -228,3 +221,125 @@ const RoleAdd = ({ model, callback, edit, formContent = {}, match, additional_qu
228
221
  };
229
222
 
230
223
  export default RoleAdd;
224
+
225
+ // ------------------------
226
+ // Recursive Nested Menu Component
227
+ // ------------------------
228
+ // ------------------------
229
+ // Recursive Nested Menu Component
230
+ // ------------------------
231
+ const MenuTree = ({ menus, selectedMenus, toggleMenu, parentId = null }) => {
232
+ // Helper: check if parent should be checked
233
+ const isParentChecked = (menu) => {
234
+ if (!menu.sub_menus || menu.sub_menus.length === 0) {
235
+ return selectedMenus.includes(menu.id);
236
+ }
237
+ const allChildIds = menu.sub_menus.map((c) => c.id);
238
+ return allChildIds.every((id) => selectedMenus.includes(id));
239
+ };
240
+
241
+ // Helper: check if parent is indeterminate
242
+ const isParentIndeterminate = (menu) => {
243
+ if (!menu.sub_menus || menu.sub_menus.length === 0) return false;
244
+ const allChildIds = menu.sub_menus.map((c) => c.id);
245
+ const checkedCount = allChildIds.filter((id) => selectedMenus.includes(id)).length;
246
+ return checkedCount > 0 && checkedCount < allChildIds.length;
247
+ };
248
+
249
+ return (
250
+ <>
251
+ {menus.map((menu) => {
252
+ const children = menu.sub_menus || [];
253
+ const parentChecked = isParentChecked(menu);
254
+ const parentIndeterminate = isParentIndeterminate(menu);
255
+
256
+ const onParentChange = (checked) => {
257
+ toggleMenu(menu.id, checked);
258
+ // toggle children recursively
259
+ children.forEach((c) => toggleMenuRecursive(c, checked));
260
+ };
261
+
262
+ const toggleMenuRecursive = (menu, checked) => {
263
+ toggleMenu(menu.id, checked);
264
+ if (menu.sub_menus && menu.sub_menus.length > 0) {
265
+ menu.sub_menus.forEach((c) => toggleMenuRecursive(c, checked));
266
+ }
267
+ };
268
+
269
+ if (children.length === 0) {
270
+ return (
271
+ <div
272
+ style={{
273
+ justifyContent: 'space-between',
274
+ display: 'flex',
275
+ alignItems: 'center',
276
+ border: '1px solid rgba(198, 195, 195, 0.85)',
277
+ // borderRadius: 6,
278
+ padding: '12px 16px',
279
+ marginBottom: 6,
280
+ background: '#fff',
281
+ }}
282
+ >
283
+ <div
284
+ key={menu.id}
285
+ style={{
286
+ display: 'flex',
287
+ alignItems: 'center',
288
+ gap: 8,
289
+ }}
290
+ >
291
+ <Checkbox
292
+ checked={selectedMenus.includes(menu.id)}
293
+ onChange={(e) => {
294
+ const checked = e.target.checked;
295
+
296
+ toggleMenu(menu.id, checked);
297
+
298
+ // FORCE parent selection
299
+ if (checked && parentId) {
300
+ toggleMenu(parentId, true);
301
+ }
302
+ }}
303
+ />
304
+ <span>{menu.caption}</span>
305
+ </div>
306
+ <Tag color={menu.is_visible === true ? 'green' : 'blue'}>{menu.is_visible === true ? 'VISIBLE' : 'HIDDEN'}</Tag>{' '}
307
+ </div>
308
+ );
309
+ }
310
+
311
+ return (
312
+ <Collapse
313
+ key={menu.id}
314
+ style={{ marginBottom: 6 }}
315
+ // defaultActiveKey={[menu.id]}
316
+ >
317
+ <Panel
318
+ key={menu.id}
319
+ header={
320
+ <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
321
+ <div
322
+ style={{
323
+ display: 'flex',
324
+ alignItems: 'center',
325
+ gap: 8,
326
+ }}
327
+ onClick={(e) => e.stopPropagation()}
328
+ >
329
+ <Checkbox checked={parentChecked} indeterminate={parentIndeterminate} onChange={(e) => onParentChange(e.target.checked)} />
330
+ <span>{menu.caption}</span>
331
+ </div>
332
+ <Tag color={menu.is_visible === true ? 'green' : 'blue'}>{menu.is_visible === true ? 'VISIBLE' : 'HIDDEN'}</Tag>{' '}
333
+ </div>
334
+ }
335
+ >
336
+ <div style={{ paddingLeft: 20 }}>
337
+ <MenuTree menus={children} selectedMenus={selectedMenus} toggleMenu={toggleMenu} parentId={menu.id} />
338
+ </div>
339
+ </Panel>
340
+ </Collapse>
341
+ );
342
+ })}
343
+ </>
344
+ );
345
+ };
@@ -40,12 +40,6 @@ const RoleList = ({ model, match, relativeAdd = false, additional_queries = [],
40
40
 
41
41
  const [single, setSingle] = useState({});
42
42
 
43
- // tracks RoleAdd form submission so the Drawer footer button shows loading
44
- const [submitting, setSubmitting] = useState(false);
45
-
46
- // holds the RoleAdd form instance so the Drawer footer Save button can submit it
47
- const formRef = useRef(null);
48
-
49
43
  var [queryValue, setQueryValue] = useState('');
50
44
 
51
45
  const { Search } = Input;
@@ -366,26 +360,12 @@ const RoleList = ({ model, match, relativeAdd = false, additional_queries = [],
366
360
  destroyOnClose
367
361
  onClose={closeModal}
368
362
  bodyStyle={{ paddingBottom: 80 }}
369
- footer={
370
- <div style={{ textAlign: 'right' }}>
371
- <Space size="small">
372
- <Button type="default" onClick={closeModal}>
373
- Cancel
374
- </Button>
375
- <Button type="primary" loading={submitting} onClick={() => formRef.current?.submit()}>
376
- Save
377
- </Button>
378
- </Space>
379
- </div>
380
- }
381
363
  >
382
364
  <model.ModalAddComponent
383
365
  match={match}
384
366
  model={model}
385
367
  additional_queries={additional_queries}
386
368
  formContent={single}
387
- formRef={formRef}
388
- onSubmittingChange={setSubmitting}
389
369
  callback={(event) => {
390
370
  closeModal();
391
371
  getRecords();
@@ -163,9 +163,6 @@ class RolesAPI extends BaseAPI {
163
163
  return ApiUtils.post({
164
164
  url: `core-roles/save-core-role`,
165
165
  formBody,
166
- // Let the caller surface the backend message (as a warning) instead of
167
- // the default red error toast from the http layer.
168
- hideError: true,
169
166
  });
170
167
  };
171
168
 
@@ -160,7 +160,7 @@ export default function AssignRole() {
160
160
  const role_id = role.id;
161
161
  try {
162
162
  const res = await MenusAPI.getCoreMenuByRoleId(role_id);
163
- const allMenus = Array.isArray(res?.result) ? res.result : [];
163
+ const allMenus = res.result || [];
164
164
 
165
165
  setModules(allMenus);
166
166
  } catch (e) {
@@ -264,12 +264,11 @@ export default function AssignRole() {
264
264
  const handleViewAll = async () => {
265
265
  setLoadingMenus(true);
266
266
  setModules([]);
267
- setActiveRole({ name: 'All Roles', isViewAll: true });
267
+ setActiveRole({ name: 'All Roles' });
268
268
 
269
269
  try {
270
270
  const res = await MenusAPI.getMenubyUser(id);
271
- const userMenus = Array.isArray(res?.result?.menus) ? res.result.menus : [];
272
- setModules(userMenus);
271
+ setModules(res?.result?.menus ?? []);
273
272
  setLoadingMenus(false);
274
273
  } catch (err) {
275
274
  setLoadingMenus(false);
@@ -278,18 +277,6 @@ export default function AssignRole() {
278
277
  }
279
278
  };
280
279
 
281
- /**
282
- * Context-specific empty-state message for the right (menus) panel:
283
- * - View Access for a user with no roles → no role assigned
284
- * - A selected role with no menus → no menus assigned
285
- * - Nothing selected yet → prompt to pick a role
286
- */
287
- const emptyMenusMessage = !activeRole
288
- ? 'Select a role to view menus'
289
- : activeRole.isViewAll
290
- ? 'No role has been assigned to this user.'
291
- : 'No menus have been assigned to this role.';
292
-
293
280
  return (
294
281
  <section className="assign-role">
295
282
  {/* LEFT PANEL */}
@@ -396,19 +383,17 @@ export default function AssignRole() {
396
383
  padding: 16,
397
384
  }}
398
385
  >
399
- {modules.length > 0 && (
400
- <div className="menus-header">
401
- <div className="title">Menus {activeRole ? `– ${activeRole.name}` : ''}</div>
402
- <div className="sub-text">You don’t have permission to edit this here. Update it in Role Settings.</div>
403
- </div>
404
- )}
386
+ <div className="menus-header">
387
+ <div className="title">Menus {activeRole ? `– ${activeRole.name}` : ''}</div>
388
+ <div className="sub-text">You don’t have permission to edit this here. Update it in Role Settings.</div>
389
+ </div>
405
390
 
406
391
  <div className="menus-content">
407
392
  {loadingMenus ? (
408
393
  <Skeleton active paragraph={{ rows: 6 }} />
409
394
  ) : modules.length === 0 ? (
410
395
  <div className="empty-state">
411
- <Empty description={emptyMenusMessage} />
396
+ <Empty description="Select a role to view menus" />
412
397
  </div>
413
398
  ) : (
414
399
  <MenuTree menus={modules} selectedMenus={selectedMenus} toggleMenu={toggleMenu} showCheckbox={false} />