From 32a24c9df14dbe55716d3b26ef8905b7d8b6b746 Mon Sep 17 00:00:00 2001 From: faisolavolut Date: Thu, 6 Mar 2025 13:58:51 +0700 Subject: [PATCH] fix: enhance error handling and loading state management in Form and Table components --- components/form/Form.tsx | 143 ++++++++++-------- components/form/FormBetter.tsx | 18 ++- components/tablelist/List.tsx | 180 +++++++++++++---------- components/tablelist/TableBetter.tsx | 66 +++++---- components/tablelist/TableList.tsx | 118 ++++++++------- components/tablelist/TableListBetter.tsx | 130 +++++++++------- utils/action.tsx | 78 +++++----- 7 files changed, 429 insertions(+), 304 deletions(-) diff --git a/components/form/Form.tsx b/components/form/Form.tsx index e3d00ea..5cebdea 100644 --- a/components/form/Form.tsx +++ b/components/form/Form.tsx @@ -55,7 +55,10 @@ export const Form: React.FC = ({ )} /> {toastMessage ? `${toastMessage}...` : "Saving..."} - + , + { + duration: Infinity, + } ); local.btn_ready = false; local.render(); @@ -167,42 +170,50 @@ export const Form: React.FC = ({ await onSubmit(local); } setTimeout(() => { - toast.success( -
{ - toast.dismiss(); - }} - > -
- + toast.dismiss(); + setTimeout(() => { + toast.success( +
{ + toast.dismiss(); + }} + > +
+ - {toastMessage ? `${toastMessage} success` : "Record Saved"} + {toastMessage ? `${toastMessage} success` : "Record Saved"} +
-
- ); + ); + }, 100); }, 100); } catch (ex: any) { const msg = get(ex, "response.data.meta.message") || ex.message; - toast.error( -
-
- - {toastMessage - ? `${toastMessage} failed ${msg}.` - : `Submit Failed ${msg}.`} -
-
, - { - dismissible: true, - className: css` - background: #ffecec; - border: 2px solid red; - `, - } - ); + setTimeout(() => { + toast.dismiss(); + setTimeout(() => { + toast.error( +
+
+ + {toastMessage + ? `${toastMessage} failed ${msg}.` + : `Submit Failed ${msg}.`} +
+
, + { + dismissible: true, + className: css` + background: #ffecec; + border: 2px solid red; + `, + } + ); + }, 100); + }, 100); } local.btn_ready = true; local.render(); @@ -229,13 +240,18 @@ export const Form: React.FC = ({ )} /> {"Loading..."} - + , + { + duration: Infinity, + } ); local.data = null; local.render(); - const res = await onLoad(); + try { + const res = await onLoad(); + local.data = res; + } catch (ex) {} local.ready = true; - local.data = res; local.render(); if (typeof afterLoad === "function") { afterLoad(local); @@ -243,22 +259,6 @@ export const Form: React.FC = ({ setTimeout(() => { toast.dismiss(); }, 100); - - // if (res instanceof Promise) { - // res.then((data) => { - // local.ready = true; - // local.data = data; - // local.render(); // Panggil render setelah data diperbarui - // // toast.dismiss(); - // // toast.success("Data Loaded Successfully!"); - // }); - // } else { - // local.ready = true; - // local.data = res; - // local.render(); // Panggil render untuk memicu re-render - // toast.dismiss(); - // toast.success("Data Loaded Successfully!"); - // } }, fields: {} as any, render: () => {}, @@ -295,19 +295,46 @@ export const Form: React.FC = ({ )} /> {"Loading..."} - + , + { + duration: Infinity, + } ); - const res = await onLoad(); - + let res = null as any; + try { + res = await onLoad(); + local.data = res; + setTimeout(() => { + toast.dismiss(); + }, 100); + } catch (ex: any) { + const msg = get(ex, "response.data.meta.message") || ex.message; + setTimeout(() => { + toast.dismiss(); + setTimeout(() => { + toast.error( +
+
+ + {`Failed ${msg}.`} +
+
, + { + dismissible: true, + className: css` + background: #ffecec; + border: 2px solid red; + `, + } + ); + }, 100); + }, 100); + } local.ready = true; - local.data = res; local.render(); if (typeof afterLoad === "function") { await afterLoad(local); } - setTimeout(() => { - toast.dismiss(); - }, 100); }; run(); }, []); diff --git a/components/form/FormBetter.tsx b/components/form/FormBetter.tsx index ae80d97..007582c 100644 --- a/components/form/FormBetter.tsx +++ b/components/form/FormBetter.tsx @@ -1,7 +1,9 @@ "use client"; +import { notFound } from "next/navigation"; import { ScrollArea } from "../ui/scroll-area"; import { Form } from "./Form"; import { useEffect, useState } from "react"; +import get from "lodash.get"; export const FormBetter: React.FC = ({ children, @@ -19,6 +21,10 @@ export const FormBetter: React.FC = ({ const [fm, setFM] = useState({ data: null as any, }); + const [show, setShow] = useState(true as boolean); + if (!show) { + notFound(); + } useEffect(() => {}, [fm.data]); return (
@@ -37,7 +43,17 @@ export const FormBetter: React.FC = ({ children, header, onTitle, - onLoad, + onLoad: async () => { + try { + const res = await onLoad(); + return res; + } catch (ex: any) { + setShow(false); + throw new Error( + get(ex, "response.data.meta.message") || ex.message + ); + } + }, onSubmit, onFooter, showResize, diff --git a/components/tablelist/List.tsx b/components/tablelist/List.tsx index 0c83acd..34f6ae9 100644 --- a/components/tablelist/List.tsx +++ b/components/tablelist/List.tsx @@ -4,6 +4,7 @@ import { useLocal } from "@/lib/utils/use-local"; import { toast } from "sonner"; import { Loader2 } from "lucide-react"; import { ScrollArea } from "../ui/scroll-area"; +import get from "lodash.get"; export const ListBetter: React.FC = ({ autoPagination = true, @@ -82,36 +83,46 @@ export const ListBetter: React.FC = ({ )} /> {"Loading..."} - + , + { + duration: Infinity, + } ); local.ready = false; local.render(); - if (typeof onCount === "function") { - const res = await onCount(); - local.count = res; - local.maxPage = Math.ceil(res / take); - local.paging = 1; - local.render(); - } - if (Array.isArray(onLoad)) { - let res = onLoad; - local.data = res; - local.render(); - setData(res); - } else { - let res: any = await onLoad({ - search: local.search, - sort: local.sort, - take, - paging: 1, - }); - local.data = res; - local.render(); - setData(res); - setTimeout(() => { - toast.dismiss(); - }, 100); + try { + if (typeof onCount === "function") { + const res = await onCount(); + local.count = res; + local.maxPage = Math.ceil(res / take); + local.paging = 1; + local.render(); + } + if (Array.isArray(onLoad)) { + let res = onLoad; + local.data = res; + local.render(); + setData(res); + } else { + let res: any = await onLoad({ + search: local.search, + sort: local.sort, + take, + paging: 1, + }); + local.data = res; + local.render(); + setData(res); + setTimeout(() => { + toast.dismiss(); + }, 100); + } + } catch (ex: any) { + console.error(get(ex, "response.data.meta.message") || ex.message); } + setTimeout(() => { + toast.dismiss(); + }, 100); local.ready = true; local.render(); }, @@ -135,28 +146,38 @@ export const ListBetter: React.FC = ({ )} /> {"Loading..."} - + , + { + duration: Infinity, + } ); const listData = local.data || []; - if (Array.isArray(onLoad)) { - let res = onLoad; - local.data = listData.concat(res); - local.render(); - setData(res); - } else { - let res: any = await onLoad({ - search: local.search, - sort: local.sort, - take, - paging: local.paging, - }); - local.data = listData.concat(res); - local.render(); - setData(res); - setTimeout(() => { - toast.dismiss(); - }, 100); + try { + if (Array.isArray(onLoad)) { + let res = onLoad; + local.data = listData.concat(res); + local.render(); + setData(res); + } else { + let res: any = await onLoad({ + search: local.search, + sort: local.sort, + take, + paging: local.paging, + }); + local.data = listData.concat(res); + local.render(); + setData(res); + setTimeout(() => { + toast.dismiss(); + }, 100); + } + } catch (ex: any) { + console.error(get(ex, "response.data.meta.message") || ex.message); } + setTimeout(() => { + toast.dismiss(); + }, 100); }, }); useEffect(() => { @@ -180,42 +201,49 @@ export const ListBetter: React.FC = ({ )} /> {"Loading..."} - + , + { + duration: Infinity, + } ); local.ready = false; local.render(); - if (typeof onCount === "function") { - const res = await onCount(); - setMaxPage(Math.ceil(res / take)); - local.maxPage = Math.ceil(res / take); - local.count = res; - local.render(); - } - if (mode === "form") { - local.data = fm.data?.[name] || []; - local.render(); - setData(fm.data?.[name] || []); - } else { - if (Array.isArray(onLoad)) { - local.data = onLoad; + try { + if (typeof onCount === "function") { + const res = await onCount(); + setMaxPage(Math.ceil(res / take)); + local.maxPage = Math.ceil(res / take); + local.count = res; local.render(); - setData(onLoad); - } else if (typeof onLoad === "function") { - let res: any = await onLoad({ - search: local.search, - sort: local.sort, - take, - paging: 1, - }); - local.data = res; - local.render(); - setData(local.data); - } else { - let res = onLoad; - local.data = res; - local.render(); - setData(local.data); } + if (mode === "form") { + local.data = fm.data?.[name] || []; + local.render(); + setData(fm.data?.[name] || []); + } else { + if (Array.isArray(onLoad)) { + local.data = onLoad; + local.render(); + setData(onLoad); + } else if (typeof onLoad === "function") { + let res: any = await onLoad({ + search: local.search, + sort: local.sort, + take, + paging: 1, + }); + local.data = res; + local.render(); + setData(local.data); + } else { + let res = onLoad; + local.data = res; + local.render(); + setData(local.data); + } + } + } catch (ex: any) { + console.error(get(ex, "response.data.meta.message") || ex.message); } if (typeof onInit === "function") { onInit(local); diff --git a/components/tablelist/TableBetter.tsx b/components/tablelist/TableBetter.tsx index addaf1e..0973ef2 100644 --- a/components/tablelist/TableBetter.tsx +++ b/components/tablelist/TableBetter.tsx @@ -10,6 +10,7 @@ import { getNumber } from "@/lib/utils/getNumber"; import { formatMoney } from "@/lib/components/form/field/TypeInput"; import "react-resizable/css/styles.css"; import { Resizable } from "react-resizable"; +import get from "lodash.get"; export const TableEditBetter: React.FC = ({ name, column, @@ -101,37 +102,44 @@ export const TableEditBetter: React.FC = ({ )} /> {"Loading..."} - - ); - if (Array.isArray(onLoad)) { - local.data = onLoad; - local.render(); - setData(onLoad); - } else { - const res: any = onLoad({ - search: local.search, - sort: local.sort, - take, - paging: 1, - }); - if (res instanceof Promise) { - res.then((e) => { - local.data = e; - local.render(); - setData(e); - setTimeout(() => { - toast.dismiss(); - }, 100); - }); - } else { - local.data = res; - local.render(); - setData(res); - setTimeout(() => { - toast.dismiss(); - }, 100); + , + { + duration: Infinity, } + ); + try { + if (Array.isArray(onLoad)) { + local.data = onLoad; + local.render(); + setData(onLoad); + } else { + const res: any = onLoad({ + search: local.search, + sort: local.sort, + take, + paging: 1, + }); + if (res instanceof Promise) { + res.then((e) => { + local.data = e; + local.render(); + setData(e); + setTimeout(() => { + toast.dismiss(); + }, 100); + }); + } else { + local.data = res; + local.render(); + setData(res); + } + } + } catch (ex: any) { + console.error(get(ex, "response.data.meta.message") || ex.message); } + setTimeout(() => { + toast.dismiss(); + }, 100); }, }); // const cloneListFM = (data: any[]) => { diff --git a/components/tablelist/TableList.tsx b/components/tablelist/TableList.tsx index 75eafcb..127beae 100644 --- a/components/tablelist/TableList.tsx +++ b/components/tablelist/TableList.tsx @@ -193,7 +193,10 @@ export const TableList = ({ )} /> {"Loading..."} - + , + { + duration: Infinity, + } ); if (typeof onCount === "function") { const params = await events("onload-param", { @@ -255,7 +258,10 @@ export const TableList = ({ )} /> {"Loading..."} - + , + { + duration: Infinity, + } ); if (Array.isArray(onLoad)) { let res = onLoad; @@ -285,6 +291,9 @@ export const TableList = ({ toast.dismiss(); }, 100); } + setTimeout(() => { + toast.dismiss(); + }, 100); }, }); const cloneListFM = (data: any[]) => { @@ -351,61 +360,66 @@ export const TableList = ({ )} /> {"Loading..."} - + , + { + duration: Infinity, + } ); try { - if (typeof onCount === "function") { - const params = await events("onload-param", { - take: 1, - paging: 1, - search: local.search, - ...local.filter, - ...local.fieldResultFilter, - }); - const res = await onCount(params); - local.count = res; + try { + if (typeof onCount === "function") { + const params = await events("onload-param", { + take: 1, + paging: 1, + search: local.search, + ...local.filter, + ...local.fieldResultFilter, + }); + const res = await onCount(params); + local.count = res; + local.render(); + } + } catch (ex) {} + if (mode === "form") { + local.data = fm.data?.[name] || []; + cloneListFM(fm.data?.[name] || []); local.render(); + setData(fm.data?.[name] || []); + } else { + if (Array.isArray(onLoad)) { + local.data = onLoad; + cloneListFM(onLoad); + local.render(); + setData(onLoad); + } else if (typeof onLoad === "function") { + let res: any = await onLoad({ + search: local.search, + sort: local.sort, + take, + paging: 1, + ...local.filter, + ...local.fieldResultFilter, + }); + if (!autoPagination) { + res = paginateArray(res, take, 1); + } + if (!local.count) { + local.count = res?.length; + } + local.data = res; + local.render(); + setData(local.data); + } else { + let res: any[] = onLoad; + if (!autoPagination) { + res = paginateArray(res, take, 1); + } + local.data = res; + local.render(); + setData(local.data); + } } } catch (ex) {} - if (mode === "form") { - local.data = fm.data?.[name] || []; - cloneListFM(fm.data?.[name] || []); - local.render(); - setData(fm.data?.[name] || []); - } else { - if (Array.isArray(onLoad)) { - local.data = onLoad; - cloneListFM(onLoad); - local.render(); - setData(onLoad); - } else if (typeof onLoad === "function") { - let res: any = await onLoad({ - search: local.search, - sort: local.sort, - take, - paging: 1, - ...local.filter, - ...local.fieldResultFilter, - }); - if (!autoPagination) { - res = paginateArray(res, take, 1); - } - if (!local.count) { - local.count = res?.length; - } - local.data = res; - local.render(); - setData(local.data); - } else { - let res: any[] = onLoad; - if (!autoPagination) { - res = paginateArray(res, take, 1); - } - local.data = res; - local.render(); - setData(local.data); - } - } setTimeout(() => { toast.dismiss(); }, 100); diff --git a/components/tablelist/TableListBetter.tsx b/components/tablelist/TableListBetter.tsx index 13ac9c6..c525673 100644 --- a/components/tablelist/TableListBetter.tsx +++ b/components/tablelist/TableListBetter.tsx @@ -111,39 +111,52 @@ export const TableListBetter: React.FC = ({ )} /> {"Loading..."} - + , + { + duration: Infinity, + } ); - if (Array.isArray(onLoad)) { - local.data = onLoad; - local.render(); - setData(onLoad); - } else { - const res: any = onLoad({ - search: local.search, - sort: local.sort, - take, - paging: 1, - }); - if (res instanceof Promise) { - res.then((e) => { - local.data = e; - cloneListFM(e); - local.render(); - setData(e); - setTimeout(() => { - toast.dismiss(); - }, 2000); - }); - } else { - local.data = res; - cloneListFM(res); + try { + if (Array.isArray(onLoad)) { + local.data = onLoad; local.render(); - setData(res); + setData(onLoad); setTimeout(() => { toast.dismiss(); - }, 2000); + }, 100); + } else { + const res: any = onLoad({ + search: local.search, + sort: local.sort, + take, + paging: 1, + }); + if (res instanceof Promise) { + res.then((e) => { + local.data = e; + cloneListFM(e); + local.render(); + setData(e); + setTimeout(() => { + toast.dismiss(); + }, 100); + }); + } else { + local.data = res; + cloneListFM(res); + local.render(); + setData(res); + setTimeout(() => { + toast.dismiss(); + }, 100); + } } + } catch (ex: any) { + console.error(get(ex, "response.data.meta.message") || ex.message); } + setTimeout(() => { + toast.dismiss(); + }, 100); }, }); const cloneListFM = (data: any[]) => { @@ -173,35 +186,48 @@ export const TableListBetter: React.FC = ({ )} /> {"Loading..."} - + , + { + duration: Infinity, + } ); - if (typeof onCount === "function") { - const res = await onCount(); - local.count = res; + try { + if (typeof onCount === "function") { + const res = await onCount(); + local.count = res; - local.render(); - } + local.render(); + } - if (Array.isArray(onLoad)) { - local.data = onLoad; - cloneListFM(onLoad); - local.render(); - setData(onLoad); - } else { - const res: any = await onLoad({ - search: local.search, - sort: local.sort, - take, - paging: 1, - }); - local.data = res; - cloneListFM(res); - local.render(); - setData(res); - setTimeout(() => { - toast.dismiss(); - }, 2000); + if (Array.isArray(onLoad)) { + local.data = onLoad; + cloneListFM(onLoad); + local.render(); + setData(onLoad); + setTimeout(() => { + toast.dismiss(); + }, 100); + } else { + const res: any = await onLoad({ + search: local.search, + sort: local.sort, + take, + paging: 1, + }); + local.data = res; + cloneListFM(res); + local.render(); + setData(res); + setTimeout(() => { + toast.dismiss(); + }, 100); + } + } catch (ex: any) { + console.error(get(ex, "response.data.meta.message") || ex.message); } + setTimeout(() => { + toast.dismiss(); + }, 100); }; if (typeof onInit === "function") { onInit(local); diff --git a/utils/action.tsx b/utils/action.tsx index 64c7b32..d40c95c 100644 --- a/utils/action.tsx +++ b/utils/action.tsx @@ -46,27 +46,31 @@ export const actionToast = async (data: { )} /> {msg_load ? msg_load : " Load..."} - + , + { + duration: Infinity, + } ); if (typeof task === "function") await task(); setTimeout(() => { toast.dismiss(); - toast.success( -
{ - toast.dismiss(); - }} - > -
- - {msg_succes ? msg_succes : " Success"} + setTimeout(() => { + toast.success( +
{ + toast.dismiss(); + }} + > +
+ + {msg_succes ? msg_succes : " Success"} +
-
- ); - + ); + }, 100); if (typeof after === "function") after(); if (typeof success === "function") success(); }, 100); @@ -74,26 +78,28 @@ export const actionToast = async (data: { setTimeout(() => { if (typeof failed === "function") failed(); toast.dismiss(); - toast.error( -
-
- {hidden_icon !== true ? ( - - ) : ( - <> - )} - {msg_error ? msg_error : " Failed"}{" "} - {get(ex, "response.data.meta.message") || ex.message}. -
-
, - { - dismissible: true, - className: css` - background: #ffecec; - border: 2px solid red; - `, - } - ); + setTimeout(() => { + toast.error( +
+
+ {hidden_icon !== true ? ( + + ) : ( + <> + )} + {msg_error ? msg_error : " Failed"}{" "} + {get(ex, "response.data.meta.message") || ex.message}. +
+
, + { + dismissible: true, + className: css` + background: #ffecec; + border: 2px solid red; + `, + } + ); + }, 100); }, 100); } };