ui-soxo-bootstrap-core 2.4.25-dev.26 → 2.4.25-dev.28
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/core/lib/components/sidemenu/sidemenu.js +3 -3
- package/core/lib/elements/basic/dragabble-wrapper/draggable-wrapper.js +82 -84
- package/core/models/menus/components/menu-lists/menu-lists.scss +3 -2
- package/core/models/roles/components/role-add/role-add.js +76 -18
- package/core/models/roles/components/role-add/role-add.scss +4 -0
- package/core/modules/index.js +0 -4
- package/package.json +1 -1
|
@@ -214,7 +214,7 @@ export default function SideMenu({ loading, modules = [], callback, appSettings,
|
|
|
214
214
|
// document.documentElement.style.setProperty('--custom-text-color', state.theme.colors.colorText);
|
|
215
215
|
}, [state.theme]);
|
|
216
216
|
|
|
217
|
-
const rootSubmenuKeys = Menus.screenMenus(modules, 'order').map((m) => m.path || m.caption);
|
|
217
|
+
const rootSubmenuKeys = Menus.screenMenus(modules, 'order').map((m) => m.id || m.path || m.caption);
|
|
218
218
|
|
|
219
219
|
return (
|
|
220
220
|
<div className="sidemenu">
|
|
@@ -354,7 +354,7 @@ export default function SideMenu({ loading, modules = [], callback, appSettings,
|
|
|
354
354
|
style={{ color: state.theme.colors.leftSectionColor }}
|
|
355
355
|
// key={`first-level-${randomIndex}-${menu.caption}`}
|
|
356
356
|
|
|
357
|
-
key={menu.path || menu.caption}
|
|
357
|
+
key={menu.id || menu.path || menu.caption}
|
|
358
358
|
title={
|
|
359
359
|
<>
|
|
360
360
|
<CollapsedIconMenu
|
|
@@ -377,7 +377,7 @@ export default function SideMenu({ loading, modules = [], callback, appSettings,
|
|
|
377
377
|
className="popup"
|
|
378
378
|
// key={`second-level-${randomIndex}-${submenu.id}`}
|
|
379
379
|
|
|
380
|
-
key={submenu.path || submenu.caption}
|
|
380
|
+
key={submenu.id || submenu.path || submenu.caption}
|
|
381
381
|
title={
|
|
382
382
|
<span>
|
|
383
383
|
<CollapsedIconMenu
|
|
@@ -2,26 +2,25 @@ import React, { useRef } from 'react';
|
|
|
2
2
|
import { useDrag, useDrop } from 'react-dnd';
|
|
3
3
|
|
|
4
4
|
export default function DraggableWrapper({ id, index, movePanel, item, dragEnabled, level, parentId, onCrossLevelMove, canAcceptChildren }) {
|
|
5
|
-
|
|
6
5
|
const autoScrollWindow = (monitor) => {
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
const offset = monitor.getClientOffset();
|
|
7
|
+
if (!offset) return;
|
|
9
8
|
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
const EDGE = 80;
|
|
10
|
+
const SPEED = 20;
|
|
12
11
|
|
|
13
|
-
|
|
12
|
+
const viewportHeight = window.innerHeight;
|
|
14
13
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
14
|
+
// 🔼 scroll UP
|
|
15
|
+
if (offset.y < EDGE) {
|
|
16
|
+
window.scrollBy(0, -SPEED);
|
|
17
|
+
}
|
|
19
18
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
};
|
|
19
|
+
// 🔽 scroll DOWN
|
|
20
|
+
if (offset.y > viewportHeight - EDGE) {
|
|
21
|
+
window.scrollBy(0, SPEED);
|
|
22
|
+
}
|
|
23
|
+
};
|
|
25
24
|
|
|
26
25
|
const [{ isDragging }, drag] = useDrag({
|
|
27
26
|
type: 'PANEL',
|
|
@@ -34,36 +33,30 @@ export default function DraggableWrapper({ id, index, movePanel, item, dragEnabl
|
|
|
34
33
|
|
|
35
34
|
const [{ isOver, canDrop }, drop] = useDrop({
|
|
36
35
|
accept: 'PANEL',
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
dragItem.index = index; // keep in sync
|
|
49
|
-
}
|
|
50
|
-
},
|
|
36
|
+
hover: (dragItem, monitor) => {
|
|
37
|
+
// THIS FIXES BOTTOM → TOP
|
|
38
|
+
autoScrollWindow(monitor);
|
|
39
|
+
|
|
40
|
+
if (dragItem.index === index) return;
|
|
41
|
+
|
|
42
|
+
if (dragItem.level === level && dragItem.parentId === parentId) {
|
|
43
|
+
movePanel(dragItem.index, index);
|
|
44
|
+
dragItem.index = index; // keep in sync
|
|
45
|
+
}
|
|
46
|
+
},
|
|
51
47
|
|
|
52
48
|
canDrop: (item) => dragEnabled,
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
});
|
|
65
|
-
}
|
|
66
|
-
},
|
|
49
|
+
drop: (dragItem, monitor) => {
|
|
50
|
+
if (monitor.didDrop()) return;
|
|
51
|
+
|
|
52
|
+
if (dragItem.level !== level || dragItem.parentId !== parentId) {
|
|
53
|
+
onCrossLevelMove?.(dragItem, {
|
|
54
|
+
targetLevel: level,
|
|
55
|
+
targetParentId: parentId,
|
|
56
|
+
targetIndex: index,
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
},
|
|
67
60
|
collect: (monitor) => ({
|
|
68
61
|
isOver: monitor.isOver({ shallow: true }),
|
|
69
62
|
canDrop: monitor.canDrop(),
|
|
@@ -75,7 +68,7 @@ export default function DraggableWrapper({ id, index, movePanel, item, dragEnabl
|
|
|
75
68
|
accept: 'PANEL',
|
|
76
69
|
canDrop: (item) => dragEnabled && item.id !== id && canAcceptChildren,
|
|
77
70
|
drop: (item, monitor) => {
|
|
78
|
-
if (monitor.didDrop()) return;
|
|
71
|
+
// if (monitor.didDrop()) return;
|
|
79
72
|
|
|
80
73
|
if (onCrossLevelMove && item.id !== id) {
|
|
81
74
|
// Drop as child of this item
|
|
@@ -88,51 +81,56 @@ export default function DraggableWrapper({ id, index, movePanel, item, dragEnabl
|
|
|
88
81
|
}),
|
|
89
82
|
});
|
|
90
83
|
|
|
91
|
-
const backgroundColor = isOver && canDrop ? '#bae7ff' : isDragging ? '#
|
|
84
|
+
const backgroundColor = isOver && canDrop ? '#bae7ff' : isDragging ? '#ffff' : 'transparent';
|
|
92
85
|
const childZoneBackgroundColor = isOverChild && canDropChild ? '#d4f4dd' : 'transparent';
|
|
93
86
|
|
|
94
87
|
return (
|
|
95
|
-
<div
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
ref={dropChild}
|
|
119
|
-
onClick={(e) => e.stopPropagation()}
|
|
120
|
-
style={{
|
|
121
|
-
padding: '4px 12px',
|
|
122
|
-
marginLeft: 8,
|
|
123
|
-
backgroundColor: childZoneBackgroundColor,
|
|
124
|
-
border: isOverChild && canDropChild ? '2px solid #52c41a' : '1px dashed #d9d9d9',
|
|
125
|
-
borderRadius: 4,
|
|
126
|
-
fontSize: 11,
|
|
127
|
-
color: isOverChild && canDropChild ? '#52c41a' : '#999',
|
|
128
|
-
cursor: 'default',
|
|
129
|
-
transition: 'all 0.2s ease',
|
|
130
|
-
}}
|
|
131
|
-
>
|
|
132
|
-
{isOverChild && canDropChild ? '✓ Drop as child' : '↳ Drop here'}
|
|
88
|
+
<div style={{ width: '100%', display: 'flex' }}>
|
|
89
|
+
{/* HEADER DROP — reorder only */}
|
|
90
|
+
<div ref={drop}>
|
|
91
|
+
<div
|
|
92
|
+
ref={drag}
|
|
93
|
+
style={{
|
|
94
|
+
opacity: isDragging ? 0.5 : 1,
|
|
95
|
+
cursor: dragEnabled ? 'move' : 'default',
|
|
96
|
+
backgroundColor,
|
|
97
|
+
border: isOver && canDrop ? '2px dashed #1890ff' : '2px solid transparent',
|
|
98
|
+
display: 'flex',
|
|
99
|
+
justifyContent: 'space-between',
|
|
100
|
+
alignItems: 'center',
|
|
101
|
+
}}
|
|
102
|
+
>
|
|
103
|
+
<div style={{ flex: 1 }}>
|
|
104
|
+
{dragEnabled && <span style={{ marginRight: 8 }}>⋮⋮</span>}
|
|
105
|
+
<span>{item.name}</span>
|
|
106
|
+
{dragEnabled ? (
|
|
107
|
+
<span style={{ marginLeft: 8, fontSize: 11, color: '#999' }}>(Level {level})</span>
|
|
108
|
+
) : (
|
|
109
|
+
<span style={{ marginLeft: 8, fontSize: 11, color: '#999' }}>{item.path}</span>
|
|
110
|
+
)}
|
|
133
111
|
</div>
|
|
134
|
-
|
|
112
|
+
</div>
|
|
135
113
|
</div>
|
|
114
|
+
|
|
115
|
+
{/* CHILD DROP — nesting */}
|
|
116
|
+
{canAcceptChildren && dragEnabled && (
|
|
117
|
+
<div
|
|
118
|
+
ref={dropChild}
|
|
119
|
+
style={{
|
|
120
|
+
marginLeft: 8,
|
|
121
|
+
// flex:1,
|
|
122
|
+
// marginTop: 4,
|
|
123
|
+
padding: '4px 12px',
|
|
124
|
+
backgroundColor: isOverChild && canDropChild ? '#d4f4dd' : 'transparent',
|
|
125
|
+
border: isOverChild && canDropChild ? '2px solid #52c41a' : '1px dashed #d9d9d9',
|
|
126
|
+
borderRadius: 4,
|
|
127
|
+
fontSize: 11,
|
|
128
|
+
color: isOverChild && canDropChild ? '#52c41a' : '#999',
|
|
129
|
+
}}
|
|
130
|
+
>
|
|
131
|
+
{isOverChild && canDropChild ? '✓ Drop as child' : '↳ Drop here'}
|
|
132
|
+
</div>
|
|
133
|
+
)}
|
|
136
134
|
</div>
|
|
137
135
|
);
|
|
138
136
|
}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
}
|
|
5
5
|
|
|
6
6
|
.ant-collapse {
|
|
7
|
-
background-color: #
|
|
7
|
+
background-color: #ffff !important;
|
|
8
8
|
border: none !important;
|
|
9
9
|
}
|
|
10
10
|
.ant-collapse > .ant-collapse-item {
|
|
@@ -39,7 +39,8 @@
|
|
|
39
39
|
box-shadow: 0 1px 4px rgba(0,0,0,0.04);
|
|
40
40
|
}
|
|
41
41
|
.ant-collapse > .ant-collapse-item > .ant-collapse-heade{
|
|
42
|
-
align-items: center;
|
|
42
|
+
align-items: center !important;
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
|
|
46
|
+
|
|
@@ -4,6 +4,7 @@ import { Skeleton, Typography, message, Form, Input, Collapse, Checkbox } from '
|
|
|
4
4
|
import { GlobalContext } from './../../../../lib';
|
|
5
5
|
import { Button } from './../../../../lib';
|
|
6
6
|
import { ModelsAPI, PagesAPI, RolesAPI, MenusAPI } from '../../..';
|
|
7
|
+
import './role-add.scss';
|
|
7
8
|
|
|
8
9
|
const { Title } = Typography;
|
|
9
10
|
const { Panel } = Collapse;
|
|
@@ -59,7 +60,7 @@ const RoleAdd = ({ model, callback, edit, formContent = {}, match, additional_qu
|
|
|
59
60
|
|
|
60
61
|
setSelectedMenus(menus);
|
|
61
62
|
// keep original copy for deselect comparison
|
|
62
|
-
setOriginalMenus(menus);
|
|
63
|
+
setOriginalMenus(menus);
|
|
63
64
|
}
|
|
64
65
|
|
|
65
66
|
setLoading(false);
|
|
@@ -203,48 +204,105 @@ export default RoleAdd;
|
|
|
203
204
|
// ------------------------
|
|
204
205
|
// Recursive Nested Menu Component
|
|
205
206
|
// ------------------------
|
|
206
|
-
|
|
207
|
+
// ------------------------
|
|
208
|
+
// Recursive Nested Menu Component
|
|
209
|
+
// ------------------------
|
|
210
|
+
const MenuTree = ({ menus, selectedMenus, toggleMenu, parentId = null }) => {
|
|
211
|
+
// Helper: check if parent should be checked
|
|
212
|
+
const isParentChecked = (menu) => {
|
|
213
|
+
if (!menu.sub_menus || menu.sub_menus.length === 0) {
|
|
214
|
+
return selectedMenus.includes(menu.id);
|
|
215
|
+
}
|
|
216
|
+
const allChildIds = menu.sub_menus.map((c) => c.id);
|
|
217
|
+
return allChildIds.every((id) => selectedMenus.includes(id));
|
|
218
|
+
};
|
|
219
|
+
|
|
220
|
+
// Helper: check if parent is indeterminate
|
|
221
|
+
const isParentIndeterminate = (menu) => {
|
|
222
|
+
if (!menu.sub_menus || menu.sub_menus.length === 0) return false;
|
|
223
|
+
const allChildIds = menu.sub_menus.map((c) => c.id);
|
|
224
|
+
const checkedCount = allChildIds.filter((id) => selectedMenus.includes(id)).length;
|
|
225
|
+
return checkedCount > 0 && checkedCount < allChildIds.length;
|
|
226
|
+
};
|
|
227
|
+
|
|
207
228
|
return (
|
|
208
229
|
<>
|
|
209
230
|
{menus.map((menu) => {
|
|
210
|
-
const
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
231
|
+
const children = menu.sub_menus || [];
|
|
232
|
+
const parentChecked = isParentChecked(menu);
|
|
233
|
+
const parentIndeterminate = isParentIndeterminate(menu);
|
|
234
|
+
|
|
235
|
+
const onParentChange = (checked) => {
|
|
236
|
+
toggleMenu(menu.id, checked);
|
|
237
|
+
// toggle children recursively
|
|
238
|
+
children.forEach((c) => toggleMenuRecursive(c, checked));
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
const toggleMenuRecursive = (menu, checked) => {
|
|
242
|
+
toggleMenu(menu.id, checked);
|
|
243
|
+
if (menu.sub_menus && menu.sub_menus.length > 0) {
|
|
244
|
+
menu.sub_menus.forEach((c) => toggleMenuRecursive(c, checked));
|
|
245
|
+
}
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
if (children.length === 0) {
|
|
214
249
|
return (
|
|
215
250
|
<div
|
|
216
251
|
key={menu.id}
|
|
217
252
|
style={{
|
|
218
|
-
|
|
253
|
+
border: '1px solid rgba(198, 195, 195, 0.85)',
|
|
254
|
+
// borderRadius: 6,
|
|
255
|
+
padding: '12px 16px',
|
|
256
|
+
marginBottom: 6,
|
|
257
|
+
background: '#fff',
|
|
219
258
|
display: 'flex',
|
|
220
259
|
alignItems: 'center',
|
|
221
260
|
gap: 8,
|
|
222
|
-
borderBottom: '1px solid #f0f0f0',
|
|
223
261
|
}}
|
|
224
262
|
>
|
|
225
|
-
<Checkbox
|
|
263
|
+
<Checkbox
|
|
264
|
+
checked={selectedMenus.includes(menu.id)}
|
|
265
|
+
onChange={(e) => {
|
|
266
|
+
const checked = e.target.checked;
|
|
267
|
+
|
|
268
|
+
toggleMenu(menu.id, checked);
|
|
269
|
+
|
|
270
|
+
// ✅ FORCE parent selection
|
|
271
|
+
if (checked && parentId) {
|
|
272
|
+
toggleMenu(parentId, true);
|
|
273
|
+
}
|
|
274
|
+
}}
|
|
275
|
+
/>
|
|
226
276
|
<span>{menu.title || menu.caption}</span>
|
|
227
277
|
</div>
|
|
228
278
|
);
|
|
229
279
|
}
|
|
230
280
|
|
|
231
|
-
// HAS CHILD → COLLAPSE WITH ICON
|
|
232
281
|
return (
|
|
233
|
-
<Collapse
|
|
282
|
+
<Collapse
|
|
283
|
+
key={menu.id}
|
|
284
|
+
style={{ marginBottom: 6 }}
|
|
285
|
+
// defaultActiveKey={[menu.id]}
|
|
286
|
+
>
|
|
234
287
|
<Panel
|
|
235
288
|
key={menu.id}
|
|
236
289
|
header={
|
|
237
|
-
<div
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
290
|
+
<div
|
|
291
|
+
style={{
|
|
292
|
+
display: 'flex',
|
|
293
|
+
alignItems: 'center',
|
|
294
|
+
gap: 8,
|
|
295
|
+
}}
|
|
296
|
+
onClick={(e) => e.stopPropagation()}
|
|
297
|
+
>
|
|
298
|
+
<Checkbox checked={parentChecked} indeterminate={parentIndeterminate} onChange={(e) => onParentChange(e.target.checked)} />
|
|
243
299
|
<span>{menu.title || menu.caption}</span>
|
|
244
300
|
</div>
|
|
245
301
|
}
|
|
246
302
|
>
|
|
247
|
-
<
|
|
303
|
+
<div style={{ paddingLeft: 20 }}>
|
|
304
|
+
<MenuTree menus={children} selectedMenus={selectedMenus} toggleMenu={toggleMenu} parentId={menu.id} />
|
|
305
|
+
</div>
|
|
248
306
|
</Panel>
|
|
249
307
|
</Collapse>
|
|
250
308
|
);
|
package/core/modules/index.js
CHANGED
|
@@ -16,8 +16,6 @@ import PopQueryDashboard from './dashboard/components/pop-query-dashboard/pop-qu
|
|
|
16
16
|
|
|
17
17
|
import HomePageAPI from './../pages/homepage-api/homepage-api';
|
|
18
18
|
|
|
19
|
-
import { Profile, ChangePassword } from './../lib';
|
|
20
|
-
|
|
21
19
|
import ReportingDashboard from '../modules/reporting/components/reporting-dashboard/reporting-dashboard';
|
|
22
20
|
|
|
23
21
|
import ChangeInfo from './Informations/change-info/change-info';
|
|
@@ -34,9 +32,7 @@ export {
|
|
|
34
32
|
DashboardCard,
|
|
35
33
|
PopQueryDashboard,
|
|
36
34
|
HomePageAPI,
|
|
37
|
-
Profile,
|
|
38
35
|
ReportingDashboard,
|
|
39
|
-
ChangePassword,
|
|
40
36
|
ChangeInfo,
|
|
41
37
|
};
|
|
42
38
|
|