diff --git a/src/components/Expenses/ExpenseList.jsx b/src/components/Expenses/ExpenseList.jsx index a56d340b..3b58316e 100644 --- a/src/components/Expenses/ExpenseList.jsx +++ b/src/components/Expenses/ExpenseList.jsx @@ -12,7 +12,6 @@ import { } from "../../utils/constants"; import { formatCurrency, - formatFigure, getColorNameFromHex, useDebounce, } from "../../utils/appUtils"; @@ -167,7 +166,7 @@ const ExpenseList = ({ filters, groupBy = "transactionDate", searchText }) => { { key: "amount", label: "Amount", - getValue: (e) => <>{formatFigure(e?.amount,{type:"currency",currency : e?.currency?.currencyCode ?? "INR"} )}, + getValue: (e) => <>{formatCurrency(e?.amount)}, isAlwaysVisible: true, align: "text-end", }, @@ -289,11 +288,11 @@ const ExpenseList = ({ filters, groupBy = "transactionDate", searchText }) => { (col.isAlwaysVisible || groupBy !== col.key) && ( -
{col.customRender + {col.customRender ? col.customRender(expense) - : col.getValue(expense)}
+ : col.getValue(expense)} ) )} @@ -308,7 +307,7 @@ const ExpenseList = ({ filters, groupBy = "transactionDate", searchText }) => { }) } > - { + {canDetetExpense(expense) && canEditExpense(expense) && (
+
+ )} */} diff --git a/src/components/Expenses/Filelist.jsx b/src/components/Expenses/Filelist.jsx index 208a596b..2b1ce74e 100644 --- a/src/components/Expenses/Filelist.jsx +++ b/src/components/Expenses/Filelist.jsx @@ -1,10 +1,9 @@ import React from "react"; import { formatFileSize, getIconByFileType } from "../../utils/appUtils"; -import Tooltip from "../common/Tooltip"; const Filelist = ({ files, removeFile, expenseToEdit }) => { return ( -
+
{files .filter((file) => { if (expenseToEdit) { @@ -13,12 +12,14 @@ const Filelist = ({ files, removeFile, expenseToEdit }) => { return true; }) .map((file, idx) => ( -
+
{/* File icon and info */}
@@ -33,17 +34,14 @@ const Filelist = ({ files, removeFile, expenseToEdit }) => {
- - { - e.preventDefault(); - debugger; - removeFile(expenseToEdit ? file.documentId : idx); - }} - > - + { + e.preventDefault(); + removeFile(expenseToEdit ? file.documentId : idx); + }} + >
@@ -53,44 +51,3 @@ const Filelist = ({ files, removeFile, expenseToEdit }) => { }; export default Filelist; - -export const FilelistView = ({ files, viewFile }) => { - return ( -
- {files?.map((file, idx) => ( -
-
- {/* File icon and info */} -
- - -
{ - e.preventDefault(); - viewFile({ - IsOpen: true, - Image: file.preSignedUrl, - }); - }} - > - - {file.fileName} - - - - {" "} - {file.fileSize ? formatFileSize(file.fileSize) : ""} - - -
-
-
-
- ))} -
- ); -}; diff --git a/src/components/Expenses/ManageExpense.jsx b/src/components/Expenses/ManageExpense.jsx index 4ee1679c..f3872614 100644 --- a/src/components/Expenses/ManageExpense.jsx +++ b/src/components/Expenses/ManageExpense.jsx @@ -31,7 +31,6 @@ import Label from "../common/Label"; import EmployeeSearchInput from "../common/EmployeeSearchInput"; import Filelist from "./Filelist"; - const ManageExpense = ({ closeModal, expenseToEdit = null }) => { const { data, @@ -129,7 +128,7 @@ const ManageExpense = ({ closeModal, expenseToEdit = null }) => { reader.onload = () => resolve(reader.result.split(",")[1]); reader.onerror = (error) => reject(error); }); - const removeFile = (index) => {documentId + const removeFile = (index) => { if (expenseToEdit) { const newFiles = files.map((file, i) => { if (file.documentId !== index) return file; @@ -149,7 +148,7 @@ const ManageExpense = ({ closeModal, expenseToEdit = null }) => { if (expenseToEdit && data) { reset({ projectId: data.project.id || "", - expenseCategoryId: data.expensesCategory?.id || "", + expensesCategoryId: data.expensesType.id || "", paymentModeId: data.paymentMode.id || "", paidById: data.paidBy.id || "", transactionDate: data.transactionDate?.slice(0, 10) || "", @@ -246,7 +245,7 @@ const ManageExpense = ({ closeModal, expenseToEdit = null }) => { + {errors.reimburseTransactionId && ( + + {errors.reimburseTransactionId.message} + + )} +
+
+ + + {errors.reimburseDate && ( + + {errors.reimburseDate.message} + + )} +
+
+ + +
+
+ )} +
+ {((nextStatusWithPermission.length > 0 && !IsRejectedExpense) || + (IsRejectedExpense && isCreatedBy)) && ( + <> + + - {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 = ""; - }} - /> -
- {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) - } -
- ))} -
-
-
- - -
- - - ); +const handleSetItSelf=(e)=>{ +setisItself(e.target.value); +setValue('payee',profile?.employeeInfo.id) } -export default ManagePaymentRequest; + return ( +
+
+ {requestToEdit ? "Update Payment Request " : "Create Payment Request"} +
+
+ {/* Project and Category */} +
+
+ + + {errors.projectId && ( + {errors.projectId.message} + )} +
+ +
+ + + {errors.expenseCategoryId && ( + + {errors.expenseCategoryId.message} + + )} +
+ +
+ + {/* Title and Advance Payment */} +
+
+ + + {errors.title && ( + + {errors.title.message} + + )} +
+ +
+ + + {errors.isAdvancePayment && ( + {errors.isAdvancePayment.message} + )} +
+ + +
+ + + {/* Date and Amount */} +
+
+ + + + {errors.dueDate && ( + + {errors.dueDate.message} + + )} +
+ +
+ + + {errors.amount && ( + {errors.amount.message} + )} +
+
+ + {/* Payee and Currency */} +
+
+ + + {errors.payee && ( + + {errors.payee.message} + + )} + + {/* 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 = ""; + }} + /> +
+ {errors.billAttachments && ( + + {errors.billAttachments.message} + + )} + {Array.isArray(files) && files.length > 0 && ( + +
+ {files + .filter((file) => { + if (requestToEdit) { + return file.isActive; + } + return true; + }) + .map((file, idx) => ( + +
+ + {file.fileName} + + + {file.fileSize ? formatFileSize(file.fileSize) : ""} + +
+ { + e.preventDefault(); + removeFile(requestToEdit ? 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 ManagePaymentRequest diff --git a/src/components/PaymentRequest/PaymentRequestList.jsx b/src/components/PaymentRequest/PaymentRequestList.jsx index 3e8152d5..9e5f351d 100644 --- a/src/components/PaymentRequest/PaymentRequestList.jsx +++ b/src/components/PaymentRequest/PaymentRequestList.jsx @@ -6,7 +6,6 @@ import { } from "../../utils/constants"; import { formatCurrency, - formatFigure, getColorNameFromHex, useDebounce, } from "../../utils/appUtils"; @@ -132,7 +131,7 @@ const PaymentRequestList = ({ filters, groupBy = "submittedBy", search }) => { align: "text-start", getValue: (e) => ( <> - {formatFigure(e?.amount,{type:"currency",currency : e?.currency?.currencyCode})} + {formatCurrency(e?.amount)} {e.currency.currencyCode} ), @@ -254,7 +253,7 @@ const PaymentRequestList = ({ filters, groupBy = "submittedBy", search }) => {
{" "} - + {displayField} :{" "} {" "} @@ -272,9 +271,9 @@ const PaymentRequestList = ({ filters, groupBy = "submittedBy", search }) => { key={col.key} className={`d-table-cell ${col.align ?? ""}`} > -
{col?.customRender + {col?.customRender ? col?.customRender(paymentRequest) - : col?.getValue(paymentRequest)}
+ : col?.getValue(paymentRequest)} ) )} diff --git a/src/components/PaymentRequest/PaymentRequestSchema.js b/src/components/PaymentRequest/PaymentRequestSchema.js index 018b092f..fb813b63 100644 --- a/src/components/PaymentRequest/PaymentRequestSchema.js +++ b/src/components/PaymentRequest/PaymentRequestSchema.js @@ -7,48 +7,56 @@ const ALLOWED_TYPES = [ "image/jpg", "image/jpeg", ]; -export const PaymentRequestSchema = (expenseTypes, isItself) => { - return z.object({ - title: z.string().min(1, { message: "Project is required" }), - projectId: z.string().min(1, { message: "Project is required" }), - expenseCategoryId: z - .string() - .min(1, { message: "Expense Category is required" }), - currencyId: z.string().min(1, { message: "Currency is required" }), - dueDate: z.string().min(1, { message: "Date is required" }), - description: z.string().min(1, { message: "Description is required" }), - payee: z.string().min(1, { message: "Supplier name is required" }), - isAdvancePayment: z.boolean().optional(), - amount: z.coerce - .number({ - invalid_type_error: "Amount is required and must be a number", - }) - .min(1, "Amount must be Enter") - .refine((val) => /^\d+(\.\d{1,2})?$/.test(val.toString()), { - message: "Amount must have at most 2 decimal places", - }), - billAttachments: z - .array( - z.object({ - fileName: z.string().min(1, { message: "Filename is required" }), - base64Data: z.string().nullable(), - contentType: z.string().refine((val) => ALLOWED_TYPES.includes(val), { - message: "Only PDF, PNG, JPG, or JPEG files are allowed", - }), - documentId: z.string().optional(), - fileSize: z.number().max(MAX_FILE_SIZE, { - message: "File size must be less than or equal to 5MB", - }), - description: z.string().optional(), - isActive: z.boolean().default(true), +export const PaymentRequestSchema = (expenseTypes,isItself) => { + return z + .object({ + title: z.string().min(1, { message: "Project is required" }), + projectId: z.string().min(1, { message: "Project is required" }), + expenseCategoryId: z + .string() + .min(1, { message: "Expense Category is required" }), + currencyId: z + .string() + .min(1, { message: "Currency is required" }), + dueDate: z.string().min(1, { message: "Date is required" }), + description: z.string().min(1, { message: "Description is required" }), + payee: z.string().min(1, { message: "Supplier name is required" }), + isAdvancePayment: z.boolean().optional(), + amount: z.coerce + .number({ + invalid_type_error: "Amount is required and must be a number", }) - ) - .optional(), - }); -}; + .min(1, "Amount must be Enter") + .refine((val) => /^\d+(\.\d{1,2})?$/.test(val.toString()), { + message: "Amount must have at most 2 decimal places", + }), + billAttachments: z + .array( + z.object({ + fileName: z.string().min(1, { message: "Filename is required" }), + base64Data: z.string().nullable(), + contentType: z + .string() + .refine((val) => ALLOWED_TYPES.includes(val), { + message: "Only PDF, PNG, JPG, or JPEG files are allowed", + }), + documentId: z.string().optional(), + fileSize: z.number().max(MAX_FILE_SIZE, { + message: "File size must be less than or equal to 5MB", + }), + description: z.string().optional(), + isActive: z.boolean().default(true), + }) + ).refine((data)=>{ + if(isItself){ + payee.z.string().optional(); + } + }), + }) + }; export const defaultPaymentRequest = { - title: "", + title:"", description: "", payee: "", currencyId: "", @@ -56,10 +64,11 @@ export const defaultPaymentRequest = { dueDate: "", projectId: "", expenseCategoryId: "", - isAdvancePayment: boolean, + isAdvancePayment:boolean, billAttachments: [], }; + export const SearchPaymentRequestSchema = z.object({ projectIds: z.array(z.string()).optional(), statusIds: z.array(z.string()).optional(), @@ -82,6 +91,7 @@ export const defaultPaymentRequestFilter = { endDate: null, }; + export const PaymentRequestActionScheam = ( isTransaction = false, transactionDate @@ -90,16 +100,17 @@ export const PaymentRequestActionScheam = ( .object({ comment: z.string().min(1, { message: "Please leave comment" }), statusId: z.string().min(1, { message: "Please select a status" }), - paidTransactionId: z.string().nullable().optional(), + paymentRequestId: z.string().nullable().optional(), paidAt: z.string().nullable().optional(), paidById: z.string().nullable().optional(), + }) .superRefine((data, ctx) => { if (isTransaction) { - if (!data.paidTransactionId?.trim()) { + if (!data.paymentRequestId?.trim()) { ctx.addIssue({ code: z.ZodIssueCode.custom, - path: ["paidTransactionId"], + path: ["reimburseTransactionId"], message: "Reimburse Transaction ID is required", }); } @@ -110,7 +121,15 @@ export const PaymentRequestActionScheam = ( message: "Transacion Date is required", }); } - + // let reimburse_Date = localToUtc(data.reimburseDate); + // if (transactionDate > reimburse_Date) { + // ctx.addIssue({ + // code: z.ZodIssueCode.custom, + // path: ["reimburseDate"], + // message: + // "Reimburse Date must be greater than or equal to Expense created Date", + // }); + // } if (!data.paidById) { ctx.addIssue({ code: z.ZodIssueCode.custom, @@ -122,11 +141,11 @@ export const PaymentRequestActionScheam = ( }); }; -export const defaultPaymentRequestActionValues = { +export const defaultActionValues = { comment: "", statusId: "", paidTransactionId: null, paidAt: null, paidById: null, -}; +}; \ No newline at end of file diff --git a/src/components/PaymentRequest/PaymentStatusLogs.jsx b/src/components/PaymentRequest/PaymentStatusLogs.jsx deleted file mode 100644 index 9abb3fa5..00000000 --- a/src/components/PaymentRequest/PaymentStatusLogs.jsx +++ /dev/null @@ -1,93 +0,0 @@ -import { useState, useMemo } from "react"; -import Avatar from "../common/Avatar"; -import { formatUTCToLocalTime } from "../../utils/dateUtils"; -import Timeline from "../common/TimeLine"; -import moment from "moment"; -import { getColorNameFromHex } from "../../utils/appUtils"; -const PaymentStatusLogs = ({ data }) => { - const [visibleCount, setVisibleCount] = useState(4); - - const sortedLogs = useMemo(() => { - if (!data?.updateLogs) return []; - return [...data.updateLogs].sort( - (a, b) => new Date(b.updatedAt) - new Date(a.updatedAt) - ); - }, [data?.updateLogs]); - - const logsToShow = useMemo( - () => sortedLogs.slice(0, visibleCount), - [sortedLogs, visibleCount] - ); - - const timelineData = useMemo(() => { - return logsToShow.map((log, index) => ({ - id: index + 1, - title: log.nextStatus?.name || "Status Updated", - description: log.nextStatus?.description || "", - timeAgo: log.updatedAt, - color: getColorNameFromHex(log.nextStatus?.color) || "primary", - users: log.updatedBy - ? [ - { - firstName: log.updatedBy.firstName || "", - lastName: log?.updatedBy?.lastName || "", - role: log.updatedBy.jobRoleName || "", - avatar: log.updatedBy.photo, - }, - ] - : [], - })); - }, [logsToShow]); - - const handleShowMore = () => { - setVisibleCount((prev) => prev + 4); - }; - - return ( -
- {/*
- {logsToShow.map((log) => ( -
- - -
-
-
- {`${log.updatedBy.firstName} ${log.updatedBy.lastName}`} - - {log.action} - - - {formatUTCToLocalTime(log.updateAt, true)} - -
-
- {log.comment} -
-
-
-
- ))} -
- - {sortedLogs.length > visibleCount && ( -
- -
- )} */} - - -
- ); -}; - -export default PaymentStatusLogs; diff --git a/src/components/PaymentRequest/ViewPaymentRequest.jsx b/src/components/PaymentRequest/ViewPaymentRequest.jsx index 0d7d280b..7be1b5e3 100644 --- a/src/components/PaymentRequest/ViewPaymentRequest.jsx +++ b/src/components/PaymentRequest/ViewPaymentRequest.jsx @@ -1,4 +1,4 @@ -import { useEffect, useMemo, useState } from "react"; +import { useMemo, useState } from "react"; import { useActionOnExpense, useActionOnPaymentRequest, @@ -6,7 +6,6 @@ import { } from "../../hooks/useExpense"; import { formatCurrency, - formatFigure, getColorNameFromHex, getIconByFileType, localToUtc, @@ -29,18 +28,11 @@ import { useNavigate } from "react-router-dom"; import { usePaymentRequestContext } from "../../pages/PaymentRequest/PaymentRequestPage"; import { useHasUserPermission } from "../../hooks/useHasUserPermission"; import { - EXPENSE_DRAFT, EXPENSE_REJECTEDBY, PROCESS_EXPENSE, REVIEW_EXPENSE, } from "../../utils/constants"; import Label from "../common/Label"; -import { - defaultPaymentRequestActionValues, - PaymentRequestActionScheam, -} from "./PaymentRequestSchema"; -import PaymentStatusLogs from "./PaymentStatusLogs"; -import { FilelistView } from "../Expenses/Filelist"; const ViewPaymentRequest = ({ requestId }) => { const { data, isLoading, isError, error, isFetching } = @@ -50,10 +42,9 @@ const ViewPaymentRequest = ({ requestId }) => { const IsReview = useHasUserPermission(REVIEW_EXPENSE); const [imageLoaded, setImageLoaded] = useState({}); - const { setDocumentView, setModalSize } = usePaymentRequestContext(); + const { setDocumentView } = usePaymentRequestContext(); const ActionSchema = - PaymentRequestActionScheam(IsPaymentProcess, data?.createdAt) ?? - z.object({}); + ExpenseActionScheam(IsPaymentProcess, data?.createdAt) ?? z.object({}); const navigate = useNavigate(); const { register, @@ -64,7 +55,7 @@ const ViewPaymentRequest = ({ requestId }) => { formState: { errors }, } = useForm({ resolver: zodResolver(ActionSchema), - defaultValues: defaultPaymentRequestActionValues, + defaultValues: defaultActionValues, }); const userPermissions = useSelector( @@ -106,7 +97,7 @@ const ViewPaymentRequest = ({ requestId }) => { const onSubmit = (formData) => { const Payload = { ...formData, - paidAt: localToUtc(formData.paidAt), + paidAt: localToUtc(formData.reimburseDate), paymentRequestId: data.id, comment: formData.comment, }; @@ -121,24 +112,17 @@ const ViewPaymentRequest = ({ requestId }) => { return (
-
+
Payment Request Details
+
-
-
+
+
-
+
{data?.paymentRequestUID} - - - {data?.expenseStatus?.name} -
-
+
-
+
-
+
{/* Row 2 */} -
+
-
+
- {formatFigure(data?.amount,{type:"currency",currency : data?.currency?.currencyCode})} + {formatCurrency(data?.amount, data?.currency?.currencyCode)}
+ {/* Row 3 */} + {/*
+
+ +
{data?.paymentMode?.name}
+
+
*/} {data?.gstNumber && ( -
+