fixed Transaction details inside request payment

This commit is contained in:
pramod.mahajan 2025-11-04 19:31:05 +05:30
parent 0dc68eb20d
commit ecf34b499e
5 changed files with 69 additions and 84 deletions

View File

@ -24,7 +24,7 @@ const ExpenseStatusLogs = ({ data }) => {
id: index + 1, id: index + 1,
title: log.nextStatus?.name || "Status Updated", title: log.nextStatus?.name || "Status Updated",
description: log.nextStatus?.description || "", description: log.nextStatus?.description || "",
timeAgo: moment.utc(log?.updatedAt).local().fromNow(), timeAgo: log.updatedAt,
color: getColorNameFromHex(log.nextStatus?.color) || "primary", color: getColorNameFromHex(log.nextStatus?.color) || "primary",
users: log.updatedBy users: log.updatedBy
? [ ? [

View File

@ -90,16 +90,16 @@ export const PaymentRequestActionScheam = (
.object({ .object({
comment: z.string().min(1, { message: "Please leave comment" }), comment: z.string().min(1, { message: "Please leave comment" }),
statusId: z.string().min(1, { message: "Please select a status" }), statusId: z.string().min(1, { message: "Please select a status" }),
paymentRequestId: z.string().nullable().optional(), paidTransactionId: z.string().nullable().optional(),
paidAt: z.string().nullable().optional(), paidAt: z.string().nullable().optional(),
paidById: z.string().nullable().optional(), paidById: z.string().nullable().optional(),
}) })
.superRefine((data, ctx) => { .superRefine((data, ctx) => {
if (isTransaction) { if (isTransaction) {
if (!data.paymentRequestId?.trim()) { if (!data.paidTransactionId?.trim()) {
ctx.addIssue({ ctx.addIssue({
code: z.ZodIssueCode.custom, code: z.ZodIssueCode.custom,
path: ["paymentRequestId"], path: ["paidTransactionId"],
message: "Reimburse Transaction ID is required", message: "Reimburse Transaction ID is required",
}); });
} }
@ -110,15 +110,7 @@ export const PaymentRequestActionScheam = (
message: "Transacion Date is required", 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) { if (!data.paidById) {
ctx.addIssue({ ctx.addIssue({
code: z.ZodIssueCode.custom, code: z.ZodIssueCode.custom,
@ -130,7 +122,7 @@ export const PaymentRequestActionScheam = (
}); });
}; };
export const defaultActionValues = { export const defaultPaymentRequestActionValues = {
comment: "", comment: "",
statusId: "", statusId: "",

View File

@ -1,4 +1,4 @@
import { useMemo, useState } from "react"; import { useEffect, useMemo, useState } from "react";
import { import {
useActionOnExpense, useActionOnExpense,
useActionOnPaymentRequest, useActionOnPaymentRequest,
@ -28,11 +28,16 @@ import { useNavigate } from "react-router-dom";
import { usePaymentRequestContext } from "../../pages/PaymentRequest/PaymentRequestPage"; import { usePaymentRequestContext } from "../../pages/PaymentRequest/PaymentRequestPage";
import { useHasUserPermission } from "../../hooks/useHasUserPermission"; import { useHasUserPermission } from "../../hooks/useHasUserPermission";
import { import {
EXPENSE_DRAFT,
EXPENSE_REJECTEDBY, EXPENSE_REJECTEDBY,
PROCESS_EXPENSE, PROCESS_EXPENSE,
REVIEW_EXPENSE, REVIEW_EXPENSE,
} from "../../utils/constants"; } from "../../utils/constants";
import Label from "../common/Label"; import Label from "../common/Label";
import {
defaultPaymentRequestActionValues,
PaymentRequestActionScheam,
} from "./PaymentRequestSchema";
const ViewPaymentRequest = ({ requestId }) => { const ViewPaymentRequest = ({ requestId }) => {
const { data, isLoading, isError, error, isFetching } = const { data, isLoading, isError, error, isFetching } =
@ -42,9 +47,10 @@ const ViewPaymentRequest = ({ requestId }) => {
const IsReview = useHasUserPermission(REVIEW_EXPENSE); const IsReview = useHasUserPermission(REVIEW_EXPENSE);
const [imageLoaded, setImageLoaded] = useState({}); const [imageLoaded, setImageLoaded] = useState({});
const { setDocumentView } = usePaymentRequestContext(); const { setDocumentView, setModalSize } = usePaymentRequestContext();
const ActionSchema = const ActionSchema =
ExpenseActionScheam(IsPaymentProcess, data?.createdAt) ?? z.object({}); PaymentRequestActionScheam(IsPaymentProcess, data?.createdAt) ??
z.object({});
const navigate = useNavigate(); const navigate = useNavigate();
const { const {
register, register,
@ -55,7 +61,7 @@ const ViewPaymentRequest = ({ requestId }) => {
formState: { errors }, formState: { errors },
} = useForm({ } = useForm({
resolver: zodResolver(ActionSchema), resolver: zodResolver(ActionSchema),
defaultValues: defaultActionValues, defaultValues: defaultPaymentRequestActionValues,
}); });
const userPermissions = useSelector( const userPermissions = useSelector(
@ -97,7 +103,7 @@ const ViewPaymentRequest = ({ requestId }) => {
const onSubmit = (formData) => { const onSubmit = (formData) => {
const Payload = { const Payload = {
...formData, ...formData,
paidAt: localToUtc(formData.reimburseDate), paidAt: localToUtc(formData.paidAt),
paymentRequestId: data.id, paymentRequestId: data.id,
comment: formData.comment, comment: formData.comment,
}; };
@ -111,18 +117,25 @@ const ViewPaymentRequest = ({ requestId }) => {
}; };
return ( return (
<form className="container-xl px-3" onSubmit={handleSubmit(onSubmit)}> <form className="container px-3" onSubmit={handleSubmit(onSubmit)}>
<div className="col-12 mb-1"> <div className="col-12 mb-2 text-center ">
<h5 className="fw-semibold m-0">Payment Request Details</h5> <h5 className="fw-semibold m-0">Payment Request Details</h5>
<hr />
</div> </div>
<div className="row mb-1"> <div className="row text-start">
<div className="col-12 col-sm-6 col-md-8"> <div className=" col-sm-12 col-md-7 border-none border-md-end">
<div className="row"> <div className="row">
<div className="col-12 text-start fw-semibold mb-2"> <div className="col-12 d-flex justify-content-between text-start fw-semibold mb-2">
{data?.paymentRequestUID} {data?.paymentRequestUID}
<span
className={`badge bg-label-${
getColorNameFromHex(data?.expenseStatus?.color) || "secondary"
}`}
>
{data?.expenseStatus?.name}
</span>
</div> </div>
<div className="col-md-6 mb-3"> <div className="col-md-12 mb-3">
<div className="d-block d-md-flex align-items-center"> <div className="d-block d-md-flex align-items-center">
<label <label
className="form-label me-2 mb-0 fw-semibold text-start" className="form-label me-2 mb-0 fw-semibold text-start"
@ -134,7 +147,7 @@ const ViewPaymentRequest = ({ requestId }) => {
</div> </div>
</div> </div>
<div className="col-md-6 mb-3"> <div className="col-md-12 mb-3">
<div className="d-flex"> <div className="d-flex">
<label <label
className="form-label me-2 mb-0 fw-semibold text-start" className="form-label me-2 mb-0 fw-semibold text-start"
@ -147,7 +160,7 @@ const ViewPaymentRequest = ({ requestId }) => {
</div> </div>
</div> </div>
</div> </div>
<div className="col-md-6 mb-3"> <div className="col-md-12 mb-3">
<div className="d-flex"> <div className="d-flex">
<label <label
className="form-label me-2 mb-0 fw-semibold text-start" className="form-label me-2 mb-0 fw-semibold text-start"
@ -160,7 +173,7 @@ const ViewPaymentRequest = ({ requestId }) => {
</div> </div>
{/* Row 2 */} {/* Row 2 */}
<div className="col-md-6 mb-3"> <div className="col-md-12 mb-3">
<div className="d-flex"> <div className="d-flex">
<label <label
className="form-label me-2 mb-0 fw-semibold text-start" className="form-label me-2 mb-0 fw-semibold text-start"
@ -171,7 +184,7 @@ const ViewPaymentRequest = ({ requestId }) => {
<div className="text-muted">{data?.payee}</div> <div className="text-muted">{data?.payee}</div>
</div> </div>
</div> </div>
<div className="col-md-6 mb-3"> <div className="col-md-1 mb-3">
<div className="d-flex"> <div className="d-flex">
<label <label
className="form-label me-2 mb-0 fw-semibold text-start" className="form-label me-2 mb-0 fw-semibold text-start"
@ -185,20 +198,8 @@ const ViewPaymentRequest = ({ requestId }) => {
</div> </div>
</div> </div>
{/* Row 3 */}
{/* <div className="col-md-6 mb-3">
<div className="d-flex">
<label
className="form-label me-2 mb-0 fw-semibold text-start"
style={{ minWidth: "130px" }}
>
Payment Mode :
</label>
<div className="text-muted">{data?.paymentMode?.name}</div>
</div>
</div> */}
{data?.gstNumber && ( {data?.gstNumber && (
<div className="col-md-6 mb-3"> <div className="col-md-12 mb-3">
<div className="d-flex"> <div className="d-flex">
<label <label
className="form-label me-2 mb-0 fw-semibold text-start" className="form-label me-2 mb-0 fw-semibold text-start"
@ -212,25 +213,8 @@ const ViewPaymentRequest = ({ requestId }) => {
)} )}
{/* Row 4 */} {/* Row 4 */}
<div className="col-md-6 mb-3">
<div className="d-flex"> <div className="col-md-12 mb-3">
<label
className="form-label me-2 mb-0 fw-semibold text-start"
style={{ minWidth: "130px" }}
>
Status :
</label>
<span
className={`badge bg-label-${
getColorNameFromHex(data?.expenseStatus?.color) ||
"secondary"
}`}
>
{data?.expenseStatus?.name}
</span>
</div>
</div>
<div className="col-md-6 mb-3">
<div className="d-flex"> <div className="d-flex">
<label <label
className="form-label me-2 mb-0 fw-semibold text-start" className="form-label me-2 mb-0 fw-semibold text-start"
@ -244,18 +228,7 @@ const ViewPaymentRequest = ({ requestId }) => {
</div> </div>
</div> </div>
<div className="col-md-6 mb-3"> <div className="col-md-12 mb-3">
<div className="d-flex">
<label
className="form-label me-2 mb-0 fw-semibold text-start"
style={{ minWidth: "130px" }}
>
Project :
</label>
<div className="text-muted">{data?.project?.name}</div>
</div>
</div>
<div className="col-md-6 mb-3">
<div className="d-flex"> <div className="d-flex">
<label <label
className="form-label me-2 mb-0 fw-semibold text-start" className="form-label me-2 mb-0 fw-semibold text-start"
@ -271,7 +244,7 @@ const ViewPaymentRequest = ({ requestId }) => {
{/* Row 6 */} {/* Row 6 */}
{data?.createdBy && ( {data?.createdBy && (
<div className="col-md-6 text-start"> <div className="col-md-12 text-start">
<div className="d-flex align-items-center"> <div className="d-flex align-items-center">
<label <label
className="form-label me-2 mb-0 fw-semibold" className="form-label me-2 mb-0 fw-semibold"
@ -296,7 +269,7 @@ const ViewPaymentRequest = ({ requestId }) => {
</div> </div>
)} )}
{data?.paidBy && ( {data?.paidBy && (
<div className="col-md-6 text-start"> <div className="col-md-12 text-start">
<div className="d-flex align-items-center"> <div className="d-flex align-items-center">
<label <label
className="form-label me-2 mb-0 fw-semibold" className="form-label me-2 mb-0 fw-semibold"
@ -496,7 +469,11 @@ const ViewPaymentRequest = ({ requestId }) => {
)} )}
</div> </div>
</div> </div>
<div className="col-12 col-sm-6 col-md-4"> <div className=" col-sm-12 my-2 my-md-0 border-top border-md-none col-md-5">
<div className="d-flex my-2">
<i className="bx bx-time-five me-2 "></i>{" "}
<p className="fw-medium">TimeLine</p>
</div>
<ExpenseStatusLogs data={data} /> <ExpenseStatusLogs data={data} />
</div> </div>
</div> </div>

View File

@ -1,5 +1,8 @@
import React from "react"; import React from "react";
import Avatar from "./Avatar"; import Avatar from "./Avatar";
import Tooltip from "./Tooltip";
import { formatUTCToLocalTime } from "../../utils/dateUtils";
import moment from "moment";
const Timeline = ({ items = [], transparent = true }) => { const Timeline = ({ items = [], transparent = true }) => {
return ( return (
@ -8,7 +11,7 @@ const Timeline = ({ items = [], transparent = true }) => {
transparent ? "timeline-transparent text-start" : "" transparent ? "timeline-transparent text-start" : ""
}`} }`}
> >
{items.map((item) => ( {items && items?.map((item) => (
<li <li
key={item.id} key={item.id}
className={`timeline-item ${ className={`timeline-item ${
@ -24,7 +27,7 @@ const Timeline = ({ items = [], transparent = true }) => {
<div className="timeline-event"> <div className="timeline-event">
<div className="timeline-header mb-3 d-flex justify-content-between"> <div className="timeline-header mb-3 d-flex justify-content-between">
<h6 className="mb-0 text-body">{item.title}</h6> <h6 className="mb-0 text-body">{item.title}</h6>
<small className="text-body-secondary">{item.timeAgo}</small> <small className="text-body-secondary"><Tooltip text={formatUTCToLocalTime(item.timeAgo,true)}>{moment.utc(item.timeAgo).local().fromNow()}</Tooltip></small>
</div> </div>
{item.description && <p className="mb-2">{item.description}</p>} {item.description && <p className="mb-2">{item.description}</p>}
@ -64,7 +67,8 @@ const Timeline = ({ items = [], transparent = true }) => {
height="32" height="32"
/> />
) : ( ) : (
<Avatar size="xs" <Avatar
size="xs"
firstName={user.firstName} firstName={user.firstName}
lastName={user.lastName} lastName={user.lastName}
/> />
@ -84,6 +88,16 @@ const Timeline = ({ items = [], transparent = true }) => {
</div> </div>
</li> </li>
))} ))}
{!items || items.length == 0 && (
<li
className={`timeline-item text-center ${
transparent ? "timeline-item-transparent" : ""
}`}
>
Not action yet.
</li>
)}
</ul> </ul>
); );
}; };

View File

@ -14,7 +14,7 @@ export const PaymentRequestContext = createContext();
export const usePaymentRequestContext = () => { export const usePaymentRequestContext = () => {
const context = useContext(PaymentRequestContext); const context = useContext(PaymentRequestContext);
if (!context) { if (!context) {
throw new Error("usePaymentRequestContext must be used within an ExpenseProvider"); throw new Error("usePaymentRequestContext must be used within an RequestPaymentProvider");
} }
return context; return context;
}; };
@ -30,12 +30,14 @@ const PaymentRequestPage = () => {
IsOpen: false, IsOpen: false,
Image: null, Image: null,
}); });
const [modalSize,setModalSize] = useState("md")
const [search, setSearch] = useState(""); const [search, setSearch] = useState("");
const contextValue = { const contextValue = {
setManageRequest, setManageRequest,
setVieRequest, setVieRequest,
setDocumentView setDocumentView,
setModalSize
}; };
useEffect(() => { useEffect(() => {
@ -125,7 +127,7 @@ const PaymentRequestPage = () => {
{ViewRequest.view && ( {ViewRequest.view && (
<GlobalModel <GlobalModel
isOpen isOpen
size="xl"
modalType="top" modalType="top"
closeModal={() => setVieRequest({ requestId: null, view: false })} closeModal={() => setVieRequest({ requestId: null, view: false })}
> >