ui-soxo-bootstrap-core 2.6.40-dev.16 → 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.
- package/DEVELOPER_GUIDE.md +9 -38
- package/core/components/index.js +11 -2
- package/core/components/landing-api/landing-api.js +5 -165
- package/core/lib/components/global-header/global-header.js +77 -20
- package/core/lib/components/index.js +2 -2
- package/core/lib/components/sidemenu/sidemenu.js +147 -66
- package/core/lib/elements/basic/dragabble-wrapper/draggable-wrapper.js +24 -91
- package/core/lib/models/menus/components/menu-add/menu-add.js +2 -2
- package/core/lib/modules/generic/generic-list/ExportReactCSV.js +8 -334
- package/core/lib/modules/generic/generic-list/generic-list.scss +0 -34
- package/core/models/core-scripts/core-scripts.js +1 -22
- package/core/models/menus/components/menu-add/menu-add.js +4 -31
- package/core/models/menus/components/menu-lists/menu-lists.js +9 -76
- package/core/models/menus/menus.js +1 -29
- package/core/models/roles/components/role-add/role-add.js +169 -54
- package/core/models/roles/components/role-list/role-list.js +0 -20
- package/core/models/roles/roles.js +0 -3
- package/core/models/users/components/assign-role/assign-role.js +8 -23
- package/core/modules/reporting/components/reporting-dashboard/display-columns/display-cell-renderer.js +5 -202
- package/core/modules/reporting/components/reporting-dashboard/display-columns/display-cell-renderer.test.js +0 -73
- package/core/modules/reporting/components/reporting-dashboard/reporting-dashboard.js +35 -56
- package/core/modules/reporting/components/reporting-dashboard/reporting-dashboard.scss +0 -1
- package/package.json +1 -1
- package/core/components/license-management/license-alert.js +0 -97
- package/core/models/roles/components/role-add/menu-label.js +0 -14
- package/core/models/roles/components/role-add/menu-tree.js +0 -127
- package/core/modules/reporting/components/reporting-dashboard/reporting-table.js +0 -519
|
@@ -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 = []
|
|
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
|
-
//
|
|
105
|
-
//
|
|
106
|
-
//
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
160
|
-
message.warning(err?.message || err?.result?.message || 'Failed to save role');
|
|
160
|
+
// message.error('Something went wrong');
|
|
161
161
|
} finally {
|
|
162
|
-
|
|
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
|
-
<
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
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
|
-
{
|
|
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 =
|
|
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'
|
|
267
|
+
setActiveRole({ name: 'All Roles' });
|
|
268
268
|
|
|
269
269
|
try {
|
|
270
270
|
const res = await MenusAPI.getMenubyUser(id);
|
|
271
|
-
|
|
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
|
-
|
|
400
|
-
<div className="
|
|
401
|
-
|
|
402
|
-
|
|
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=
|
|
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} />
|