import { zodResolver } from "@hookform/resolvers/zod"; import React, { useEffect, useState } from "react"; import { useForm } from "react-hook-form"; import { defaultExpense, ExpenseSchema } from "./ExpenseSchema"; import { formatFileSize } from "../../utils/appUtils"; import { useProjectName } from "../../hooks/useProjects"; import { useDispatch, useSelector } from "react-redux"; import { changeMaster } from "../../slices/localVariablesSlice"; import useMaster, { useExpenseStatus, useExpenseType, usePaymentMode, } from "../../hooks/masterHook/useMaster"; import { useEmployeesAllOrByProjectId, useEmployeesByProject, } from "../../hooks/useEmployees"; import Avatar from "../common/Avatar"; import { useCreateExpnse, useExpense, useUpdateExpense, } from "../../hooks/useExpense"; import ExpenseSkeleton from "./ExpenseSkeleton"; import moment from "moment"; import DatePicker from "../common/DatePicker"; const ManageExpense = ({ closeModal, expenseToEdit = null }) => { const { data, isLoading, error: ExpenseErrorLoad, } = useExpense(expenseToEdit); const [ExpenseType, setExpenseType] = useState(); const dispatch = useDispatch(); const { ExpenseTypes, loading: ExpenseLoading, error: ExpenseError, } = useExpenseType(); const schema = ExpenseSchema(ExpenseTypes); const { register, handleSubmit, watch, setValue, reset, control, formState: { errors }, } = useForm({ resolver: zodResolver(schema), defaultValues: defaultExpense, }); const selectedproject = watch("projectId"); const selectedProject = useSelector( (store) => store.localVariables.projectId ); const { projectNames, loading: projectLoading, error } = useProjectName(); debugger const { PaymentModes, loading: PaymentModeLoading, error: PaymentModeError, } = usePaymentMode(); const { ExpenseStatus, loading: StatusLoadding, error: stausError, } = useExpenseStatus(); const { employees, loading: EmpLoading, error: EmpError, } = useEmployeesByProject(selectedproject); const files = watch("billAttachments"); const onFileChange = async (e) => { const newFiles = Array.from(e.target.files); if (newFiles.length === 0) return; const existingFiles = watch("billAttachments") || []; const parsedFiles = await Promise.all( newFiles.map(async (file) => { const base64Data = await toBase64(file); return { fileName: file.name, base64Data, contentType: file.type, fileSize: file.size, description: "", isActive:true }; }) ); const combinedFiles = [ ...existingFiles, ...parsedFiles.filter( (newFile) => !existingFiles.some( (f) => f.fileName === newFile.fileName && f.fileSize === newFile.fileSize ) ), ]; setValue("billAttachments", combinedFiles, { shouldDirty: true, shouldValidate: true, }); }; const toBase64 = (file) => new Promise((resolve, reject) => { const reader = new FileReader(); reader.readAsDataURL(file); reader.onload = () => resolve(reader.result.split(",")[1]); reader.onerror = (error) => reject(error); }); 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); setValue("billAttachments", newFiles, { shouldValidate: true }); } }; useEffect(() => { if (expenseToEdit && data && employees) { reset({ projectId: data.project.id || "", expensesTypeId: data.expensesType.id || "", paymentModeId: data.paymentMode.id || "", paidById: data.paidBy.id || "", transactionDate: data.transactionDate?.slice(0, 10) || "", transactionId: data.transactionId || "", description: data.description || "", location: data.location || "", supplerName: data.supplerName || "", amount: data.amount || "", noOfPersons: data.noOfPersons || "", billAttachments: data.documents ? data.documents.map((doc) => ({ fileName: doc.fileName, base64Data: null, contentType: doc.contentType, documentId: doc.documentId, fileSize: 0, description: "", preSignedUrl: doc.preSignedUrl, isActive: doc.isActive ?? true, })) : [], }); } }, [data, reset, employees]); const { mutate: ExpenseUpdate, isPending } = useUpdateExpense(() => handleClose() ); const { mutate: CreateExpense, isPending: createPending } = useCreateExpnse( () => { handleClose(); } ); const onSubmit = (fromdata) => { let payload = {...fromdata,transactionDate: moment.utc(fromdata.transactionDate, 'DD-MM-YYYY').toISOString()} if (expenseToEdit) { const editPayload = { ...payload, id: data.id }; ExpenseUpdate({ id: data.id, payload: editPayload }); } else { CreateExpense(payload); } }; const ExpenseTypeId = watch("expensesTypeId"); useEffect(() => { setExpenseType(ExpenseTypes?.find((type) => type.id === ExpenseTypeId)); }, [ExpenseTypeId]); const handleClose = () => { reset(); closeModal(); }; if ( StatusLoadding || projectLoading || ExpenseLoading || isLoading ) return ; return (
{expenseToEdit ? "Update Expense " : "Create New Expense"}
{errors.projectId && ( {errors.projectId.message} )}
{errors.expensesTypeId && ( {errors.expensesTypeId.message} )}
{errors.paymentModeId && ( {errors.paymentModeId.message} )}
{errors.paidById && ( {errors.paidById.message} )}
{/* */} {errors.transactionDate && ( {errors.transactionDate.message} )}
{errors.amount && ( {errors.amount.message} )}
{errors.supplerName && ( {errors.supplerName.message} )}
{errors.location && ( {errors.location.message} )}
{errors.transactionId && ( {errors.transactionId.message} )}
{ExpenseType?.noOfPersonsRequired && (
{errors.noOfPersons && ( {errors.noOfPersons.message} )}
)}
{errors.description && ( {errors.description.message} )}
document.getElementById("billAttachments").click()} > Click to select or click here to browse (PDF, JPG, PNG, max 5MB) { onFileChange(e); e.target.value = ""; }} />
{errors.billAttachments && ( {errors.billAttachments.message} )} {files.length > 0 && (
{files .filter((file) => { if (expenseToEdit) { return file.isActive; } return true; }) .map((file, idx) => (
{file.fileName} {file.fileSize ? formatFileSize(file.fileSize) : ""}
{ e.preventDefault(); removeFile(expenseToEdit ? file.documentId : idx); }} >
))}
)} {Array.isArray(errors.billAttachments) && errors.billAttachments.map((fileError, index) => (
{ (fileError?.fileSize?.message || fileError?.contentType?.message || fileError?.base64Data?.message, fileError?.documentId?.message) }
))}
{" "}
); }; export default ManageExpense;