@visns-studio/visns-components 4.7.1 → 4.7.3

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 (96) hide show
  1. package/package.json +13 -9
  2. package/src/components/cms/DataGrid.jsx +1461 -0
  3. package/src/components/cms/DropZone.jsx +349 -0
  4. package/src/components/cms/Field.jsx +802 -0
  5. package/src/components/cms/Form.jsx +476 -0
  6. package/src/components/cms/generic/CmsDetail.jsx +36 -0
  7. package/src/components/cms/generic/CmsIndex.jsx +73 -0
  8. package/src/components/cms/generic/CmsSort.jsx +125 -0
  9. package/src/components/cms/sorting/Item.jsx +115 -0
  10. package/src/components/cms/sorting/List.jsx +30 -0
  11. package/src/components/crm/AsyncSelect.jsx +125 -0
  12. package/src/components/crm/Autocomplete.jsx +174 -0
  13. package/src/components/crm/Breadcrumb.jsx +52 -0
  14. package/src/components/crm/Call.jsx +220 -0
  15. package/src/components/crm/DataGrid.jsx +2701 -0
  16. package/src/components/crm/Download.jsx +101 -0
  17. package/src/components/crm/Fetch.jsx +119 -0
  18. package/src/components/crm/Field.jsx +1557 -0
  19. package/src/components/crm/Form.jsx +1409 -0
  20. package/src/components/crm/Loader.jsx +29 -0
  21. package/src/components/crm/MultiSelect.jsx +78 -0
  22. package/src/components/crm/Navigation.jsx +619 -0
  23. package/src/components/crm/Notification.jsx +197 -0
  24. package/src/components/crm/QrCode.jsx +83 -0
  25. package/src/components/crm/Select.jsx +58 -0
  26. package/src/components/crm/SelectList.jsx +21 -0
  27. package/src/components/crm/TableFilter.jsx +198 -0
  28. package/src/components/crm/auth/Login.jsx +223 -0
  29. package/src/components/crm/auth/Profile.jsx +280 -0
  30. package/src/components/crm/auth/Reset.jsx +111 -0
  31. package/src/components/crm/auth/Verify.jsx +174 -0
  32. package/src/components/crm/auth/styles/Login.module.scss +243 -0
  33. package/src/components/crm/auth/styles/Profile.module.scss +222 -0
  34. package/src/components/crm/auth/styles/Reset.module.scss +216 -0
  35. package/src/components/crm/auth/styles/Verify.module.scss +216 -0
  36. package/src/components/crm/generic/AuditLog.jsx +108 -0
  37. package/src/components/crm/generic/AuditLogs.jsx +115 -0
  38. package/src/components/crm/generic/GenericAuth.jsx +166 -0
  39. package/src/components/crm/generic/GenericDashboard.jsx +42 -0
  40. package/src/components/crm/generic/GenericDetail.jsx +1499 -0
  41. package/src/components/crm/generic/GenericDynamic.jsx +915 -0
  42. package/src/components/crm/generic/GenericFormBuilder.jsx +1765 -0
  43. package/src/components/crm/generic/GenericIndex.jsx +376 -0
  44. package/src/components/crm/generic/GenericMain.jsx +129 -0
  45. package/src/components/crm/generic/GenericSort.jsx +188 -0
  46. package/src/components/crm/generic/NotificationList.jsx +64 -0
  47. package/src/components/crm/generic/styles/AuditLog.module.scss +147 -0
  48. package/src/components/crm/generic/styles/AuditLogs.module.scss +92 -0
  49. package/src/components/crm/generic/styles/GenericDetail.module.scss +634 -0
  50. package/src/components/crm/generic/styles/GenericDynamic.module.scss +932 -0
  51. package/src/components/crm/generic/styles/GenericFormBuilder.module.scss +944 -0
  52. package/src/components/crm/generic/styles/GenericIndex.module.scss +247 -0
  53. package/src/components/crm/generic/styles/GenericMain.module.scss +91 -0
  54. package/src/components/crm/generic/styles/GenericSort.module.scss +123 -0
  55. package/src/components/crm/generic/styles/NotificationList.module.scss +67 -0
  56. package/src/components/crm/sorting/Item.jsx +117 -0
  57. package/src/components/crm/sorting/List.jsx +32 -0
  58. package/src/components/crm/sorting/styles/Item.module.scss +68 -0
  59. package/src/components/crm/sorting/styles/List.module.scss +68 -0
  60. package/src/components/crm/styles/AsyncSelect.module.scss +8 -0
  61. package/src/components/crm/styles/Autocomplete.module.scss +44 -0
  62. package/src/components/crm/styles/DataGrid.module.scss +210 -0
  63. package/src/components/crm/styles/Field.module.scss +311 -0
  64. package/src/components/crm/styles/Form.module.scss +499 -0
  65. package/src/components/crm/styles/Loader.module.scss +94 -0
  66. package/src/components/crm/styles/MultiSelect.module.scss +8 -0
  67. package/src/components/crm/styles/Navigation.module.scss +978 -0
  68. package/src/components/crm/styles/Notification.module.scss +147 -0
  69. package/src/components/crm/styles/QrCode.module.scss +87 -0
  70. package/src/components/crm/styles/TableFilter.module.scss +52 -0
  71. package/src/components/styles/global.css +73 -0
  72. package/src/index.js +86 -0
  73. package/dist/104e5636d8ddb791614c.woff2 +0 -0
  74. package/dist/11fe6a83d647de5daf95.woff +0 -0
  75. package/dist/141e2a8c7836495afe2a.woff +0 -0
  76. package/dist/2ba5877f7079830b5383.woff +0 -0
  77. package/dist/2d6836f45b2037be41f3.woff +0 -0
  78. package/dist/399.0a0f3e0d21be1057d599.css +0 -8
  79. package/dist/399.b8f57fa3f8c0c10119e4.js +0 -1
  80. package/dist/47d566c3b1293dfd9d77.woff +0 -0
  81. package/dist/6282a13cbba671a71913.woff2 +0 -0
  82. package/dist/78266f551031aa9db127.woff +0 -0
  83. package/dist/7b7da89f1e3fdcbfe36c.woff2 +0 -0
  84. package/dist/7e78e8338af7efeda712.woff2 +0 -0
  85. package/dist/7fa387951673abf164b1.woff2 +0 -0
  86. package/dist/819d59862c2d0640aad8.woff +0 -0
  87. package/dist/b615e1a6c838ea059a56.woff +0 -0
  88. package/dist/b9b1aa739c660c4ce67c.woff2 +0 -0
  89. package/dist/c5f3640b11df9414facb.woff +0 -0
  90. package/dist/da26d162ff5af71d6590.woff2 +0 -0
  91. package/dist/dd5b2912dbf896310865.woff2 +0 -0
  92. package/dist/f282b331272463c1aa6c.woff2 +0 -0
  93. package/dist/main.83b033670d64419bb497.css +0 -1
  94. package/dist/main.b1723d17cd2d3513ab62.js +0 -1
  95. package/dist/report.html +0 -39
  96. package/dist/runtime.33c276593b23f8035b8d.js +0 -1
@@ -0,0 +1,376 @@
1
+ import '../../styles/global.css';
2
+
3
+ import React, { useCallback, useEffect, useRef, useState } from 'react';
4
+ import { Outlet } from 'react-router-dom';
5
+ import { toast } from 'react-toastify';
6
+ import { saveAs } from 'file-saver';
7
+ import _ from 'lodash';
8
+
9
+ import CustomFetch from '../Fetch';
10
+ import Download from '../Download';
11
+ import Field from '../Field';
12
+ import Table from '../DataGrid';
13
+ import TableFilter from '../TableFilter';
14
+
15
+ import styles from './styles/GenericIndex.module.scss';
16
+
17
+ function useWindowHeight() {
18
+ const [windowHeight, setWindowHeight] = useState(window.innerHeight);
19
+
20
+ useEffect(() => {
21
+ const handleResize = () => setWindowHeight(window.innerHeight);
22
+ window.addEventListener('resize', handleResize);
23
+ return () => window.removeEventListener('resize', handleResize);
24
+ }, []);
25
+
26
+ return windowHeight;
27
+ }
28
+
29
+ function useConfig(setting, tabs, filters, setRowsSelected, tableSetting) {
30
+ const [config, setConfig] = useState({});
31
+ const [subnav, setSubnav] = useState(filters || []);
32
+
33
+ useEffect(() => {
34
+ setConfig({
35
+ ajaxSetting: setting.ajaxSetting,
36
+ form: setting.form,
37
+ columns: setting.columns,
38
+ setRowsSelected,
39
+ settings: setting.settings,
40
+ tableSetting,
41
+ type: setting.type ? setting.type : 'table',
42
+ });
43
+ setSubnav(filters);
44
+ }, [setting, filters, setRowsSelected, tableSetting]);
45
+
46
+ useEffect(() => {
47
+ if (tabs?.length > 0 && subnav) {
48
+ const activeTab = subnav.find((s) => s.show);
49
+ const activeTabConfig = tabs.find((t) => t.id === activeTab?.id);
50
+
51
+ if (activeTabConfig) {
52
+ setConfig((currentConfig) => ({
53
+ ...currentConfig,
54
+ id: activeTabConfig.id,
55
+ ajaxSetting: activeTabConfig.ajaxSetting,
56
+ form: activeTabConfig.form,
57
+ columns: activeTabConfig.columns,
58
+ settings: activeTabConfig.settings,
59
+ type: activeTabConfig.type ? activeTabConfig.type : 'table',
60
+ }));
61
+ }
62
+ }
63
+ }, [subnav, tabs]);
64
+
65
+ return { config, setConfig, subnav, setSubnav };
66
+ }
67
+
68
+ const ReportForm = ({ config, userProfile }) => {
69
+ const [formData, setFormData] = useState({});
70
+
71
+ const handleChange = (e) => {
72
+ const {
73
+ value,
74
+ dataset: { name },
75
+ } = e.target;
76
+
77
+ setFormData((prevData) => ({ ...prevData, [name]: value }));
78
+ };
79
+
80
+ const handleChangeDate = (date, id) => {
81
+ setFormData((prevData) => ({ ...prevData, [id]: date }));
82
+ };
83
+
84
+ const handleChangeSelect = (inputValue, action, id) => {
85
+ setFormData((prevData) => ({ ...prevData, [id]: inputValue }));
86
+ };
87
+
88
+ const handleExport = async (e) => {
89
+ try {
90
+ if (e) {
91
+ e.preventDefault();
92
+ }
93
+
94
+ const missingFields = config.form.filters
95
+ .filter(
96
+ (item) => item.required === true && formData[item.id] === ''
97
+ )
98
+ .map((item) => item.label);
99
+
100
+ if (missingFields.length > 0) {
101
+ const fieldNames = missingFields.join(', ');
102
+ const errorMessage = `Please fill in a value for the following field(s): ${fieldNames}.`;
103
+ toast.error(errorMessage);
104
+ } else {
105
+ const res = await Download(
106
+ config.form.url,
107
+ config.form.method,
108
+ formData
109
+ );
110
+
111
+ if (res.data.error) {
112
+ toast.error(res.data.error);
113
+ } else {
114
+ // Assuming the response contains a Blob or similar data
115
+ if (res.data && res.data instanceof Blob) {
116
+ const fileName = `${config.form.filename}`;
117
+ saveAs(res.data, fileName);
118
+ } else {
119
+ // Handle the case where the response is not a Blob
120
+ toast.error('Invalid file format received.');
121
+ }
122
+ }
123
+ }
124
+ } catch (err) {
125
+ console.error(err);
126
+ }
127
+ };
128
+
129
+ useEffect(() => {
130
+ const tmpForm = config.form?.filters.reduce((acc, item) => {
131
+ acc[item.id] = '';
132
+ return acc;
133
+ }, {});
134
+ setFormData(tmpForm);
135
+ }, [config.form?.filters]);
136
+
137
+ return (
138
+ <div className="formcontainer">
139
+ <form className="modalForm">
140
+ {config.form?.filters.map((item, key) => (
141
+ <Field
142
+ api={userProfile.settings.api}
143
+ autocompleteCallback={() => {}}
144
+ childDropdownCallback={() => {}}
145
+ fetchData={() => {}}
146
+ formData={formData}
147
+ inputClass={''}
148
+ inputValue={formData[item.id]}
149
+ key={`filter-fields-${key}`}
150
+ mapbox={{}}
151
+ onChange={handleChange}
152
+ onChangeCheckbox={() => {}}
153
+ onChangeDate={handleChangeDate}
154
+ onChangeNumberFormat={() => {}}
155
+ onChangeRicheditor={() => {}}
156
+ onChangeSelect={handleChangeSelect}
157
+ onChangeToggle={() => {}}
158
+ settings={item}
159
+ setFormData={setFormData}
160
+ style={userProfile.settings.style}
161
+ uploadProgress={0}
162
+ userProfile={userProfile}
163
+ />
164
+ ))}
165
+ <div className="formItem fwItem lastItem">
166
+ <button className="btn modalsave" onClick={handleExport}>
167
+ Export
168
+ </button>
169
+ </div>
170
+ </form>
171
+ </div>
172
+ );
173
+ };
174
+
175
+ function GenericIndex({ layout = 'full', setting, userProfile }) {
176
+ const gridRef = useRef(null);
177
+ const [rowsSelected, setRowsSelected] = useState({});
178
+ const [total, setTotal] = useState(0);
179
+ const windowHeight = useWindowHeight();
180
+ const { config, setConfig, subnav, setSubnav } = useConfig(
181
+ setting,
182
+ setting.tabs,
183
+ setting.filters,
184
+ setRowsSelected,
185
+ setting.tableSetting
186
+ );
187
+
188
+ const handleCheckboxUpdate = useCallback(async () => {
189
+ try {
190
+ const { data, method, message, url } = setting?.functions
191
+ ?.checkboxUpdate
192
+ ? setting.functions.checkboxUpdate
193
+ : config.form.functions.checkboxUpdate;
194
+ if (!Object.keys(rowsSelected).length) {
195
+ toast.warning(message.warning);
196
+ return;
197
+ }
198
+ const res = await CustomFetch(url, method, {
199
+ ...data,
200
+ rows: rowsSelected,
201
+ });
202
+ if (res.data.error === '') {
203
+ toast.success(message.success);
204
+ gridRef.current.reload();
205
+ } else {
206
+ toast.error(message.error);
207
+ }
208
+ } catch (error) {
209
+ console.error(error);
210
+ }
211
+ }, [rowsSelected, setting?.functions?.checkboxUpdate]);
212
+
213
+ const renderTable = useCallback(() => {
214
+ const isConfigValid = (config) => {
215
+ return (
216
+ config.ajaxSetting &&
217
+ config.form &&
218
+ config.columns &&
219
+ config.settings
220
+ );
221
+ };
222
+
223
+ const renderTableComponent = () => (
224
+ <Table
225
+ {...config}
226
+ ref={gridRef}
227
+ gridHeight={windowHeight * 0.65}
228
+ setConfig={setConfig}
229
+ setTotal={setTotal}
230
+ style={userProfile?.settings?.style || {}}
231
+ userProfile={userProfile}
232
+ />
233
+ );
234
+
235
+ if (subnav) {
236
+ // Find the active subnav item with show: true
237
+ const activeSubnav = subnav.find((item) => item.show === true);
238
+
239
+ // Check if the config.id matches the active subnav id
240
+ if (
241
+ activeSubnav &&
242
+ activeSubnav.id === config.id &&
243
+ isConfigValid(config)
244
+ ) {
245
+ return renderTableComponent();
246
+ }
247
+
248
+ // Check for active alternative subnav
249
+ const activeAltSubNav = subnav.find(
250
+ (item) =>
251
+ item.class === 'subactive' || item.class === 'activetab'
252
+ );
253
+
254
+ const activeTabExist =
255
+ config.ajaxSetting?.where?.some(
256
+ (w) =>
257
+ w.id === activeAltSubNav?.id &&
258
+ parseInt(w.value) === parseInt(activeAltSubNav?.value)
259
+ ) || false;
260
+
261
+ if (isConfigValid(config) && activeTabExist) {
262
+ return renderTableComponent();
263
+ }
264
+ } else if (isConfigValid(config)) {
265
+ return renderTableComponent();
266
+ }
267
+
268
+ return null;
269
+ }, [config, subnav, windowHeight, userProfile, setConfig, setTotal]);
270
+
271
+ const renderContentBasedOnType = useCallback(() => {
272
+ switch (config.type) {
273
+ case 'table':
274
+ return renderTable();
275
+ case 'report':
276
+ return <ReportForm config={config} userProfile={userProfile} />;
277
+ default:
278
+ return renderTable(); // Fallback to table if no type is specified or recognized
279
+ }
280
+ }, [config, renderTable]);
281
+
282
+ const renderContent = useCallback(() => {
283
+ if (subnav?.length > 0) {
284
+ return (
285
+ <div className={styles.grid__subrow}>
286
+ {layout === 'simple' ? (
287
+ <div className={styles.grid__full}>
288
+ <div className={styles.tabcontent}>
289
+ <div className={styles.tabcontent__menu}>
290
+ <TableFilter
291
+ filters={subnav}
292
+ setFilters={setSubnav}
293
+ setSettings={setConfig}
294
+ type={layout}
295
+ />
296
+ </div>
297
+ <div className={styles.tabcontent__main}>
298
+ {renderContentBasedOnType()}
299
+ </div>
300
+ </div>
301
+ </div>
302
+ ) : (
303
+ <>
304
+ <div className={styles.grid__subnav}>
305
+ <TableFilter
306
+ filters={subnav}
307
+ setFilters={setSubnav}
308
+ setSettings={setConfig}
309
+ type={layout}
310
+ />
311
+ </div>
312
+ <div className={styles.grid__subcontent}>
313
+ {renderContentBasedOnType()}
314
+ </div>
315
+ </>
316
+ )}
317
+ </div>
318
+ );
319
+ } else {
320
+ return (
321
+ <div className={styles.grid__full}>
322
+ {renderContentBasedOnType()}
323
+ </div>
324
+ );
325
+ }
326
+ }, [layout, subnav, setSubnav, setConfig, renderTable]);
327
+
328
+ return (
329
+ <>
330
+ <div className={styles.grid}>
331
+ <div className={styles.grid__row}>
332
+ <div
333
+ className={`${styles.grid__full} ${
334
+ layout === 'full'
335
+ ? styles.crmtitle
336
+ : styles.crmtitleSimple
337
+ }`}
338
+ >
339
+ <h1>{setting.page?.title}</h1>
340
+ <div className={styles.titleInfo}>
341
+ <span>
342
+ [<strong>{total}</strong> Total{' '}
343
+ {setting.page?.title}]
344
+ </span>
345
+ </div>
346
+ </div>
347
+ </div>
348
+ </div>
349
+
350
+ <div className={styles.grid}>{renderContent()}</div>
351
+ <Outlet />
352
+ {setting.functions?.checkboxUpdate && (
353
+ <div className={styles.polActions}>
354
+ <button
355
+ className={styles.btn}
356
+ onClick={handleCheckboxUpdate}
357
+ >
358
+ {setting.functions.checkboxUpdate.label || 'Update'}
359
+ </button>
360
+ </div>
361
+ )}
362
+ {config?.form?.functions?.checkboxUpdate && (
363
+ <div className={styles.polActions}>
364
+ <button
365
+ className={styles.btn}
366
+ onClick={handleCheckboxUpdate}
367
+ >
368
+ {config.form.functions.checkboxUpdate.label || 'Update'}
369
+ </button>
370
+ </div>
371
+ )}
372
+ </>
373
+ );
374
+ }
375
+
376
+ export default GenericIndex;
@@ -0,0 +1,129 @@
1
+ import '../../styles/global.css';
2
+
3
+ import React, { useState } from 'react';
4
+ import { Routes, Route, Navigate } from 'react-router-dom';
5
+
6
+ import Call from '../Call';
7
+ import Loader from '../Loader';
8
+ import Navigation from '../Navigation';
9
+
10
+ import styles from './styles/GenericMain.module.scss';
11
+
12
+ const GenericMain = ({
13
+ layout = 'full',
14
+ logo,
15
+ navigation,
16
+ routeConfig,
17
+ setSystemAuth,
18
+ setUserProfile,
19
+ themeType,
20
+ userProfile,
21
+ }) => {
22
+ const [incomingCallData, setIncomingCallData] = useState({});
23
+
24
+ // Function to extract route parameters
25
+ const extractRouteParams = (path) => {
26
+ const params = path.match(/:\w+/g) || [];
27
+ return params.map((param) => param.substring(1));
28
+ };
29
+
30
+ // Recursive function to render routes
31
+ const renderRoutes = (routes) => {
32
+ return routes.map((route, index) => {
33
+ let elementProps = {
34
+ userProfile: userProfile,
35
+ setUserProfile: setUserProfile,
36
+ };
37
+
38
+ const routeParams = extractRouteParams(route.path);
39
+ const extraParams = routeParams.filter(
40
+ (param) => param !== 'dataId'
41
+ );
42
+
43
+ if (routeParams.includes('dataId')) {
44
+ elementProps.urlParam = 'dataId';
45
+ }
46
+
47
+ if (extraParams.length > 0) {
48
+ elementProps.extraUrlParam = extraParams;
49
+ }
50
+
51
+ let element = route.element
52
+ ? React.cloneElement(route.element, elementProps)
53
+ : null;
54
+
55
+ if (route.children && route.children.length > 0) {
56
+ // If the route has children, render them recursively
57
+ return (
58
+ <Route key={index} path={route.path}>
59
+ {renderRoutes(route.children)}
60
+ </Route>
61
+ );
62
+ } else {
63
+ return (
64
+ <Route key={index} path={route.path} element={element} />
65
+ );
66
+ }
67
+ });
68
+ };
69
+
70
+ return layout === 'full' ? (
71
+ <div>
72
+ <header
73
+ className={
74
+ themeType === 'alternate'
75
+ ? styles.headerAlternate
76
+ : styles.header
77
+ }
78
+ >
79
+ <Navigation
80
+ layout={layout}
81
+ logo={logo}
82
+ navConfig={navigation}
83
+ setSystemAuth={setSystemAuth}
84
+ themeType={themeType}
85
+ userProfile={userProfile}
86
+ />
87
+ </header>
88
+ <main className={styles.content}>
89
+ <div className={styles['content-main']} id="app">
90
+ <div className={styles.padding}>
91
+ <Routes>
92
+ {renderRoutes(routeConfig)}
93
+ {/* Add a wildcard route for handling unknown paths */}
94
+ <Route
95
+ path="*"
96
+ element={<Navigate to="/" replace />}
97
+ />
98
+ </Routes>
99
+ </div>
100
+ <Call incomingCallData={incomingCallData} />
101
+ <Loader />
102
+ </div>
103
+ </main>
104
+ </div>
105
+ ) : (
106
+ <Navigation
107
+ layout={layout}
108
+ logo={logo}
109
+ navConfig={navigation}
110
+ setSystemAuth={setSystemAuth}
111
+ themeType={themeType}
112
+ userProfile={userProfile}
113
+ >
114
+ <div className={styles['content-main']} id="app">
115
+ <div className={styles.paddingSimple}>
116
+ <Routes>
117
+ {renderRoutes(routeConfig)}
118
+ {/* Add a wildcard route for handling unknown paths */}
119
+ <Route path="*" element={<Navigate to="/" replace />} />
120
+ </Routes>
121
+ </div>
122
+ <Call incomingCallData={incomingCallData} />
123
+ <Loader />
124
+ </div>
125
+ </Navigation>
126
+ );
127
+ };
128
+
129
+ export default GenericMain;
@@ -0,0 +1,188 @@
1
+ import React, { useState, useEffect } from 'react';
2
+ import { useLocation, useParams } from 'react-router-dom';
3
+ import { arrayMoveImmutable } from 'array-move';
4
+ import { toast } from 'react-toastify';
5
+ import CustomFetch from '../../crm/Fetch';
6
+ import SortableList from '../sorting/List';
7
+ import Breadcrumb from '../Breadcrumb';
8
+ import styles from './styles/GenericSort.module.scss';
9
+
10
+ // Utility function to convert camelCase to space-separated words with uppercase first letters
11
+ function camelCaseToUcFirst(str) {
12
+ return str
13
+ .replace(/([A-Z])/g, ' $1') // Add a space before each uppercase letter
14
+ .replace(/^./, function (str) {
15
+ return str.toUpperCase();
16
+ }) // Capitalize the first letter
17
+ .trim(); // Remove leading and trailing whitespace
18
+ }
19
+
20
+ const ChildSort = ({ item, url }) => {
21
+ const [data, setData] = useState(item.child);
22
+
23
+ const onSortEnd = ({ oldIndex, newIndex }) => {
24
+ setData((prevItem) => arrayMoveImmutable(prevItem, oldIndex, newIndex));
25
+ };
26
+
27
+ useEffect(() => {
28
+ if (data.length > 0) {
29
+ CustomFetch(
30
+ `${url}/sort_update`,
31
+ 'POST',
32
+ {
33
+ list: data,
34
+ },
35
+ function (result) {
36
+ if (result.error === '') {
37
+ toast.success('Sort updated successfully');
38
+ } else {
39
+ toast.error(String(result.error));
40
+ }
41
+ }
42
+ );
43
+ }
44
+ }, [data]);
45
+
46
+ return (
47
+ <>
48
+ <h1>{item.label}</h1>
49
+ <SortableList
50
+ items={data}
51
+ onSortEnd={onSortEnd}
52
+ axis="xy"
53
+ helperClass="dragitem"
54
+ transitionDuration={250}
55
+ useDragHandle
56
+ />
57
+ </>
58
+ );
59
+ };
60
+
61
+ const GenericSort = ({ category = false, layout = 'full' }) => {
62
+ const location = useLocation();
63
+ const routeParams = useParams();
64
+ const {
65
+ form,
66
+ page = {}, // Provide a default empty object for page
67
+ paramValue,
68
+ } = location.state || {}; // Safely access location.state
69
+
70
+ const [sortData, setSortData] = useState([]);
71
+ const url = form?.url;
72
+ const data = page?.data || {}; // Safely access data from page, default to an empty object
73
+
74
+ const onSortEnd = ({ oldIndex, newIndex }) => {
75
+ setSortData((prevItem) =>
76
+ arrayMoveImmutable(prevItem, oldIndex, newIndex)
77
+ );
78
+ };
79
+
80
+ const sortList = async () => {
81
+ try {
82
+ let fetchUrl = url;
83
+ let payload = {};
84
+
85
+ if (form?.json) {
86
+ fetchUrl = fetchUrl + '/jsonSortList';
87
+ payload.id = paramValue;
88
+ } else {
89
+ fetchUrl = fetchUrl + '/sort_list';
90
+ }
91
+
92
+ const res = await CustomFetch(fetchUrl, 'POST', payload);
93
+ setSortData(res.data);
94
+ } catch (err) {
95
+ console.log(err);
96
+ }
97
+ };
98
+
99
+ const sortUpdate = async () => {
100
+ try {
101
+ let fetchUrl = url;
102
+ let payload = {
103
+ list: sortData,
104
+ };
105
+
106
+ if (form?.json) {
107
+ fetchUrl = fetchUrl + '/jsonSortUpdate';
108
+ payload.id = paramValue;
109
+ } else {
110
+ fetchUrl = fetchUrl + '/sort_update';
111
+ }
112
+
113
+ if (category === false) {
114
+ if (sortData.length > 0) {
115
+ const res = await CustomFetch(fetchUrl, 'POST', payload);
116
+
117
+ if (res.data.error !== '') {
118
+ toast.error(String(res.data.error));
119
+ }
120
+ }
121
+ }
122
+ } catch (err) {
123
+ console.log(err);
124
+ }
125
+ };
126
+
127
+ useEffect(() => {
128
+ sortUpdate();
129
+ }, [sortData]);
130
+
131
+ useEffect(() => {
132
+ sortList();
133
+ }, []);
134
+
135
+ return (
136
+ <>
137
+ <div className={styles.grid}>
138
+ <div className={styles.grid__row}>
139
+ <div
140
+ className={`${styles.grid__full} ${
141
+ layout === 'full'
142
+ ? styles.crmtitle
143
+ : styles.crmtitleSimple
144
+ }`}
145
+ >
146
+ <Breadcrumb
147
+ data={{
148
+ label: camelCaseToUcFirst(routeParams.dataId),
149
+ }} // Use utility function here
150
+ page={{
151
+ parentTitle: 'Settings',
152
+ parentUrl: '/settings',
153
+ titleKey: 'label',
154
+ }}
155
+ />
156
+ </div>
157
+ </div>
158
+ </div>
159
+ <div className={styles.grid}>
160
+ <div className={styles.grid__row}>
161
+ <div className={styles.grid__full}>
162
+ {category === false ? (
163
+ <SortableList
164
+ items={sortData}
165
+ onSortEnd={onSortEnd}
166
+ axis="xy"
167
+ helperClass={styles.dragitem}
168
+ transitionDuration={250}
169
+ useDragHandle
170
+ />
171
+ ) : (
172
+ sortData.length > 0 &&
173
+ sortData.map((item) => (
174
+ <ChildSort
175
+ key={`category-${item.id}`}
176
+ item={item}
177
+ url={url}
178
+ />
179
+ ))
180
+ )}
181
+ </div>
182
+ </div>
183
+ </div>
184
+ </>
185
+ );
186
+ };
187
+
188
+ export default GenericSort;