@visns-studio/visns-components 5.12.7 → 5.12.9
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/package.json +1 -1
- package/src/components/CategorizedDropZone.jsx +311 -0
- package/src/components/DropZone.jsx +6 -2
- package/src/components/Form.jsx +1 -1
- package/src/components/generic/CmsSort.jsx +10 -2
- package/src/components/generic/ConfirmationDialog.jsx +16 -3
- package/src/components/generic/GenericDashboard.jsx +1 -1
- package/src/components/generic/GenericDetail.jsx +25 -0
- package/src/components/generic/GenericDynamic.jsx +1 -1
- package/src/components/generic/GenericEditableTable.jsx +1 -1
- package/src/components/generic/GenericFormBuilder.jsx +1 -1
- package/src/components/generic/GenericSort.jsx +8 -1
- package/src/components/generic/StagePopupModal.jsx +14 -1
- package/src/components/styles/CategorizedDropZone.module.scss +222 -0
- package/src/components/styles/DropZone.module.css +1 -1
- package/src/components/styles/GenericEditableTable.module.scss.backup +488 -0
- package/src/components/utils/ConfirmDialog.js +4 -10
- package/src/index.js +8 -0
- package/src/utils/ConfirmDialog.jsx +11 -65
package/package.json
CHANGED
|
@@ -87,7 +87,7 @@
|
|
|
87
87
|
"react-dom": "^17.0.0 || ^18.0.0"
|
|
88
88
|
},
|
|
89
89
|
"name": "@visns-studio/visns-components",
|
|
90
|
-
"version": "5.12.
|
|
90
|
+
"version": "5.12.9",
|
|
91
91
|
"description": "Various packages to assist in the development of our Custom Applications.",
|
|
92
92
|
"main": "src/index.js",
|
|
93
93
|
"files": [
|
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
import React, { useState, useEffect } from 'react';
|
|
2
|
+
import DropZone from './DropZone';
|
|
3
|
+
import CustomFetch from './Fetch';
|
|
4
|
+
import styles from './styles/CategorizedDropZone.module.scss';
|
|
5
|
+
|
|
6
|
+
const CategorizedDropZone = ({
|
|
7
|
+
filetype = 'image',
|
|
8
|
+
folder = 'uploads',
|
|
9
|
+
dataKey = 'files',
|
|
10
|
+
file_relationship = 'files',
|
|
11
|
+
url = '/ajax/files',
|
|
12
|
+
method = 'POST',
|
|
13
|
+
deleteUrl = '/ajax/files/delete',
|
|
14
|
+
categories = [],
|
|
15
|
+
categoriesUrl = null,
|
|
16
|
+
categoriesWhere = [],
|
|
17
|
+
entityData = {},
|
|
18
|
+
routeParams = {},
|
|
19
|
+
fetchData = null
|
|
20
|
+
}) => {
|
|
21
|
+
const [activeCategory, setActiveCategory] = useState(null);
|
|
22
|
+
const [categorizedFiles, setCategorizedFiles] = useState({});
|
|
23
|
+
const [dynamicCategories, setDynamicCategories] = useState([]);
|
|
24
|
+
const [loadingCategories, setLoadingCategories] = useState(false);
|
|
25
|
+
|
|
26
|
+
// Get final categories (dynamic or static)
|
|
27
|
+
const finalCategories = dynamicCategories.length > 0 ? dynamicCategories : categories;
|
|
28
|
+
|
|
29
|
+
// Fetch categories from API if categoriesUrl is provided
|
|
30
|
+
useEffect(() => {
|
|
31
|
+
if (categoriesUrl) {
|
|
32
|
+
setLoadingCategories(true);
|
|
33
|
+
|
|
34
|
+
const requestData = {};
|
|
35
|
+
if (categoriesWhere && categoriesWhere.length > 0) {
|
|
36
|
+
requestData.where = categoriesWhere;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
CustomFetch(categoriesUrl, 'POST', requestData)
|
|
40
|
+
.then(response => {
|
|
41
|
+
if (response.data && !response.data.error) {
|
|
42
|
+
const categoriesData = Array.isArray(response.data.data) ? response.data.data :
|
|
43
|
+
Array.isArray(response.data) ? response.data : [];
|
|
44
|
+
|
|
45
|
+
// Transform API data to match expected format
|
|
46
|
+
const transformedCategories = categoriesData.map(cat => ({
|
|
47
|
+
id: cat.slug || cat.id,
|
|
48
|
+
label: cat.name || cat.label,
|
|
49
|
+
description: cat.description || ''
|
|
50
|
+
}));
|
|
51
|
+
|
|
52
|
+
setDynamicCategories(transformedCategories);
|
|
53
|
+
|
|
54
|
+
// Set first category as active if none selected
|
|
55
|
+
if (!activeCategory && transformedCategories.length > 0) {
|
|
56
|
+
setActiveCategory(transformedCategories[0].id);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
})
|
|
60
|
+
.catch(error => {
|
|
61
|
+
console.error('Failed to load categories:', error);
|
|
62
|
+
})
|
|
63
|
+
.finally(() => {
|
|
64
|
+
setLoadingCategories(false);
|
|
65
|
+
});
|
|
66
|
+
} else if (categories.length > 0 && !activeCategory) {
|
|
67
|
+
// Set first static category as active
|
|
68
|
+
setActiveCategory(categories[0].id);
|
|
69
|
+
}
|
|
70
|
+
}, [categoriesUrl, categoriesWhere, categories, activeCategory]);
|
|
71
|
+
|
|
72
|
+
useEffect(() => {
|
|
73
|
+
console.log('CategorizedDropZone: Initializing files', {
|
|
74
|
+
finalCategories,
|
|
75
|
+
entityData: entityData[dataKey],
|
|
76
|
+
dataKey
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
// Initialize categorized files structure
|
|
80
|
+
const initialCategorizedFiles = {};
|
|
81
|
+
finalCategories.forEach(category => {
|
|
82
|
+
initialCategorizedFiles[category.id] = [];
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
// If there are existing files, categorize them based on file_description
|
|
86
|
+
if (entityData[dataKey] && Array.isArray(entityData[dataKey])) {
|
|
87
|
+
console.log('CategorizedDropZone: Processing files', entityData[dataKey]);
|
|
88
|
+
|
|
89
|
+
entityData[dataKey].forEach(file => {
|
|
90
|
+
console.log('CategorizedDropZone: Processing file', {
|
|
91
|
+
file_name: file.file_name,
|
|
92
|
+
file_description: file.file_description,
|
|
93
|
+
id: file.id
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
const category = getCategoryFromDescription(file.file_description);
|
|
97
|
+
console.log('CategorizedDropZone: Category extracted', category);
|
|
98
|
+
|
|
99
|
+
if (category && initialCategorizedFiles[category]) {
|
|
100
|
+
initialCategorizedFiles[category].push(file);
|
|
101
|
+
console.log(`CategorizedDropZone: Added file ${file.file_name} to category ${category}`);
|
|
102
|
+
} else {
|
|
103
|
+
console.log(`CategorizedDropZone: No category match for file ${file.file_name}`, {
|
|
104
|
+
extractedCategory: category,
|
|
105
|
+
availableCategories: Object.keys(initialCategorizedFiles)
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
console.log('CategorizedDropZone: Final categorized files', initialCategorizedFiles);
|
|
112
|
+
setCategorizedFiles(initialCategorizedFiles);
|
|
113
|
+
}, [entityData, dataKey, finalCategories]);
|
|
114
|
+
|
|
115
|
+
const getCategoryFromDescription = (description) => {
|
|
116
|
+
if (!description) return null;
|
|
117
|
+
|
|
118
|
+
// Check if the description contains a category identifier (handles both slugs and numeric IDs)
|
|
119
|
+
const categoryMatch = description.match(/^category:([^-\s]+)/i);
|
|
120
|
+
if (categoryMatch) {
|
|
121
|
+
const extractedId = categoryMatch[1];
|
|
122
|
+
|
|
123
|
+
// First, try to find by exact ID match
|
|
124
|
+
const exactMatch = finalCategories.find(cat => cat.id === extractedId);
|
|
125
|
+
if (exactMatch) return extractedId;
|
|
126
|
+
|
|
127
|
+
// If no exact match, try to find by numeric ID if extracted ID is numeric
|
|
128
|
+
if (/^\d+$/.test(extractedId)) {
|
|
129
|
+
const numericMatch = finalCategories.find(cat => cat.id == extractedId);
|
|
130
|
+
if (numericMatch) return numericMatch.id;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Log for debugging
|
|
134
|
+
console.log('Category extraction debug:', {
|
|
135
|
+
description,
|
|
136
|
+
extractedId,
|
|
137
|
+
availableCategories: finalCategories.map(c => ({ id: c.id, label: c.label }))
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Fallback: check if description matches any category label
|
|
142
|
+
const matchedCategory = finalCategories.find(cat =>
|
|
143
|
+
description.toLowerCase().includes(cat.label.toLowerCase())
|
|
144
|
+
);
|
|
145
|
+
return matchedCategory?.id || null;
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
const handleCategoryChange = (categoryId) => {
|
|
149
|
+
setActiveCategory(categoryId);
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
const getCurrentCategoryFiles = () => {
|
|
153
|
+
return categorizedFiles[activeCategory] || [];
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
const handleFileUpload = (files) => {
|
|
157
|
+
// Add category prefix to file descriptions
|
|
158
|
+
const activeCategoryData = finalCategories.find(cat => cat.id === activeCategory);
|
|
159
|
+
const categoryPrefix = `category:${activeCategory}`;
|
|
160
|
+
|
|
161
|
+
// Update the files with category information
|
|
162
|
+
const updatedFiles = files.map(file => ({
|
|
163
|
+
...file,
|
|
164
|
+
file_description: file.file_description ?
|
|
165
|
+
`${categoryPrefix} - ${file.file_description}` :
|
|
166
|
+
`${categoryPrefix} - ${activeCategoryData?.description || activeCategoryData?.label}`
|
|
167
|
+
}));
|
|
168
|
+
|
|
169
|
+
// Update the categorized files state
|
|
170
|
+
setCategorizedFiles(prev => ({
|
|
171
|
+
...prev,
|
|
172
|
+
[activeCategory]: [...(prev[activeCategory] || []), ...updatedFiles]
|
|
173
|
+
}));
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
const handleFileDelete = (fileId) => {
|
|
177
|
+
// Remove file from the appropriate category
|
|
178
|
+
setCategorizedFiles(prev => {
|
|
179
|
+
const updated = { ...prev };
|
|
180
|
+
Object.keys(updated).forEach(categoryId => {
|
|
181
|
+
updated[categoryId] = updated[categoryId].filter(file => file.id !== fileId);
|
|
182
|
+
});
|
|
183
|
+
return updated;
|
|
184
|
+
});
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
const handleFileUpdate = (updatedFile) => {
|
|
188
|
+
// Update file in the appropriate category
|
|
189
|
+
const newCategory = getCategoryFromDescription(updatedFile.file_description);
|
|
190
|
+
|
|
191
|
+
setCategorizedFiles(prev => {
|
|
192
|
+
const updated = { ...prev };
|
|
193
|
+
|
|
194
|
+
// Remove from old category
|
|
195
|
+
Object.keys(updated).forEach(categoryId => {
|
|
196
|
+
updated[categoryId] = updated[categoryId].filter(file => file.id !== updatedFile.id);
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
// Add to new category
|
|
200
|
+
if (newCategory && updated[newCategory]) {
|
|
201
|
+
updated[newCategory].push(updatedFile);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
return updated;
|
|
205
|
+
});
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
// Prepare entity data for the current category
|
|
209
|
+
const currentCategoryEntityData = {
|
|
210
|
+
...entityData,
|
|
211
|
+
[dataKey]: getCurrentCategoryFiles()
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
if (loadingCategories) {
|
|
215
|
+
return (
|
|
216
|
+
<div className={styles.categorizedDropZone}>
|
|
217
|
+
<div className={styles.loadingState}>
|
|
218
|
+
Loading categories...
|
|
219
|
+
</div>
|
|
220
|
+
</div>
|
|
221
|
+
);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
if (finalCategories.length === 0) {
|
|
225
|
+
// No categories available - show a simple dropzone without categorization
|
|
226
|
+
console.log('CategorizedDropZone: No categories, showing all files', {
|
|
227
|
+
dataKey,
|
|
228
|
+
files: entityData[dataKey] || [],
|
|
229
|
+
entityData
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
return (
|
|
233
|
+
<div className={styles.categorizedDropZone}>
|
|
234
|
+
<div className={styles.noCategoryContent}>
|
|
235
|
+
<DropZone
|
|
236
|
+
fetchData={fetchData || (() => {
|
|
237
|
+
console.log('Files updated (no categories)');
|
|
238
|
+
})}
|
|
239
|
+
files={entityData[dataKey] || []}
|
|
240
|
+
settings={{
|
|
241
|
+
url: url,
|
|
242
|
+
method: method,
|
|
243
|
+
type: 'gallery',
|
|
244
|
+
data: {
|
|
245
|
+
file_relationship: file_relationship,
|
|
246
|
+
file_description: '',
|
|
247
|
+
},
|
|
248
|
+
model: entityData?.constructor?.name || 'Design',
|
|
249
|
+
folder: folder,
|
|
250
|
+
filetype: filetype,
|
|
251
|
+
deleteUrl: deleteUrl
|
|
252
|
+
}}
|
|
253
|
+
/>
|
|
254
|
+
</div>
|
|
255
|
+
</div>
|
|
256
|
+
);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
return (
|
|
260
|
+
<div className={styles.categorizedDropZone}>
|
|
261
|
+
<div className={styles.categoryTabs}>
|
|
262
|
+
{finalCategories.map(category => (
|
|
263
|
+
<button
|
|
264
|
+
key={category.id}
|
|
265
|
+
className={`${styles.categoryTab} ${activeCategory === category.id ? styles.active : ''}`}
|
|
266
|
+
onClick={() => handleCategoryChange(category.id)}
|
|
267
|
+
>
|
|
268
|
+
{category.label}
|
|
269
|
+
{categorizedFiles[category.id] && categorizedFiles[category.id].length > 0 && (
|
|
270
|
+
<span className={styles.fileCount}>
|
|
271
|
+
({categorizedFiles[category.id].length})
|
|
272
|
+
</span>
|
|
273
|
+
)}
|
|
274
|
+
</button>
|
|
275
|
+
))}
|
|
276
|
+
</div>
|
|
277
|
+
|
|
278
|
+
<div className={styles.categoryContent}>
|
|
279
|
+
{activeCategory && (
|
|
280
|
+
<div className={styles.categoryPanel}>
|
|
281
|
+
{finalCategories.find(cat => cat.id === activeCategory)?.description && (
|
|
282
|
+
<p className={styles.categoryDescription}>
|
|
283
|
+
{finalCategories.find(cat => cat.id === activeCategory)?.description}
|
|
284
|
+
</p>
|
|
285
|
+
)}
|
|
286
|
+
|
|
287
|
+
<DropZone
|
|
288
|
+
fetchData={fetchData || (() => {
|
|
289
|
+
// Default fetch function - refresh current category files
|
|
290
|
+
console.log('Files updated for category:', activeCategory);
|
|
291
|
+
})}
|
|
292
|
+
files={getCurrentCategoryFiles()}
|
|
293
|
+
settings={{
|
|
294
|
+
url: url,
|
|
295
|
+
method: method,
|
|
296
|
+
type: 'gallery',
|
|
297
|
+
data: {
|
|
298
|
+
file_relationship: file_relationship,
|
|
299
|
+
file_description: `category:${activeCategory} - ${finalCategories.find(cat => cat.id === activeCategory)?.label || activeCategory}`,
|
|
300
|
+
},
|
|
301
|
+
model: entityData?.constructor?.name || 'Design'
|
|
302
|
+
}}
|
|
303
|
+
/>
|
|
304
|
+
</div>
|
|
305
|
+
)}
|
|
306
|
+
</div>
|
|
307
|
+
</div>
|
|
308
|
+
);
|
|
309
|
+
};
|
|
310
|
+
|
|
311
|
+
export default CategorizedDropZone;
|
|
@@ -7,7 +7,7 @@ import { arrayMoveImmutable } from 'array-move';
|
|
|
7
7
|
import Popup from 'reactjs-popup';
|
|
8
8
|
import imageCompression from 'browser-image-compression';
|
|
9
9
|
|
|
10
|
-
import { confirmDialog } from '
|
|
10
|
+
import { confirmDialog } from './utils/ConfirmDialog';
|
|
11
11
|
import CustomFetch from './Fetch';
|
|
12
12
|
import Gallery from './Gallery';
|
|
13
13
|
import styles from './styles/DropZone.module.css';
|
|
@@ -189,14 +189,18 @@ function VisnsDropZone({ fetchData, files, settings }) {
|
|
|
189
189
|
|
|
190
190
|
useEffect(() => {
|
|
191
191
|
if (data?.length > 0) {
|
|
192
|
+
// Only send the ID field for each file for sorting
|
|
193
|
+
const sortData = data.map(file => ({ id: file.id }));
|
|
194
|
+
|
|
192
195
|
CustomFetch(
|
|
193
196
|
'/ajax/files/sort_update',
|
|
194
197
|
'POST',
|
|
195
198
|
{
|
|
196
|
-
list:
|
|
199
|
+
list: sortData,
|
|
197
200
|
},
|
|
198
201
|
function (result) {
|
|
199
202
|
if (result.error === '') {
|
|
203
|
+
console.log('Sort order updated successfully');
|
|
200
204
|
} else {
|
|
201
205
|
toast.error(String(result.error));
|
|
202
206
|
}
|
package/src/components/Form.jsx
CHANGED
|
@@ -11,7 +11,7 @@ import _ from 'lodash';
|
|
|
11
11
|
import ReactDataGrid from '@visns-studio/visns-datagrid-enterprise';
|
|
12
12
|
import { X } from 'lucide-react';
|
|
13
13
|
import imageCompression from 'browser-image-compression';
|
|
14
|
-
import { confirmDialog } from '
|
|
14
|
+
import { confirmDialog } from './utils/ConfirmDialog';
|
|
15
15
|
|
|
16
16
|
import '@visns-studio/visns-datagrid-enterprise/index.css';
|
|
17
17
|
|
|
@@ -15,14 +15,18 @@ const ChildSort = ({ item, url }) => {
|
|
|
15
15
|
|
|
16
16
|
useEffect(() => {
|
|
17
17
|
if (data.length > 0) {
|
|
18
|
+
// Only send the ID field for each item for sorting
|
|
19
|
+
const sortData = data.map(item => ({ id: item.id }));
|
|
20
|
+
|
|
18
21
|
CustomFetch(
|
|
19
22
|
url + '/sort_update',
|
|
20
23
|
'POST',
|
|
21
24
|
{
|
|
22
|
-
list:
|
|
25
|
+
list: sortData,
|
|
23
26
|
},
|
|
24
27
|
function (result) {
|
|
25
28
|
if (result.error === '') {
|
|
29
|
+
console.log('Sort order updated successfully');
|
|
26
30
|
} else {
|
|
27
31
|
toast.error(String(result.error));
|
|
28
32
|
}
|
|
@@ -60,14 +64,18 @@ const CmsSort = ({ setting, category = false }) => {
|
|
|
60
64
|
useEffect(() => {
|
|
61
65
|
if (category === false) {
|
|
62
66
|
if (data.length > 0) {
|
|
67
|
+
// Only send the ID field for each item for sorting
|
|
68
|
+
const sortData = data.map(item => ({ id: item.id }));
|
|
69
|
+
|
|
63
70
|
CustomFetch(
|
|
64
71
|
url + '/sort_update',
|
|
65
72
|
'POST',
|
|
66
73
|
{
|
|
67
|
-
list:
|
|
74
|
+
list: sortData,
|
|
68
75
|
},
|
|
69
76
|
function (result) {
|
|
70
77
|
if (result.error === '') {
|
|
78
|
+
console.log('Sort order updated successfully');
|
|
71
79
|
} else {
|
|
72
80
|
toast.error(String(result.error));
|
|
73
81
|
}
|
|
@@ -87,10 +87,23 @@ export const showConfirmDialog = ({
|
|
|
87
87
|
// Show SweetAlert2 dialog
|
|
88
88
|
return Swal.fire(swalOptions).then((result) => {
|
|
89
89
|
if (result.isConfirmed) {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
90
|
+
try {
|
|
91
|
+
onConfirm();
|
|
92
|
+
} catch (error) {
|
|
93
|
+
console.error('Error executing onConfirm callback:', error);
|
|
94
|
+
}
|
|
95
|
+
} else if (result.dismiss === Swal.DismissReason.cancel || result.isDenied) {
|
|
96
|
+
try {
|
|
97
|
+
onCancel();
|
|
98
|
+
} catch (error) {
|
|
99
|
+
console.error('Error executing onCancel callback:', error);
|
|
100
|
+
}
|
|
93
101
|
}
|
|
102
|
+
|
|
103
|
+
return result;
|
|
104
|
+
}).catch((error) => {
|
|
105
|
+
console.error('SweetAlert2 error:', error);
|
|
106
|
+
return error;
|
|
94
107
|
});
|
|
95
108
|
};
|
|
96
109
|
|
|
@@ -9,7 +9,7 @@ import dayjs from 'dayjs';
|
|
|
9
9
|
import parse from 'html-react-parser';
|
|
10
10
|
import { createSwapy } from 'swapy';
|
|
11
11
|
import { X, Minus, Plus, Star, Trash2 } from 'lucide-react';
|
|
12
|
-
import { confirmDialog } from '
|
|
12
|
+
import { confirmDialog } from '../utils/ConfirmDialog';
|
|
13
13
|
|
|
14
14
|
import CustomFetch from '../Fetch';
|
|
15
15
|
import MultiSelect from '../MultiSelect';
|
|
@@ -80,6 +80,7 @@ import QrCode from '../QrCode';
|
|
|
80
80
|
import StagePopupModal from './StagePopupModal';
|
|
81
81
|
import Table from '../DataGrid';
|
|
82
82
|
import TableFilter from '../TableFilter';
|
|
83
|
+
import CategorizedDropZone from '../CategorizedDropZone';
|
|
83
84
|
|
|
84
85
|
// Proposal components
|
|
85
86
|
import ProposalTemplateSectionManager from '../proposal/ProposalTemplateSectionManager';
|
|
@@ -2748,6 +2749,30 @@ function GenericDetail({
|
|
|
2748
2749
|
} else {
|
|
2749
2750
|
return null;
|
|
2750
2751
|
}
|
|
2752
|
+
case 'categorized_dropzone':
|
|
2753
|
+
// Replace URL parameters like {dataId} with actual values
|
|
2754
|
+
const replaceUrlParams = (url) => {
|
|
2755
|
+
if (!url) return url;
|
|
2756
|
+
return url.replace(/{dataId}/g, routeParams[urlParam] || '');
|
|
2757
|
+
};
|
|
2758
|
+
|
|
2759
|
+
return (
|
|
2760
|
+
<CategorizedDropZone
|
|
2761
|
+
filetype={activeTabConfig.filetype}
|
|
2762
|
+
folder={activeTabConfig.folder}
|
|
2763
|
+
dataKey={activeTabConfig.key}
|
|
2764
|
+
file_relationship={activeTabConfig.file_relationship}
|
|
2765
|
+
url={replaceUrlParams(activeTabConfig.url)}
|
|
2766
|
+
method={activeTabConfig.method}
|
|
2767
|
+
deleteUrl={replaceUrlParams(activeTabConfig.deleteUrl)}
|
|
2768
|
+
categories={activeTabConfig.categories || []}
|
|
2769
|
+
categoriesUrl={activeTabConfig.categoriesUrl}
|
|
2770
|
+
categoriesWhere={activeTabConfig.categoriesWhere}
|
|
2771
|
+
entityData={data}
|
|
2772
|
+
routeParams={routeParams}
|
|
2773
|
+
fetchData={handleReload}
|
|
2774
|
+
/>
|
|
2775
|
+
);
|
|
2751
2776
|
case 'scheduling':
|
|
2752
2777
|
return (
|
|
2753
2778
|
<GenericEditableTable
|
|
@@ -4,7 +4,7 @@ import { toast } from 'react-toastify';
|
|
|
4
4
|
import { saveAs } from 'file-saver';
|
|
5
5
|
import parse from 'html-react-parser';
|
|
6
6
|
import { Delete, Download as DownloadIcon } from 'lucide-react';
|
|
7
|
-
import { confirmDialog } from '
|
|
7
|
+
import { confirmDialog } from '../utils/ConfirmDialog';
|
|
8
8
|
|
|
9
9
|
import CustomFetch from '../Fetch';
|
|
10
10
|
import Download from '../Download';
|
|
@@ -4,7 +4,7 @@ import debounce from 'lodash.debounce';
|
|
|
4
4
|
import { toast } from 'react-toastify';
|
|
5
5
|
import { Trash2, ArrowUp, ArrowDown, Copy, Droplets } from 'lucide-react';
|
|
6
6
|
import { Compact } from '@uiw/react-color';
|
|
7
|
-
import { confirmDialog } from '
|
|
7
|
+
import { confirmDialog } from '../utils/ConfirmDialog';
|
|
8
8
|
|
|
9
9
|
import CustomFetch from '../Fetch';
|
|
10
10
|
import MultiSelect from '../MultiSelect';
|
|
@@ -34,7 +34,7 @@ import {
|
|
|
34
34
|
Edit as Pencil,
|
|
35
35
|
GripVertical as ChevronVertical,
|
|
36
36
|
} from 'lucide-react';
|
|
37
|
-
import { confirmDialog } from '
|
|
37
|
+
import { confirmDialog } from '../utils/ConfirmDialog';
|
|
38
38
|
|
|
39
39
|
import 'react-toggle/style.css';
|
|
40
40
|
import 'react-datepicker/dist/react-datepicker.css';
|
|
@@ -26,11 +26,14 @@ const ChildSort = ({ item, url }) => {
|
|
|
26
26
|
|
|
27
27
|
useEffect(() => {
|
|
28
28
|
if (data.length > 0) {
|
|
29
|
+
// Only send the ID field for each item for sorting
|
|
30
|
+
const sortData = data.map(item => ({ id: item.id }));
|
|
31
|
+
|
|
29
32
|
CustomFetch(
|
|
30
33
|
`${url}/sort_update`,
|
|
31
34
|
'POST',
|
|
32
35
|
{
|
|
33
|
-
list:
|
|
36
|
+
list: sortData,
|
|
34
37
|
},
|
|
35
38
|
function (result) {
|
|
36
39
|
if (result.error === '') {
|
|
@@ -108,6 +111,8 @@ const GenericSort = ({ category = false, layout = 'full' }) => {
|
|
|
108
111
|
payload.id = paramValue;
|
|
109
112
|
} else {
|
|
110
113
|
fetchUrl = fetchUrl + '/sort_update';
|
|
114
|
+
// For the standard sort_update endpoint, only send IDs
|
|
115
|
+
payload.list = sortData.map(item => ({ id: item.id }));
|
|
111
116
|
}
|
|
112
117
|
|
|
113
118
|
if (category === false) {
|
|
@@ -116,6 +121,8 @@ const GenericSort = ({ category = false, layout = 'full' }) => {
|
|
|
116
121
|
|
|
117
122
|
if (res.data.error !== '') {
|
|
118
123
|
toast.error(String(res.data.error));
|
|
124
|
+
} else {
|
|
125
|
+
toast.success('Sort updated successfully');
|
|
119
126
|
}
|
|
120
127
|
}
|
|
121
128
|
}
|
|
@@ -56,7 +56,20 @@ const StagePopupModal = ({
|
|
|
56
56
|
url = url.replace(`{${key}}`, routeParams[key]);
|
|
57
57
|
});
|
|
58
58
|
|
|
59
|
-
|
|
59
|
+
// Prepare request data with where and fields parameters
|
|
60
|
+
const requestData = {};
|
|
61
|
+
|
|
62
|
+
// Add where parameter if available
|
|
63
|
+
if (field.where) {
|
|
64
|
+
requestData.where = field.where;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Add fields parameter if available
|
|
68
|
+
if (field.fields) {
|
|
69
|
+
requestData.fields = field.fields;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const response = await CustomFetch(url, 'POST', requestData);
|
|
60
73
|
|
|
61
74
|
if (response.data && !response.data.error) {
|
|
62
75
|
// Handle both nested (response.data.data) and direct (response.data) array formats
|