merged recurring into upgrade expense

This commit is contained in:
pramod.mahajan 2025-11-06 01:00:11 +05:30
parent 4ae0b403a6
commit 1f784f330d
11 changed files with 208 additions and 74 deletions

View File

@ -169,7 +169,7 @@ export const defaultActionValues = {
reimburseById: null,
tdsPercentage: 0,
baseAmount:null,
taxAmount: 0,
taxAmount: null,
};
export const SearchSchema = z.object({

View File

@ -18,7 +18,7 @@ const ExpenseStatusLogs = ({ data }) => {
() => sortedLogs.slice(0, visibleCount),
[sortedLogs, visibleCount]
);
console.log(logsToShow)
const timelineData = useMemo(() => {
return logsToShow.map((log, index) => ({

View File

@ -1,9 +1,10 @@
import React from "react";
import { formatFileSize, getIconByFileType } from "../../utils/appUtils";
import Tooltip from "../common/Tooltip";
const Filelist = ({ files, removeFile, expenseToEdit }) => {
return (
<div className="d-block">
<div className="d-flex flex-wrap gap-2 my-1">
{files
.filter((file) => {
if (expenseToEdit) {
@ -13,7 +14,7 @@ const Filelist = ({ files, removeFile, expenseToEdit }) => {
})
.map((file, idx) => (
<div className="col-12 col-sm-6 col-md-4 mb-2" key={idx}>
<div className="d-flex align-items-center justify-content-between bg-white border rounded p-1 ">
<div className="d-flex align-items-center justify-content-between bg-white border rounded p-1">
{/* File icon and info */}
<div className="d-flex align-items-center flex-grow-1 gap-2 overflow-hidden">
<i
@ -47,8 +48,49 @@ const Filelist = ({ files, removeFile, expenseToEdit }) => {
</div>
</div>
))}
</div>
</div>
);
};
export default Filelist;
export const FilelistView = ({ files, viewFile }) => {
console.log( files)
return (
<div className="d-flex flex-wrap gap-2 mt-2">
{files?.map((file, idx) => (
<div className=" bg-white " key={idx}>
<div className="row align-items-center">
{/* File icon and info */}
<div className="col-12 d-flex align-items-center gap-2">
<i
className={`bx ${getIconByFileType(file?.fileName)} fs-3`}
></i>
<div
className="d-flex flex-column text-truncate"
onClick={(e) => {
e.preventDefault();
viewFile({
IsOpen: true,
Image: file.preSignedUrl,
});
}}
>
<span className="fw-medium small text-truncate">
{file.fileName}
</span>
<span className="text-body-secondary small">
<Tooltip text={"Click on file"}>
{" "}
{file.fileSize ? formatFileSize(file.fileSize) : ""}
</Tooltip>
</span>
</div>
</div>
</div>
</div>
))}
</div>
);
};

View File

@ -134,7 +134,6 @@ const ManageExpense = ({ closeModal, expenseToEdit = null }) => {
reader.onerror = (error) => reject(error);
});
const removeFile = (index) => {
documentId;
if (expenseToEdit) {
const newFiles = files.map((file, i) => {
if (file.documentId !== index) return file;

View File

@ -109,6 +109,7 @@ const ViewExpense = ({ ExpenseId }) => {
const handleImageLoad = (id) => {
setImageLoaded((prev) => ({ ...prev, [id]: true }));
};
console.log(errors)
return (
<form className="container px-3" onSubmit={handleSubmit(onSubmit)}>
<div className="col-12 mb-1">

View File

@ -392,9 +392,6 @@ function ManagePaymentRequest({ closeModal, requestToEdit = null }) {
error={errors.payee?.message}
/>
{errors.payee && (
<small className="danger-text">{errors.payee.message}</small>
)}
{/* Checkbox below input */}
<div className="form-check mt-2">

View File

@ -45,11 +45,8 @@ export const PaymentRequestSchema = (expenseTypes, isItself) => {
description: z.string().optional(),
isActive: z.boolean().default(true),
})
).refine((data)=>{
if(isItself){
payee.z.string().optional();
}
}),
)
,
})
};

View File

@ -0,0 +1,93 @@
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 (
<div className="page-min-h overflow-auto">
{/* <div className="row g-2">
{logsToShow.map((log) => (
<div key={log.id} className="col-12 d-flex align-items-start mb-1">
<Avatar
size="xs"
firstName={log.updatedBy.firstName}
lastName={log.updatedBy.lastName}
/>
<div className="flex-grow-1">
<div className="text-start">
<div className="flex">
<span>{`${log.updatedBy.firstName} ${log.updatedBy.lastName}`}</span>
<small className="text-secondary text-tiny ms-2">
<em>{log.action}</em>
</small>
<span className="text-tiny text-secondary d-block">
{formatUTCToLocalTime(log.updateAt, true)}
</span>
</div>
<div className="d-flex align-items-center text-muted small mt-1">
<span>{log.comment}</span>
</div>
</div>
</div>
</div>
))}
</div>
{sortedLogs.length > visibleCount && (
<div className="text-center my-1">
<button
className="btn btn-xs btn-outline-primary"
onClick={handleShowMore}
>
Show More
</button>
</div>
)} */}
<Timeline items={timelineData} />
</div>
);
};
export default PaymentStatusLogs;

View File

@ -34,6 +34,8 @@ import {
REVIEW_EXPENSE,
} from "../../utils/constants";
import Label from "../common/Label";
import { FilelistView } from "../Expenses/Filelist";
import PaymentStatusLogs from "./PaymentStatusLogs";
const ViewPaymentRequest = ({ requestId }) => {
const { data, isLoading, isError, error, isFetching } =
@ -125,7 +127,7 @@ const ViewPaymentRequest = ({ requestId }) => {
<hr />
</div>
<div className="row mb-1">
<div className="col-12 col-sm-6 col-md-8">
<div className="col-12 col-sm-6 col-md-7">
<div className="row">
<div className="col-12 text-start fw-semibold mb-2">
{data?.paymentRequestUID}
@ -537,7 +539,7 @@ const ViewPaymentRequest = ({ requestId }) => {
<i className="bx bx-time-five me-2 "></i>{" "}
<p className="fw-medium">TimeLine</p>
</div>
<PaymentStat data={data} />
<PaymentStatusLogs data={data} />
</div>
</div>
</form>

View File

@ -1,5 +1,8 @@
import React from "react";
import Avatar from "./Avatar";
import Tooltip from "./Tooltip";
import { formatUTCToLocalTime } from "../../utils/dateUtils";
import moment from "moment";
const Timeline = ({ items = [], transparent = true }) => {
return (
@ -24,7 +27,7 @@ const Timeline = ({ items = [], transparent = true }) => {
<div className="timeline-event">
<div className="timeline-header mb-3 d-flex justify-content-between">
<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(item.timeAgo).fromNow()}</Tooltip></small>
</div>
{item.description && <p className="mb-2">{item.description}</p>}

View File

@ -190,7 +190,7 @@ const ExpensePage = () => {
{viewExpense.view && (
<GlobalModel
isOpen
size="lg"
size="xl"
modalType="top"
closeModal={() => setViewExpense({ expenseId: null, view: false })}
>