Refactor_Expenses #321
@ -23,10 +23,10 @@ const ExpenseList = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const { data, isLoading, isError,isInitialLoading,error } = useExpenseList(10, currentPage, filter);
|
const { data, isLoading, isError,isInitialLoading,error,isFetching } = useExpenseList(10, currentPage, filter);
|
||||||
if (isInitialLoading) return <ExpenseTableSkeleton/>;
|
if (isInitialLoading ) return <ExpenseTableSkeleton/>;
|
||||||
if (isError) return <div>{error}</div>;
|
if (isError) return <div>{error}</div>;
|
||||||
const items = data.data ?? [];
|
const items = data?.data ?? [];
|
||||||
const totalPages = data?.totalPages ?? 1;
|
const totalPages = data?.totalPages ?? 1;
|
||||||
const hasMore = currentPage < totalPages;
|
const hasMore = currentPage < totalPages;
|
||||||
|
|
||||||
@ -128,7 +128,7 @@ const ExpenseList = () => {
|
|||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{isLoading && (
|
{/* {isLoading && (
|
||||||
<tr>
|
<tr>
|
||||||
<td colSpan={7} className="text-center py-3">
|
<td colSpan={7} className="text-center py-3">
|
||||||
Loading...
|
Loading...
|
||||||
@ -142,7 +142,7 @@ const ExpenseList = () => {
|
|||||||
No expenses found.
|
No expenses found.
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
)}
|
)} */}
|
||||||
|
|
||||||
{!isInitialLoading &&
|
{!isInitialLoading &&
|
||||||
items.map((expense) => (
|
items.map((expense) => (
|
||||||
|
@ -39,10 +39,12 @@ export const ExpenseSchema = (expenseTypes) => {
|
|||||||
contentType: z.string().refine((val) => ALLOWED_TYPES.includes(val), {
|
contentType: z.string().refine((val) => ALLOWED_TYPES.includes(val), {
|
||||||
message: "Only PDF, PNG, JPG, or JPEG files are allowed",
|
message: "Only PDF, PNG, JPG, or JPEG files are allowed",
|
||||||
}),
|
}),
|
||||||
|
documentId:z.string().optional(),
|
||||||
fileSize: z.number().max(MAX_FILE_SIZE, {
|
fileSize: z.number().max(MAX_FILE_SIZE, {
|
||||||
message: "File size must be less than or equal to 5MB",
|
message: "File size must be less than or equal to 5MB",
|
||||||
}),
|
}),
|
||||||
description: z.string().optional(),
|
description: z.string().optional(),
|
||||||
|
isActive:z.boolean().default(true)
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
.nonempty({ message: "At least one file attachment is required" }),
|
.nonempty({ message: "At least one file attachment is required" }),
|
||||||
|
@ -19,7 +19,7 @@ import Avatar from "../common/Avatar";
|
|||||||
import {
|
import {
|
||||||
useCreateExpnse,
|
useCreateExpnse,
|
||||||
useExpense,
|
useExpense,
|
||||||
useUpdateExepse,
|
useUpdateExpense,
|
||||||
} from "../../hooks/useExpense";
|
} from "../../hooks/useExpense";
|
||||||
import ExpenseSkeleton from "./ExpenseSkeleton";
|
import ExpenseSkeleton from "./ExpenseSkeleton";
|
||||||
|
|
||||||
@ -49,7 +49,6 @@ const ManageExpense = ({ closeModal, expenseToEdit = null }) => {
|
|||||||
defaultValues: defaultExpense,
|
defaultValues: defaultExpense,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
const selectedproject = watch("projectId");
|
const selectedproject = watch("projectId");
|
||||||
const selectedProject = useSelector(
|
const selectedProject = useSelector(
|
||||||
(store) => store.localVariables.projectId
|
(store) => store.localVariables.projectId
|
||||||
@ -72,7 +71,6 @@ const ManageExpense = ({ closeModal, expenseToEdit = null }) => {
|
|||||||
error: EmpError,
|
error: EmpError,
|
||||||
} = useEmployeesByProject(selectedproject);
|
} = useEmployeesByProject(selectedproject);
|
||||||
|
|
||||||
|
|
||||||
const files = watch("billAttachments");
|
const files = watch("billAttachments");
|
||||||
const onFileChange = async (e) => {
|
const onFileChange = async (e) => {
|
||||||
const newFiles = Array.from(e.target.files);
|
const newFiles = Array.from(e.target.files);
|
||||||
@ -89,6 +87,7 @@ const ManageExpense = ({ closeModal, expenseToEdit = null }) => {
|
|||||||
contentType: file.type,
|
contentType: file.type,
|
||||||
fileSize: file.size,
|
fileSize: file.size,
|
||||||
description: "",
|
description: "",
|
||||||
|
isActive:true
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
@ -114,12 +113,23 @@ const ManageExpense = ({ closeModal, expenseToEdit = null }) => {
|
|||||||
new Promise((resolve, reject) => {
|
new Promise((resolve, reject) => {
|
||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
reader.readAsDataURL(file);
|
reader.readAsDataURL(file);
|
||||||
reader.onload = () => resolve(reader.result.split(",")[1]); // base64 only, no prefix
|
reader.onload = () => resolve(reader.result.split(",")[1]);
|
||||||
reader.onerror = (error) => reject(error);
|
reader.onerror = (error) => reject(error);
|
||||||
});
|
});
|
||||||
const removeFile = (index) => {
|
const removeFile = (index) => {
|
||||||
|
if (expenseToEdit) {
|
||||||
|
const newFiles = files.map((file, i) => {
|
||||||
|
if (file.documentId !== index) return file;
|
||||||
|
return {
|
||||||
|
...file,
|
||||||
|
isActive: false,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
setValue("billAttachments", newFiles, { shouldValidate: true });
|
||||||
|
} else {
|
||||||
const newFiles = files.filter((_, i) => i !== index);
|
const newFiles = files.filter((_, i) => i !== index);
|
||||||
setValue("billAttachments", newFiles, { shouldValidate: true });
|
setValue("billAttachments", newFiles, { shouldValidate: true });
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -141,15 +151,17 @@ const ManageExpense = ({ closeModal, expenseToEdit = null }) => {
|
|||||||
fileName: doc.fileName,
|
fileName: doc.fileName,
|
||||||
base64Data: null,
|
base64Data: null,
|
||||||
contentType: doc.contentType,
|
contentType: doc.contentType,
|
||||||
|
documentId: doc.documentId,
|
||||||
fileSize: 0,
|
fileSize: 0,
|
||||||
description: "",
|
description: "",
|
||||||
preSignedUrl: doc.preSignedUrl,
|
preSignedUrl: doc.preSignedUrl,
|
||||||
|
isActive: doc.isActive || true,
|
||||||
}))
|
}))
|
||||||
: [],
|
: [],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}, [data, reset, employees]);
|
}, [data, reset, employees]);
|
||||||
const { mutate: UpdateExpense, isPending } = useUpdateExepse(() =>
|
const { mutate: ExpenseUpdate, isPending } = useUpdateExpense(() =>
|
||||||
handleClose()
|
handleClose()
|
||||||
);
|
);
|
||||||
const { mutate: CreateExpense, isPending: createPending } = useCreateExpnse(
|
const { mutate: CreateExpense, isPending: createPending } = useCreateExpnse(
|
||||||
@ -160,7 +172,7 @@ const ManageExpense = ({ closeModal, expenseToEdit = null }) => {
|
|||||||
const onSubmit = (payload) => {
|
const onSubmit = (payload) => {
|
||||||
if (expenseToEdit) {
|
if (expenseToEdit) {
|
||||||
const editPayload = { ...payload, id: data.id };
|
const editPayload = { ...payload, id: data.id };
|
||||||
UpdateExpense({ id: data.id, payload: editPayload });
|
ExpenseUpdate({ id: data.id, payload: editPayload });
|
||||||
} else {
|
} else {
|
||||||
CreateExpense(payload);
|
CreateExpense(payload);
|
||||||
}
|
}
|
||||||
@ -176,12 +188,20 @@ const ManageExpense = ({ closeModal, expenseToEdit = null }) => {
|
|||||||
reset();
|
reset();
|
||||||
closeModal();
|
closeModal();
|
||||||
};
|
};
|
||||||
if(EmpLoading || StatusLoadding || projectLoading || ExpenseLoading || isLoading) return <ExpenseSkeleton/>
|
if (
|
||||||
|
EmpLoading ||
|
||||||
|
StatusLoadding ||
|
||||||
|
projectLoading ||
|
||||||
|
ExpenseLoading ||
|
||||||
|
isLoading
|
||||||
|
)
|
||||||
|
return <ExpenseSkeleton />;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="container p-3">
|
<div className="container p-3">
|
||||||
<h5 className="m-0">{expenseToEdit ? "Update Expense ": "Create New Expense"}</h5>
|
<h5 className="m-0">
|
||||||
|
{expenseToEdit ? "Update Expense " : "Create New Expense"}
|
||||||
|
</h5>
|
||||||
<form id="expenseForm" onSubmit={handleSubmit(onSubmit)}>
|
<form id="expenseForm" onSubmit={handleSubmit(onSubmit)}>
|
||||||
<div className="row my-2">
|
<div className="row my-2">
|
||||||
<div className="col-md-6">
|
<div className="col-md-6">
|
||||||
@ -458,10 +478,16 @@ const ManageExpense = ({ closeModal, expenseToEdit = null }) => {
|
|||||||
{errors.billAttachments.message}
|
{errors.billAttachments.message}
|
||||||
</small>
|
</small>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{files.length > 0 && (
|
{files.length > 0 && (
|
||||||
<div className="d-block">
|
<div className="d-block">
|
||||||
{files.map((file, idx) => (
|
{files
|
||||||
|
.filter((file) => {
|
||||||
|
if (expenseToEdit) {
|
||||||
|
return file.isActive;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.map((file, idx) => (
|
||||||
<a
|
<a
|
||||||
key={idx}
|
key={idx}
|
||||||
className="d-flex justify-content-between text-start p-1"
|
className="d-flex justify-content-between text-start p-1"
|
||||||
@ -481,7 +507,7 @@ const ManageExpense = ({ closeModal, expenseToEdit = null }) => {
|
|||||||
className="bx bx-trash bx-sm cursor-pointer text-danger"
|
className="bx bx-trash bx-sm cursor-pointer text-danger"
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
removeFile(idx);
|
removeFile(expenseToEdit ? file.documentId : idx);
|
||||||
}}
|
}}
|
||||||
></i>
|
></i>
|
||||||
</a>
|
</a>
|
||||||
@ -492,9 +518,12 @@ const ManageExpense = ({ closeModal, expenseToEdit = null }) => {
|
|||||||
{Array.isArray(errors.billAttachments) &&
|
{Array.isArray(errors.billAttachments) &&
|
||||||
errors.billAttachments.map((fileError, index) => (
|
errors.billAttachments.map((fileError, index) => (
|
||||||
<div key={index} className="danger-text small mt-1">
|
<div key={index} className="danger-text small mt-1">
|
||||||
{fileError?.fileSize?.message ||
|
{
|
||||||
|
(fileError?.fileSize?.message ||
|
||||||
fileError?.contentType?.message ||
|
fileError?.contentType?.message ||
|
||||||
fileError?.base64Data?.message}
|
fileError?.base64Data?.message,
|
||||||
|
fileError?.documentId.message)
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
@ -511,6 +540,7 @@ const ManageExpense = ({ closeModal, expenseToEdit = null }) => {
|
|||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
type="reset"
|
type="reset"
|
||||||
|
disabled={isPending || createPending}
|
||||||
onClick={handleClose}
|
onClick={handleClose}
|
||||||
className="btn btn-secondary btn-sm mt-3"
|
className="btn btn-secondary btn-sm mt-3"
|
||||||
>
|
>
|
||||||
|
@ -16,18 +16,16 @@ export const useExpenseList = (pageSize, pageNumber, filter) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const useExpense = (ExpenseId) => {
|
export const useExpense = (ExpenseId) => {
|
||||||
|
|
||||||
console.log("ExpenseId:", ExpenseId, "Enabled:", ExpenseId !== undefined && ExpenseId !== null);
|
|
||||||
return useQuery({
|
return useQuery({
|
||||||
queryKey: ["Expense", ExpenseId],
|
queryKey: ["Expense", ExpenseId],
|
||||||
queryFn: async () => await ExpenseRepository.GetExpenseDetails(ExpenseId).then(
|
queryFn: async () =>
|
||||||
|
await ExpenseRepository.GetExpenseDetails(ExpenseId).then(
|
||||||
(res) => res.data
|
(res) => res.data
|
||||||
),
|
),
|
||||||
enabled: !!ExpenseId,
|
enabled: !!ExpenseId,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// ---------------------------Mutation---------------------------------------------
|
// ---------------------------Mutation---------------------------------------------
|
||||||
|
|
||||||
export const useCreateExpnse = (onSuccessCallBack) => {
|
export const useCreateExpnse = (onSuccessCallBack) => {
|
||||||
@ -37,8 +35,8 @@ export const useCreateExpnse = (onSuccessCallBack) => {
|
|||||||
await ExpenseRepository.CreateExpense(payload);
|
await ExpenseRepository.CreateExpense(payload);
|
||||||
},
|
},
|
||||||
onSuccess: (_, variables) => {
|
onSuccess: (_, variables) => {
|
||||||
|
queryClient.invalidateQueries({ queryKey: ["Expenses"] });
|
||||||
showToast("Expense Created Successfully", "success");
|
showToast("Expense Created Successfully", "success");
|
||||||
queryClient.invalidateQueries({ queryKey: ["expenses"] });
|
|
||||||
if (onSuccessCallBack) onSuccessCallBack();
|
if (onSuccessCallBack) onSuccessCallBack();
|
||||||
},
|
},
|
||||||
onError: (error) => {
|
onError: (error) => {
|
||||||
@ -50,18 +48,67 @@ export const useCreateExpnse = (onSuccessCallBack) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useUpdateExepse =()=>{
|
export const useUpdateExpense = (onSuccessCallBack) => {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
|
return useMutation({
|
||||||
return useMutation({
|
mutationFn: async ({ id, payload }) => {
|
||||||
mutationFn:async ({id,payload})=>{
|
const response = await ExpenseRepository.UpdateExpense(id, payload);
|
||||||
const response = await ExpenseRepository.UpdateExpense(id,payload)
|
return response.data;
|
||||||
},
|
},
|
||||||
onSuccess:(updatedExpense,variables)=>{
|
onSuccess: (updatedExpense, variables) => {
|
||||||
// updation list and details
|
// queryClient.setQueriesData(
|
||||||
}
|
// {queryKey:['expenses'],exact:true},
|
||||||
})
|
// (oldData) => {
|
||||||
}
|
// if (!oldData || !oldData.data) return oldData;
|
||||||
|
|
||||||
|
// const updatedList = oldData.data.map((expense) => {
|
||||||
|
// if (expense.id !== variables.id) return expense;
|
||||||
|
|
||||||
|
// return {
|
||||||
|
// ...expense,
|
||||||
|
// project:
|
||||||
|
// expense.project.id !== updatedExpense.project.id
|
||||||
|
// ? updatedExpense.project
|
||||||
|
// : expense.project,
|
||||||
|
// expensesType:
|
||||||
|
// expense.expensesType.id !== updatedExpense.expensesType.id
|
||||||
|
// ? updatedExpense.expensesType
|
||||||
|
// : expense.expensesType,
|
||||||
|
// paymentMode:
|
||||||
|
// expense.paymentMode.id !== updatedExpense.paymentMode.id
|
||||||
|
// ? updatedExpense.paymentMode
|
||||||
|
// : expense.paymentMode,
|
||||||
|
// paidBy:
|
||||||
|
// expense.paidBy.id !== updatedExpense.paidBy.id
|
||||||
|
// ? updatedExpense.paidBy
|
||||||
|
// : expense.paidBy,
|
||||||
|
// createdBy:
|
||||||
|
// expense.createdBy.id !== updatedExpense.createdBy.id
|
||||||
|
// ? updatedExpense.createdBy
|
||||||
|
// : expense.createdBy,
|
||||||
|
// createdAt: updatedExpense.createdAt,
|
||||||
|
// status: updatedExpense.status,
|
||||||
|
// nextStatus: updatedExpense.nextStatus,
|
||||||
|
// preApproved: updatedExpense.preApproved,
|
||||||
|
// transactionDate: updatedExpense.transactionDate,
|
||||||
|
// amount: updatedExpense.amount,
|
||||||
|
// };
|
||||||
|
// });
|
||||||
|
|
||||||
|
// return {
|
||||||
|
// ...oldData,
|
||||||
|
// data: updatedList,
|
||||||
|
// };
|
||||||
|
// }
|
||||||
|
// );
|
||||||
|
queryClient.removeQueries({queryKey:['Expense', variables.id]});
|
||||||
|
queryClient.invalidateQueries({queryKey:['Expenses']})
|
||||||
|
showToast('Expense updated Successfully', 'success');
|
||||||
|
|
||||||
|
if (onSuccessCallBack) onSuccessCallBack();
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
export const useActionOnExpense = (onSuccessCallBack) => {
|
export const useActionOnExpense = (onSuccessCallBack) => {
|
||||||
const queryClient = useQueryClient();
|
const queryClient = useQueryClient();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user