trithuc-mvc-react 3.4.4 → 3.4.6

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,28 +87,49 @@ export default api;
87
87
  export const getDatasFromTable = async ({ tableName, page, pageSize, data }) => {
88
88
  try {
89
89
  let res;
90
- // Trường hợp dùng tableName với MVC4
90
+
91
91
  if (!apiUrl) {
92
+ // MVC4
92
93
  res = await api.get(`${getBaseUrl()}/Admin/${tableName}/LoadData`, {
93
94
  params: {
94
- json: JSON.stringify(data),
95
+ json: JSON.stringify(data ?? {}),
95
96
  page,
96
97
  pageSize
97
98
  }
98
99
  });
99
100
  } else {
100
- // Trường hợp dùng api .NET Core
101
+ // .NET Core
101
102
  res = await api.get(`${getBaseUrl()}/${apiUrl}/${tableName}`, {
102
103
  params: {
103
104
  PageNumber: page,
104
105
  PageSize: pageSize,
105
- ...data
106
+ ...(data ?? {})
106
107
  }
107
108
  });
108
109
  }
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
+
109
128
  return res.data;
110
129
  } catch (error) {
111
- console.error("Error fetching data:", error);
130
+ // console.error("Error fetching data:", error);
131
+
132
+ // ✅ THROW → React Query onError xử lý
112
133
  throw error;
113
134
  }
114
135
  };
@@ -144,188 +165,278 @@ export const getDatasFromTableDropdownlist = async ({ tableName, page, pageSize,
144
165
 
145
166
  export const getDataFromTable = async ({ tableName, id }) => {
146
167
  try {
168
+ let res;
169
+
147
170
  if (!apiUrl) {
148
- const url = `${getBaseUrl()}/Admin/${tableName}/GetDetail`;
149
- const res = await api.get(url, {
150
- params: {
151
- id
152
- }
171
+ // MVC4
172
+ res = await api.get(`${getBaseUrl()}/Admin/${tableName}/GetDetail`, {
173
+ params: { id }
153
174
  });
154
- return res.data;
155
175
  } else {
156
- const url = `${getBaseUrl()}/${apiUrl}/${tableName}/${id}`;
157
- const res = await api.get(url);
158
- return res.data;
176
+ // .NET Core
177
+ res = await api.get(`${getBaseUrl()}/${apiUrl}/${tableName}/${id}`);
159
178
  }
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;
160
192
  } catch (error) {
161
- console.error("Error fetching data:", error);
162
- throw error;
193
+ // console.error("Error fetching detail:", error);
194
+ throw error; // React Query onError xử lý
163
195
  }
164
196
  };
165
197
 
166
198
  export const deleteDataFromTable = async ({ tableName, id }) => {
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
- });
199
+ try {
200
+ let res;
201
+
202
+ if (!apiUrl) {
203
+ // MVC4
204
+ res = await api.post(`${getBaseUrl()}/Admin/${tableName}/Delete`, { id });
205
+ } else {
206
+ // .NET Core
207
+ const ids = Array.isArray(id) ? id : [id];
208
+
209
+ res = await api.delete(`${getBaseUrl()}/${apiUrl}/${tableName}`, {
210
+ data: ids,
211
+ headers: {
212
+ "Content-Type": "application/json-patch+json"
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
+
182
227
  return res.data;
228
+ } catch (error) {
229
+ // console.error("Error deleting data:", error);
230
+ throw error; // React Query mutation onError xử lý
183
231
  }
184
232
  };
233
+
185
234
  export const deleteMultipleDataFromTable = async ({ tableName, ids }) => {
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
- });
235
+ try {
236
+ let res;
237
+
238
+ if (!apiUrl) {
239
+ // MVC4
240
+ res = await api.post(`${getBaseUrl()}/Admin/${tableName}/DeleteMulti`, {
241
+ ids
242
+ });
243
+ } else {
244
+ // .NET Core
245
+ res = await api.delete(`${getBaseUrl()}/${apiUrl}/${tableName}`, {
246
+ data: ids,
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
+
198
266
  return res.data;
267
+ } catch (error) {
268
+ // console.error("Error deleting multiple data:", error);
269
+ throw error; // để useMutation onError xử lý
199
270
  }
200
271
  };
272
+
201
273
  export const saveDataToTable = async ({ tableName, data }) => {
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;
274
+ try {
275
+ let res;
276
+
277
+ if (!apiUrl) {
278
+ // MVC4
279
+ res = await api.post(`${getBaseUrl()}/Admin/${tableName}/SaveData`, {
280
+ json: JSON.stringify({ ...data })
281
+ });
212
282
  } else {
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;
283
+ // .NET Core
284
+ if (!data?.Id || data.Id === "0") {
285
+ // CREATE
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
+ }
216
291
  }
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ý
217
310
  }
218
311
  };
312
+
219
313
  export const changeStatusDataToTable = async ({ tableName, id, fieldName }) => {
220
- if (!apiUrl) {
221
- const res = await api.post(`${getBaseUrl()}/Admin/${tableName}/ChangeStatus`, {
222
- id
223
- });
224
- return res.data;
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 };
314
+ try {
315
+ let res;
316
+
317
+ if (!apiUrl) {
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
+ };
253
340
  }
341
+
342
+ return res.data;
343
+ } catch (error) {
344
+ // console.error("Error changing status:", error);
345
+ throw error; // để useMutation onError xử lý
254
346
  }
255
347
  };
256
348
 
257
349
  export const exportExcel = async ({ tableName, data }) => {
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
- {
350
+ try {
351
+ let res;
352
+
353
+ if (!apiUrl) {
354
+ // MVC4
355
+ res = await api.get(`${getBaseUrl()}/Admin/${tableName}/ExportData`, {
269
356
  params: {
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;
357
+ json: JSON.stringify(data)
358
+ },
359
+ responseType: "blob"
360
+ });
361
+ } else {
362
+ // .NET Core
363
+ res = await api.get(`${getBaseUrl()}/${apiUrl}/${tableName}/ExportData`, {
364
+ params: { ...data },
365
+ responseType: "blob"
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;
280
378
  }
281
379
  };
282
380
 
283
381
  export const uploadFile = async (formData) => {
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
- }
382
+ try {
383
+ // Clone FormData để tránh append trùng
384
+ const fd = new FormData();
385
+ formData.forEach((value, key) => {
386
+ fd.append(key, value);
307
387
  });
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
+
308
412
  return res.data;
413
+ } catch (error) {
414
+ // console.error("Error uploading file:", error);
415
+ throw error;
309
416
  }
310
417
  };
311
418
 
312
- export const huongDan = async ({ data }) => {
313
- if (!apiUrl) {
419
+ export const huongDan = async ({ data } = {}) => {
420
+ try {
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);
314
440
  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;
330
441
  }
331
442
  };
@@ -1,11 +1,26 @@
1
- import { Grid, Table, TableBody, TableCell, TableContainer, TableRow, Typography, useMediaQuery, useTheme } from "@mui/material";
1
+ import {
2
+ Grid,
3
+ Table,
4
+ TableBody,
5
+ TableCell,
6
+ TableContainer,
7
+ TableRow,
8
+ Typography,
9
+ useMediaQuery,
10
+ useTheme
11
+ } from "@mui/material";
2
12
  import { useEffect, useMemo, useState } from "react";
3
13
  import TablePaginationCustom from "../table/TablePagination";
4
14
 
5
15
  import { useConfirm } from "material-ui-confirm";
6
16
  import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
7
17
  import { toast } from "react-toastify";
8
- import { changeStatusDataToTable, deleteDataFromTable, deleteMultipleDataFromTable, getDatasFromTable } from "../../api";
18
+ import {
19
+ changeStatusDataToTable,
20
+ deleteDataFromTable,
21
+ deleteMultipleDataFromTable,
22
+ getDatasFromTable
23
+ } from "../../api";
9
24
  import TableRowsLoader from "../table/TableRowsLoader";
10
25
  import { TableHead } from "./TableHead";
11
26
 
@@ -66,11 +81,12 @@ const DataTable = ({ multipleActions = [], page, setPage = () => {}, disableEdit
66
81
  setOrderBy(property);
67
82
  };
68
83
 
84
+ // 1️⃣ Xoá defaultQueryOptions khỏi useQuery
69
85
  const { data, isLoading } = useQuery({
70
86
  queryKey: [tableName, page, rowsPerPage, dataSearch, order, orderBy],
71
87
  queryFn: () =>
72
88
  getDatasFromTable({
73
- tableName: tableName,
89
+ tableName,
74
90
  page: page + 1,
75
91
  pageSize: rowsPerPage,
76
92
  data: {
@@ -81,96 +97,76 @@ const DataTable = ({ multipleActions = [], page, setPage = () => {}, disableEdit
81
97
  })
82
98
  }
83
99
  }),
84
- defaultQueryOptions,
85
- // keepPreviousData: true,
86
- onSuccess: ({ PermissionModel, CountTrangThai, status }) => {
100
+ keepPreviousData: true,
101
+ placeholderData: () => ({
102
+ data: [],
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 }) => {
87
112
  if (dataSearch?.TrangThaiXuLy !== undefined) {
88
- PermissionModel.TrangThaiXuLy = dataSearch?.TrangThaiXuLy;
113
+ PermissionModel.TrangThaiXuLy = dataSearch.TrangThaiXuLy;
89
114
  }
115
+
90
116
  setPermission(PermissionModel);
91
117
 
92
118
  if (CountTrangThai) {
93
119
  const keyCounter = `${tableName}_${userId}_TrangThaiXuLyCounter`;
94
120
  localStorage.setItem(keyCounter, JSON.stringify(CountTrangThai));
95
- // 👉 Invalidate query để Tab reload ngay
96
- queryClient.invalidateQueries({ queryKey: [tableName, "CountAllTrangThaiXuly"] });
121
+ queryClient.invalidateQueries({
122
+ queryKey: [tableName, "CountAllTrangThaiXuly"]
123
+ });
97
124
  }
98
125
 
99
126
  if (status) {
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
- });
127
+ window.scrollTo({ top: 0, behavior: "smooth" });
106
128
  }
107
129
  }
108
130
  });
109
131
 
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
- }
132
+ // 2️⃣ Mutation cũng cần sửa, bỏ defaultMutationOptions nếu có
133
+ const changeStatusMutation = useMutation({
134
+ mutationFn: changeStatusDataToTable,
135
+ onSuccess: ({ status = false, message = "Có lỗi xảy ra !" }) => {
136
+ toast.success(status ? message : "Thay đổi trạng thái thành công !");
117
137
  queryClient.invalidateQueries({ queryKey: [tableName] });
118
138
  queryClient.invalidateQueries({ queryKey: [tableName, "CountAllTrangThaiXuly"] });
119
139
  },
120
140
  onError: (error) => {
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
- }
141
+ const message = error?.response?.data?.message || "Đã xảy ra lỗi không mong muốn.";
142
+ toast.error(message);
128
143
  }
129
144
  });
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
- }
141
145
 
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 !");
142
150
  queryClient.invalidateQueries({ queryKey: [tableName] });
143
151
  queryClient.invalidateQueries({ queryKey: [tableName, "CountAllTrangThaiXuly"] });
144
152
  },
145
153
  onError: (error) => {
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
- }
154
+ const message = error?.response?.data?.message || "Đã xảy ra lỗi không mong muốn.";
155
+ toast.error(message);
153
156
  }
154
157
  });
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
- }
158
+
159
+ const deleteMultipleMutation = useMutation({
160
+ mutationFn: deleteMultipleDataFromTable,
161
+ onSuccess: ({ status = false, message = "Có lỗi xảy ra !" }) => {
162
+ toast[status ? "success" : "error"](status ? message : "Xóa thành công !");
162
163
  setSelectedItems([]);
163
164
  queryClient.invalidateQueries({ queryKey: [tableName] });
164
165
  queryClient.invalidateQueries({ queryKey: [tableName, "CountAllTrangThaiXuly"] });
165
166
  },
166
167
  onError: (error) => {
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
- }
168
+ const message = error?.response?.data?.message || "Đã xảy ra lỗi không mong muốn.";
169
+ toast.error(message);
174
170
  }
175
171
  });
176
172
  // Hàm gọi khi người dùng muốn xóa một bản ghi
@@ -370,7 +366,12 @@ const DataTable = ({ multipleActions = [], page, setPage = () => {}, disableEdit
370
366
  />
371
367
  </Grid>
372
368
  </Grid>
373
- <DeleteConfirmationDialog open={openDialog} onClose={handleDialogClose} onConfirm={handleDialogClose} id={deleteId} />
369
+ <DeleteConfirmationDialog
370
+ open={openDialog}
371
+ onClose={handleDialogClose}
372
+ onConfirm={handleDialogClose}
373
+ id={deleteId}
374
+ />
374
375
  <DeleteMultipleConfirmationDialog
375
376
  open={openDeleteMultipleDialog}
376
377
  onClose={handleDeleteMultipleDialogClose}
@@ -80,11 +80,12 @@ const DataTableSM = ({ multipleActions = [], page, setPage = () => {}, disableEd
80
80
  setOrderBy(property);
81
81
  };
82
82
 
83
+ // 1️⃣ Xoá defaultQueryOptions khỏi useQuery
83
84
  const { data, isLoading } = useQuery({
84
85
  queryKey: [tableName, page, rowsPerPage, dataSearch, order, orderBy],
85
86
  queryFn: () =>
86
87
  getDatasFromTable({
87
- tableName: tableName,
88
+ tableName,
88
89
  page: page + 1,
89
90
  pageSize: rowsPerPage,
90
91
  data: {
@@ -95,107 +96,76 @@ const DataTableSM = ({ multipleActions = [], page, setPage = () => {}, disableEd
95
96
  })
96
97
  }
97
98
  }),
98
- defaultQueryOptions,
99
- // keepPreviousData: true,
100
- onSuccess: ({ PermissionModel, CountTrangThai, status }) => {
99
+ keepPreviousData: true,
100
+ placeholderData: () => ({
101
+ data: [],
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 }) => {
101
111
  if (dataSearch?.TrangThaiXuLy !== undefined) {
102
- PermissionModel.TrangThaiXuLy = dataSearch?.TrangThaiXuLy;
112
+ PermissionModel.TrangThaiXuLy = dataSearch.TrangThaiXuLy;
103
113
  }
114
+
104
115
  setPermission(PermissionModel);
105
116
 
106
117
  if (CountTrangThai) {
107
118
  const keyCounter = `${tableName}_${userId}_TrangThaiXuLyCounter`;
108
119
  localStorage.setItem(keyCounter, JSON.stringify(CountTrangThai));
109
- // 👉 Invalidate query để Tab reload ngay
110
- queryClient.invalidateQueries({ queryKey: [tableName, "CountAllTrangThaiXuly"] });
120
+ queryClient.invalidateQueries({
121
+ queryKey: [tableName, "CountAllTrangThaiXuly"]
122
+ });
111
123
  }
112
124
 
113
125
  if (status) {
114
- // console.log("LOAD LAI PermissionModel");
115
- // Cuộn lên đầu trang khi tải dữ liệu thành công
116
- window.scrollTo({
117
- top: 0, // Vị trí pixel muốn cuộn tới
118
- behavior: "smooth" // Tùy chọn cuộn mượt
119
- });
126
+ window.scrollTo({ top: 0, behavior: "smooth" });
120
127
  }
121
128
  }
122
129
  });
123
- const changeStatusMutation = useMutation(changeStatusDataToTable, {
124
- onSuccess: ({ status = false, message = " Có lỗi xảy ra !" }) => {
125
- if (URL_APPLICATION_API) {
126
- toast.success(message);
127
- } else {
128
- toast.success("Thay đổi trạng thái thành công !");
129
- }
130
+
131
+ // 2️⃣ Mutation cũng cần sửa, bỏ defaultMutationOptions nếu
132
+ const changeStatusMutation = useMutation({
133
+ mutationFn: changeStatusDataToTable,
134
+ onSuccess: ({ status = false, message = "Có lỗi xảy ra !" }) => {
135
+ toast.success(status ? message : "Thay đổi trạng thái thành công !");
130
136
  queryClient.invalidateQueries({ queryKey: [tableName] });
131
137
  queryClient.invalidateQueries({ queryKey: [tableName, "CountAllTrangThaiXuly"] });
132
138
  },
133
139
  onError: (error) => {
134
- if (error.response && error.response.data && error.response.data.errors) {
135
- const errors = error.response.data.errors;
136
- // Chuyển đổi đối tượng lỗi thành chuỗi để hiển thị
137
- const errorMessages = Object.entries(errors)
138
- .map(([field, messages]) => `${field}: ${messages.join(", ")}`)
139
- .join("\n");
140
- toast.error(errorMessages);
141
- } else {
142
- // Nếu lỗi không theo định dạng mong đợi, hiển thị thông tin lỗi chung
143
- toast.error("Đã xảy ra lỗi không mong muốn.");
144
- }
140
+ const message = error?.response?.data?.message || "Đã xảy ra lỗi không mong muốn.";
141
+ toast.error(message);
145
142
  }
146
143
  });
147
- const deleteMutation = useMutation(deleteDataFromTable, {
148
- onSuccess: ({ status = false, message = " Có lỗi xảy ra !" }) => {
149
- if (status) {
150
- if (URL_APPLICATION_API) {
151
- toast.success(message);
152
- } else {
153
- toast.success("Xóa thành công !");
154
- }
155
- } else {
156
- toast.error(" Có lỗi xảy ra !");
157
- }
158
144
 
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 !");
159
149
  queryClient.invalidateQueries({ queryKey: [tableName] });
160
150
  queryClient.invalidateQueries({ queryKey: [tableName, "CountAllTrangThaiXuly"] });
161
151
  },
162
152
  onError: (error) => {
163
- if (error.response && error.response.data && error.response.data.errors) {
164
- const errors = error.response.data.errors;
165
- // Chuyển đổi đối tượng lỗi thành chuỗi để hiển thị
166
- const errorMessages = Object.entries(errors)
167
- .map(([field, messages]) => `${field}: ${messages.join(", ")}`)
168
- .join("\n");
169
- toast.error(errorMessages);
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
- }
153
+ const message = error?.response?.data?.message || "Đã xảy ra lỗi không mong muốn.";
154
+ toast.error(message);
174
155
  }
175
156
  });
176
- const deleteMultipleMutation = useMutation(deleteMultipleDataFromTable, {
177
- onSuccess: ({ status = false, message = " Có lỗi xảy ra !" }) => {
178
- if (URL_APPLICATION_API) {
179
- toast.success(message);
180
- } else {
181
- toast.success("Xóa thành công !");
182
- }
157
+
158
+ const deleteMultipleMutation = useMutation({
159
+ mutationFn: deleteMultipleDataFromTable,
160
+ onSuccess: ({ status = false, message = "Có lỗi xảy ra !" }) => {
161
+ toast[status ? "success" : "error"](status ? message : "Xóa thành công !");
183
162
  setSelectedItems([]);
184
163
  queryClient.invalidateQueries({ queryKey: [tableName] });
185
164
  queryClient.invalidateQueries({ queryKey: [tableName, "CountAllTrangThaiXuly"] });
186
165
  },
187
166
  onError: (error) => {
188
- if (error.response && error.response.data && error.response.data.errors) {
189
- const errors = error.response.data.errors;
190
- // Chuyển đổi đối tượng lỗi thành chuỗi để hiển thị
191
- const errorMessages = Object.entries(errors)
192
- .map(([field, messages]) => `${field}: ${messages.join(", ")}`)
193
- .join("\n");
194
- toast.error(errorMessages);
195
- } else {
196
- // Nếu lỗi không theo định dạng mong đợi, hiển thị thông tin lỗi chung
197
- toast.error("Đã xảy ra lỗi không mong muốn.");
198
- }
167
+ const message = error?.response?.data?.message || "Đã xảy ra lỗi không mong muốn.";
168
+ toast.error(message);
199
169
  }
200
170
  });
201
171
  // Hàm gọi khi người dùng muốn xóa một bản ghi
@@ -91,35 +91,30 @@ function EditorForm({ fields, submitRef }) {
91
91
  }
92
92
  }, [selectedEditItem]);
93
93
 
94
- const saveMutation = useMutation(saveDataToTable, {
94
+ const saveMutation = useMutation({
95
+ mutationFn: saveDataToTable,
95
96
  onSuccess: ({ status = false, message = " Có lỗi xảy ra !" }, { data: { Id } }) => {
96
97
  if (status) {
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);
98
+ toast.success(URL_APPLICATION_API ? message : Id === 0 ? "Thêm thành công!" : "Cập nhật thành công!");
99
+ queryClient.invalidateQueries({ queryKey: [tableName] });
103
100
  setOpenEditorDialog(false);
104
101
  } else {
105
- console.log(status, Id);
106
102
  toast.error(message);
107
103
  }
108
104
  },
109
105
  onError: (error) => {
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ị
106
+ const errors = error?.response?.data?.errors;
107
+ if (errors) {
113
108
  const errorMessages = Object.entries(errors)
114
109
  .map(([field, messages]) => `${field}: ${messages.join(", ")}`)
115
110
  .join("\n");
116
111
  toast.error(errorMessages);
117
112
  } else {
118
- // Nếu lỗi không theo định dạng mong đợi, hiển thị thông tin lỗi chung
119
113
  toast.error("Đã xảy ra lỗi không mong muốn.");
120
114
  }
121
115
  }
122
116
  });
117
+
123
118
  const onSubmit = (data) => {
124
119
  fields.reduce((data, { type, field, datas, keyValueLabel, keyValue, keyLabel }) => {
125
120
  if (type === "date") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "trithuc-mvc-react",
3
- "version": "3.4.4",
3
+ "version": "3.4.6",
4
4
  "main": "index.js",
5
5
  "scripts": {
6
6
  "test": "echo \"Error: no test specified\" && exit 1"