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.
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 +9 -76
  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
@@ -6,62 +6,24 @@
6
6
  * @param {Array<{ label: string, key: string }>} [headers] - Optional column headers and field mappings.
7
7
  * @param {string} [fileName='Report.xlsx'] - Optional name for the downloaded Excel file.
8
8
  */
9
-
10
- import React, { useState } from 'react';
9
+
10
+ import React from 'react';
11
11
  // Import XLSX library to handle Excel file creation
12
12
  import * as XLSX from 'xlsx';
13
- import { PDFDocument, StandardFonts, rgb } from 'pdf-lib';
14
13
  // import * as XLSX from 'xlsx-js-style';
15
14
  // import * as XLSX from 'xlsx-js-style';
16
15
  // Import saveAs from file-saver to trigger file download in browser
17
16
  import { saveAs } from 'file-saver';
18
- import { Button, Dropdown, Form, Input, Menu, Modal, Radio, message } from 'antd';
19
- import { DownOutlined, FileExcelFilled, FilePdfFilled, MailFilled, DownloadOutlined } from '@ant-design/icons';
17
+ import { Button } from 'antd';
20
18
  import { Trans } from 'react-i18next';
21
- import { CoreScripts } from '../../../../models';
22
- import './generic-list.scss';
23
-
24
- const normalizeScriptId = (value) => {
25
- if (value === undefined || value === null || value === '') return value;
26
-
27
- const numberValue = Number(value);
28
- return Number.isNaN(numberValue) ? value : numberValue;
29
- };
30
-
31
- const getRecipients = (value) => {
32
- if (Array.isArray(value)) return value.filter(Boolean);
33
-
34
- return String(value || '')
35
- .split(/[\n,;]+/)
36
- .map((email) => email.trim())
37
- .filter(Boolean);
38
- };
39
-
40
-
41
- export const ExportReactCSV = ({
42
- csvData,
43
- headers,
44
- fileName,
45
- pdfFileName,
46
- title,
47
- format = 'excel',
48
- buttonText,
49
- dropdown = false,
50
- onSendReportToMail,
51
- scriptId,
52
- inputParameters,
53
- }) => {
54
- const [mailModalVisible, setMailModalVisible] = useState(false);
55
- const [sendingMail, setSendingMail] = useState(false);
56
- const [selectedFormat, setSelectedFormat] = useState('excel');
57
- const [mailForm] = Form.useForm();
58
19
 
20
+ export const ExportReactCSV = ({ csvData, headers, fileName,title }) => {
59
21
  /**
60
22
  * exportToExcel function
61
23
  * Triggered on button click, generates and downloads an Excel file
62
24
  */
63
25
 
64
- const exportToExcel = (downloadFileName) => {
26
+ const exportToExcel = () => {
65
27
  // if (!csvData?.length) return;
66
28
 
67
29
  let worksheet;
@@ -113,302 +75,14 @@ export const ExportReactCSV = ({
113
75
  const blob = new Blob([excelBuffer], {
114
76
  type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
115
77
  });
116
- const flName = downloadFileName || fileName || 'Report.xlsx';
78
+ const flName = fileName || 'Report.xlsx';
117
79
  // Trigger download using file-saver
118
80
  saveAs(blob, flName);
119
81
  };
120
-
121
- const exportToPdf = async (downloadFileName) => {
122
- const pdfDoc = await PDFDocument.create();
123
- const font = await pdfDoc.embedFont(StandardFonts.Helvetica);
124
- const boldFont = await pdfDoc.embedFont(StandardFonts.HelveticaBold);
125
- const pageSize = [842, 595];
126
- const margin = 30;
127
- const rowHeight = 20;
128
- const titleHeight = 38;
129
- const fontSize = 8;
130
- const headerFontSize = 8;
131
- const headersToUse = headers?.length
132
- ? headers
133
- : Object.keys(csvData?.[0] || {}).map((key) => ({ label: key, key }));
134
- const availableWidth = pageSize[0] - margin * 2;
135
- const columnWidth = headersToUse.length ? availableWidth / headersToUse.length : availableWidth;
136
- let page;
137
- let y;
138
-
139
- const sanitizeValue = (value) => {
140
- if (value === undefined || value === null) return '';
141
- if (typeof value === 'object') return JSON.stringify(value);
142
- return String(value);
143
- };
144
-
145
- const fitText = (text, width, textFont, size) => {
146
- const sanitizedText = sanitizeValue(text);
147
- if (textFont.widthOfTextAtSize(sanitizedText, size) <= width) return sanitizedText;
148
-
149
- let fittedText = sanitizedText;
150
- while (fittedText.length && textFont.widthOfTextAtSize(`${fittedText}...`, size) > width) {
151
- fittedText = fittedText.slice(0, -1);
152
- }
153
- return fittedText ? `${fittedText}...` : '';
154
- };
155
-
156
- const addPage = () => {
157
- page = pdfDoc.addPage(pageSize);
158
- y = pageSize[1] - margin;
159
-
160
- page.drawText(title || 'Report', {
161
- x: margin,
162
- y,
163
- size: 14,
164
- font: boldFont,
165
- color: rgb(0.1, 0.1, 0.1),
166
- });
167
-
168
- y -= titleHeight;
169
-
170
- headersToUse.forEach((header, index) => {
171
- const x = margin + index * columnWidth;
172
- page.drawRectangle({
173
- x,
174
- y: y - 5,
175
- width: columnWidth,
176
- height: rowHeight,
177
- color: rgb(0.92, 0.94, 0.96),
178
- borderColor: rgb(0.72, 0.75, 0.78),
179
- borderWidth: 0.5,
180
- });
181
- page.drawText(fitText(header.label, columnWidth - 8, boldFont, headerFontSize), {
182
- x: x + 4,
183
- y: y + 1,
184
- size: headerFontSize,
185
- font: boldFont,
186
- color: rgb(0.1, 0.1, 0.1),
187
- });
188
- });
189
-
190
- y -= rowHeight;
191
- };
192
-
193
- addPage();
194
-
195
- (csvData || []).forEach((row) => {
196
- if (y < margin + rowHeight) addPage();
197
-
198
- headersToUse.forEach((header, index) => {
199
- const x = margin + index * columnWidth;
200
- page.drawRectangle({
201
- x,
202
- y: y - 5,
203
- width: columnWidth,
204
- height: rowHeight,
205
- color: row.isSummaryRow ? rgb(0.97, 0.97, 0.97) : rgb(1, 1, 1),
206
- borderColor: rgb(0.82, 0.84, 0.86),
207
- borderWidth: 0.5,
208
- });
209
- page.drawText(fitText(row[header.key], columnWidth - 8, row.isSummaryRow ? boldFont : font, fontSize), {
210
- x: x + 4,
211
- y: y + 1,
212
- size: fontSize,
213
- font: row.isSummaryRow ? boldFont : font,
214
- color: rgb(0.12, 0.12, 0.12),
215
- });
216
- });
217
-
218
- y -= rowHeight;
219
- });
220
-
221
- const pdfBytes = await pdfDoc.save();
222
- const blob = new Blob([pdfBytes], { type: 'application/pdf' });
223
- const flName = downloadFileName || pdfFileName || fileName || 'Report.pdf';
224
- saveAs(blob, flName);
225
- };
226
-
227
- const handleDownloadMenuClick = ({ key }) => {
228
- if (key === 'excel') {
229
- exportToExcel(fileName);
230
- return;
231
- }
232
-
233
- if (key === 'pdf') {
234
- exportToPdf(pdfFileName);
235
- return;
236
- }
237
-
238
- if (key === 'mail') {
239
- setMailModalVisible(true);
240
- }
241
- };
242
-
243
- const handleMailModalClose = () => {
244
- setMailModalVisible(false);
245
- mailForm.resetFields();
246
- setSelectedFormat('excel');
247
- };
248
-
249
- const handleFormatToggle = (value) => {
250
- const nextValue = selectedFormat === value ? null : value;
251
- setSelectedFormat(nextValue);
252
- mailForm.setFieldsValue({ format: nextValue });
253
- };
254
-
255
- // const handleSendReportToMail = async (values) => {
256
- // setSendingMail(true);
257
-
258
- // try {
259
- // const mailFormat = values.format || selectedFormat || 'excel';
260
- // const isBoth = mailFormat === 'both';
261
- // const payload = {
262
- // script_id: normalizeScriptId(scriptId),
263
- // input_parameters: inputParameters || {},
264
- // is_pdf: mailFormat === 'pdf' || isBoth,
265
- // is_excel: mailFormat === 'excel' || isBoth,
266
- // is_both: isBoth,
267
- // recipients: getRecipients(values.email),
268
- // };
269
- // const data = onSendReportToMail
270
- // ? await onSendReportToMail(payload)
271
- // : await CoreScripts.sentToReportMail(payload);
272
-
273
- // if (data?.success === false) throw data;
274
-
275
- // message.success(data?.message || 'Report mail sent successfully');
276
- // handleMailModalClose();
277
- // } catch (error) {
278
- // message.error(error?.message || 'Unable to send report mail');
279
- // } finally {
280
- // setSendingMail(false);
281
- // }
282
- // };
283
-
284
- const downloadMenu = (
285
- <Menu className="export-download-menu" onClick={handleDownloadMenuClick}>
286
- <Menu.Item
287
- key="excel"
288
- icon={<FileExcelFilled className="export-download-menu__icon export-download-menu__icon--excel" />}
289
- >
290
- Download Excel Sheet
291
- </Menu.Item>
292
- <Menu.Item
293
- key="pdf"
294
- icon={<FilePdfFilled className="export-download-menu__icon export-download-menu__icon--pdf" />}
295
- >
296
- Download PDF
297
- </Menu.Item>
298
- {/* <Menu.Item
299
- key="mail"
300
- icon={<MailFilled className="export-download-menu__icon export-download-menu__icon--mail" />}
301
- >
302
- Send report to mail
303
- </Menu.Item> */}
304
- </Menu>
305
- );
306
-
307
- const isPdf = format === 'pdf';
308
- const handleExportButtonClick = () => {
309
- if (isPdf) {
310
- exportToPdf();
311
- return;
312
- }
313
-
314
- exportToExcel();
315
- };
316
-
317
- if (dropdown) {
318
- return (
319
- <>
320
- <Dropdown overlay={downloadMenu} trigger={['click']} placement="bottomRight">
321
- <Button type="primary" size="small" className="export-download-button">
322
- <Trans i18nKey={buttonText || 'Download'} /> <DownloadOutlined className="export-download-button__icon" />
323
- </Button>
324
- </Dropdown>
325
- {/*
326
- <Modal
327
- title="Send report to mail"
328
- open={mailModalVisible}
329
- onCancel={handleMailModalClose}
330
- footer={null}
331
- destroyOnClose
332
- width={460}
333
- >
334
- <Form
335
- form={mailForm}
336
- layout="vertical"
337
- initialValues={{ format: 'excel' }}
338
- onFinish={handleSendReportToMail}
339
- >
340
- <Form.Item
341
- name="format"
342
- style={{ marginBottom: 16 }}
343
- rules={[
344
- {
345
- validator: () =>
346
- selectedFormat
347
- ? Promise.resolve()
348
- : Promise.reject(new Error('Please select a format')),
349
- },
350
- ]}
351
- >
352
- <div
353
- className="export-mail-format-options"
354
- style={{ display: 'flex', alignItems: 'center', columnGap: 24 }}
355
- >
356
- <Radio
357
- checked={selectedFormat === 'excel'}
358
- style={{ marginRight: 0 }}
359
- onClick={() => handleFormatToggle('excel')}
360
- >
361
- Excel
362
- </Radio>
363
- <Radio
364
- checked={selectedFormat === 'pdf'}
365
- style={{ marginRight: 0 }}
366
- onClick={() => handleFormatToggle('pdf')}
367
- >
368
- PDF
369
- </Radio>
370
- <Radio
371
- checked={selectedFormat === 'both'}
372
- style={{ marginRight: 0 }}
373
- onClick={() => handleFormatToggle('both')}
374
- >
375
- Both
376
- </Radio>
377
- </div>
378
- </Form.Item>
379
-
380
- <Form.Item
381
- name="email"
382
- label="EMAIL ID"
383
- rules={[
384
- { required: true, message: 'Please enter email id' },
385
- { type: 'email', message: 'Please enter a valid email id' },
386
- ]}
387
- >
388
- <Input placeholder="Enter Mail Id" />
389
- </Form.Item>
390
-
391
- <Form.Item
392
- name="remarks"
393
- label="REMARKS"
394
- rules={[{ required: true, message: 'Please enter remarks' }]}
395
- >
396
- <Input.TextArea placeholder="Enter Remarks" rows={4} />
397
- </Form.Item>
398
-
399
- <Button type="primary" htmlType="submit" loading={sendingMail}>
400
- Send
401
- </Button>
402
- </Form>
403
- </Modal> */}
404
- </>
405
- );
406
- }
407
-
408
82
  // Render an Ant Design Button that triggers the Excel export
409
83
  return (
410
- <Button type="secondary" size="small" onClick={handleExportButtonClick} className="export-download-button">
411
- <Trans i18nKey={buttonText || (isPdf ? 'PDF Download' : 'Download')} /> <DownloadOutlined className="export-download-button__icon" />
84
+ <Button type="secondary" size="small" onClick={exportToExcel}>
85
+ <Trans i18nKey="Download" />
412
86
  </Button>
413
87
  );
414
88
  };
@@ -32,37 +32,3 @@
32
32
  }
33
33
  }
34
34
  }
35
-
36
- .export-download-button {
37
- &.ant-btn-primary {
38
- background: #2f80ed;
39
- border-color: #2f80ed;
40
- color: #fff;
41
-
42
- &:hover,
43
- &:focus {
44
- background: #1c7ed6;
45
- border-color: #1c7ed6;
46
- color: #fff;
47
- }
48
- }
49
- }
50
-
51
- .export-download-menu {
52
- &__icon {
53
- font-size: 16px;
54
- vertical-align: -0.15em;
55
-
56
- &--excel {
57
- color: #18a058;
58
- }
59
-
60
- &--pdf {
61
- color: #e03131;
62
- }
63
-
64
- &--mail {
65
- color: #1c7ed6;
66
- }
67
- }
68
- }
@@ -69,6 +69,7 @@ class CoreScript extends Base {
69
69
  // Settings db pointer
70
70
  if (!dbPtr) dbPtr = localStorage.db_ptr;
71
71
  return ApiUtils.post({
72
+ // baseUrl: 'http://localhost:8002/dev/',
72
73
  url: `core-scripts/dashboardquery/${id}`,
73
74
  formBody,
74
75
  headers: {
@@ -79,14 +80,6 @@ class CoreScript extends Base {
79
80
  });
80
81
  };
81
82
 
82
- // sentToReportMail = (formBody) => {
83
- // return ApiUtils.post({
84
- // baseUrl: 'http://localhost:8002/dev/',
85
- // url: `core-scripts/send-to-report-mail`,
86
- // formBody,
87
- // });
88
- // };
89
-
90
83
  /**
91
84
  *Updating user details
92
85
  * @returns
@@ -98,20 +91,6 @@ class CoreScript extends Base {
98
91
  });
99
92
  };
100
93
 
101
- getCorescript = (formBody,dbPtr) => {
102
-
103
- if (!dbPtr) dbPtr = localStorage.db_ptr;
104
- return ApiUtils.post({
105
- // baseUrl: 'http://localhost:8002/dev/',
106
- url: `core-scripts/get-core-script`,
107
- headers: {
108
- 'Content-Type': 'application/json',
109
- Authorization: 'Bearer ' + localStorage.access_token,
110
- db_ptr: dbPtr,
111
- },
112
- formBody,
113
- });
114
- };
115
94
  getQuery = (formBody) => {
116
95
  return ApiUtils.post({
117
96
  url: `core-scripts/execute-script-api`,
@@ -116,13 +116,6 @@ const MenuAdd = ({ model, callback, edit, history, formContent, match, additiona
116
116
  values.step = formContent.step || step;
117
117
  }
118
118
 
119
- // Cleared selects come back as undefined; send null so the backend clears them
120
- ['model_id', 'page_id', 'header_id'].forEach((field) => {
121
- if (values[field] === undefined) {
122
- values[field] = null;
123
- }
124
- });
125
-
126
119
  if (values.attributes && typeof values === 'object') {
127
120
  values = {
128
121
  ...values,
@@ -190,7 +183,7 @@ const MenuAdd = ({ model, callback, edit, history, formContent, match, additiona
190
183
  {/* Model */}
191
184
  <Form.Item label="Models" name={'model_id'}>
192
185
  {/* <ReferenceSelect value={model.id} label={model.name} model={Models} /> */}
193
- <Select showSearch style={{ width: '100%' }} placeholder="Select a Page" optionFilterProp="label" allowClear>
186
+ <Select showSearch style={{ width: '100%' }} placeholder="Select a Page" optionFilterProp="label">
194
187
  {models.map((model, key) => (
195
188
  <Option key={key} label={model.name} value={model.id}>
196
189
  {model.name}
@@ -205,7 +198,7 @@ const MenuAdd = ({ model, callback, edit, history, formContent, match, additiona
205
198
  <Form.Item label="Pages" name={'page_id'}>
206
199
  {/* <ReferenceSelect value={pages.id} label={pages.name} model={Pages} /> */}
207
200
 
208
- <Select showSearch optionFilterProp="children" style={{ width: '100%' }} allowClear>
201
+ <Select showSearch optionFilterProp="children" style={{ width: '100%' }}>
209
202
  {pages.map((model, key) => (
210
203
  <Option key={key} value={model.id}>
211
204
  {model.name}
@@ -236,33 +229,13 @@ const MenuAdd = ({ model, callback, edit, history, formContent, match, additiona
236
229
  {/* Pages Ends */}
237
230
 
238
231
  {/* Path */}
239
- <Form.Item
240
- name="path"
241
- label="Path"
242
- rules={[
243
- { required: true, message: 'Path is required' },
244
- {
245
- pattern: /^\//,
246
- message: 'Path must start with /',
247
- },
248
- ]}
249
- >
232
+ <Form.Item name="path" label="Path" rules={[{ required: true, message: 'Path is required' }]}>
250
233
  <Input placeholder="Enter path" />
251
234
  </Form.Item>
252
235
  {/* Path Ends */}
253
236
 
254
237
  {/* Route */}
255
- <Form.Item
256
- name="route"
257
- label="Route"
258
- rules={[
259
- { required: false, message: 'Route is required' },
260
- {
261
- pattern: /^\//,
262
- message: 'Route must start with /',
263
- },
264
- ]}
265
- >
238
+ <Form.Item name="route" label="Route" rules={[{ required: true, message: 'Route is required' }]}>
266
239
  <Input placeholder="Enter route" />
267
240
  </Form.Item>
268
241
  {/* Route Ends */}
@@ -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));
244
-
245
- const searchActive = !!query;
246
- const visibleItems = filterMenus(records, query);
206
+ const filtered = records.filter((r) => r.caption?.toUpperCase().includes(query.toUpperCase()));
247
207
 
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,7 +305,7 @@ 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
311
  <Button onClick={getRecords} size={'small'} type="default">
@@ -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}
@@ -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,11 +536,7 @@ function NestedMenu({
597
536
  }
598
537
 
599
538
  return (
600
- <Collapse
601
- accordion={!searchActive}
602
- activeKey={openKeys}
603
- onChange={(keys) => setOpenKeys(Array.isArray(keys) ? keys : keys ? [keys] : [])}
604
- >
539
+ <Collapse accordion>
605
540
  {localItems.map((child, index) => (
606
541
  <Panel
607
542
  key={child.id}
@@ -628,8 +563,6 @@ function NestedMenu({
628
563
  step={step + 1}
629
564
  items={child.sub_menus || []}
630
565
  dragMode={dragMode}
631
- searchActive={searchActive}
632
- query={query}
633
566
  setSelectedRecord={setSelectedRecord}
634
567
  setDrawerTitle={setDrawerTitle}
635
568
  setDrawerVisible={setDrawerVisible}