trithuc-mvc-react 3.4.5 → 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
  };
@@ -86,7 +86,7 @@ const DataTable = ({ multipleActions = [], page, setPage = () => {}, disableEdit
86
86
  queryKey: [tableName, page, rowsPerPage, dataSearch, order, orderBy],
87
87
  queryFn: () =>
88
88
  getDatasFromTable({
89
- tableName: tableName,
89
+ tableName,
90
90
  page: page + 1,
91
91
  pageSize: rowsPerPage,
92
92
  data: {
@@ -97,22 +97,30 @@ const DataTable = ({ multipleActions = [], page, setPage = () => {}, disableEdit
97
97
  })
98
98
  }
99
99
  }),
100
- // defaultQueryOptions ❌ bỏ đi
101
- keepPreviousData: true, // nếu muốn giữ dữ liệu cũ khi paging
102
- staleTime: 1000 * 60 * 1,
103
- cacheTime: 1000 * 60 * 30,
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)
104
109
  refetchOnWindowFocus: true,
105
- refetchInterval: 1000 * 30,
106
- onSuccess: ({ PermissionModel, CountTrangThai, status }) => {
110
+ refetchInterval: 30_000,
111
+ onSuccess: ({ PermissionModel = {}, CountTrangThai, status }) => {
107
112
  if (dataSearch?.TrangThaiXuLy !== undefined) {
108
- PermissionModel.TrangThaiXuLy = dataSearch?.TrangThaiXuLy;
113
+ PermissionModel.TrangThaiXuLy = dataSearch.TrangThaiXuLy;
109
114
  }
115
+
110
116
  setPermission(PermissionModel);
111
117
 
112
118
  if (CountTrangThai) {
113
119
  const keyCounter = `${tableName}_${userId}_TrangThaiXuLyCounter`;
114
120
  localStorage.setItem(keyCounter, JSON.stringify(CountTrangThai));
115
- queryClient.invalidateQueries({ queryKey: [tableName, "CountAllTrangThaiXuly"] });
121
+ queryClient.invalidateQueries({
122
+ queryKey: [tableName, "CountAllTrangThaiXuly"]
123
+ });
116
124
  }
117
125
 
118
126
  if (status) {
@@ -85,7 +85,7 @@ const DataTableSM = ({ multipleActions = [], page, setPage = () => {}, disableEd
85
85
  queryKey: [tableName, page, rowsPerPage, dataSearch, order, orderBy],
86
86
  queryFn: () =>
87
87
  getDatasFromTable({
88
- tableName: tableName,
88
+ tableName,
89
89
  page: page + 1,
90
90
  pageSize: rowsPerPage,
91
91
  data: {
@@ -96,22 +96,30 @@ const DataTableSM = ({ multipleActions = [], page, setPage = () => {}, disableEd
96
96
  })
97
97
  }
98
98
  }),
99
- // defaultQueryOptions ❌ bỏ đi
100
- keepPreviousData: true, // nếu muốn giữ dữ liệu cũ khi paging
101
- staleTime: 1000 * 60 * 1,
102
- cacheTime: 1000 * 60 * 30,
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)
103
108
  refetchOnWindowFocus: true,
104
- refetchInterval: 1000 * 30,
105
- onSuccess: ({ PermissionModel, CountTrangThai, status }) => {
109
+ refetchInterval: 30_000,
110
+ onSuccess: ({ PermissionModel = {}, CountTrangThai, status }) => {
106
111
  if (dataSearch?.TrangThaiXuLy !== undefined) {
107
- PermissionModel.TrangThaiXuLy = dataSearch?.TrangThaiXuLy;
112
+ PermissionModel.TrangThaiXuLy = dataSearch.TrangThaiXuLy;
108
113
  }
114
+
109
115
  setPermission(PermissionModel);
110
116
 
111
117
  if (CountTrangThai) {
112
118
  const keyCounter = `${tableName}_${userId}_TrangThaiXuLyCounter`;
113
119
  localStorage.setItem(keyCounter, JSON.stringify(CountTrangThai));
114
- queryClient.invalidateQueries({ queryKey: [tableName, "CountAllTrangThaiXuly"] });
120
+ queryClient.invalidateQueries({
121
+ queryKey: [tableName, "CountAllTrangThaiXuly"]
122
+ });
115
123
  }
116
124
 
117
125
  if (status) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "trithuc-mvc-react",
3
- "version": "3.4.5",
3
+ "version": "3.4.6",
4
4
  "main": "index.js",
5
5
  "scripts": {
6
6
  "test": "echo \"Error: no test specified\" && exit 1"