added mark as completd api operation
This commit is contained in:
parent
76df08e921
commit
962286a4da
@ -11,12 +11,19 @@ import { formatFigure, localToUtc } from "../../utils/appUtils";
|
||||
import { formatUTCToLocalTime } from "../../utils/dateUtils";
|
||||
import Avatar from "../common/Avatar";
|
||||
import { PaymentHistorySkeleton } from "./CollectionSkeleton";
|
||||
import { usePaymentType } from "../../hooks/masterHook/useMaster";
|
||||
|
||||
const AddPayment = ({ onClose }) => {
|
||||
const { addPayment } = useCollectionContext();
|
||||
const { data, isLoading, isError, error } = useCollection(
|
||||
addPayment?.invoiceId
|
||||
);
|
||||
const {
|
||||
data: paymentTypes,
|
||||
isLoading: isPaymentTypeLoading,
|
||||
isError: isPaymentTypeError,
|
||||
error: paymentError,
|
||||
} = usePaymentType(true);
|
||||
const methods = useForm({
|
||||
resolver: zodResolver(paymentSchema),
|
||||
defaultValues: defaultPayment,
|
||||
@ -37,7 +44,6 @@ const AddPayment = ({ onClose }) => {
|
||||
paymentReceivedDate: localToUtc(formData.paymentReceivedDate),
|
||||
invoiceId: addPayment.invoiceId,
|
||||
};
|
||||
|
||||
AddPayment(payload);
|
||||
};
|
||||
const handleClose = (formData) => {
|
||||
@ -78,6 +84,38 @@ const AddPayment = ({ onClose }) => {
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="col-12 col-md-6 mb-2">
|
||||
<Label
|
||||
htmlFor="paymentAdjustmentHeadId"
|
||||
className="form-label"
|
||||
required
|
||||
>
|
||||
Payment Adjustment Head
|
||||
</Label>
|
||||
<select
|
||||
className="form-select form-select-sm "
|
||||
{...register("paymentAdjustmentHeadId")}
|
||||
>
|
||||
{isPaymentTypeLoading ? (
|
||||
<option>Loading..</option>
|
||||
) : (
|
||||
<>
|
||||
<option value="" >Select Payment Head</option>
|
||||
{paymentTypes?.data?.sort((a, b) => a.name.localeCompare(b.name))?.map((type) => (
|
||||
<option key={type.id} value={type.id}>
|
||||
{type.name}
|
||||
</option>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
</select>
|
||||
{errors.paymentAdjustmentHeadId && (
|
||||
<small className="danger-text">
|
||||
{errors.paymentAdjustmentHeadId.message}
|
||||
</small>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="col-12 col-md-6 mb-2">
|
||||
<Label htmlFor="amount" className="form-label" required>
|
||||
Amount
|
||||
@ -114,8 +152,11 @@ const AddPayment = ({ onClose }) => {
|
||||
<button
|
||||
type="reset"
|
||||
className="btn btn-label-secondary btn-sm mt-3"
|
||||
onClick={handleClose}
|
||||
// disabled={isPending}
|
||||
onClick={()=>{
|
||||
handleClose()
|
||||
onClose()
|
||||
}}
|
||||
disabled={isPending}
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
@ -141,7 +182,9 @@ const AddPayment = ({ onClose }) => {
|
||||
</div>
|
||||
|
||||
<div className="row text-start mx-2">
|
||||
{data.receivedInvoicePayments.sort((a, b) => new Date(b.createdAt) - new Date(a.createdAt)).map((payment, index) => (
|
||||
{data.receivedInvoicePayments
|
||||
.sort((a, b) => new Date(b.createdAt) - new Date(a.createdAt))
|
||||
.map((payment, index) => (
|
||||
<div className="col-12 mb-2" key={payment.id}>
|
||||
<div className=" p-2 border-start border-warning">
|
||||
<div className="row">
|
||||
@ -161,7 +204,9 @@ const AddPayment = ({ onClose }) => {
|
||||
</div>
|
||||
|
||||
<div className="col-12 col-md-6 mb-0 d-flex align-items-center m-0">
|
||||
<small className="fw-semibold me-2">Received By:</small>{" "}
|
||||
<small className="fw-semibold me-2">
|
||||
Updated By:
|
||||
</small>{" "}
|
||||
<Avatar
|
||||
size="xs"
|
||||
firstName={payment?.createdBy?.firstName}
|
||||
@ -175,18 +220,25 @@ const AddPayment = ({ onClose }) => {
|
||||
<div className="row">
|
||||
<div className="col-12 col-md-6">
|
||||
<p className="mb-1">
|
||||
<small className="fw-semibold">Transaction ID:</small>{" "}
|
||||
<small className="fw-semibold">
|
||||
Transaction ID:
|
||||
</small>{" "}
|
||||
{payment.transactionId}
|
||||
</p>
|
||||
</div>
|
||||
<div className="col-12 ">
|
||||
<div className="d-flex justify-content-between">
|
||||
<span>{payment?.paymentAdjustmentHead?.name}</span>
|
||||
<span className="fs-semibold d-none d-md-block">
|
||||
{formatFigure(payment.amount, {
|
||||
type: "currency",
|
||||
currency: "INR",
|
||||
})}
|
||||
</span>
|
||||
<p className="text-tiny m-0 mt-1">{payment?.comment}</p>
|
||||
</div>
|
||||
<p className="text-tiny m-0 mt-1">
|
||||
{payment?.comment}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -5,6 +5,7 @@ import { formatFigure, localToUtc, useDebounce } from "../../utils/appUtils";
|
||||
import { formatUTCToLocalTime } from "../../utils/dateUtils";
|
||||
import Pagination from "../common/Pagination";
|
||||
import { useCollectionContext } from "../../pages/collections/CollectionPage";
|
||||
import { CollectionTableSkeleton } from "./CollectionSkeleton";
|
||||
|
||||
const CollectionList = ({ fromDate, toDate, isPending, searchString }) => {
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
@ -19,7 +20,8 @@ const CollectionList = ({ fromDate, toDate, isPending, searchString }) => {
|
||||
true,
|
||||
searchDebounce
|
||||
);
|
||||
const {setProcessedPayment,setAddPayment,setViewCollection} = useCollectionContext()
|
||||
const { setProcessedPayment, setAddPayment, setViewCollection } =
|
||||
useCollectionContext();
|
||||
|
||||
const paginate = (page) => {
|
||||
if (page >= 1 && page <= (data?.totalPages ?? 1)) {
|
||||
@ -127,7 +129,7 @@ const CollectionList = ({ fromDate, toDate, isPending, searchString }) => {
|
||||
},
|
||||
];
|
||||
|
||||
if (isLoading) return <p>Loading...</p>;
|
||||
if (isLoading) return <CollectionTableSkeleton />;
|
||||
if (isError) return <p>{error.message}</p>;
|
||||
|
||||
return (
|
||||
@ -183,15 +185,28 @@ const CollectionList = ({ fromDate, toDate, isPending, searchString }) => {
|
||||
<ul className="dropdown-menu dropdown-menu-end">
|
||||
{/* View */}
|
||||
<li>
|
||||
<a className="dropdown-item cursor-pointer" onClick={()=>setViewCollection(row.id)}>
|
||||
<a
|
||||
className="dropdown-item cursor-pointer"
|
||||
onClick={() => setViewCollection(row.id)}
|
||||
>
|
||||
<i className="bx bx-show me-2 text-primary"></i>
|
||||
<span>View</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
{!row?.markAsCompleted && (
|
||||
<>
|
||||
{/* Add Payment */}
|
||||
<li>
|
||||
<a className="dropdown-item cursor-pointer" onClick={()=>setAddPayment({isOpen:true,invoiceId:row.id})}>
|
||||
<a
|
||||
className="dropdown-item cursor-pointer"
|
||||
onClick={() =>
|
||||
setAddPayment({
|
||||
isOpen: true,
|
||||
invoiceId: row.id,
|
||||
})
|
||||
}
|
||||
>
|
||||
<i className="bx bx-wallet me-2 text-warning"></i>
|
||||
<span>Add Payment</span>
|
||||
</a>
|
||||
@ -199,11 +214,21 @@ const CollectionList = ({ fromDate, toDate, isPending, searchString }) => {
|
||||
|
||||
{/* Mark Payment */}
|
||||
<li>
|
||||
<a className="dropdown-item cursor-pointer" onClick={()=>setProcessedPayment({isOpen:true,invoiceId:row})}>
|
||||
<a
|
||||
className="dropdown-item cursor-pointer"
|
||||
onClick={() =>
|
||||
setProcessedPayment({
|
||||
isOpen: true,
|
||||
invoiceId: row.id,
|
||||
})
|
||||
}
|
||||
>
|
||||
<i className="bx bx-check-circle me-2 text-success"></i>
|
||||
<span>Mark Payment</span>
|
||||
</a>
|
||||
</li>
|
||||
</>
|
||||
)}
|
||||
</ul>
|
||||
</div>
|
||||
</td>
|
||||
|
@ -30,7 +30,12 @@ export const PaymentHistorySkeleton = ({ count = 2 }) => {
|
||||
|
||||
{/* Received By (Avatar + Name) */}
|
||||
<div className="col-12 col-md-6 d-flex align-items-center gap-2">
|
||||
<SkeletonLine width="30px" height={30} className="rounded-circle" /> {/* Avatar */}
|
||||
<SkeletonLine
|
||||
width="30px"
|
||||
height={30}
|
||||
className="rounded-circle"
|
||||
/>{" "}
|
||||
{/* Avatar */}
|
||||
<SkeletonLine width="120px" height={16} /> {/* Name */}
|
||||
</div>
|
||||
</div>
|
||||
@ -41,3 +46,166 @@ export const PaymentHistorySkeleton = ({ count = 2 }) => {
|
||||
);
|
||||
};
|
||||
|
||||
export const CollectionDetailsSkeleton = () => {
|
||||
return (
|
||||
<div className="container p-3">
|
||||
{/* Title */}
|
||||
<SkeletonLine height={24} width="200px" className="mx-auto" />
|
||||
|
||||
{/* Header Row */}
|
||||
<div className="row mb-3 px-1">
|
||||
<div className="col-10">
|
||||
<SkeletonLine height={20} />
|
||||
</div>
|
||||
<div className="col-2 d-flex justify-content-end">
|
||||
<SkeletonLine height={20} width="60px" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Project */}
|
||||
<div className="row mb-3">
|
||||
<div className="col-md-6">
|
||||
<SkeletonLine width="60%" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Invoice & E-Invoice */}
|
||||
<div className="row mb-3">
|
||||
<div className="col-md-6">
|
||||
<SkeletonLine />
|
||||
</div>
|
||||
<div className="col-md-6">
|
||||
<SkeletonLine />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Invoice Date & Client Submitted */}
|
||||
<div className="row mb-3">
|
||||
<div className="col-md-6">
|
||||
<SkeletonLine />
|
||||
</div>
|
||||
<div className="col-md-6">
|
||||
<SkeletonLine />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Expected Payment & Mark as Completed */}
|
||||
<div className="row mb-3">
|
||||
<div className="col-md-6">
|
||||
<SkeletonLine />
|
||||
</div>
|
||||
<div className="col-md-6">
|
||||
<SkeletonLine width="40%" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Basic & Tax Amount */}
|
||||
<div className="row mb-3">
|
||||
<div className="col-md-6">
|
||||
<SkeletonLine />
|
||||
</div>
|
||||
<div className="col-md-6">
|
||||
<SkeletonLine />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Balance & Created At */}
|
||||
<div className="row mb-3">
|
||||
<div className="col-md-6">
|
||||
<SkeletonLine />
|
||||
</div>
|
||||
<div className="col-md-6">
|
||||
<SkeletonLine />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Created By */}
|
||||
<div className="row mb-3">
|
||||
<div className="col-md-6 d-flex align-items-center">
|
||||
<SkeletonLine
|
||||
width="40px"
|
||||
height={40}
|
||||
className="me-2 rounded-circle"
|
||||
/>
|
||||
<SkeletonLine width="100px" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Description */}
|
||||
<div className="row mb-3">
|
||||
<div className="col-12">
|
||||
<SkeletonLine height={50} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Attachments */}
|
||||
<div className="row mb-3">
|
||||
<div className="col-12 d-flex gap-2 flex-wrap">
|
||||
{[...Array(3)].map((_, idx) => (
|
||||
<SkeletonLine key={idx} height={60} width="80px" />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Tabs */}
|
||||
<div className="row mb-2">
|
||||
<div className="col-12 d-flex gap-2">
|
||||
<SkeletonLine height={35} width="120px" />
|
||||
<SkeletonLine height={35} width="150px" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Tab Content (Comments / Payments) */}
|
||||
<SkeletonLine height={200} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const CollectionTableSkeleton = () => {
|
||||
const columnCount = 8;
|
||||
|
||||
return (
|
||||
<div className="card ">
|
||||
<div
|
||||
className="card-datatable table-responsive page-min-h"
|
||||
id="horizontal-example"
|
||||
>
|
||||
<div className="dataTables_wrapper no-footer mx-5 pb-2">
|
||||
<table className="table dataTable text-nowrap">
|
||||
<thead>
|
||||
<tr >
|
||||
{[...Array(columnCount - 1)].map((_, i) => (
|
||||
<th key={i}>
|
||||
<SkeletonLine height={15} width="80px" />
|
||||
</th>
|
||||
))}
|
||||
<th className="d-flex justify-content-center">
|
||||
<SkeletonLine height={16} width="40px" />
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{[...Array(8)].map((_, rowIdx) => (
|
||||
<tr key={rowIdx}>
|
||||
{[...Array(columnCount - 1)].map((_, colIdx) => (
|
||||
<td key={colIdx}>
|
||||
<SkeletonLine height={33} />
|
||||
</td>
|
||||
))}
|
||||
<td className="d-flex justify-content-center">
|
||||
<SkeletonLine height={16} width="20px" />
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
{/* Pagination Skeleton */}
|
||||
<div className="d-flex justify-content-end mt-2">
|
||||
<SkeletonLine height={30} width="200px" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -9,7 +9,8 @@ import moment from "moment";
|
||||
|
||||
const Comment = ({ invoice }) => {
|
||||
const {
|
||||
register,reset,
|
||||
register,
|
||||
reset,
|
||||
handleSubmit,
|
||||
formState: { errors },
|
||||
} = useForm({
|
||||
@ -17,45 +18,16 @@ const Comment = ({ invoice }) => {
|
||||
defaultValues: { comment: "" },
|
||||
});
|
||||
|
||||
const { mutate: AddComment, isPending } = useAddComment(() => {reset()});
|
||||
const { mutate: AddComment, isPending } = useAddComment(() => {
|
||||
reset();
|
||||
});
|
||||
|
||||
const onSubmit = (formData) => {
|
||||
const payload = { ...formData, invoiceId: invoice?.id };
|
||||
debugger;
|
||||
AddComment(payload);
|
||||
};
|
||||
return (
|
||||
<div className="row">
|
||||
{invoice?.comments?.length > 0 ? (
|
||||
invoice.comments.map((comment, index) => (
|
||||
<div
|
||||
className="border-start border-primary ps-3 py-2 mb-3"
|
||||
key={comment.id || index}
|
||||
>
|
||||
<div className="d-flex justify-content-between align-items-center mb-1">
|
||||
<div className="d-flex align-items-center">
|
||||
<Avatar
|
||||
size="xs"
|
||||
firstName={comment?.createdBy?.firstName}
|
||||
lastName={comment?.createdBy?.lastName}
|
||||
/>
|
||||
<span className="ms-1 fw-semibold">
|
||||
{comment?.createdBy?.firstName} {comment?.createdBy?.lastName}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<small className="text-secondary">
|
||||
{moment.utc(comment?.createdAt).local().fromNow()}
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<p className="mb-1">{comment?.comment}</p>
|
||||
</div>
|
||||
))
|
||||
) : (
|
||||
<p className="text-muted">No comments yet.</p>
|
||||
)}
|
||||
|
||||
<div className="row pt-1">
|
||||
<form onSubmit={handleSubmit(onSubmit)}>
|
||||
<div className="col-12">
|
||||
<textarea
|
||||
@ -77,6 +49,35 @@ const Comment = ({ invoice }) => {
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
{invoice?.comments?.length > 0 ? (
|
||||
invoice.comments.map((comment, index) => (
|
||||
<div
|
||||
className="border-start border-primary ps-1 py-2 mb-3"
|
||||
key={comment.id || index}
|
||||
>
|
||||
<div className="d-flex justify-content-between align-items-center mb-1">
|
||||
<div className="d-flex align-items-center">
|
||||
<Avatar
|
||||
size="xs"
|
||||
firstName={comment?.createdBy?.firstName}
|
||||
lastName={comment?.createdBy?.lastName}
|
||||
/>
|
||||
<span className="ms-1 fw-semibold">
|
||||
{comment?.createdBy?.firstName} {comment?.createdBy?.lastName}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<small className="text-secondary">
|
||||
{moment.utc(comment?.createdAt).local().fromNow()}
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<div className="ms-9"> <p className="mb-1">{comment?.comment}</p></div>
|
||||
</div>
|
||||
))
|
||||
) : (
|
||||
<p className="text-muted">No comments yet.</p>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -8,13 +8,14 @@ const PaymentHistoryTable = ({data}) => {
|
||||
<div>
|
||||
|
||||
{data?.receivedInvoicePayments?.length > 0 && (
|
||||
<div className="mt-4">
|
||||
<table className="table table-bordered mt-2">
|
||||
<div className="pt-1 data-tabe table-responsive">
|
||||
<table className="table table-bordered table-responsive mt-2">
|
||||
<thead className="table-light">
|
||||
<tr>
|
||||
<th className="text-center">Sr.No</th>
|
||||
<th className="text-center">Transaction ID</th>
|
||||
<th className="text-center"> Received Date</th>
|
||||
<th className="text-center"> Payment Adjustment-Head</th>
|
||||
<th className="text-center">Amount</th>
|
||||
<th className="text-center">Updated By</th>
|
||||
</tr>
|
||||
@ -25,6 +26,7 @@ const PaymentHistoryTable = ({data}) => {
|
||||
<td className="text-center">{index + 1}</td>
|
||||
<td ><span className="mx-2">{payment.transactionId}</span></td>
|
||||
<td className="text-center">{formatUTCToLocalTime(payment.paymentReceivedDate)}</td>
|
||||
<td className="text-start">{payment?.paymentAdjustmentHead?.name ?? "--"}</td>
|
||||
<td className="text-end">
|
||||
<span className="px-1">{formatFigure(payment.amount, {
|
||||
type: "currency",
|
||||
@ -32,12 +34,8 @@ const PaymentHistoryTable = ({data}) => {
|
||||
})}</span>
|
||||
</td>
|
||||
<td>
|
||||
<div className="d-flex align-items-center mx-2">
|
||||
<Avatar
|
||||
size="xs"
|
||||
firstName={payment.createdBy?.firstName}
|
||||
lastName={payment.createdBy?.lastName}
|
||||
/>
|
||||
<div className="d-flex align-items-center mx-2 py-1">
|
||||
|
||||
{payment.createdBy?.firstName}{" "}
|
||||
{payment.createdBy?.lastName}
|
||||
</div>
|
||||
|
@ -6,9 +6,11 @@ import { formatFigure, getIconByFileType } from "../../utils/appUtils";
|
||||
import Avatar from "../common/Avatar";
|
||||
import PaymentHistoryTable from "./PaymentHistoryTable";
|
||||
import Comment from "./Comment";
|
||||
import { CollectionDetailsSkeleton } from "./CollectionSkeleton";
|
||||
|
||||
const ViewCollection = ({ onClose }) => {
|
||||
const { viewCollection, setCollection , setDocumentView} = useCollectionContext();
|
||||
const { viewCollection, setCollection, setDocumentView } =
|
||||
useCollectionContext();
|
||||
const { data, isLoading, isError, error } = useCollection(viewCollection);
|
||||
|
||||
const handleEdit = () => {
|
||||
@ -16,18 +18,25 @@ const ViewCollection = ({ onClose }) => {
|
||||
onClose();
|
||||
};
|
||||
|
||||
if (isLoading) return <div>isLoading...</div>;
|
||||
if (isLoading) return <CollectionDetailsSkeleton />;
|
||||
if (isError) return <div>{error.message}</div>;
|
||||
|
||||
return (
|
||||
<div className="container p-3">
|
||||
<p className="fs-5 fw-semibold">Collection Details</p>
|
||||
<div className="text-start ">
|
||||
<div className="row mb-3 px-1">
|
||||
<div className="col-10">
|
||||
<p className="mb-1 fs-6">{data?.title}</p>
|
||||
<div className="row text-start ">
|
||||
<div className="col-12 mb-3 d-flex justify-content-between">
|
||||
<div className="d-flex">
|
||||
<label
|
||||
className=" me-2 mb-0 fw-semibold"
|
||||
style={{ minWidth: "130px" }}
|
||||
>
|
||||
Project :
|
||||
</label>
|
||||
<div className="text-muted">{data?.project?.name}</div>
|
||||
</div>
|
||||
<div className="col-2">
|
||||
<div>
|
||||
{" "}
|
||||
<span
|
||||
className={`badge bg-label-${
|
||||
data?.isActive ? "primary" : "danger"
|
||||
@ -35,101 +44,127 @@ const ViewCollection = ({ onClose }) => {
|
||||
>
|
||||
{data?.isActive ? "Active" : "Inactive"}
|
||||
</span>
|
||||
{!data?.receivedInvoicePayments && (<span onClick={handleEdit} className="ms-2 cursor-pointer">
|
||||
{!data?.receivedInvoicePayments && (
|
||||
<span onClick={handleEdit} className="ms-2 cursor-pointer">
|
||||
<i className="bx bx-edit text-primary bx-sm"></i>
|
||||
</span>)}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="row mb-3">
|
||||
<div className="col-md-6 d-flex ">
|
||||
<p className="m-0 fw-semibold me-1">Project:</p>{" "}
|
||||
{data?.project?.name}
|
||||
<div className="col-md-6">
|
||||
<div className="row mb-2 text-wrap">
|
||||
<div className="col-4 fw-semibold">Title :</div>
|
||||
<div className="col-8 text-wrap">{data?.title}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="row mb-3">
|
||||
<div className="col-md-6 mb-3 d-flex">
|
||||
<p className="m-0 fw-semibold me-1">Invoice Number:</p>{" "}
|
||||
{data?.invoiceNumber}
|
||||
<div className="col-md-6">
|
||||
<div className="row mb-4 align-items-end">
|
||||
<div className="col-4 fw-semibold">Invoice Number:</div>
|
||||
<div className="col-8">{data?.invoiceNumber}</div>
|
||||
</div>
|
||||
<div className="col-md-6 d-flex">
|
||||
<p className="m-0 fw-semibold me-1">E-Invoice Number:</p>{" "}
|
||||
{data?.eInvoiceNumber}
|
||||
</div>
|
||||
{/* Row 2: E-Invoice Number + Project */}
|
||||
<div className="col-md-6">
|
||||
<div className="row mb-4 align-items-end">
|
||||
<div className="col-4 fw-semibold">E-Invoice Number:</div>
|
||||
<div className="col-8">{data?.eInvoiceNumber}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="row mb-3">
|
||||
<div className="col-md-6 mb-3 d-flex">
|
||||
<p className="m-0 fw-semibold me-1">Invoice Date:</p>{" "}
|
||||
{/* Row 3: Invoice Date + Client Submitted Date */}
|
||||
<div className="col-md-6">
|
||||
<div className="row mb-4 align-items-end">
|
||||
<div className="col-4 fw-semibold">Invoice Date:</div>
|
||||
<div className="col-8">
|
||||
{formatUTCToLocalTime(data?.invoiceDate)}
|
||||
</div>
|
||||
<div className="col-md-6 d-flex">
|
||||
<p className="m-0 fw-semibold me-1">Client Submitted Date:</p>{" "}
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-md-6">
|
||||
<div className="row mb-4 align-items-end">
|
||||
<div className="col-4 fw-semibold">Client Submitted Date:</div>
|
||||
<div className="col-8">
|
||||
{formatUTCToLocalTime(data?.clientSubmitedDate)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="row mb-3">
|
||||
<div className="col-md-6 mb-3 d-flex">
|
||||
<p className="m-0 fw-semibold me-1">Expected Payment Date:</p>{" "}
|
||||
</div>
|
||||
{/* Row 4: Expected Payment Date + Mark as Completed */}
|
||||
<div className="col-md-6">
|
||||
<div className="row mb-4 align-items-end">
|
||||
<div className="col-4 fw-semibold">Expected Payment Date:</div>
|
||||
<div className="col-8">
|
||||
{formatUTCToLocalTime(data?.exceptedPaymentDate)}
|
||||
</div>
|
||||
<div className="col-md-6 d-flex">
|
||||
<p className="m-0 fw-semibold me-1">Mark as Completed:</p>{" "}
|
||||
{data?.markAsCompleted ? "Yes" : "No"}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="row mb-3">
|
||||
<div className="col-md-6 mb-3 d-flex">
|
||||
<p className="m-0 fw-semibold me-1">Basic Amount:</p>{" "}
|
||||
{/* Row 5: Basic Amount + Tax Amount */}
|
||||
<div className="col-md-6">
|
||||
<div className="row mb-4 align-items-end">
|
||||
<div className="col-4 fw-semibold">Basic Amount :</div>
|
||||
<div className="col-8">
|
||||
{formatFigure(data?.basicAmount, {
|
||||
type: "currency",
|
||||
currency: "INR",
|
||||
})}
|
||||
</div>
|
||||
<div className="col-md-6 d-flex">
|
||||
<p className="m-0 fw-semibold me-1">Tax Amount:</p>{" "}
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-md-6">
|
||||
<div className="row mb-4 align-items-end">
|
||||
<div className="col-4 fw-semibold">Tax Amount :</div>
|
||||
<div className="col-8">
|
||||
{formatFigure(data?.taxAmount, {
|
||||
type: "currency",
|
||||
currency: "INR",
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="row mb-3">
|
||||
<div className="col-md-6 mb-3 d-flex">
|
||||
<p className="m-0 fw-semibold me-1">Balance Amount:</p>{" "}
|
||||
</div>
|
||||
{/* Row 6: Balance Amount + Created At */}
|
||||
<div className="col-md-6">
|
||||
<div className="row mb-4 align-items-end">
|
||||
<div className="col-4 fw-semibold">Balance Amount :</div>
|
||||
<div className="col-8">
|
||||
{formatFigure(data?.balanceAmount, {
|
||||
type: "currency",
|
||||
currency: "INR",
|
||||
})}
|
||||
</div>
|
||||
<div className="col-md-6 d-flex">
|
||||
<p className="m-0 fw-semibold me-1">Created At:</p>{" "}
|
||||
{formatUTCToLocalTime(data?.createdAt)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="row mb-3">
|
||||
<div className="col-md-6 mb-3 d-flex align-items-center">
|
||||
<p className="m-0 fw-semibold">Created By:</p>{" "}
|
||||
<div className="d-flex align-items-center">
|
||||
<div className="col-md-6">
|
||||
<div className="row mb-4 align-items-end">
|
||||
<div className="col-4 fw-semibold">Created At :</div>
|
||||
<div className="col-8">{formatUTCToLocalTime(data?.createdAt)}</div>
|
||||
</div>
|
||||
</div>
|
||||
{/* Row 7: Created By */}
|
||||
<div className="col-md-6">
|
||||
<div className="row mb-4 align-items-center">
|
||||
<div className="col-4 fw-semibold">Created By :</div>
|
||||
<div className="col-8 d-flex align-items-center">
|
||||
<Avatar
|
||||
size="xs"
|
||||
firstName={data.createdBy?.firstName}
|
||||
lastName={data.createdBy?.lastName}
|
||||
/>
|
||||
<span className="ms-1 text-muted">
|
||||
{data?.createdBy?.firstName} {data?.createdBy?.lastName}
|
||||
</div>{" "}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/* Description */}
|
||||
<div className="col-12 my-1 mb-2">
|
||||
<div className=" me-2 mb-0 fw-semibold">
|
||||
Description :
|
||||
</div>
|
||||
<div className="text-muted">{data?.description}</div>
|
||||
</div>
|
||||
|
||||
<div className="col-12">
|
||||
<p className="m-0 fw-semibold">Description : </p>
|
||||
{data?.description}
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-12 text-start">
|
||||
<label className="form-label me-2 mb-2 fw-semibold">
|
||||
<label className=" me-2 mb-2 fw-semibold">
|
||||
Attachment :
|
||||
</label>
|
||||
|
||||
@ -166,27 +201,15 @@ const ViewCollection = ({ onClose }) => {
|
||||
</small>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
}) ?? "No Attachment"}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div className="container px-1 ">
|
||||
{/* Tabs Navigation */}
|
||||
<ul className="nav nav-tabs" role="tablist">
|
||||
<li className="nav-item">
|
||||
<button
|
||||
className="nav-link active"
|
||||
id="details-tab"
|
||||
data-bs-toggle="tab"
|
||||
data-bs-target="#details"
|
||||
type="button"
|
||||
role="tab"
|
||||
>
|
||||
<i className="bx bx-message-dots bx-sm me-2"></i> Comments (
|
||||
{data?.comments?.length ?? "0"})
|
||||
</button>
|
||||
</li>
|
||||
<li className="nav-item">
|
||||
<li className="nav-item active">
|
||||
<button
|
||||
className="nav-link"
|
||||
id="payments-tab"
|
||||
@ -198,19 +221,32 @@ const ViewCollection = ({ onClose }) => {
|
||||
<i className="bx bx-history bx-sm me-1"></i> Payments History
|
||||
</button>
|
||||
</li>
|
||||
<li className="nav-item">
|
||||
<button
|
||||
className="nav-link "
|
||||
id="details-tab"
|
||||
data-bs-toggle="tab"
|
||||
data-bs-target="#details"
|
||||
type="button"
|
||||
role="tab"
|
||||
>
|
||||
<i className="bx bx-message-dots bx-sm me-2"></i> Comments (
|
||||
{data?.comments?.length ?? "0"})
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div className="tab-content py-1 border-top-0 ">
|
||||
<div
|
||||
className="tab-pane fade show active"
|
||||
id="details"
|
||||
role="tabpanel"
|
||||
>
|
||||
<div className="tab-content px-1 py-0 border-top-0 ">
|
||||
<div className="tab-pane fade " id="details" role="tabpanel">
|
||||
<Comment invoice={data} />
|
||||
</div>
|
||||
|
||||
{/* Payments History Tab Content */}
|
||||
<div className="tab-pane fade" id="payments" role="tabpanel">
|
||||
<div
|
||||
className="tab-pane fade show active"
|
||||
id="payments"
|
||||
role="tabpanel"
|
||||
>
|
||||
<div className="row text-start">
|
||||
<PaymentHistoryTable data={data} />
|
||||
</div>
|
||||
|
@ -77,7 +77,8 @@ export const paymentSchema = z.object({
|
||||
paymentReceivedDate: z.string().min(1, { message: "Date is required" }),
|
||||
transactionId: z.string().min(1, "Transaction ID is required"),
|
||||
amount: z.number().min(1, "Amount must be greater than zero"),
|
||||
comment:z.string().min(1,{message:"Comment required"})
|
||||
comment:z.string().min(1,{message:"Comment required"}),
|
||||
paymentAdjustmentHeadId:z.string().min(1,{message:"Payment Type required"})
|
||||
});
|
||||
|
||||
// Default Value
|
||||
@ -85,7 +86,8 @@ export const defaultPayment = {
|
||||
paymentReceivedDate: null,
|
||||
transactionId: "",
|
||||
amount: 0,
|
||||
comment:""
|
||||
comment:"",
|
||||
paymentAdjustmentHeadId:""
|
||||
};
|
||||
|
||||
|
||||
|
@ -10,6 +10,14 @@ import {
|
||||
} from "@tanstack/react-query";
|
||||
import showToast from "../../services/toastService";
|
||||
|
||||
export const usePaymentType = (isActive) => {
|
||||
return useQuery({
|
||||
queryKey: ["paymentType",isActive],
|
||||
queryFn: async () => await MasterRespository.getPaymentType(isActive),
|
||||
|
||||
});
|
||||
};
|
||||
|
||||
export const useServices = () => {
|
||||
return useQuery({
|
||||
queryKey: ["services"],
|
||||
@ -30,7 +38,6 @@ export const useActivitiesByGroups = (groupId) => {
|
||||
queryFn: async () => await MasterRespository.getActivitesByGroup(groupId),
|
||||
|
||||
enabled: !!groupId,
|
||||
|
||||
});
|
||||
};
|
||||
export const useGlobalServices = () => {
|
||||
@ -448,8 +455,6 @@ export const useUpdateApplicationRole = (onSuccessCallback) => {
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
|
||||
//-----Create work Category-------------------------------
|
||||
export const useCreateWorkCategory = (onSuccessCallback) => {
|
||||
const queryClient = useQueryClient();
|
||||
@ -703,13 +708,11 @@ export const useCreateService = (onSuccessCallback) => {
|
||||
|
||||
return useMutation({
|
||||
mutationFn: async (payload) => {
|
||||
|
||||
const resp = await MasterRespository.createService(payload);
|
||||
|
||||
return resp.data;
|
||||
},
|
||||
onSuccess: (data) => {
|
||||
|
||||
queryClient.invalidateQueries({ queryKey: ["masterData", "Services"] });
|
||||
|
||||
showToast(data?.message || "Service added successfully", "success");
|
||||
@ -717,8 +720,12 @@ export const useCreateService = (onSuccessCallback) => {
|
||||
if (onSuccessCallback) onSuccessCallback(data?.data);
|
||||
},
|
||||
onError: (error) => {
|
||||
|
||||
showToast( error?.response?.data?.message || error?.message || "Something went wrong", "error");
|
||||
showToast(
|
||||
error?.response?.data?.message ||
|
||||
error?.message ||
|
||||
"Something went wrong",
|
||||
"error"
|
||||
);
|
||||
},
|
||||
});
|
||||
};
|
||||
@ -741,7 +748,12 @@ export const useUpdateService = (onSuccessCallback) => {
|
||||
if (onSuccessCallback) onSuccessCallback(data);
|
||||
},
|
||||
onError: (error) => {
|
||||
showToast(error?.response?.data?.message || error?.message || "Something went wrong", "error");
|
||||
showToast(
|
||||
error?.response?.data?.message ||
|
||||
error?.message ||
|
||||
"Something went wrong",
|
||||
"error"
|
||||
);
|
||||
},
|
||||
});
|
||||
};
|
||||
@ -759,15 +771,22 @@ export const useCreateActivityGroup = (onSuccessCallback) => {
|
||||
queryKey: ["groups"],
|
||||
});
|
||||
|
||||
showToast( data?.message ||
|
||||
data?.response?.data?.message || "Activity Group created successfully.",
|
||||
showToast(
|
||||
data?.message ||
|
||||
data?.response?.data?.message ||
|
||||
"Activity Group created successfully.",
|
||||
"success"
|
||||
);
|
||||
|
||||
if (onSuccessCallback) onSuccessCallback(data);
|
||||
},
|
||||
onError: (error) => {
|
||||
showToast(error?.response?.data?.message || error?.message || "Something went wrong", "error");
|
||||
showToast(
|
||||
error?.response?.data?.message ||
|
||||
error?.message ||
|
||||
"Something went wrong",
|
||||
"error"
|
||||
);
|
||||
},
|
||||
});
|
||||
};
|
||||
@ -786,7 +805,8 @@ export const useUpdateActivityGroup = (onSuccessCallback) => {
|
||||
|
||||
showToast(
|
||||
data?.message ||
|
||||
data?.response?.data?.message|| "Activity Group Updated successfully.",
|
||||
data?.response?.data?.message ||
|
||||
"Activity Group Updated successfully.",
|
||||
"success"
|
||||
);
|
||||
|
||||
@ -812,7 +832,12 @@ export const useCreateActivity = (onSuccessCallback) => {
|
||||
if (onSuccessCallback) onSuccessCallback(data);
|
||||
},
|
||||
onError: (error) => {
|
||||
showToast(error?.response?.data?.message || error?.message || "Something went wrong", "error");
|
||||
showToast(
|
||||
error?.response?.data?.message ||
|
||||
error?.message ||
|
||||
"Something went wrong",
|
||||
"error"
|
||||
);
|
||||
},
|
||||
});
|
||||
};
|
||||
@ -834,7 +859,12 @@ export const useUpdateActivity = (onSuccessCallback) => {
|
||||
if (onSuccessCallback) onSuccessCallback(data);
|
||||
},
|
||||
onError: (error) => {
|
||||
showToast(error?.response?.data?.message || error?.message || "Something went wrong", "error");
|
||||
showToast(
|
||||
error?.response?.data?.message ||
|
||||
error?.message ||
|
||||
"Something went wrong",
|
||||
"error"
|
||||
);
|
||||
},
|
||||
});
|
||||
};
|
||||
@ -1001,14 +1031,12 @@ export const useDeleteMasterItem = () => {
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
export const useDeleteServiceGroup = () => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: async (id) => await MasterRespository.deleteActivityGroup(id),
|
||||
onSuccess: ({ _, variable }) => {
|
||||
|
||||
queryClient.invalidateQueries({ queryKey: ["groups"] });
|
||||
|
||||
showToast(`Group deleted successfully.`, "success");
|
||||
@ -1022,15 +1050,13 @@ export const useDeleteServiceGroup =()=>{
|
||||
showToast(message, "error");
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
export const useDeleteActivity = () => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: async (id) => await MasterRespository.deleteActivity(id),
|
||||
onSuccess: ({ _, variable }) => {
|
||||
|
||||
|
||||
queryClient.invalidateQueries({ queryKey: ["activties"] });
|
||||
|
||||
showToast(`Acivity deleted successfully.`, "success");
|
||||
@ -1044,4 +1070,4 @@ export const useDeleteActivity =()=>{
|
||||
showToast(message, "error");
|
||||
},
|
||||
});
|
||||
}
|
||||
};
|
||||
|
@ -26,7 +26,7 @@ export const useCollectionContext = () => {
|
||||
return context;
|
||||
};
|
||||
const CollectionPage = () => {
|
||||
const [viewCollection,setViewCollection] = useState(null)
|
||||
const [viewCollection, setViewCollection] = useState(null);
|
||||
const [makeCollection, setCollection] = useState({
|
||||
isOpen: false,
|
||||
invoiceId: null,
|
||||
@ -59,12 +59,14 @@ const CollectionPage = () => {
|
||||
addPayment,
|
||||
setViewCollection,
|
||||
viewCollection,
|
||||
setDocumentView
|
||||
setDocumentView,
|
||||
};
|
||||
const { mutate: MarkedReceived, isPending } = useMarkedPaymentReceived(() => {
|
||||
setProcessedPayment(null);
|
||||
});
|
||||
const handleMarkedPayment = () => {};
|
||||
const handleMarkedPayment = (payload) => {
|
||||
MarkedReceived(payload);
|
||||
};
|
||||
return (
|
||||
<CollectionContext.Provider value={contextMassager}>
|
||||
<div className="container-fluid">
|
||||
@ -99,7 +101,7 @@ const CollectionPage = () => {
|
||||
</div>
|
||||
|
||||
<div className="col-12 col-md-6 d-flex justify-content-end gap-4">
|
||||
<div className="">
|
||||
<div className=" w-md-auto">
|
||||
{" "}
|
||||
<input
|
||||
type="search"
|
||||
@ -156,7 +158,11 @@ const CollectionPage = () => {
|
||||
)}
|
||||
|
||||
{viewCollection && (
|
||||
<GlobalModel size="lg" isOpen={viewCollection} closeModal={()=>setViewCollection(null)}>
|
||||
<GlobalModel
|
||||
size="lg"
|
||||
isOpen={viewCollection}
|
||||
closeModal={() => setViewCollection(null)}
|
||||
>
|
||||
<ViewCollection onClose={() => setViewCollection(null)} />
|
||||
</GlobalModel>
|
||||
)}
|
||||
@ -169,7 +175,8 @@ const CollectionPage = () => {
|
||||
closeModal={() => setDocumentView({ IsOpen: false, Image: null })}
|
||||
>
|
||||
<PreviewDocument imageUrl={ViewDocument.Image} />
|
||||
</GlobalModel>)}
|
||||
</GlobalModel>
|
||||
)}
|
||||
|
||||
<ConfirmModal
|
||||
type="success"
|
||||
@ -177,7 +184,7 @@ const CollectionPage = () => {
|
||||
message="Your payment has been processed successfully. Do you want to continue?"
|
||||
isOpen={processedPayment?.isOpen}
|
||||
loading={isPending}
|
||||
onSubmit={handleMarkedPayment}
|
||||
onSubmit={() => handleMarkedPayment(processedPayment?.invoiceId)}
|
||||
onClose={() => setProcessedPayment(null)}
|
||||
/>
|
||||
</div>
|
||||
|
@ -124,10 +124,13 @@ export const MasterRespository = {
|
||||
api.put(`/api/Master/activity-group/edit/${serviceId}`, data),
|
||||
getActivitesByGroup: (activityGroupId) =>
|
||||
api.get(`api/master/activities?activityGroupId=${activityGroupId}`),
|
||||
deleteActivityGroup:(id)=>api.delete(`/api/Master/activity-group/delete/${id}`),
|
||||
|
||||
deleteActivityGroup: (id) =>
|
||||
api.delete(`/api/Master/activity-group/delete/${id}`),
|
||||
|
||||
deleteActivity: (id) => api.delete(`/api/Master/activity/delete/${id}`),
|
||||
|
||||
getOrganizationType: () => api.get("/api/Master/organization-type/list"),
|
||||
|
||||
getPaymentType: (isActive) =>
|
||||
api.get(`/api/Master/payment-adjustment-head/list?isActive=${isActive}`),
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user