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
- ...(data ?? {})
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
- // console.error("Error fetching data:", error);
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
- // MVC4
172
- res = await api.get(`${getBaseUrl()}/Admin/${tableName}/GetDetail`, {
173
- params: { id }
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
- // .NET Core
177
- res = await api.get(`${getBaseUrl()}/${apiUrl}/${tableName}/${id}`);
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
- // console.error("Error fetching detail:", error);
194
- throw error; // React Query onError xử lý
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
- 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
-
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
- 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
-
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
- 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
- });
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
- // .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
- }
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
- 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
- };
340
- }
341
-
220
+ if (!apiUrl) {
221
+ const res = await api.post(`${getBaseUrl()}/Admin/${tableName}/ChangeStatus`, {
222
+ id
223
+ });
342
224
  return res.data;
343
- } catch (error) {
344
- // console.error("Error changing status:", error);
345
- throw error; // để useMutation onError xử lý
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
- try {
351
- let res;
352
-
353
- if (!apiUrl) {
354
- // MVC4
355
- res = await api.get(`${getBaseUrl()}/Admin/${tableName}/ExportData`, {
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
- 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;
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
- try {
383
- // Clone FormData để tránh append trùng
384
- const fd = new FormData();
385
- formData.forEach((value, key) => {
386
- fd.append(key, value);
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
- 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);
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 "@tanstack/react-query";
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
- 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 }) => {
84
+ defaultQueryOptions,
85
+ // keepPreviousData: true,
86
+ onSuccess: ({ PermissionModel, CountTrangThai, status }) => {
112
87
  if (dataSearch?.TrangThaiXuLy !== undefined) {
113
- PermissionModel.TrangThaiXuLy = dataSearch.TrangThaiXuLy;
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
- queryClient.invalidateQueries({
122
- queryKey: [tableName, "CountAllTrangThaiXuly"]
123
- });
95
+ // 👉 Invalidate query để Tab reload ngay
96
+ queryClient.invalidateQueries({ queryKey: [tableName, "CountAllTrangThaiXuly"] });
124
97
  }
125
98
 
126
99
  if (status) {
127
- window.scrollTo({ top: 0, behavior: "smooth" });
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
- // 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 !");
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
- const message = error?.response?.data?.message || "Đã xảy ra lỗi không mong muốn.";
142
- toast.error(message);
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
- const message = error?.response?.data?.message || "Đã xảy ra lỗi không mong muốn.";
155
- toast.error(message);
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
- 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 !");
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
- const message = error?.response?.data?.message || "Đã xảy ra lỗi không mong muốn.";
169
- toast.error(message);
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 "@tanstack/react-query";
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
- 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 }) => {
83
+ defaultQueryOptions,
84
+ // keepPreviousData: true,
85
+ onSuccess: ({ PermissionModel, CountTrangThai, status }) => {
111
86
  if (dataSearch?.TrangThaiXuLy !== undefined) {
112
- PermissionModel.TrangThaiXuLy = dataSearch.TrangThaiXuLy;
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
- queryClient.invalidateQueries({
121
- queryKey: [tableName, "CountAllTrangThaiXuly"]
122
- });
94
+ // 👉 Invalidate query để Tab reload ngay
95
+ queryClient.invalidateQueries({ queryKey: [tableName, "CountAllTrangThaiXuly"] });
123
96
  }
124
97
 
125
98
  if (status) {
126
- window.scrollTo({ top: 0, behavior: "smooth" });
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
- // 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 !");
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
- const message = error?.response?.data?.message || "Đã xảy ra lỗi không mong muốn.";
141
- toast.error(message);
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
- const message = error?.response?.data?.message || "Đã xảy ra lỗi không mong muốn.";
154
- toast.error(message);
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
- 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 !");
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
- const message = error?.response?.data?.message || "Đã xảy ra lỗi không mong muốn.";
168
- toast.error(message);
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 "@tanstack/react-query";
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
- 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] });
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
- const errors = error?.response?.data?.errors;
107
- if (errors) {
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.6",
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
  }