feat: enhance form validation logic to include table field checks and improve error handling

This commit is contained in:
faisolavolut 2025-02-26 08:19:48 +07:00
parent 6b122bafd8
commit 6c311f4b2e
5 changed files with 114 additions and 43 deletions

View File

@ -57,29 +57,79 @@ export const Form: React.FC<any> = ({
); );
try { try {
const fieldDate: any = local?.fields; const fieldDate: any = local?.fields;
let isError = false;
let error: Record<string, string> = {};
try { try {
const dateFields = Object.values(fieldDate).filter( const dateFields = Object.values(fieldDate).filter(
(field: any) => get(field, "type") === "date" (field: any) => get(field, "type") === "date"
); );
if (Array.isArray(dateFields) && dateFields?.length) { if (dateFields.length) {
dateFields.map((e: any) => { dateFields.forEach((e: any) => {
if (e?.name) if (e?.name) {
local.data[e?.name] = normalDate(local.data[e?.name]); local.data[e.name] = normalDate(local.data[e.name]);
}
}); });
local.render(); local.render();
} }
} catch (ex) {} } catch (ex) {
console.error("Error processing date fields:", ex);
}
const fieldRequired = Object.values(fieldDate).filter( const fieldRequired = Object.values(fieldDate).filter(
(field: any) => field?.required || field?.type === "table"
);
if (fieldRequired.length) {
fieldRequired.forEach((e: any) => {
let keys = e?.name;
const type = e?.type;
if (type === "table" && e?.fields?.length) {
e.fields.forEach((item: any, index: number) => {
let errorChilds: Record<string, string> = {};
const fieldRequired = Object.values(item?.fields).filter(
(field: any) => field?.required (field: any) => field?.required
); );
let error = {} as any; console.log({ fieldRequired });
if (Array.isArray(fieldRequired) && fieldRequired?.length) { fieldRequired.forEach((subField: any) => {
fieldRequired.map((e: any) => { let keySub = subField?.name;
const type = e?.type; const typeSub = subField?.type;
let keys = e?.name; const val = get(local.data, `${keys}[${index}].${keySub}`);
if (["dropdown-async", "multi-async"].includes(typeSub)) {
keySub = subField?.target || subField?.name;
}
if (
[
"multi-dropdown",
"checkbox",
"multi-upload",
"multi-async",
].includes(typeSub)
) {
if (!Array.isArray(get(local.data, keys)) || !val?.length) {
errorChilds[subField.name] =
"This field requires at least one item.";
isError = true;
}
} else if (!val) {
errorChilds[subField.name] = "Please fill out this field.";
isError = true;
}
console.log({
keySub,
data: get(local.data, `${keys}[${index}]`),
val,
});
});
item.error = errorChilds;
});
} else {
if (["dropdown-async", "multi-async"].includes(type)) { if (["dropdown-async", "multi-async"].includes(type)) {
keys = e?.target || e?.name; keys = e?.target || e?.name;
} }
const val = get(local.data, keys);
if ( if (
[ [
"multi-dropdown", "multi-dropdown",
@ -88,27 +138,26 @@ export const Form: React.FC<any> = ({
"multi-async", "multi-async",
].includes(type) ].includes(type)
) { ) {
if ( if (!Array.isArray(val) || !val?.length) {
!Array.isArray(get(local.data, keys)) || error[e.name] = "This field requires at least one item.";
!get(local.data, `${keys}.length`) isError = true;
) {
error[e?.name] = `This field requires at least one item.`;
} }
} else { } else if (!val) {
if (!get(local.data, keys)) { error[e.name] = "Please fill out this field.";
error[e?.name] = `Please fill out this field.`; isError = true;
} }
} }
}); });
} }
local.error = error; local.error = error;
local.render(); local.render();
console.log({ error }); console.log(isError);
if (Object.keys(error).length) { // if (isError) {
throw new Error("please check your input field."); // throw new Error("please check your input field.");
} else { // } else {
await onSubmit(local); // await onSubmit(local);
} // }
setTimeout(() => { setTimeout(() => {
toast.success( toast.success(
<div <div

View File

@ -285,7 +285,7 @@ export const TypeAsyncDropdown: React.FC<any> = ({
placeholder={disabled ? "" : placeholderField} placeholder={disabled ? "" : placeholderField}
isDisabled={disabled} isDisabled={disabled}
className={cx( className={cx(
"rounded-md border-none text-sm", "rounded-md border-none text-sm w-full",
css` css`
[role="listbox"] { [role="listbox"] {
padding: 0px !important; padding: 0px !important;

View File

@ -230,6 +230,7 @@ export const TypeInput: React.FC<any> = ({
asSingle={true} asSingle={true}
useRange={false} useRange={false}
onChange={(value) => { onChange={(value) => {
console.log(value);
fm.data[name] = value?.startDate fm.data[name] = value?.startDate
? new Date(value?.startDate) ? new Date(value?.startDate)
: null; : null;

View File

@ -154,6 +154,12 @@ export const TableEditBetter: React.FC<any> = ({
local.data = fm?.data[name] || []; local.data = fm?.data[name] || [];
local.render(); local.render();
console.log(columns); console.log(columns);
fm.fields[name] = {
name: name,
type: "table",
fields: [],
};
fm.render();
}, []); }, []);
const handleResize = (index: any, width: any) => { const handleResize = (index: any, width: any) => {
@ -168,9 +174,11 @@ export const TableEditBetter: React.FC<any> = ({
<div className="tbl-wrapper flex flex-grow flex-col"> <div className="tbl-wrapper flex flex-grow flex-col">
{!disabledHeader ? ( {!disabledHeader ? (
<div className="head-tbl-list block items-start justify-between bg-white px-0 py-4 sm:flex"> <div className="head-tbl-list block items-start justify-between bg-white px-0 py-4 sm:flex">
<div className="flex flex-row items-end"> <div className="flex flex-row h-full">
<div className="sm:flex flex flex-col space-y-2"> <div className="sm:flex flex flex-col space-y-2 flex-grow">
<div className="flex">{sideLeft ? sideLeft(local) : <></>}</div> <div className="flex flex-grow flex-row">
{sideLeft ? sideLeft(local) : <></>}
</div>
</div> </div>
</div> </div>
<div className="ml-auto flex items-center flex-row"> <div className="ml-auto flex items-center flex-row">
@ -265,10 +273,21 @@ export const TableEditBetter: React.FC<any> = ({
)} )}
<tbody> <tbody>
{local.data.map((row: any, index: any) => { {local.data.map((row: any, index: any) => {
if (
typeof fm.fields?.[name]?.fields?.[index]?.fields !==
"object"
) {
fm.fields[name].fields[index] = {
fields: {},
};
}
const fm_row = { const fm_row = {
...fm, ...fm,
name: name,
type: "table",
data: row, data: row,
error: fm.fields?.[name]?.fields?.[index]?.error,
fields: fm.fields?.[name]?.fields?.[index]?.fields,
render: () => { render: () => {
local.render(); local.render();
fm.data[name] = local.data; fm.data[name] = local.data;
@ -302,7 +321,9 @@ export const TableEditBetter: React.FC<any> = ({
return ( return (
<td <td
key={`row_${name}_${index}_${col?.accessorKey}_${idx}`} key={`row_${name}_${index}_${col?.accessorKey}_${idx}`}
className={"table-header-tbl capitalize"} className={
"table-header-tbl align-top capitalize"
}
> >
<div className="p-1">{renderData}</div> <div className="p-1">{renderData}</div>
</td> </td>

View File

@ -74,9 +74,7 @@ const Input: React.FC<Props> = (e: Props) => {
const handleInputChange = useCallback( const handleInputChange = useCallback(
(e: React.ChangeEvent<HTMLInputElement>) => { (e: React.ChangeEvent<HTMLInputElement>) => {
const inputValue = e.target.value; const inputValue = e.target.value;
const dates = []; const dates = [];
if (asSingle) { if (asSingle) {
const date = parseFormattedDate(inputValue, displayFormat); const date = parseFormattedDate(inputValue, displayFormat);
if (dateIsValid(date.toDate())) { if (dateIsValid(date.toDate())) {
@ -121,6 +119,8 @@ const Input: React.FC<Props> = (e: Props) => {
if (dates[1]) if (dates[1])
changeDayHover(dayjs(dates[1]).add(-1, "day").format(DATE_FORMAT)); changeDayHover(dayjs(dates[1]).add(-1, "day").format(DATE_FORMAT));
else changeDayHover(dates[0]); else changeDayHover(dates[0]);
} else {
changeDatepickerValue(null, e.target);
} }
changeInputText(e.target.value); changeInputText(e.target.value);