fix: enhance error handling and loading state management in Form and Table components

This commit is contained in:
faisolavolut 2025-03-06 13:58:51 +07:00
parent dfabe899d6
commit 32a24c9df1
7 changed files with 429 additions and 304 deletions

View File

@ -55,7 +55,10 @@ export const Form: React.FC<any> = ({
)} )}
/> />
{toastMessage ? `${toastMessage}...` : "Saving..."} {toastMessage ? `${toastMessage}...` : "Saving..."}
</> </>,
{
duration: Infinity,
}
); );
local.btn_ready = false; local.btn_ready = false;
local.render(); local.render();
@ -166,6 +169,8 @@ export const Form: React.FC<any> = ({
} else { } else {
await onSubmit(local); await onSubmit(local);
} }
setTimeout(() => {
toast.dismiss();
setTimeout(() => { setTimeout(() => {
toast.success( toast.success(
<div <div
@ -184,8 +189,12 @@ export const Form: React.FC<any> = ({
</div> </div>
); );
}, 100); }, 100);
}, 100);
} catch (ex: any) { } catch (ex: any) {
const msg = get(ex, "response.data.meta.message") || ex.message; const msg = get(ex, "response.data.meta.message") || ex.message;
setTimeout(() => {
toast.dismiss();
setTimeout(() => {
toast.error( toast.error(
<div className="flex flex-col w-full"> <div className="flex flex-col w-full">
<div className="flex text-red-600 items-center"> <div className="flex text-red-600 items-center">
@ -203,6 +212,8 @@ export const Form: React.FC<any> = ({
`, `,
} }
); );
}, 100);
}, 100);
} }
local.btn_ready = true; local.btn_ready = true;
local.render(); local.render();
@ -229,13 +240,18 @@ export const Form: React.FC<any> = ({
)} )}
/> />
{"Loading..."} {"Loading..."}
</> </>,
{
duration: Infinity,
}
); );
local.data = null; local.data = null;
local.render(); local.render();
try {
const res = await onLoad(); const res = await onLoad();
local.ready = true;
local.data = res; local.data = res;
} catch (ex) {}
local.ready = true;
local.render(); local.render();
if (typeof afterLoad === "function") { if (typeof afterLoad === "function") {
afterLoad(local); afterLoad(local);
@ -243,22 +259,6 @@ export const Form: React.FC<any> = ({
setTimeout(() => { setTimeout(() => {
toast.dismiss(); toast.dismiss();
}, 100); }, 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, fields: {} as any,
render: () => {}, render: () => {},
@ -295,19 +295,46 @@ export const Form: React.FC<any> = ({
)} )}
/> />
{"Loading..."} {"Loading..."}
</> </>,
{
duration: Infinity,
}
); );
const res = await onLoad(); let res = null as any;
try {
local.ready = true; res = await onLoad();
local.data = res; 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(
<div className="flex flex-col w-full">
<div className="flex text-red-600 items-center">
<AlertTriangle className="h-4 w-4 mr-1" />
{`Failed ${msg}.`}
</div>
</div>,
{
dismissible: true,
className: css`
background: #ffecec;
border: 2px solid red;
`,
}
);
}, 100);
}, 100);
}
local.ready = true;
local.render(); local.render();
if (typeof afterLoad === "function") { if (typeof afterLoad === "function") {
await afterLoad(local); await afterLoad(local);
} }
setTimeout(() => {
toast.dismiss();
}, 100);
}; };
run(); run();
}, []); }, []);

View File

@ -1,7 +1,9 @@
"use client"; "use client";
import { notFound } from "next/navigation";
import { ScrollArea } from "../ui/scroll-area"; import { ScrollArea } from "../ui/scroll-area";
import { Form } from "./Form"; import { Form } from "./Form";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import get from "lodash.get";
export const FormBetter: React.FC<any> = ({ export const FormBetter: React.FC<any> = ({
children, children,
@ -19,6 +21,10 @@ export const FormBetter: React.FC<any> = ({
const [fm, setFM] = useState<any>({ const [fm, setFM] = useState<any>({
data: null as any, data: null as any,
}); });
const [show, setShow] = useState(true as boolean);
if (!show) {
notFound();
}
useEffect(() => {}, [fm.data]); useEffect(() => {}, [fm.data]);
return ( return (
<div className="flex flex-col flex-grow gap-y-3 "> <div className="flex flex-col flex-grow gap-y-3 ">
@ -37,7 +43,17 @@ export const FormBetter: React.FC<any> = ({
children, children,
header, header,
onTitle, 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, onSubmit,
onFooter, onFooter,
showResize, showResize,

View File

@ -4,6 +4,7 @@ import { useLocal } from "@/lib/utils/use-local";
import { toast } from "sonner"; import { toast } from "sonner";
import { Loader2 } from "lucide-react"; import { Loader2 } from "lucide-react";
import { ScrollArea } from "../ui/scroll-area"; import { ScrollArea } from "../ui/scroll-area";
import get from "lodash.get";
export const ListBetter: React.FC<any> = ({ export const ListBetter: React.FC<any> = ({
autoPagination = true, autoPagination = true,
@ -82,10 +83,14 @@ export const ListBetter: React.FC<any> = ({
)} )}
/> />
{"Loading..."} {"Loading..."}
</> </>,
{
duration: Infinity,
}
); );
local.ready = false; local.ready = false;
local.render(); local.render();
try {
if (typeof onCount === "function") { if (typeof onCount === "function") {
const res = await onCount(); const res = await onCount();
local.count = res; local.count = res;
@ -112,6 +117,12 @@ export const ListBetter: React.FC<any> = ({
toast.dismiss(); toast.dismiss();
}, 100); }, 100);
} }
} catch (ex: any) {
console.error(get(ex, "response.data.meta.message") || ex.message);
}
setTimeout(() => {
toast.dismiss();
}, 100);
local.ready = true; local.ready = true;
local.render(); local.render();
}, },
@ -135,9 +146,13 @@ export const ListBetter: React.FC<any> = ({
)} )}
/> />
{"Loading..."} {"Loading..."}
</> </>,
{
duration: Infinity,
}
); );
const listData = local.data || []; const listData = local.data || [];
try {
if (Array.isArray(onLoad)) { if (Array.isArray(onLoad)) {
let res = onLoad; let res = onLoad;
local.data = listData.concat(res); local.data = listData.concat(res);
@ -157,6 +172,12 @@ export const ListBetter: React.FC<any> = ({
toast.dismiss(); toast.dismiss();
}, 100); }, 100);
} }
} catch (ex: any) {
console.error(get(ex, "response.data.meta.message") || ex.message);
}
setTimeout(() => {
toast.dismiss();
}, 100);
}, },
}); });
useEffect(() => { useEffect(() => {
@ -180,10 +201,14 @@ export const ListBetter: React.FC<any> = ({
)} )}
/> />
{"Loading..."} {"Loading..."}
</> </>,
{
duration: Infinity,
}
); );
local.ready = false; local.ready = false;
local.render(); local.render();
try {
if (typeof onCount === "function") { if (typeof onCount === "function") {
const res = await onCount(); const res = await onCount();
setMaxPage(Math.ceil(res / take)); setMaxPage(Math.ceil(res / take));
@ -217,6 +242,9 @@ export const ListBetter: React.FC<any> = ({
setData(local.data); setData(local.data);
} }
} }
} catch (ex: any) {
console.error(get(ex, "response.data.meta.message") || ex.message);
}
if (typeof onInit === "function") { if (typeof onInit === "function") {
onInit(local); onInit(local);
} }

View File

@ -10,6 +10,7 @@ import { getNumber } from "@/lib/utils/getNumber";
import { formatMoney } from "@/lib/components/form/field/TypeInput"; import { formatMoney } from "@/lib/components/form/field/TypeInput";
import "react-resizable/css/styles.css"; import "react-resizable/css/styles.css";
import { Resizable } from "react-resizable"; import { Resizable } from "react-resizable";
import get from "lodash.get";
export const TableEditBetter: React.FC<any> = ({ export const TableEditBetter: React.FC<any> = ({
name, name,
column, column,
@ -101,8 +102,12 @@ export const TableEditBetter: React.FC<any> = ({
)} )}
/> />
{"Loading..."} {"Loading..."}
</> </>,
{
duration: Infinity,
}
); );
try {
if (Array.isArray(onLoad)) { if (Array.isArray(onLoad)) {
local.data = onLoad; local.data = onLoad;
local.render(); local.render();
@ -127,11 +132,14 @@ export const TableEditBetter: React.FC<any> = ({
local.data = res; local.data = res;
local.render(); local.render();
setData(res); setData(res);
}
}
} catch (ex: any) {
console.error(get(ex, "response.data.meta.message") || ex.message);
}
setTimeout(() => { setTimeout(() => {
toast.dismiss(); toast.dismiss();
}, 100); }, 100);
}
}
}, },
}); });
// const cloneListFM = (data: any[]) => { // const cloneListFM = (data: any[]) => {

View File

@ -193,7 +193,10 @@ export const TableList = <T extends object>({
)} )}
/> />
{"Loading..."} {"Loading..."}
</> </>,
{
duration: Infinity,
}
); );
if (typeof onCount === "function") { if (typeof onCount === "function") {
const params = await events("onload-param", { const params = await events("onload-param", {
@ -255,7 +258,10 @@ export const TableList = <T extends object>({
)} )}
/> />
{"Loading..."} {"Loading..."}
</> </>,
{
duration: Infinity,
}
); );
if (Array.isArray(onLoad)) { if (Array.isArray(onLoad)) {
let res = onLoad; let res = onLoad;
@ -285,6 +291,9 @@ export const TableList = <T extends object>({
toast.dismiss(); toast.dismiss();
}, 100); }, 100);
} }
setTimeout(() => {
toast.dismiss();
}, 100);
}, },
}); });
const cloneListFM = (data: any[]) => { const cloneListFM = (data: any[]) => {
@ -351,8 +360,12 @@ export const TableList = <T extends object>({
)} )}
/> />
{"Loading..."} {"Loading..."}
</> </>,
{
duration: Infinity,
}
); );
try {
try { try {
if (typeof onCount === "function") { if (typeof onCount === "function") {
const params = await events("onload-param", { const params = await events("onload-param", {
@ -406,6 +419,7 @@ export const TableList = <T extends object>({
setData(local.data); setData(local.data);
} }
} }
} catch (ex) {}
setTimeout(() => { setTimeout(() => {
toast.dismiss(); toast.dismiss();
}, 100); }, 100);

View File

@ -111,12 +111,19 @@ export const TableListBetter: React.FC<any> = ({
)} )}
/> />
{"Loading..."} {"Loading..."}
</> </>,
{
duration: Infinity,
}
); );
try {
if (Array.isArray(onLoad)) { if (Array.isArray(onLoad)) {
local.data = onLoad; local.data = onLoad;
local.render(); local.render();
setData(onLoad); setData(onLoad);
setTimeout(() => {
toast.dismiss();
}, 100);
} else { } else {
const res: any = onLoad({ const res: any = onLoad({
search: local.search, search: local.search,
@ -132,7 +139,7 @@ export const TableListBetter: React.FC<any> = ({
setData(e); setData(e);
setTimeout(() => { setTimeout(() => {
toast.dismiss(); toast.dismiss();
}, 2000); }, 100);
}); });
} else { } else {
local.data = res; local.data = res;
@ -141,9 +148,15 @@ export const TableListBetter: React.FC<any> = ({
setData(res); setData(res);
setTimeout(() => { setTimeout(() => {
toast.dismiss(); toast.dismiss();
}, 2000); }, 100);
} }
} }
} catch (ex: any) {
console.error(get(ex, "response.data.meta.message") || ex.message);
}
setTimeout(() => {
toast.dismiss();
}, 100);
}, },
}); });
const cloneListFM = (data: any[]) => { const cloneListFM = (data: any[]) => {
@ -173,8 +186,12 @@ export const TableListBetter: React.FC<any> = ({
)} )}
/> />
{"Loading..."} {"Loading..."}
</> </>,
{
duration: Infinity,
}
); );
try {
if (typeof onCount === "function") { if (typeof onCount === "function") {
const res = await onCount(); const res = await onCount();
local.count = res; local.count = res;
@ -187,6 +204,9 @@ export const TableListBetter: React.FC<any> = ({
cloneListFM(onLoad); cloneListFM(onLoad);
local.render(); local.render();
setData(onLoad); setData(onLoad);
setTimeout(() => {
toast.dismiss();
}, 100);
} else { } else {
const res: any = await onLoad({ const res: any = await onLoad({
search: local.search, search: local.search,
@ -200,8 +220,14 @@ export const TableListBetter: React.FC<any> = ({
setData(res); setData(res);
setTimeout(() => { setTimeout(() => {
toast.dismiss(); toast.dismiss();
}, 2000); }, 100);
} }
} catch (ex: any) {
console.error(get(ex, "response.data.meta.message") || ex.message);
}
setTimeout(() => {
toast.dismiss();
}, 100);
}; };
if (typeof onInit === "function") { if (typeof onInit === "function") {
onInit(local); onInit(local);

View File

@ -46,11 +46,15 @@ export const actionToast = async (data: {
)} )}
/> />
{msg_load ? msg_load : " Load..."} {msg_load ? msg_load : " Load..."}
</> </>,
{
duration: Infinity,
}
); );
if (typeof task === "function") await task(); if (typeof task === "function") await task();
setTimeout(() => { setTimeout(() => {
toast.dismiss(); toast.dismiss();
setTimeout(() => {
toast.success( toast.success(
<div <div
className={cx( className={cx(
@ -66,7 +70,7 @@ export const actionToast = async (data: {
</div> </div>
</div> </div>
); );
}, 100);
if (typeof after === "function") after(); if (typeof after === "function") after();
if (typeof success === "function") success(); if (typeof success === "function") success();
}, 100); }, 100);
@ -74,6 +78,7 @@ export const actionToast = async (data: {
setTimeout(() => { setTimeout(() => {
if (typeof failed === "function") failed(); if (typeof failed === "function") failed();
toast.dismiss(); toast.dismiss();
setTimeout(() => {
toast.error( toast.error(
<div className="flex flex-col w-full"> <div className="flex flex-col w-full">
<div className="flex text-red-600 items-center"> <div className="flex text-red-600 items-center">
@ -95,5 +100,6 @@ export const actionToast = async (data: {
} }
); );
}, 100); }, 100);
}, 100);
} }
}; };