import React, { useEffect, useMemo, useState } from "react"; import { useProjectName } from "../../hooks/useProjects"; import Label from "../common/Label"; import { Controller, useForm } from "react-hook-form"; import { useCurrencies, useExpenseCategory, } from "../../hooks/masterHook/useMaster"; import DatePicker from "../common/DatePicker"; import { useCreatePaymentRequest, usePayee, usePaymentRequestDetail, useUpdatePaymentRequest, } from "../../hooks/useExpense"; import { zodResolver } from "@hookform/resolvers/zod"; import { formatFileSize, localToUtc } from "../../utils/appUtils"; import { defaultPaymentRequest, PaymentRequestSchema, } from "./PaymentRequestSchema"; import { EXPENSE_DRAFT, EXPENSE_STATUS, INR_CURRENCY_CODE, } from "../../utils/constants"; import Filelist from "../Expenses/Filelist"; import InputSuggestions from "../common/InputSuggestion"; import { useProfile } from "../../hooks/useProfile"; import { blockUI } from "../../utils/blockUI"; import { SelectProjectField } from "../common/Forms/SelectFieldServerSide"; function ManagePaymentRequest({ closeModal, requestToEdit = null }) { const { data, isLoading, isError, error: requestError, } = usePaymentRequestDetail(requestToEdit); const { profile } = useProfile(); const { projectNames, loading: projectLoading, error, isError: isProjectError, } = useProjectName(); const { data: currencyData, isLoading: currencyLoading, isError: currencyError, } = useCurrencies(); const { expenseCategories, loading: ExpenseLoading, error: ExpenseError, } = useExpenseCategory(); const { data: Payees, isLoading: isPayeeLoaing, isError: isPayeeError, error: payeeError, } = usePayee(); const schema = PaymentRequestSchema(expenseCategories); const { register, control, watch, handleSubmit, setValue, reset, formState: { errors }, } = useForm({ resolver: zodResolver(schema), defaultValues: defaultPaymentRequest, }); const [isItself, setisItself] = useState(false); const isDraft = useMemo(() => { if (!data) return false; return EXPENSE_STATUS.daft === data?.expenseStatus.id; }, [data]); const isProcessed = useMemo(() => { if (!data) return false; return EXPENSE_STATUS.payment_processed === data?.expenseStatus.id; }, [data]); 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 (requestToEdit) { 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 }); } }; const handleClose = () => { reset(); closeModal(); }; const { mutate: CreatePaymentRequest, isPending: createPending } = useCreatePaymentRequest(() => { handleClose(); }); const { mutate: PaymentRequestUpdate, isPending } = useUpdatePaymentRequest( () => handleClose() ); useEffect(() => { if (requestToEdit && data) { reset({ title: data.title || "", description: data.description || "", payee: data.payee || "", currencyId: data.currency.id || "", amount: data.amount || "", dueDate: data.dueDate?.slice(0, 10) || "", projectId: data.project.id || "", expenseCategoryId: data.expenseCategory.id || "", isAdvancePayment: data.isAdvancePayment || false, billAttachments: data.attachments ? data?.attachments?.map((doc) => ({ fileName: doc.fileName, base64Data: null, contentType: doc.contentType, documentId: doc.id, fileSize: 0, description: "", preSignedUrl: doc.preSignedUrl, isActive: doc.isActive ?? true, })) : [], }); } }, [data, reset]); useEffect(() => { if (!requestToEdit && currencyData && currencyData?.length > 0) { const inrCurrency = currencyData.find((c) => c.id === INR_CURRENCY_CODE); if (inrCurrency) { setValue("currencyId", INR_CURRENCY_CODE, { shouldValidate: true }); } } }, [currencyData, requestToEdit, setValue]); const onSubmit = (fromdata) => { let payload = { ...fromdata, dueDate: localToUtc(fromdata.dueDate), payee: isItself ? `${profile?.employeeInfo?.firstName} ${profile?.employeeInfo?.lastName}` : fromdata.payee, }; if (requestToEdit) { const editPayload = { ...payload, id: data.id, payee: isItself ? `${profile?.employeeInfo?.firstName} ${profile?.employeeInfo?.lastName}` : fromdata.payee, }; PaymentRequestUpdate({ id: data.id, payload: editPayload }); } else { CreatePaymentRequest(payload); } }; const handleSetItSelf = (e) => { setisItself(e.target.value); let name = `${profile?.employeeInfo.firstName} ${profile?.employeeInfo.lastName}`; setValue("payee", name); }; return (
{requestToEdit ? "Update Payment Request " : "Create Payment Request"}
{/* Project and Category */}
{/* */} {/* */} setValue("projectId", val, { shouldDirty: true, shouldValidate: true, }) } disabled={ data?.recurringPayment?.isVariable && !isDraft && !isProcessed } /> {errors.projectId && ( {errors.projectId.message} )}
{errors.expenseCategoryId && ( {errors.expenseCategoryId.message} )}
{/* Title and Advance Payment */}
{errors.title && ( {errors.title.message} )}
(
field.onChange(true)} // send boolean true />
field.onChange(false)} // send boolean false />
)} /> {errors.isVariable && ( {errors.isVariable.message} )}
{/* Date and Amount */}
{errors.dueDate && ( {errors.dueDate.message} )}
{errors.amount && ( {errors.amount.message} )}
{/* Payee and Currency */}
setValue("payee", val, { shouldValidate: true }) } error={errors.payee?.message} disabled={ data?.recurringPayment?.isVariable && !isDraft && !isProcessed } /> {/* Checkbox below input */}
{errors.currencyId && ( {errors.currencyId.message} )}
{/* Description */}
{errors.description && ( {errors.description.message} )}
{/* Upload Document */}
document.getElementById("billAttachments").click()} > Click to select or click here to browse (PDF, JPG, PNG, max 5MB) { onFileChange(e); e.target.value = ""; }} disabled={ data?.recurringPayment?.isVariable && !isDraft && !isProcessed } />
{errors.billAttachments && ( {errors.billAttachments.message} )} {files?.length > 0 && ( )} {Array.isArray(errors.billAttachments) && errors.billAttachments.map((fileError, index) => (
{ (fileError?.fileSize?.message || fileError?.contentType?.message || fileError?.base64Data?.message, fileError?.documentId?.message) }
))}
); } export default ManagePaymentRequest;