ui-soxo-bootstrap-core 2.4.25-dev.25 → 2.4.25-dev.27

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.
@@ -163,6 +163,10 @@ export default function SideMenu({ loading, modules = [], callback, appSettings,
163
163
  const onMenuClick = (menu, index) => {
164
164
  const key = menu.path || `menu-${menu.id || index}`;
165
165
 
166
+ if (menu.isRoot) {
167
+ setOpenKeys([]);
168
+ }
169
+
166
170
  setSelectedKeys([key]);
167
171
 
168
172
  if (menu.path) {
@@ -447,7 +451,7 @@ export default function SideMenu({ loading, modules = [], callback, appSettings,
447
451
  // }}
448
452
 
449
453
  onClick={() => {
450
- onMenuClick({ ...menu, parentKey: menu.path || menu.caption }, index);
454
+ onMenuClick({ ...menu, parentKey: menu.path || menu.caption, isRoot: true }, index);
451
455
  }}
452
456
  // key={`${menu.id}-${randomIndex}`}
453
457
  key={menu.path || menu.caption}
@@ -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
- const offset = monitor.getClientOffset();
8
- if (!offset) return;
6
+ const offset = monitor.getClientOffset();
7
+ if (!offset) return;
9
8
 
10
- const EDGE = 80;
11
- const SPEED = 20;
9
+ const EDGE = 80;
10
+ const SPEED = 20;
12
11
 
13
- const viewportHeight = window.innerHeight;
12
+ const viewportHeight = window.innerHeight;
14
13
 
15
- // 🔼 scroll UP
16
- if (offset.y < EDGE) {
17
- window.scrollBy(0, -SPEED);
18
- }
14
+ // 🔼 scroll UP
15
+ if (offset.y < EDGE) {
16
+ window.scrollBy(0, -SPEED);
17
+ }
19
18
 
20
- // 🔽 scroll DOWN
21
- if (offset.y > viewportHeight - EDGE) {
22
- window.scrollBy(0, SPEED);
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
- hover: (dragItem, monitor) => {
38
- // THIS FIXES BOTTOM → TOP
39
- autoScrollWindow(monitor);
40
-
41
- if (dragItem.index === index) return;
42
-
43
- if (
44
- dragItem.level === level &&
45
- dragItem.parentId === parentId
46
- ) {
47
- movePanel(dragItem.index, index);
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
- drop: (dragItem, monitor) => {
54
- if (monitor.didDrop()) return;
55
-
56
- if (
57
- dragItem.level !== level ||
58
- dragItem.parentId !== parentId
59
- ) {
60
- onCrossLevelMove?.(dragItem, {
61
- targetLevel: level,
62
- targetParentId: parentId,
63
- targetIndex: index,
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 ? '#e6f7ff' : 'transparent';
84
+ const backgroundColor = isOver && canDrop ? '#bae7ff' : isDragging ? '#ffff' : 'transparent';
92
85
  const childZoneBackgroundColor = isOverChild && canDropChild ? '#d4f4dd' : 'transparent';
93
86
 
94
87
  return (
95
- <div ref={drop} style={{ width: '100%' }}>
96
- <div
97
- ref={drag}
98
- style={{
99
- opacity: isDragging ? 0.5 : 1,
100
- cursor: dragEnabled ? 'move' : 'default',
101
- // padding: '8px',
102
- backgroundColor,
103
- transition: 'all 0.2s ease',
104
- border: isOver && canDrop ? '2px dashed #1890ff' : '2px solid transparent',
105
- display: 'flex',
106
- justifyContent: 'space-between',
107
- alignItems: 'center',
108
- }}
109
- >
110
- <div style={{ flex: 1 }}>
111
- {dragEnabled && <span style={{ marginRight: 8, color: '#999' }}>⋮⋮</span>}
112
- <strong>{item.name}</strong>
113
- {dragEnabled ?( <span style={{ marginLeft: 8, fontSize: 11, color: '#999' }}>(Level {level})</span>):(<span style={{ marginLeft: 8, fontSize: 11, color: '#999' }}>{item.path}</span>)}
114
- </div>
115
-
116
- {canAcceptChildren && dragEnabled && (
117
- <div
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: #fafafa !important;
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
- const MenuTree = ({ menus, selectedMenus, toggleMenu }) => {
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 hasChildren = Array.isArray(menu.sub_menus) && menu.sub_menus.length > 0;
211
-
212
- // NO CHILD SIMPLE ROW (NO COLLAPSE)
213
- if (!hasChildren) {
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
- padding: '10px 16px',
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 checked={selectedMenus.includes(menu.id)} onChange={(e) => toggleMenu(menu.id, e.target.checked)} />
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 key={menu.id} collapsible="icon" style={{ marginBottom: '4px' }}>
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 style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
238
- <Checkbox
239
- checked={selectedMenus.includes(menu.id)}
240
- onClick={(e) => e.stopPropagation()}
241
- onChange={(e) => toggleMenu(menu.id, e.target.checked)}
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
- <MenuTree menus={menu.sub_menus} selectedMenus={selectedMenus} toggleMenu={toggleMenu} />
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
  );
@@ -0,0 +1,4 @@
1
+ .ant-checkbox-indeterminate .ant-checkbox-inner::after {
2
+ height: 2.5px;
3
+ opacity: 0.85;
4
+ }
@@ -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
 
@@ -220,7 +220,7 @@ export default function ReportingDashboard({
220
220
  if (['reference-select', 'reference-search', 'select'].indexOf(record.type) !== -1) {
221
221
  // let model = "";
222
222
  let model = CustomModels[record.modelName];
223
-
223
+
224
224
  return {
225
225
  ...record,
226
226
  model,
@@ -800,8 +800,7 @@ function GuestList({
800
800
  field: entry.field,
801
801
  title: entry.title,
802
802
  key: entry.field,
803
- width: entry.width || 160,
804
- ellipsis: true,
803
+ width: entry.width ? parseInt(entry.width) : undefined,
805
804
  fixed: entry.isFixedColumn ? entry.isFixedColumn : null, // Conditionally setting the 'fixed' key to 'left' if 'isColumnStatic' is true; otherwise, setting it to null.
806
805
  // Check if filtering is enabled and patients is an array
807
806
  filters:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ui-soxo-bootstrap-core",
3
- "version": "2.4.25-dev.25",
3
+ "version": "2.4.25-dev.27",
4
4
  "description": "All the Core Components for you to start",
5
5
  "keywords": [
6
6
  "all in one"