257 lines
9.3 KiB
JavaScript
257 lines
9.3 KiB
JavaScript
import React, { useState } from "react";
|
|
import { useCollectionContext } from "../../pages/collections/CollectionPage";
|
|
import { useCollection } from "../../hooks/useCollections";
|
|
import { formatUTCToLocalTime } from "../../utils/dateUtils";
|
|
import { formatFigure, getIconByFileType } from "../../utils/appUtils";
|
|
import Avatar from "../common/Avatar";
|
|
import PaymentHistoryTable from "./PaymentHistoryTable";
|
|
import Comment from "./Comment";
|
|
import { CollectionDetailsSkeleton } from "./CollectionSkeleton";
|
|
import { useHasUserPermission } from "../../hooks/useHasUserPermission";
|
|
import { ADMIN_COLLECTION, EDIT_COLLECTION } from "../../utils/constants";
|
|
|
|
const ViewCollection = ({ onClose }) => {
|
|
const [activeTab, setActiveTab] = useState("payments");
|
|
const isAdmin = useHasUserPermission(ADMIN_COLLECTION);
|
|
const canEditCollection = useHasUserPermission(EDIT_COLLECTION);
|
|
const { viewCollection, setCollection, setDocumentView } =
|
|
useCollectionContext();
|
|
const { data, isLoading, isError, error } = useCollection(viewCollection);
|
|
|
|
const handleEdit = () => {
|
|
setCollection({ isOpen: true, invoiceId: viewCollection });
|
|
onClose();
|
|
};
|
|
|
|
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="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>
|
|
{" "}
|
|
<span
|
|
className={`badge bg-label-${data?.isActive ? "primary" : "danger"
|
|
}`}
|
|
>
|
|
{data?.isActive ? "Active" : "Inactive"}
|
|
</span>
|
|
{(isAdmin || canEditCollection) &&
|
|
!data?.receivedInvoicePayments && (
|
|
<span onClick={handleEdit} className="ms-2 cursor-pointer">
|
|
<i className="bx bx-edit text-primary bx-sm"></i>
|
|
</span>
|
|
)}
|
|
</div>
|
|
</div>
|
|
<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="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>
|
|
{/* 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>
|
|
|
|
{/* 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>
|
|
</div>
|
|
<div className="col-md-6">
|
|
<div className="row mb-4 align-items-end">
|
|
<div className="col-4 fw-semibold">Client Submission Date:</div>
|
|
<div className="col-8">
|
|
{formatUTCToLocalTime(data?.clientSubmitedDate)}
|
|
</div>
|
|
</div>
|
|
</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>
|
|
</div>
|
|
|
|
{/* 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>
|
|
</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>
|
|
{/* 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>
|
|
</div>
|
|
<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}
|
|
</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 text-start">
|
|
<label className=" me-2 mb-2 fw-semibold">Attachment :</label>
|
|
|
|
<div className="d-flex flex-wrap gap-2">
|
|
{data?.attachments?.map((doc) => {
|
|
const isImage = doc.contentType?.startsWith("image");
|
|
|
|
return (
|
|
<div
|
|
key={doc.documentId}
|
|
className="border rounded hover-scale p-2 d-flex flex-column align-items-center"
|
|
style={{
|
|
width: "80px",
|
|
cursor: "pointer",
|
|
}}
|
|
onClick={() => {
|
|
if (isImage) {
|
|
setDocumentView({
|
|
IsOpen: true,
|
|
Image: doc.preSignedUrl,
|
|
});
|
|
} else {
|
|
window.open(doc.preSignedUrl, "_blank");
|
|
}
|
|
}}
|
|
>
|
|
<i
|
|
className={`bx ${getIconByFileType(doc.contentType)}`}
|
|
style={{ fontSize: "30px" }}
|
|
></i>
|
|
<small
|
|
className="text-center text-tiny text-truncate w-100"
|
|
title={doc.fileName}
|
|
>
|
|
{doc.fileName}
|
|
</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 ${activeTab === "payments" ? "active" : ""
|
|
}`}
|
|
onClick={() => setActiveTab("payments")}
|
|
type="button"
|
|
>
|
|
<i className="bx bx-history bx-sm me-1"></i> Payments History
|
|
</button>
|
|
</li>
|
|
<li className="nav-item">
|
|
<button
|
|
className={`nav-link ${activeTab === "details" ? "active" : ""
|
|
}`}
|
|
onClick={() => setActiveTab("details")}
|
|
type="button"
|
|
>
|
|
<i className="bx bx-message-dots bx-sm me-2"></i> Comments (
|
|
{data?.comments?.length ?? "0"})
|
|
</button>
|
|
</li>
|
|
</ul>
|
|
|
|
{/* Tab Content */}
|
|
<div className="tab-content px-1 py-0 border-top-0">
|
|
{activeTab === "payments" && (
|
|
<div className="tab-pane fade show active">
|
|
<PaymentHistoryTable data={data} />
|
|
</div>
|
|
)}
|
|
|
|
{activeTab === "details" && (
|
|
<div className="tab-pane fade show active">
|
|
<Comment invoice={data} />
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default ViewCollection;
|