ui-soxo-bootstrap-core 2.4.25-dev.10 → 2.4.25-dev.11
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/elements/basic/dragabble-wrapper/draggable-wrapper.js +119 -42
- package/core/lib/elements/basic/switch/switch.js +1 -1
- package/core/models/menus/components/menu-add/menu-add.js +22 -31
- package/core/models/menus/components/menu-lists/menu-lists.js +336 -218
- package/core/models/menus/components/menu-lists/menu-lists.scss +4 -9
- package/core/models/menus/menus.js +9 -0
- package/core/models/roles/components/role-add/role-add.js +123 -128
- package/core/models/roles/components/role-list/role-list.js +325 -349
- package/core/models/roles/roles.js +9 -0
- package/core/models/users/components/user-add/user-add.js +35 -1
- package/package.json +1 -1
|
@@ -1,14 +1,13 @@
|
|
|
1
|
-
import React, { useState,
|
|
2
|
-
import { Space,
|
|
3
|
-
import { ReloadOutlined, DeleteOutlined, EditOutlined,
|
|
4
|
-
import {
|
|
5
|
-
|
|
1
|
+
import React, { useState, useCallback, useEffect } from 'react';
|
|
2
|
+
import { Space, Popconfirm, Input, Drawer, Skeleton, Collapse, message } from 'antd';
|
|
3
|
+
import { ReloadOutlined, DeleteOutlined, EditOutlined, PlusCircleFilled, CopyOutlined } from '@ant-design/icons';
|
|
4
|
+
import { Button, Card, Switch, DraggableWrapper } from '../../../../lib';
|
|
6
5
|
// for draggable menu list import { DndProvider } from "react-dnd";
|
|
7
6
|
import { DndProvider } from 'react-dnd';
|
|
7
|
+
import { Link, useParams, useLocation } from 'react-router-dom';
|
|
8
8
|
import { HTML5Backend } from 'react-dnd-html5-backend';
|
|
9
|
-
|
|
9
|
+
|
|
10
10
|
import './menu-lists.scss';
|
|
11
|
-
import { Button, Card } from '../../../../lib';
|
|
12
11
|
|
|
13
12
|
const { Search } = Input;
|
|
14
13
|
const { Panel } = Collapse;
|
|
@@ -18,36 +17,45 @@ const MenuLists = ({ model, match, relativeAdd = false, additional_queries = [],
|
|
|
18
17
|
|
|
19
18
|
match = useParams();
|
|
20
19
|
const id = match.id;
|
|
21
|
-
const location = useLocation();
|
|
22
|
-
|
|
20
|
+
// const location = useLocation();
|
|
23
21
|
const step = parseInt(new URLSearchParams(location.search).get('step')) || 1;
|
|
24
22
|
|
|
25
23
|
const [records, setRecords] = useState([]);
|
|
24
|
+
const [originalRecords, setOriginalRecords] = useState([]);
|
|
26
25
|
const [loading, setLoading] = useState(false);
|
|
27
|
-
|
|
28
26
|
const [drawerVisible, setDrawerVisible] = useState(false);
|
|
29
|
-
|
|
30
|
-
const [view, setView] = useState(false);
|
|
31
|
-
|
|
32
|
-
const [visible, setVisible] = useState(false);
|
|
33
|
-
|
|
34
27
|
const [single, setSingle] = useState({});
|
|
35
28
|
const [drawerTitle, setDrawerTitle] = useState('');
|
|
36
29
|
const [selectedRecord, setSelectedRecord] = useState(null);
|
|
37
|
-
|
|
38
30
|
const [query, setQuery] = useState('');
|
|
39
|
-
|
|
40
|
-
// DEFAULT DRAG MODE: OFF
|
|
41
|
-
const [dragMode, setDragMode] = useState(false); // for drag (default OFF)
|
|
42
|
-
|
|
43
|
-
// to detect drag changes
|
|
31
|
+
const [dragMode, setDragMode] = useState(false);
|
|
44
32
|
const [orderChanged, setOrderChanged] = useState(false);
|
|
45
33
|
|
|
46
|
-
const
|
|
34
|
+
const [nextId, setNextId] = useState(10000);
|
|
47
35
|
|
|
48
36
|
useEffect(() => {
|
|
49
37
|
loadMenus();
|
|
50
38
|
}, [id]);
|
|
39
|
+
|
|
40
|
+
const loadMenus = () => {
|
|
41
|
+
setLoading(true);
|
|
42
|
+
|
|
43
|
+
model
|
|
44
|
+
.getCoreMenuLists
|
|
45
|
+
// {
|
|
46
|
+
// queries: [...additional_queries, { field: 'step', value: step }, { field: 'header_id', value: null }],
|
|
47
|
+
// }
|
|
48
|
+
()
|
|
49
|
+
.then((res) => {
|
|
50
|
+
const sorted = (res.result || []).sort((a, b) => a.order - b.order);
|
|
51
|
+
setRecords(sorted);
|
|
52
|
+
setOriginalRecords(sorted);
|
|
53
|
+
setLoading(false);
|
|
54
|
+
})
|
|
55
|
+
.catch(() => setLoading(false));
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
console.log('record', records);
|
|
51
59
|
/**
|
|
52
60
|
*
|
|
53
61
|
*/
|
|
@@ -95,198 +103,321 @@ const MenuLists = ({ model, match, relativeAdd = false, additional_queries = [],
|
|
|
95
103
|
});
|
|
96
104
|
};
|
|
97
105
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
106
|
+
const toggleDragMode = (checked) => {
|
|
107
|
+
setDragMode(checked);
|
|
108
|
+
if (checked) {
|
|
109
|
+
message.info('Drag mode enabled - Drag items anywhere, including the "Drop here" zones!');
|
|
110
|
+
}
|
|
111
|
+
};
|
|
103
112
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
113
|
+
// Function to find and remove an item from anywhere in the tree
|
|
114
|
+
const findAndRemoveItem = (items, itemId) => {
|
|
115
|
+
let foundItem = null;
|
|
116
|
+
|
|
117
|
+
const removeRecursive = (arr) => {
|
|
118
|
+
for (let i = 0; i < arr.length; i++) {
|
|
119
|
+
if (arr[i].id === itemId) {
|
|
120
|
+
foundItem = { ...arr[i] };
|
|
121
|
+
arr.splice(i, 1);
|
|
122
|
+
return true;
|
|
123
|
+
}
|
|
124
|
+
if (arr[i].sub_menus && arr[i].sub_menus.length > 0) {
|
|
125
|
+
if (removeRecursive(arr[i].sub_menus)) {
|
|
126
|
+
return true;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return false;
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
const newItems = JSON.parse(JSON.stringify(items));
|
|
134
|
+
removeRecursive(newItems);
|
|
135
|
+
return { newItems, foundItem };
|
|
114
136
|
};
|
|
115
137
|
|
|
138
|
+
// Function to insert item at specific location
|
|
139
|
+
const insertItemAt = (items, item, targetLevel, targetParentId, targetIndex) => {
|
|
140
|
+
const newItems = JSON.parse(JSON.stringify(items));
|
|
141
|
+
|
|
142
|
+
// Update item properties for new location
|
|
143
|
+
const updatedItem = {
|
|
144
|
+
...item,
|
|
145
|
+
step: targetLevel,
|
|
146
|
+
header_id: targetParentId,
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
if (targetLevel === 1) {
|
|
150
|
+
// Insert at main level
|
|
151
|
+
newItems.splice(targetIndex, 0, updatedItem);
|
|
152
|
+
} else {
|
|
153
|
+
// Insert into nested level
|
|
154
|
+
const insertRecursive = (arr) => {
|
|
155
|
+
for (let i = 0; i < arr.length; i++) {
|
|
156
|
+
if (arr[i].id === targetParentId) {
|
|
157
|
+
if (!arr[i].sub_menus) arr[i].sub_menus = [];
|
|
158
|
+
arr[i].sub_menus.splice(targetIndex, 0, updatedItem);
|
|
159
|
+
return true;
|
|
160
|
+
}
|
|
161
|
+
if (arr[i].sub_menus && arr[i].sub_menus.length > 0) {
|
|
162
|
+
if (insertRecursive(arr[i].sub_menus)) {
|
|
163
|
+
return true;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return false;
|
|
168
|
+
};
|
|
169
|
+
insertRecursive(newItems);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return newItems;
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
const handleCrossLevelMove = useCallback(
|
|
176
|
+
(draggedItem, dropTarget) => {
|
|
177
|
+
const { targetLevel, targetParentId, targetIndex } = dropTarget;
|
|
178
|
+
|
|
179
|
+
// Prevent moving item into itself or its children
|
|
180
|
+
if (draggedItem.id === targetParentId) {
|
|
181
|
+
message.warning('Cannot move item into itself');
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Remove item from original location
|
|
186
|
+
const { newItems: itemsAfterRemove, foundItem } = findAndRemoveItem(records, draggedItem.id);
|
|
187
|
+
|
|
188
|
+
if (!foundItem) {
|
|
189
|
+
message.error('Could not find item to move');
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Insert item at new location
|
|
194
|
+
const finalItems = insertItemAt(itemsAfterRemove, foundItem, targetLevel, targetParentId, targetIndex);
|
|
195
|
+
|
|
196
|
+
setRecords(finalItems);
|
|
197
|
+
setOrderChanged(true);
|
|
198
|
+
message.success(`Moved "${foundItem.name}" to level ${targetLevel}`);
|
|
199
|
+
},
|
|
200
|
+
[records]
|
|
201
|
+
);
|
|
202
|
+
|
|
116
203
|
const deleteRecord = (rec) => {
|
|
117
204
|
model.delete(rec).then(loadMenus);
|
|
118
205
|
};
|
|
119
206
|
|
|
120
|
-
const filtered = records.filter((r) => r.name?.toUpperCase().includes(query.toUpperCase()));
|
|
121
|
-
|
|
122
|
-
|
|
207
|
+
const filtered = records.filter((r) => r.name?.toUpperCase().includes(query.toUpperCase()));
|
|
208
|
+
|
|
209
|
+
const visibleItems = dragMode ? records : filtered;
|
|
210
|
+
|
|
123
211
|
const onSearch = (event) => {
|
|
124
|
-
setQuery(event.target.value);
|
|
212
|
+
setQuery(event.target.value);
|
|
125
213
|
};
|
|
126
214
|
|
|
127
215
|
// ------------------------
|
|
128
216
|
// Recursive function to build payload
|
|
129
217
|
// ------------------------
|
|
130
|
-
const
|
|
131
|
-
|
|
132
|
-
const item = { id: menu.id, order: index + 1 };
|
|
218
|
+
const buildUpdatedOrderPayload = (menus, originalMenus) => {
|
|
219
|
+
const result = [];
|
|
133
220
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
221
|
+
menus.forEach((menu, index) => {
|
|
222
|
+
const original = findMenuById(originalMenus, menu.id);
|
|
223
|
+
|
|
224
|
+
const newOrder = index + 1;
|
|
225
|
+
const oldOrder = original ? original.order : null;
|
|
226
|
+
|
|
227
|
+
const orderChanged = oldOrder !== newOrder;
|
|
228
|
+
const parentChanged = original?.header_id !== menu.header_id;
|
|
229
|
+
|
|
230
|
+
// check sub menus
|
|
231
|
+
let updatedSubMenus = [];
|
|
232
|
+
if (menu.sub_menus?.length) {
|
|
233
|
+
updatedSubMenus = buildUpdatedOrderPayload(menu.sub_menus, originalMenus);
|
|
137
234
|
}
|
|
138
235
|
|
|
139
|
-
|
|
236
|
+
// include only if changed OR has changed children
|
|
237
|
+
if (orderChanged || parentChanged || updatedSubMenus.length) {
|
|
238
|
+
result.push({
|
|
239
|
+
id: menu.id,
|
|
240
|
+
order: newOrder,
|
|
241
|
+
header_id: menu.header_id,
|
|
242
|
+
name: menu.name,
|
|
243
|
+
...(updatedSubMenus.length && { sub_menus: updatedSubMenus }),
|
|
244
|
+
});
|
|
245
|
+
}
|
|
140
246
|
});
|
|
141
247
|
|
|
248
|
+
return result;
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
// ------------------------
|
|
252
|
+
// Find menu by id (recursive)
|
|
253
|
+
// ------------------------
|
|
254
|
+
const findMenuById = (menus, id) => {
|
|
255
|
+
for (const menu of menus || []) {
|
|
256
|
+
if (menu.id === id) return menu;
|
|
257
|
+
|
|
258
|
+
if (menu.sub_menus?.length) {
|
|
259
|
+
const found = findMenuById(menu.sub_menus, id);
|
|
260
|
+
if (found) return found;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
return null;
|
|
264
|
+
};
|
|
265
|
+
|
|
142
266
|
// ------------------------
|
|
143
267
|
// Save order function
|
|
144
268
|
// ------------------------
|
|
145
269
|
const saveOrder = () => {
|
|
146
|
-
const
|
|
270
|
+
const updatedMenus = buildUpdatedOrderPayload(records, originalRecords);
|
|
271
|
+
|
|
272
|
+
if (!updatedMenus.length) {
|
|
273
|
+
message.info('No order changes to save');
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
const payload = { menus: updatedMenus };
|
|
147
278
|
|
|
148
279
|
model
|
|
149
280
|
.saveOrder({ formBody: payload })
|
|
150
|
-
.then((
|
|
281
|
+
.then(() => {
|
|
151
282
|
setOrderChanged(false);
|
|
152
|
-
|
|
153
|
-
loadMenus(); // force refresh
|
|
283
|
+
loadMenus(); // refresh from backend
|
|
154
284
|
})
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
// Combine the updated order
|
|
158
|
-
// const newOrder = [...updated_ids, ...same_order_ids];
|
|
159
|
-
// const reorderedRecords = newOrder.map((id) => records.find((r) => r.id === id)).filter(Boolean); // remove undefined if not found
|
|
160
|
-
|
|
161
|
-
// setRecords(reorderedRecords);
|
|
162
|
-
// loadMenus(); // <-- reload from backend
|
|
163
|
-
// setOrderChanged(false);
|
|
164
|
-
// })
|
|
165
|
-
.catch((err) => {
|
|
166
|
-
console.error(err);
|
|
167
|
-
|
|
168
|
-
});
|
|
285
|
+
.catch(console.error);
|
|
169
286
|
};
|
|
170
287
|
|
|
288
|
+
const movePanel = useCallback(
|
|
289
|
+
(from, to) => {
|
|
290
|
+
if (!dragMode) return;
|
|
291
|
+
if (from === to) return;
|
|
292
|
+
|
|
293
|
+
setRecords((prevRecords) => {
|
|
294
|
+
const updated = [...prevRecords];
|
|
295
|
+
const [moved] = updated.splice(from, 1);
|
|
296
|
+
updated.splice(to, 0, moved);
|
|
297
|
+
return updated;
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
setOrderChanged(true);
|
|
301
|
+
},
|
|
302
|
+
[dragMode]
|
|
303
|
+
);
|
|
304
|
+
|
|
171
305
|
return (
|
|
172
306
|
<Card className="generic-list">
|
|
173
|
-
<div
|
|
174
|
-
<div
|
|
307
|
+
<div style={{ marginBottom: 16 }}>
|
|
308
|
+
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 16 }}>
|
|
175
309
|
<Search placeholder="Enter Search Value" allowClear style={{ width: 300, marginBottom: '0px' }} onChange={onSearch} />
|
|
176
|
-
{/* <Title level={4}>{model.name}</Title> */}
|
|
177
310
|
|
|
178
|
-
|
|
311
|
+
<Space size="small">
|
|
312
|
+
<Button onClick={getRecords} size={'small'} type="default">
|
|
313
|
+
<ReloadOutlined />
|
|
314
|
+
</Button>
|
|
315
|
+
|
|
316
|
+
<Switch checked={dragMode} onChange={toggleDragMode} checkedChildren="Order On" unCheckedChildren="Order Off" />
|
|
317
|
+
|
|
318
|
+
{dragMode && orderChanged && (
|
|
319
|
+
<Button onClick={saveOrder} type="primary" size="small">
|
|
320
|
+
Save Order
|
|
321
|
+
</Button>
|
|
322
|
+
)}
|
|
323
|
+
|
|
324
|
+
<Button
|
|
325
|
+
type="primary"
|
|
326
|
+
size="small"
|
|
327
|
+
onClick={() => {
|
|
328
|
+
setSelectedRecord({ step: 1, header_id: null });
|
|
329
|
+
setDrawerTitle('Create New Menu');
|
|
330
|
+
setDrawerVisible(true);
|
|
331
|
+
}}
|
|
332
|
+
>
|
|
333
|
+
Create New Menu
|
|
334
|
+
</Button>
|
|
335
|
+
</Space>
|
|
179
336
|
</div>
|
|
180
337
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
checked={dragMode}
|
|
198
|
-
onChange={toggleDragMode}
|
|
199
|
-
checkedChildren="Order On"
|
|
200
|
-
unCheckedChildren="Order Off"
|
|
201
|
-
className="custom-switch"
|
|
202
|
-
/>
|
|
203
|
-
{/* Only show Save Order button when a change happens */}
|
|
204
|
-
{dragMode && orderChanged && (
|
|
205
|
-
<Button type="primary" size="small" onClick={saveOrder}>
|
|
206
|
-
Save Order
|
|
207
|
-
</Button>
|
|
208
|
-
)}
|
|
209
|
-
|
|
210
|
-
{disableAddModal || !model.ModalAddComponent ? null : (
|
|
211
|
-
<Button
|
|
212
|
-
type="primary"
|
|
213
|
-
size="small"
|
|
214
|
-
onClick={() => {
|
|
215
|
-
setSelectedRecord({}); // empty record for creation
|
|
216
|
-
setDrawerTitle('Create New Menu');
|
|
217
|
-
setDrawerVisible(true); // this controls Drawer visibility
|
|
218
|
-
}}
|
|
219
|
-
>
|
|
220
|
-
Create New Menu
|
|
221
|
-
</Button>
|
|
222
|
-
)}
|
|
223
|
-
</Space>
|
|
224
|
-
</div>
|
|
338
|
+
{dragMode && (
|
|
339
|
+
<div
|
|
340
|
+
style={{
|
|
341
|
+
padding: '8px 12px',
|
|
342
|
+
backgroundColor: '#e6f7ff',
|
|
343
|
+
borderRadius: 4,
|
|
344
|
+
border: '1px solid #91d5ff',
|
|
345
|
+
fontSize: 13,
|
|
346
|
+
}}
|
|
347
|
+
>
|
|
348
|
+
<strong>💡 Tips:</strong>
|
|
349
|
+
<ul style={{ margin: '4px 0', paddingLeft: 20 }}>
|
|
350
|
+
<li>Drag between items to reorder at the same level</li>
|
|
351
|
+
<li>Drop on the green "↳ Drop here" zone to make it a child of that item</li>
|
|
352
|
+
<li>Works at all levels - unlimited nesting supported!</li>
|
|
353
|
+
</ul>
|
|
225
354
|
</div>
|
|
226
|
-
|
|
355
|
+
)}
|
|
227
356
|
</div>
|
|
228
|
-
|
|
229
357
|
{loading ? (
|
|
230
358
|
<Skeleton active />
|
|
231
359
|
) : (
|
|
232
360
|
<>
|
|
233
361
|
<>
|
|
234
|
-
{!view ? (
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
>
|
|
362
|
+
{/* {!view ? ( */}
|
|
363
|
+
<Card>
|
|
364
|
+
<DndProvider backend={HTML5Backend}>
|
|
365
|
+
<Collapse accordion>
|
|
366
|
+
{visibleItems.map((item, index) => (
|
|
367
|
+
<Panel
|
|
368
|
+
key={item.id}
|
|
369
|
+
header={
|
|
370
|
+
<DraggableWrapper
|
|
371
|
+
id={item.id}
|
|
372
|
+
index={index}
|
|
373
|
+
movePanel={movePanel}
|
|
374
|
+
item={item}
|
|
375
|
+
dragEnabled={dragMode}
|
|
376
|
+
level={1}
|
|
377
|
+
parentId={null}
|
|
378
|
+
onCrossLevelMove={handleCrossLevelMove}
|
|
379
|
+
canAcceptChildren={true}
|
|
380
|
+
/>
|
|
381
|
+
}
|
|
382
|
+
// only show arrow if sub_menus exist
|
|
383
|
+
showArrow={item.sub_menus && item.sub_menus.length > 0}
|
|
384
|
+
// disable panel
|
|
385
|
+
collapsible={item.sub_menus && item.sub_menus.length > 0 ? 'header' : 'disabled'}
|
|
386
|
+
extra={panelActions(item, model, setSelectedRecord, setDrawerTitle, setDrawerVisible, deleteRecord)}
|
|
387
|
+
>
|
|
388
|
+
{item.sub_menus && item.sub_menus.length > 0 && (
|
|
262
389
|
<NestedMenu
|
|
263
390
|
parentId={item.id}
|
|
264
|
-
step={step + 1}
|
|
391
|
+
step={item.step + 1}
|
|
392
|
+
items={item.sub_menus || []}
|
|
265
393
|
model={model}
|
|
266
394
|
dragMode={dragMode}
|
|
267
395
|
setSelectedRecord={setSelectedRecord}
|
|
268
396
|
setDrawerTitle={setDrawerTitle}
|
|
269
397
|
setDrawerVisible={setDrawerVisible}
|
|
270
398
|
deleteRecord={deleteRecord}
|
|
399
|
+
level={2}
|
|
400
|
+
onCrossLevelMove={handleCrossLevelMove}
|
|
271
401
|
onChange={(subMenus) => {
|
|
272
402
|
const updated = records.map((r) => (r.id === item.id ? { ...r, sub_menus: subMenus } : r));
|
|
273
403
|
setRecords(updated);
|
|
274
404
|
setOrderChanged(true);
|
|
275
405
|
}}
|
|
276
406
|
/>
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
</
|
|
281
|
-
</
|
|
282
|
-
|
|
407
|
+
)}
|
|
408
|
+
</Panel>
|
|
409
|
+
))}
|
|
410
|
+
</Collapse>
|
|
411
|
+
</DndProvider>
|
|
412
|
+
</Card>
|
|
413
|
+
{/* ) : (
|
|
283
414
|
<CardList model={model} data={filtered ? filtered : records} />
|
|
284
|
-
)}
|
|
415
|
+
)} */}
|
|
285
416
|
</>
|
|
286
417
|
</>
|
|
287
418
|
)}
|
|
288
|
-
|
|
289
|
-
<Drawer title={drawerTitle} open={drawerVisible} width="
|
|
419
|
+
|
|
420
|
+
<Drawer title={drawerTitle} open={drawerVisible} width="80%" destroyOnClose onClose={() => setDrawerVisible(false)}>
|
|
290
421
|
{model.ModalAddComponent && (
|
|
291
422
|
<model.ModalAddComponent
|
|
292
423
|
formContent={selectedRecord}
|
|
@@ -303,15 +434,8 @@ const MenuLists = ({ model, match, relativeAdd = false, additional_queries = [],
|
|
|
303
434
|
</Card>
|
|
304
435
|
);
|
|
305
436
|
};
|
|
306
|
-
function CardList({ model, data, url, ...props }) {
|
|
307
|
-
return data.map((record, index) => {
|
|
308
|
-
return <model.Card record={record} model={model} index={index} key={index} {...record} {...props} />;
|
|
309
|
-
});
|
|
310
|
-
}
|
|
311
|
-
export default MenuLists;
|
|
312
|
-
|
|
313
437
|
/* -----------------------------------------------------------------------
|
|
314
|
-
PANEL ACTIONS
|
|
438
|
+
PANEL ACTIONS
|
|
315
439
|
------------------------------------------------------------------------ */
|
|
316
440
|
function panelActions(item, model, setSelectedRecord, setDrawerTitle, setDrawerVisible, deleteRecord) {
|
|
317
441
|
return (
|
|
@@ -322,16 +446,18 @@ function panelActions(item, model, setSelectedRecord, setDrawerTitle, setDrawerV
|
|
|
322
446
|
type="dashed"
|
|
323
447
|
onClick={() => {
|
|
324
448
|
setSelectedRecord({
|
|
325
|
-
|
|
449
|
+
// parent menu id
|
|
450
|
+
header_id: item.id,
|
|
326
451
|
step: item.step + 1,
|
|
327
|
-
|
|
452
|
+
// new record
|
|
453
|
+
id: null,
|
|
328
454
|
});
|
|
329
455
|
setDrawerTitle(`Add Submenu to "${item.name}"`);
|
|
330
456
|
setDrawerVisible(true);
|
|
331
457
|
}}
|
|
332
458
|
>
|
|
333
459
|
<PlusCircleFilled />
|
|
334
|
-
|
|
460
|
+
Add Sub Menu
|
|
335
461
|
</Button>
|
|
336
462
|
|
|
337
463
|
{model.ModalAddComponent && (
|
|
@@ -369,93 +495,83 @@ function panelActions(item, model, setSelectedRecord, setDrawerTitle, setDrawerV
|
|
|
369
495
|
);
|
|
370
496
|
}
|
|
371
497
|
|
|
372
|
-
// ------------------------
|
|
373
|
-
// NestedMenu: Pass sub_menus recursively
|
|
374
|
-
// ------------------------
|
|
375
|
-
// ------------------------
|
|
376
|
-
|
|
377
498
|
function NestedMenu({
|
|
378
499
|
parentId,
|
|
379
500
|
step,
|
|
501
|
+
items,
|
|
380
502
|
model,
|
|
381
503
|
dragMode,
|
|
382
|
-
|
|
383
504
|
setSelectedRecord,
|
|
384
505
|
setDrawerTitle,
|
|
385
506
|
setDrawerVisible,
|
|
386
507
|
deleteRecord,
|
|
387
|
-
|
|
508
|
+
level,
|
|
509
|
+
onCrossLevelMove,
|
|
510
|
+
onChange,
|
|
388
511
|
}) {
|
|
389
|
-
|
|
390
|
-
|
|
512
|
+
// do not render Collapse
|
|
513
|
+
if (!items || items.length === 0) return null;
|
|
514
|
+
|
|
515
|
+
const [localItems, setLocalItems] = useState(items);
|
|
391
516
|
|
|
392
|
-
// Load child menus
|
|
393
|
-
// NestedMenu
|
|
394
517
|
useEffect(() => {
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
.get({
|
|
398
|
-
queries: [
|
|
399
|
-
{ field: 'header_id', value: parentId },
|
|
400
|
-
{ field: 'step', value: step },
|
|
401
|
-
],
|
|
402
|
-
})
|
|
403
|
-
.then((res) => {
|
|
404
|
-
const sortedMenus = (res.result || [])
|
|
405
|
-
.sort((a, b) => a.order - b.order) // sort top-level in this submenu
|
|
406
|
-
.map((menu) => ({
|
|
407
|
-
...menu,
|
|
408
|
-
sub_menus: menu.sub_menus ? menu.sub_menus.sort((a, b) => a.order - b.order) : [],
|
|
409
|
-
}));
|
|
410
|
-
setItems(sortedMenus);
|
|
411
|
-
setLoading(false);
|
|
412
|
-
})
|
|
413
|
-
.catch(() => setLoading(false));
|
|
414
|
-
}, [parentId, step]);
|
|
415
|
-
|
|
416
|
-
// Move submenu
|
|
417
|
-
const moveSubMenu = (from, to) => {
|
|
418
|
-
const updated = [...items];
|
|
419
|
-
const [moved] = updated.splice(from, 1);
|
|
420
|
-
updated.splice(to, 0, moved);
|
|
421
|
-
setItems(updated);
|
|
422
|
-
onChange?.(updated); // propagate to parent
|
|
423
|
-
};
|
|
518
|
+
setLocalItems(items);
|
|
519
|
+
}, [items]);
|
|
424
520
|
|
|
425
|
-
|
|
426
|
-
|
|
521
|
+
const moveSubMenu = useCallback(
|
|
522
|
+
(from, to) => {
|
|
523
|
+
if (!dragMode || from === to) return;
|
|
524
|
+
|
|
525
|
+
const updated = [...localItems];
|
|
526
|
+
const [moved] = updated.splice(from, 1);
|
|
527
|
+
updated.splice(to, 0, moved);
|
|
528
|
+
|
|
529
|
+
setLocalItems(updated);
|
|
530
|
+
onChange?.(updated);
|
|
531
|
+
},
|
|
532
|
+
[dragMode, localItems, onChange]
|
|
533
|
+
);
|
|
534
|
+
if (!localItems || localItems.length === 0) {
|
|
535
|
+
// <-- don’t render anything if no submenus
|
|
536
|
+
return null;
|
|
537
|
+
}
|
|
427
538
|
|
|
428
539
|
return (
|
|
429
|
-
<Collapse accordion
|
|
430
|
-
{
|
|
540
|
+
<Collapse accordion>
|
|
541
|
+
{localItems.map((child, index) => (
|
|
431
542
|
<Panel
|
|
432
543
|
key={child.id}
|
|
433
544
|
header={
|
|
434
545
|
<DraggableWrapper
|
|
435
546
|
id={child.id}
|
|
436
547
|
index={index}
|
|
437
|
-
movePanel={
|
|
438
|
-
|
|
439
|
-
moveSubMenu(from, to);
|
|
440
|
-
}}
|
|
441
|
-
title={child.name}
|
|
548
|
+
movePanel={moveSubMenu}
|
|
549
|
+
item={child}
|
|
442
550
|
dragEnabled={dragMode}
|
|
551
|
+
level={level}
|
|
552
|
+
parentId={parentId}
|
|
553
|
+
onCrossLevelMove={onCrossLevelMove}
|
|
554
|
+
canAcceptChildren={true}
|
|
443
555
|
/>
|
|
444
556
|
}
|
|
557
|
+
// only show arrow if sub_menus exist
|
|
558
|
+
showArrow={child.sub_menus && child.sub_menus.length > 0}
|
|
445
559
|
extra={panelActions(child, model, setSelectedRecord, setDrawerTitle, setDrawerVisible, deleteRecord)}
|
|
446
560
|
>
|
|
447
|
-
{/* Recursively render submenus */}
|
|
448
561
|
<NestedMenu
|
|
449
562
|
parentId={child.id}
|
|
450
563
|
step={step + 1}
|
|
451
|
-
|
|
564
|
+
items={child.sub_menus || []}
|
|
565
|
+
dragMode={dragMode}
|
|
452
566
|
setSelectedRecord={setSelectedRecord}
|
|
453
567
|
setDrawerTitle={setDrawerTitle}
|
|
454
568
|
setDrawerVisible={setDrawerVisible}
|
|
455
569
|
deleteRecord={deleteRecord}
|
|
570
|
+
level={level + 1}
|
|
571
|
+
onCrossLevelMove={onCrossLevelMove}
|
|
456
572
|
onChange={(submenus) => {
|
|
457
|
-
const updated =
|
|
458
|
-
|
|
573
|
+
const updated = localItems.map((item) => (item.id === child.id ? { ...item, sub_menus: submenus } : item));
|
|
574
|
+
setLocalItems(updated);
|
|
459
575
|
onChange?.(updated);
|
|
460
576
|
}}
|
|
461
577
|
/>
|
|
@@ -464,3 +580,5 @@ function NestedMenu({
|
|
|
464
580
|
</Collapse>
|
|
465
581
|
);
|
|
466
582
|
}
|
|
583
|
+
|
|
584
|
+
export default MenuLists;
|