trithuc-mvc-react 3.4.6 → 3.4.7
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/api/index.js
CHANGED
|
@@ -87,49 +87,28 @@ export default api;
|
|
|
87
87
|
export const getDatasFromTable = async ({ tableName, page, pageSize, data }) => {
|
|
88
88
|
try {
|
|
89
89
|
let res;
|
|
90
|
-
|
|
90
|
+
// Trường hợp dùng tableName với MVC4
|
|
91
91
|
if (!apiUrl) {
|
|
92
|
-
// MVC4
|
|
93
92
|
res = await api.get(`${getBaseUrl()}/Admin/${tableName}/LoadData`, {
|
|
94
93
|
params: {
|
|
95
|
-
json: JSON.stringify(data
|
|
94
|
+
json: JSON.stringify(data),
|
|
96
95
|
page,
|
|
97
96
|
pageSize
|
|
98
97
|
}
|
|
99
98
|
});
|
|
100
99
|
} else {
|
|
101
|
-
// .NET Core
|
|
100
|
+
// Trường hợp dùng api .NET Core
|
|
102
101
|
res = await api.get(`${getBaseUrl()}/${apiUrl}/${tableName}`, {
|
|
103
102
|
params: {
|
|
104
103
|
PageNumber: page,
|
|
105
104
|
PageSize: pageSize,
|
|
106
|
-
...
|
|
105
|
+
...data
|
|
107
106
|
}
|
|
108
107
|
});
|
|
109
108
|
}
|
|
110
|
-
|
|
111
|
-
// 🔴 ĐIỂM FIX QUAN TRỌNG NHẤT
|
|
112
|
-
if (!res || res.data == null) {
|
|
113
|
-
// console.warn("API returned empty response:", {
|
|
114
|
-
// tableName,
|
|
115
|
-
// page,
|
|
116
|
-
// pageSize
|
|
117
|
-
// });
|
|
118
|
-
|
|
119
|
-
// ✅ RETURN OBJECT MẶC ĐỊNH – KHÔNG ĐƯỢC undefined
|
|
120
|
-
return {
|
|
121
|
-
data: [],
|
|
122
|
-
PermissionModel: {},
|
|
123
|
-
CountTrangThai: null,
|
|
124
|
-
status: false
|
|
125
|
-
};
|
|
126
|
-
}
|
|
127
|
-
|
|
128
109
|
return res.data;
|
|
129
110
|
} catch (error) {
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
// ✅ THROW → React Query onError xử lý
|
|
111
|
+
console.error("Error fetching data:", error);
|
|
133
112
|
throw error;
|
|
134
113
|
}
|
|
135
114
|
};
|
|
@@ -165,278 +144,188 @@ export const getDatasFromTableDropdownlist = async ({ tableName, page, pageSize,
|
|
|
165
144
|
|
|
166
145
|
export const getDataFromTable = async ({ tableName, id }) => {
|
|
167
146
|
try {
|
|
168
|
-
let res;
|
|
169
|
-
|
|
170
147
|
if (!apiUrl) {
|
|
171
|
-
|
|
172
|
-
res = await api.get(
|
|
173
|
-
params: {
|
|
148
|
+
const url = `${getBaseUrl()}/Admin/${tableName}/GetDetail`;
|
|
149
|
+
const res = await api.get(url, {
|
|
150
|
+
params: {
|
|
151
|
+
id
|
|
152
|
+
}
|
|
174
153
|
});
|
|
154
|
+
return res.data;
|
|
175
155
|
} else {
|
|
176
|
-
|
|
177
|
-
res = await api.get(
|
|
156
|
+
const url = `${getBaseUrl()}/${apiUrl}/${tableName}/${id}`;
|
|
157
|
+
const res = await api.get(url);
|
|
158
|
+
return res.data;
|
|
178
159
|
}
|
|
179
|
-
|
|
180
|
-
// 🔴 FIX CỐT LÕI: KHÔNG BAO GIỜ return undefined
|
|
181
|
-
if (!res || res.data == null) {
|
|
182
|
-
// console.warn("API returned empty detail:", { tableName, id });
|
|
183
|
-
|
|
184
|
-
return {
|
|
185
|
-
data: null,
|
|
186
|
-
status: false,
|
|
187
|
-
message: "Không tìm thấy dữ liệu"
|
|
188
|
-
};
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
return res.data;
|
|
192
160
|
} catch (error) {
|
|
193
|
-
|
|
194
|
-
throw error;
|
|
161
|
+
console.error("Error fetching data:", error);
|
|
162
|
+
throw error;
|
|
195
163
|
}
|
|
196
164
|
};
|
|
197
165
|
|
|
198
166
|
export const deleteDataFromTable = async ({ tableName, id }) => {
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
});
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
// 🔴 FIX QUAN TRỌNG NHẤT
|
|
218
|
-
if (!res || res.data == null) {
|
|
219
|
-
// console.warn("Delete API returned empty response:", { tableName, id });
|
|
220
|
-
|
|
221
|
-
return {
|
|
222
|
-
status: true,
|
|
223
|
-
message: "Xóa thành công"
|
|
224
|
-
};
|
|
225
|
-
}
|
|
226
|
-
|
|
167
|
+
if (!apiUrl) {
|
|
168
|
+
// Khi không có apiUrl, thực hiện DELETE đến URL cụ thể với tableName
|
|
169
|
+
const res = await api.post(`${getBaseUrl()}/Admin/${tableName}/Delete`, {
|
|
170
|
+
id
|
|
171
|
+
});
|
|
172
|
+
return res.data;
|
|
173
|
+
} else {
|
|
174
|
+
// Khi có apiUrl, chuyển id thành mảng string và thực hiện DELETE
|
|
175
|
+
const ids = Array.isArray(id) ? id : [id]; // Chuyển id thành mảng nếu chưa phải là mảng
|
|
176
|
+
const res = await api.delete(`${getBaseUrl()}/${apiUrl}/${tableName}`, {
|
|
177
|
+
data: ids, // Truyền ids như là dữ liệu của body
|
|
178
|
+
headers: {
|
|
179
|
+
"Content-Type": "application/json-patch+json" // Đảm bảo Content-Type là đúng
|
|
180
|
+
}
|
|
181
|
+
});
|
|
227
182
|
return res.data;
|
|
228
|
-
} catch (error) {
|
|
229
|
-
// console.error("Error deleting data:", error);
|
|
230
|
-
throw error; // React Query mutation onError xử lý
|
|
231
183
|
}
|
|
232
184
|
};
|
|
233
|
-
|
|
234
185
|
export const deleteMultipleDataFromTable = async ({ tableName, ids }) => {
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
headers: {
|
|
248
|
-
"Content-Type": "application/json-patch+json"
|
|
249
|
-
}
|
|
250
|
-
});
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
// 🔴 FIX QUAN TRỌNG NHẤT CHO REACT QUERY v5
|
|
254
|
-
if (!res || res.data == null) {
|
|
255
|
-
// console.warn("DeleteMultiple API returned empty response:", {
|
|
256
|
-
// tableName,
|
|
257
|
-
// ids
|
|
258
|
-
// });
|
|
259
|
-
|
|
260
|
-
return {
|
|
261
|
-
status: true,
|
|
262
|
-
message: "Xóa nhiều bản ghi thành công"
|
|
263
|
-
};
|
|
264
|
-
}
|
|
265
|
-
|
|
186
|
+
if (!apiUrl) {
|
|
187
|
+
const res = await api.post(`${getBaseUrl()}/Admin/${tableName}/DeleteMulti`, {
|
|
188
|
+
ids
|
|
189
|
+
});
|
|
190
|
+
return res.data;
|
|
191
|
+
} else {
|
|
192
|
+
const res = await api.delete(`${getBaseUrl()}/${apiUrl}/${tableName}`, {
|
|
193
|
+
data: ids, // Truyền ids như là dữ liệu của body
|
|
194
|
+
headers: {
|
|
195
|
+
"Content-Type": "application/json-patch+json" // Đảm bảo Content-Type là đúng
|
|
196
|
+
}
|
|
197
|
+
});
|
|
266
198
|
return res.data;
|
|
267
|
-
} catch (error) {
|
|
268
|
-
// console.error("Error deleting multiple data:", error);
|
|
269
|
-
throw error; // để useMutation onError xử lý
|
|
270
199
|
}
|
|
271
200
|
};
|
|
272
|
-
|
|
273
201
|
export const saveDataToTable = async ({ tableName, data }) => {
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
202
|
+
if (!apiUrl) {
|
|
203
|
+
const res = await api.post(`${getBaseUrl()}/Admin/${tableName}/SaveData`, {
|
|
204
|
+
json: JSON.stringify({ ...data })
|
|
205
|
+
});
|
|
206
|
+
return res.data;
|
|
207
|
+
} else {
|
|
208
|
+
if (!data.Id || data.Id === "0") {
|
|
209
|
+
// Khi không có ID, thực hiện POST đến apiUrl
|
|
210
|
+
const res = await api.post(`${getBaseUrl()}/${apiUrl}/${tableName}`, data);
|
|
211
|
+
return res.data;
|
|
282
212
|
} else {
|
|
283
|
-
//
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
res = await api.post(`${getBaseUrl()}/${apiUrl}/${tableName}`, data);
|
|
287
|
-
} else {
|
|
288
|
-
// UPDATE
|
|
289
|
-
res = await api.put(`${getBaseUrl()}/${apiUrl}/${tableName}/${data.Id}`, data);
|
|
290
|
-
}
|
|
213
|
+
// Khi có ID, thực hiện PUT đến apiUrl với ID trong URL
|
|
214
|
+
const res = await api.put(`${getBaseUrl()}/${apiUrl}/${tableName}/${data.Id}`, data);
|
|
215
|
+
return res.data;
|
|
291
216
|
}
|
|
292
|
-
|
|
293
|
-
// 🔴 FIX BẮT BUỘC CHO REACT QUERY v5
|
|
294
|
-
if (!res || res.data == null) {
|
|
295
|
-
// console.warn("SaveData API returned empty response:", {
|
|
296
|
-
// tableName,
|
|
297
|
-
// data
|
|
298
|
-
// });
|
|
299
|
-
|
|
300
|
-
return {
|
|
301
|
-
status: true,
|
|
302
|
-
message: "Lưu dữ liệu thành công"
|
|
303
|
-
};
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
return res.data;
|
|
307
|
-
} catch (error) {
|
|
308
|
-
// console.error("Error saving data:", error);
|
|
309
|
-
throw error; // để useMutation onError xử lý
|
|
310
217
|
}
|
|
311
218
|
};
|
|
312
|
-
|
|
313
219
|
export const changeStatusDataToTable = async ({ tableName, id, fieldName }) => {
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
// MVC4
|
|
319
|
-
res = await api.post(`${getBaseUrl()}/Admin/${tableName}/ChangeStatus`, { id });
|
|
320
|
-
} else {
|
|
321
|
-
// .NET Core
|
|
322
|
-
res = await api.post(
|
|
323
|
-
`${getBaseUrl()}/${apiUrl}/${tableName}/ChangeStatus`,
|
|
324
|
-
fieldName ? { id, fieldName } : { id }
|
|
325
|
-
);
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
// 🔴 FIX BẮT BUỘC CHO REACT QUERY v5
|
|
329
|
-
if (!res || res.data == null) {
|
|
330
|
-
// console.warn("ChangeStatus API returned empty response", {
|
|
331
|
-
// tableName,
|
|
332
|
-
// id,
|
|
333
|
-
// fieldName
|
|
334
|
-
// });
|
|
335
|
-
|
|
336
|
-
return {
|
|
337
|
-
status: true,
|
|
338
|
-
message: "Thay đổi trạng thái thành công"
|
|
339
|
-
};
|
|
340
|
-
}
|
|
341
|
-
|
|
220
|
+
if (!apiUrl) {
|
|
221
|
+
const res = await api.post(`${getBaseUrl()}/Admin/${tableName}/ChangeStatus`, {
|
|
222
|
+
id
|
|
223
|
+
});
|
|
342
224
|
return res.data;
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
|
|
225
|
+
} else {
|
|
226
|
+
try {
|
|
227
|
+
if (fieldName) {
|
|
228
|
+
const res = await api.post(
|
|
229
|
+
`${getBaseUrl()}/${apiUrl ? `${apiUrl}/` : "Admin/"}${tableName}/ChangeStatus`,
|
|
230
|
+
{
|
|
231
|
+
id,
|
|
232
|
+
fieldName
|
|
233
|
+
},
|
|
234
|
+
{
|
|
235
|
+
headers: {
|
|
236
|
+
"Content-Type": "application/json-patch+json" // Đảm bảo Content-Type là đúng
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
);
|
|
240
|
+
return res.data;
|
|
241
|
+
} else {
|
|
242
|
+
const res = await api.post(`${getBaseUrl()}/${apiUrl ? `${apiUrl}/` : "Admin/"}${tableName}/ChangeStatus`, id, {
|
|
243
|
+
headers: {
|
|
244
|
+
"Content-Type": "application/json-patch+json" // Đảm bảo Content-Type là đúng
|
|
245
|
+
}
|
|
246
|
+
});
|
|
247
|
+
return res.data;
|
|
248
|
+
}
|
|
249
|
+
} catch (error) {
|
|
250
|
+
console.error("Error changing status:", error);
|
|
251
|
+
// Xử lý lỗi tùy thuộc vào nhu cầu của bạn, có thể ném ra hoặc trả về giá trị mặc định
|
|
252
|
+
throw error; // Hoặc return { success: false, message: error.message };
|
|
253
|
+
}
|
|
346
254
|
}
|
|
347
255
|
};
|
|
348
256
|
|
|
349
257
|
export const exportExcel = async ({ tableName, data }) => {
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
258
|
+
if (!apiUrl) {
|
|
259
|
+
const res = await api.get(`${getBaseUrl()}/Admin/${tableName}/ExportData`, {
|
|
260
|
+
params: {
|
|
261
|
+
json: JSON.stringify(data)
|
|
262
|
+
}
|
|
263
|
+
});
|
|
264
|
+
return res.data;
|
|
265
|
+
} else {
|
|
266
|
+
const res = await api.get(
|
|
267
|
+
`${getBaseUrl()}/${apiUrl}/${tableName}/ExportData`,
|
|
268
|
+
{
|
|
356
269
|
params: {
|
|
357
|
-
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
// 🔴 BẮT BUỘC: luôn return value
|
|
370
|
-
if (!res || !res.data) {
|
|
371
|
-
throw new Error("Export Excel failed: empty response");
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
return res.data; // Blob
|
|
375
|
-
} catch (error) {
|
|
376
|
-
// console.error("Error exporting excel:", error);
|
|
377
|
-
throw error;
|
|
270
|
+
...data
|
|
271
|
+
}
|
|
272
|
+
},
|
|
273
|
+
{
|
|
274
|
+
headers: {
|
|
275
|
+
"Content-Type": "application/json-patch+json" // Đảm bảo Content-Type là đúng
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
);
|
|
279
|
+
return res.data;
|
|
378
280
|
}
|
|
379
281
|
};
|
|
380
282
|
|
|
381
283
|
export const uploadFile = async (formData) => {
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
284
|
+
// 1. Lấy URL hiện tại
|
|
285
|
+
const currentUrl = window.location.href;
|
|
286
|
+
|
|
287
|
+
// 2. Append vào formData
|
|
288
|
+
formData.append("currentUrl", currentUrl);
|
|
289
|
+
|
|
290
|
+
// 3. Log tất cả key-value trong FormData để kiểm tra
|
|
291
|
+
// console.log("===== FormData Contents =====");
|
|
292
|
+
// formData.forEach((value, key) => {
|
|
293
|
+
// console.log(key, value);
|
|
294
|
+
// });
|
|
295
|
+
if (!apiUrl) {
|
|
296
|
+
const res = await api.post(`${getBaseUrl()}/Handler/fileUploader.ashx`, formData, {
|
|
297
|
+
headers: {
|
|
298
|
+
"Content-Type": "multipart/form-data"
|
|
299
|
+
}
|
|
300
|
+
});
|
|
301
|
+
return res.data;
|
|
302
|
+
} else {
|
|
303
|
+
const res = await api.post(`${getBaseUrl()}/${apiUrl}/FileUploader`, formData, {
|
|
304
|
+
headers: {
|
|
305
|
+
"Content-Type": "multipart/form-data"
|
|
306
|
+
}
|
|
387
307
|
});
|
|
388
|
-
|
|
389
|
-
// Append currentUrl 1 lần
|
|
390
|
-
fd.append("currentUrl", window.location.href);
|
|
391
|
-
|
|
392
|
-
let res;
|
|
393
|
-
|
|
394
|
-
if (!apiUrl) {
|
|
395
|
-
// MVC / ashx
|
|
396
|
-
res = await api.post(`${getBaseUrl()}/Handler/fileUploader.ashx`, fd);
|
|
397
|
-
} else {
|
|
398
|
-
// .NET Core
|
|
399
|
-
res = await api.post(`${getBaseUrl()}/${apiUrl}/FileUploader`, fd);
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
// 🔴 FIX BẮT BUỘC REACT QUERY v5
|
|
403
|
-
if (!res || res.data == null) {
|
|
404
|
-
// console.warn("Upload API returned empty response");
|
|
405
|
-
|
|
406
|
-
return {
|
|
407
|
-
status: true,
|
|
408
|
-
message: "Upload thành công"
|
|
409
|
-
};
|
|
410
|
-
}
|
|
411
|
-
|
|
412
308
|
return res.data;
|
|
413
|
-
} catch (error) {
|
|
414
|
-
// console.error("Error uploading file:", error);
|
|
415
|
-
throw error;
|
|
416
309
|
}
|
|
417
310
|
};
|
|
418
311
|
|
|
419
|
-
export const huongDan = async ({ data }
|
|
420
|
-
|
|
421
|
-
// Không có apiUrl → luôn return array
|
|
422
|
-
if (!apiUrl) {
|
|
423
|
-
return [];
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
const res = await api.get(`${getBaseUrl()}/${apiUrl}/HuongDanSuDung/Dropdownlist`, {
|
|
427
|
-
params: { ...data }
|
|
428
|
-
});
|
|
429
|
-
|
|
430
|
-
// 🔴 BẮT BUỘC: không để undefined
|
|
431
|
-
if (!res || res.data == null) {
|
|
432
|
-
// console.warn("HuongDan API returned empty response");
|
|
433
|
-
return [];
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
// Đảm bảo luôn là array
|
|
437
|
-
return Array.isArray(res.data) ? res.data : [];
|
|
438
|
-
} catch (error) {
|
|
439
|
-
// console.error("Error fetching huongDan:", error);
|
|
312
|
+
export const huongDan = async ({ data }) => {
|
|
313
|
+
if (!apiUrl) {
|
|
440
314
|
return [];
|
|
315
|
+
} else {
|
|
316
|
+
const res = await api.get(
|
|
317
|
+
`${getBaseUrl()}/${apiUrl}/HuongDanSuDung/Dropdownlist`,
|
|
318
|
+
{
|
|
319
|
+
params: {
|
|
320
|
+
...data
|
|
321
|
+
}
|
|
322
|
+
},
|
|
323
|
+
{
|
|
324
|
+
headers: {
|
|
325
|
+
"Content-Type": "application/json-patch+json" // Đảm bảo Content-Type là đúng
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
);
|
|
329
|
+
return res.data;
|
|
441
330
|
}
|
|
442
331
|
};
|
|
@@ -1,26 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
Grid,
|
|
3
|
-
Table,
|
|
4
|
-
TableBody,
|
|
5
|
-
TableCell,
|
|
6
|
-
TableContainer,
|
|
7
|
-
TableRow,
|
|
8
|
-
Typography,
|
|
9
|
-
useMediaQuery,
|
|
10
|
-
useTheme
|
|
11
|
-
} from "@mui/material";
|
|
1
|
+
import { Grid, Table, TableBody, TableCell, TableContainer, TableRow, Typography, useMediaQuery, useTheme } from "@mui/material";
|
|
12
2
|
import { useEffect, useMemo, useState } from "react";
|
|
13
3
|
import TablePaginationCustom from "../table/TablePagination";
|
|
14
4
|
|
|
15
5
|
import { useConfirm } from "material-ui-confirm";
|
|
16
|
-
import { useMutation, useQuery, useQueryClient } from "
|
|
6
|
+
import { useMutation, useQuery, useQueryClient } from "react-query";
|
|
17
7
|
import { toast } from "react-toastify";
|
|
18
|
-
import {
|
|
19
|
-
changeStatusDataToTable,
|
|
20
|
-
deleteDataFromTable,
|
|
21
|
-
deleteMultipleDataFromTable,
|
|
22
|
-
getDatasFromTable
|
|
23
|
-
} from "../../api";
|
|
8
|
+
import { changeStatusDataToTable, deleteDataFromTable, deleteMultipleDataFromTable, getDatasFromTable } from "../../api";
|
|
24
9
|
import TableRowsLoader from "../table/TableRowsLoader";
|
|
25
10
|
import { TableHead } from "./TableHead";
|
|
26
11
|
|
|
@@ -81,12 +66,11 @@ const DataTable = ({ multipleActions = [], page, setPage = () => {}, disableEdit
|
|
|
81
66
|
setOrderBy(property);
|
|
82
67
|
};
|
|
83
68
|
|
|
84
|
-
// 1️⃣ Xoá defaultQueryOptions khỏi useQuery
|
|
85
69
|
const { data, isLoading } = useQuery({
|
|
86
70
|
queryKey: [tableName, page, rowsPerPage, dataSearch, order, orderBy],
|
|
87
71
|
queryFn: () =>
|
|
88
72
|
getDatasFromTable({
|
|
89
|
-
tableName,
|
|
73
|
+
tableName: tableName,
|
|
90
74
|
page: page + 1,
|
|
91
75
|
pageSize: rowsPerPage,
|
|
92
76
|
data: {
|
|
@@ -97,76 +81,96 @@ const DataTable = ({ multipleActions = [], page, setPage = () => {}, disableEdit
|
|
|
97
81
|
})
|
|
98
82
|
}
|
|
99
83
|
}),
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
PermissionModel: {},
|
|
104
|
-
CountTrangThai: null,
|
|
105
|
-
status: false
|
|
106
|
-
}),
|
|
107
|
-
staleTime: 60_000,
|
|
108
|
-
gcTime: 30 * 60_000, // ✅ v5 (cacheTime → gcTime)
|
|
109
|
-
refetchOnWindowFocus: true,
|
|
110
|
-
refetchInterval: 30_000,
|
|
111
|
-
onSuccess: ({ PermissionModel = {}, CountTrangThai, status }) => {
|
|
84
|
+
defaultQueryOptions,
|
|
85
|
+
// keepPreviousData: true,
|
|
86
|
+
onSuccess: ({ PermissionModel, CountTrangThai, status }) => {
|
|
112
87
|
if (dataSearch?.TrangThaiXuLy !== undefined) {
|
|
113
|
-
PermissionModel.TrangThaiXuLy = dataSearch
|
|
88
|
+
PermissionModel.TrangThaiXuLy = dataSearch?.TrangThaiXuLy;
|
|
114
89
|
}
|
|
115
|
-
|
|
116
90
|
setPermission(PermissionModel);
|
|
117
91
|
|
|
118
92
|
if (CountTrangThai) {
|
|
119
93
|
const keyCounter = `${tableName}_${userId}_TrangThaiXuLyCounter`;
|
|
120
94
|
localStorage.setItem(keyCounter, JSON.stringify(CountTrangThai));
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
});
|
|
95
|
+
// 👉 Invalidate query để Tab reload ngay
|
|
96
|
+
queryClient.invalidateQueries({ queryKey: [tableName, "CountAllTrangThaiXuly"] });
|
|
124
97
|
}
|
|
125
98
|
|
|
126
99
|
if (status) {
|
|
127
|
-
|
|
100
|
+
// console.log("LOAD LAI PermissionModel");
|
|
101
|
+
// Cuộn lên đầu trang khi tải dữ liệu thành công
|
|
102
|
+
window.scrollTo({
|
|
103
|
+
top: 0, // Vị trí pixel muốn cuộn tới
|
|
104
|
+
behavior: "smooth" // Tùy chọn cuộn mượt
|
|
105
|
+
});
|
|
128
106
|
}
|
|
129
107
|
}
|
|
130
108
|
});
|
|
131
109
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
110
|
+
const changeStatusMutation = useMutation(changeStatusDataToTable, {
|
|
111
|
+
onSuccess: ({ status = false, message = " Có lỗi xảy ra !" }) => {
|
|
112
|
+
if (URL_APPLICATION_API) {
|
|
113
|
+
toast.success(message);
|
|
114
|
+
} else {
|
|
115
|
+
toast.success("Thay đổi trạng thái thành công !");
|
|
116
|
+
}
|
|
137
117
|
queryClient.invalidateQueries({ queryKey: [tableName] });
|
|
138
118
|
queryClient.invalidateQueries({ queryKey: [tableName, "CountAllTrangThaiXuly"] });
|
|
139
119
|
},
|
|
140
120
|
onError: (error) => {
|
|
141
|
-
|
|
142
|
-
|
|
121
|
+
if (error.response && error.response.data) {
|
|
122
|
+
const message = error.response.data.message;
|
|
123
|
+
toast.error(message);
|
|
124
|
+
} else {
|
|
125
|
+
// Nếu lỗi không theo định dạng mong đợi, hiển thị thông tin lỗi chung
|
|
126
|
+
toast.error("Đã xảy ra lỗi không mong muốn.");
|
|
127
|
+
}
|
|
143
128
|
}
|
|
144
129
|
});
|
|
130
|
+
const deleteMutation = useMutation(deleteDataFromTable, {
|
|
131
|
+
onSuccess: ({ status = false, message = " Có lỗi xảy ra !" }) => {
|
|
132
|
+
if (status) {
|
|
133
|
+
if (URL_APPLICATION_API) {
|
|
134
|
+
toast.success(message);
|
|
135
|
+
} else {
|
|
136
|
+
toast.success("Xóa thành công !");
|
|
137
|
+
}
|
|
138
|
+
} else {
|
|
139
|
+
toast.error(" Có lỗi xảy ra !");
|
|
140
|
+
}
|
|
145
141
|
|
|
146
|
-
const deleteMutation = useMutation({
|
|
147
|
-
mutationFn: deleteDataFromTable,
|
|
148
|
-
onSuccess: ({ status = false, message = "Có lỗi xảy ra !" }) => {
|
|
149
|
-
toast[status ? "success" : "error"](status ? message : "Có lỗi xảy ra !");
|
|
150
142
|
queryClient.invalidateQueries({ queryKey: [tableName] });
|
|
151
143
|
queryClient.invalidateQueries({ queryKey: [tableName, "CountAllTrangThaiXuly"] });
|
|
152
144
|
},
|
|
153
145
|
onError: (error) => {
|
|
154
|
-
|
|
155
|
-
|
|
146
|
+
if (error.response && error.response.data) {
|
|
147
|
+
const message = error.response.data.message;
|
|
148
|
+
toast.error(message);
|
|
149
|
+
} else {
|
|
150
|
+
// Nếu lỗi không theo định dạng mong đợi, hiển thị thông tin lỗi chung
|
|
151
|
+
toast.error("Đã xảy ra lỗi không mong muốn.");
|
|
152
|
+
}
|
|
156
153
|
}
|
|
157
154
|
});
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
155
|
+
const deleteMultipleMutation = useMutation(deleteMultipleDataFromTable, {
|
|
156
|
+
onSuccess: ({ status = false, message = " Có lỗi xảy ra !" }) => {
|
|
157
|
+
if (URL_APPLICATION_API) {
|
|
158
|
+
toast.success(message);
|
|
159
|
+
} else {
|
|
160
|
+
toast.success("Xóa thành công !");
|
|
161
|
+
}
|
|
163
162
|
setSelectedItems([]);
|
|
164
163
|
queryClient.invalidateQueries({ queryKey: [tableName] });
|
|
165
164
|
queryClient.invalidateQueries({ queryKey: [tableName, "CountAllTrangThaiXuly"] });
|
|
166
165
|
},
|
|
167
166
|
onError: (error) => {
|
|
168
|
-
|
|
169
|
-
|
|
167
|
+
if (error.response && error.response.data) {
|
|
168
|
+
const message = error.response.data.message;
|
|
169
|
+
toast.error(message);
|
|
170
|
+
} else {
|
|
171
|
+
// Nếu lỗi không theo định dạng mong đợi, hiển thị thông tin lỗi chung
|
|
172
|
+
toast.error("Đã xảy ra lỗi không mong muốn.");
|
|
173
|
+
}
|
|
170
174
|
}
|
|
171
175
|
});
|
|
172
176
|
// Hàm gọi khi người dùng muốn xóa một bản ghi
|
|
@@ -366,12 +370,7 @@ const DataTable = ({ multipleActions = [], page, setPage = () => {}, disableEdit
|
|
|
366
370
|
/>
|
|
367
371
|
</Grid>
|
|
368
372
|
</Grid>
|
|
369
|
-
<DeleteConfirmationDialog
|
|
370
|
-
open={openDialog}
|
|
371
|
-
onClose={handleDialogClose}
|
|
372
|
-
onConfirm={handleDialogClose}
|
|
373
|
-
id={deleteId}
|
|
374
|
-
/>
|
|
373
|
+
<DeleteConfirmationDialog open={openDialog} onClose={handleDialogClose} onConfirm={handleDialogClose} id={deleteId} />
|
|
375
374
|
<DeleteMultipleConfirmationDialog
|
|
376
375
|
open={openDeleteMultipleDialog}
|
|
377
376
|
onClose={handleDeleteMultipleDialogClose}
|
|
@@ -1,26 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
Grid,
|
|
3
|
-
Table,
|
|
4
|
-
TableBody,
|
|
5
|
-
TableCell,
|
|
6
|
-
TableContainer,
|
|
7
|
-
TableRow,
|
|
8
|
-
Typography,
|
|
9
|
-
useMediaQuery,
|
|
10
|
-
useTheme
|
|
11
|
-
} from "@mui/material";
|
|
1
|
+
import { Grid, Table, TableBody, TableCell, TableContainer, TableRow, Typography, useMediaQuery, useTheme } from "@mui/material";
|
|
12
2
|
import { useEffect, useMemo, useState } from "react";
|
|
13
3
|
import TablePaginationCustom from "../table/TablePagination";
|
|
14
4
|
|
|
15
5
|
import { useConfirm } from "material-ui-confirm";
|
|
16
|
-
import { useMutation, useQuery, useQueryClient } from "
|
|
6
|
+
import { useMutation, useQuery, useQueryClient } from "react-query";
|
|
17
7
|
import { toast } from "react-toastify";
|
|
18
|
-
import {
|
|
19
|
-
changeStatusDataToTable,
|
|
20
|
-
deleteDataFromTable,
|
|
21
|
-
deleteMultipleDataFromTable,
|
|
22
|
-
getDatasFromTable
|
|
23
|
-
} from "../../api";
|
|
8
|
+
import { changeStatusDataToTable, deleteDataFromTable, deleteMultipleDataFromTable, getDatasFromTable } from "../../api";
|
|
24
9
|
import TableRowsLoader from "../table/TableRowsLoader";
|
|
25
10
|
|
|
26
11
|
import { URL_APPLICATION_API } from "@/constants";
|
|
@@ -80,12 +65,11 @@ const DataTableSM = ({ multipleActions = [], page, setPage = () => {}, disableEd
|
|
|
80
65
|
setOrderBy(property);
|
|
81
66
|
};
|
|
82
67
|
|
|
83
|
-
// 1️⃣ Xoá defaultQueryOptions khỏi useQuery
|
|
84
68
|
const { data, isLoading } = useQuery({
|
|
85
69
|
queryKey: [tableName, page, rowsPerPage, dataSearch, order, orderBy],
|
|
86
70
|
queryFn: () =>
|
|
87
71
|
getDatasFromTable({
|
|
88
|
-
tableName,
|
|
72
|
+
tableName: tableName,
|
|
89
73
|
page: page + 1,
|
|
90
74
|
pageSize: rowsPerPage,
|
|
91
75
|
data: {
|
|
@@ -96,76 +80,107 @@ const DataTableSM = ({ multipleActions = [], page, setPage = () => {}, disableEd
|
|
|
96
80
|
})
|
|
97
81
|
}
|
|
98
82
|
}),
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
PermissionModel: {},
|
|
103
|
-
CountTrangThai: null,
|
|
104
|
-
status: false
|
|
105
|
-
}),
|
|
106
|
-
staleTime: 60_000,
|
|
107
|
-
gcTime: 30 * 60_000, // ✅ v5 (cacheTime → gcTime)
|
|
108
|
-
refetchOnWindowFocus: true,
|
|
109
|
-
refetchInterval: 30_000,
|
|
110
|
-
onSuccess: ({ PermissionModel = {}, CountTrangThai, status }) => {
|
|
83
|
+
defaultQueryOptions,
|
|
84
|
+
// keepPreviousData: true,
|
|
85
|
+
onSuccess: ({ PermissionModel, CountTrangThai, status }) => {
|
|
111
86
|
if (dataSearch?.TrangThaiXuLy !== undefined) {
|
|
112
|
-
PermissionModel.TrangThaiXuLy = dataSearch
|
|
87
|
+
PermissionModel.TrangThaiXuLy = dataSearch?.TrangThaiXuLy;
|
|
113
88
|
}
|
|
114
|
-
|
|
115
89
|
setPermission(PermissionModel);
|
|
116
90
|
|
|
117
91
|
if (CountTrangThai) {
|
|
118
92
|
const keyCounter = `${tableName}_${userId}_TrangThaiXuLyCounter`;
|
|
119
93
|
localStorage.setItem(keyCounter, JSON.stringify(CountTrangThai));
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
});
|
|
94
|
+
// 👉 Invalidate query để Tab reload ngay
|
|
95
|
+
queryClient.invalidateQueries({ queryKey: [tableName, "CountAllTrangThaiXuly"] });
|
|
123
96
|
}
|
|
124
97
|
|
|
125
98
|
if (status) {
|
|
126
|
-
|
|
99
|
+
// console.log("LOAD LAI PermissionModel");
|
|
100
|
+
// Cuộn lên đầu trang khi tải dữ liệu thành công
|
|
101
|
+
window.scrollTo({
|
|
102
|
+
top: 0, // Vị trí pixel muốn cuộn tới
|
|
103
|
+
behavior: "smooth" // Tùy chọn cuộn mượt
|
|
104
|
+
});
|
|
127
105
|
}
|
|
128
106
|
}
|
|
129
107
|
});
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
108
|
+
const changeStatusMutation = useMutation(changeStatusDataToTable, {
|
|
109
|
+
onSuccess: ({ status = false, message = " Có lỗi xảy ra !" }) => {
|
|
110
|
+
if (URL_APPLICATION_API) {
|
|
111
|
+
toast.success(message);
|
|
112
|
+
} else {
|
|
113
|
+
toast.success("Thay đổi trạng thái thành công !");
|
|
114
|
+
}
|
|
136
115
|
queryClient.invalidateQueries({ queryKey: [tableName] });
|
|
137
116
|
queryClient.invalidateQueries({ queryKey: [tableName, "CountAllTrangThaiXuly"] });
|
|
138
117
|
},
|
|
139
118
|
onError: (error) => {
|
|
140
|
-
|
|
141
|
-
|
|
119
|
+
if (error.response && error.response.data && error.response.data.errors) {
|
|
120
|
+
const errors = error.response.data.errors;
|
|
121
|
+
// Chuyển đổi đối tượng lỗi thành chuỗi để hiển thị
|
|
122
|
+
const errorMessages = Object.entries(errors)
|
|
123
|
+
.map(([field, messages]) => `${field}: ${messages.join(", ")}`)
|
|
124
|
+
.join("\n");
|
|
125
|
+
toast.error(errorMessages);
|
|
126
|
+
} else {
|
|
127
|
+
// Nếu lỗi không theo định dạng mong đợi, hiển thị thông tin lỗi chung
|
|
128
|
+
toast.error("Đã xảy ra lỗi không mong muốn.");
|
|
129
|
+
}
|
|
142
130
|
}
|
|
143
131
|
});
|
|
132
|
+
const deleteMutation = useMutation(deleteDataFromTable, {
|
|
133
|
+
onSuccess: ({ status = false, message = " Có lỗi xảy ra !" }) => {
|
|
134
|
+
if (status) {
|
|
135
|
+
if (URL_APPLICATION_API) {
|
|
136
|
+
toast.success(message);
|
|
137
|
+
} else {
|
|
138
|
+
toast.success("Xóa thành công !");
|
|
139
|
+
}
|
|
140
|
+
} else {
|
|
141
|
+
toast.error(" Có lỗi xảy ra !");
|
|
142
|
+
}
|
|
144
143
|
|
|
145
|
-
const deleteMutation = useMutation({
|
|
146
|
-
mutationFn: deleteDataFromTable,
|
|
147
|
-
onSuccess: ({ status = false, message = "Có lỗi xảy ra !" }) => {
|
|
148
|
-
toast[status ? "success" : "error"](status ? message : "Có lỗi xảy ra !");
|
|
149
144
|
queryClient.invalidateQueries({ queryKey: [tableName] });
|
|
150
145
|
queryClient.invalidateQueries({ queryKey: [tableName, "CountAllTrangThaiXuly"] });
|
|
151
146
|
},
|
|
152
147
|
onError: (error) => {
|
|
153
|
-
|
|
154
|
-
|
|
148
|
+
if (error.response && error.response.data && error.response.data.errors) {
|
|
149
|
+
const errors = error.response.data.errors;
|
|
150
|
+
// Chuyển đổi đối tượng lỗi thành chuỗi để hiển thị
|
|
151
|
+
const errorMessages = Object.entries(errors)
|
|
152
|
+
.map(([field, messages]) => `${field}: ${messages.join(", ")}`)
|
|
153
|
+
.join("\n");
|
|
154
|
+
toast.error(errorMessages);
|
|
155
|
+
} else {
|
|
156
|
+
// Nếu lỗi không theo định dạng mong đợi, hiển thị thông tin lỗi chung
|
|
157
|
+
toast.error("Đã xảy ra lỗi không mong muốn.");
|
|
158
|
+
}
|
|
155
159
|
}
|
|
156
160
|
});
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
161
|
+
const deleteMultipleMutation = useMutation(deleteMultipleDataFromTable, {
|
|
162
|
+
onSuccess: ({ status = false, message = " Có lỗi xảy ra !" }) => {
|
|
163
|
+
if (URL_APPLICATION_API) {
|
|
164
|
+
toast.success(message);
|
|
165
|
+
} else {
|
|
166
|
+
toast.success("Xóa thành công !");
|
|
167
|
+
}
|
|
162
168
|
setSelectedItems([]);
|
|
163
169
|
queryClient.invalidateQueries({ queryKey: [tableName] });
|
|
164
170
|
queryClient.invalidateQueries({ queryKey: [tableName, "CountAllTrangThaiXuly"] });
|
|
165
171
|
},
|
|
166
172
|
onError: (error) => {
|
|
167
|
-
|
|
168
|
-
|
|
173
|
+
if (error.response && error.response.data && error.response.data.errors) {
|
|
174
|
+
const errors = error.response.data.errors;
|
|
175
|
+
// Chuyển đổi đối tượng lỗi thành chuỗi để hiển thị
|
|
176
|
+
const errorMessages = Object.entries(errors)
|
|
177
|
+
.map(([field, messages]) => `${field}: ${messages.join(", ")}`)
|
|
178
|
+
.join("\n");
|
|
179
|
+
toast.error(errorMessages);
|
|
180
|
+
} else {
|
|
181
|
+
// Nếu lỗi không theo định dạng mong đợi, hiển thị thông tin lỗi chung
|
|
182
|
+
toast.error("Đã xảy ra lỗi không mong muốn.");
|
|
183
|
+
}
|
|
169
184
|
}
|
|
170
185
|
});
|
|
171
186
|
// Hàm gọi khi người dùng muốn xóa một bản ghi
|
|
@@ -394,12 +409,7 @@ const DataTableSM = ({ multipleActions = [], page, setPage = () => {}, disableEd
|
|
|
394
409
|
/>
|
|
395
410
|
</Grid>
|
|
396
411
|
</Grid>
|
|
397
|
-
<DeleteConfirmationDialog
|
|
398
|
-
open={openDialog}
|
|
399
|
-
onClose={handleDialogClose}
|
|
400
|
-
onConfirm={handleDialogClose}
|
|
401
|
-
id={deleteId}
|
|
402
|
-
/>
|
|
412
|
+
<DeleteConfirmationDialog open={openDialog} onClose={handleDialogClose} onConfirm={handleDialogClose} id={deleteId} />
|
|
403
413
|
<DeleteMultipleConfirmationDialog
|
|
404
414
|
open={openDeleteMultipleDialog}
|
|
405
415
|
onClose={handleDeleteMultipleDialogClose}
|
|
@@ -8,7 +8,7 @@ import { useEffect } from "react";
|
|
|
8
8
|
|
|
9
9
|
import { URL_APPLICATION, URL_APPLICATION_API } from "@/constants";
|
|
10
10
|
import { yupResolver } from "@hookform/resolvers/yup";
|
|
11
|
-
import { useMutation, useQueryClient } from "
|
|
11
|
+
import { useMutation, useQueryClient } from "react-query";
|
|
12
12
|
import { toast } from "react-toastify";
|
|
13
13
|
import { saveDataToTable } from "../../api";
|
|
14
14
|
import FormField from "./FormField";
|
|
@@ -91,30 +91,35 @@ function EditorForm({ fields, submitRef }) {
|
|
|
91
91
|
}
|
|
92
92
|
}, [selectedEditItem]);
|
|
93
93
|
|
|
94
|
-
const saveMutation = useMutation({
|
|
95
|
-
mutationFn: saveDataToTable,
|
|
94
|
+
const saveMutation = useMutation(saveDataToTable, {
|
|
96
95
|
onSuccess: ({ status = false, message = " Có lỗi xảy ra !" }, { data: { Id } }) => {
|
|
97
96
|
if (status) {
|
|
98
|
-
|
|
99
|
-
|
|
97
|
+
if (URL_APPLICATION_API) {
|
|
98
|
+
toast.success(message);
|
|
99
|
+
} else {
|
|
100
|
+
toast.success(Id == 0 ? "Thêm thành công!" : "Cập nhật thành công!");
|
|
101
|
+
}
|
|
102
|
+
queryClient.invalidateQueries(tableName);
|
|
100
103
|
setOpenEditorDialog(false);
|
|
101
104
|
} else {
|
|
105
|
+
console.log(status, Id);
|
|
102
106
|
toast.error(message);
|
|
103
107
|
}
|
|
104
108
|
},
|
|
105
109
|
onError: (error) => {
|
|
106
|
-
|
|
107
|
-
|
|
110
|
+
if (error.response && error.response.data && error.response.data.errors) {
|
|
111
|
+
const errors = error.response.data.errors;
|
|
112
|
+
// Chuyển đổi đối tượng lỗi thành chuỗi để hiển thị
|
|
108
113
|
const errorMessages = Object.entries(errors)
|
|
109
114
|
.map(([field, messages]) => `${field}: ${messages.join(", ")}`)
|
|
110
115
|
.join("\n");
|
|
111
116
|
toast.error(errorMessages);
|
|
112
117
|
} else {
|
|
118
|
+
// Nếu lỗi không theo định dạng mong đợi, hiển thị thông tin lỗi chung
|
|
113
119
|
toast.error("Đã xảy ra lỗi không mong muốn.");
|
|
114
120
|
}
|
|
115
121
|
}
|
|
116
122
|
});
|
|
117
|
-
|
|
118
123
|
const onSubmit = (data) => {
|
|
119
124
|
fields.reduce((data, { type, field, datas, keyValueLabel, keyValue, keyLabel }) => {
|
|
120
125
|
if (type === "date") {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "trithuc-mvc-react",
|
|
3
|
-
"version": "3.4.
|
|
3
|
+
"version": "3.4.7",
|
|
4
4
|
"main": "index.js",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"test": "echo \"Error: no test specified\" && exit 1"
|
|
@@ -17,7 +17,6 @@
|
|
|
17
17
|
"@emotion/styled": "^11.14.0",
|
|
18
18
|
"@hookform/resolvers": "^5.0.1",
|
|
19
19
|
"@iconify/react": "^5.2.1",
|
|
20
|
-
"@tanstack/react-query": "^5.90.12",
|
|
21
20
|
"axios": "^1.8.4",
|
|
22
21
|
"date-fns": "^4.1.0",
|
|
23
22
|
"lodash": "^4.17.21",
|
|
@@ -48,6 +47,7 @@
|
|
|
48
47
|
"react": ">=16",
|
|
49
48
|
"react-dom": ">=16",
|
|
50
49
|
"react-hook-form": "^7.55.0",
|
|
50
|
+
"react-query": "^3.39.3",
|
|
51
51
|
"react-toastify": "^11.0.5"
|
|
52
52
|
}
|
|
53
53
|
}
|