253 lines
14 KiB
TypeScript
253 lines
14 KiB
TypeScript
import { AiFillSignature } from 'react-icons/ai';
|
|
import { Link, useLocation, useNavigate, useParams, useSearchParams } from 'react-router-dom';
|
|
import { LuGitGraph } from 'react-icons/lu';
|
|
import AccountNav from '../accounts/AccountNav';
|
|
import { useEffect, useState } from 'react';
|
|
import api from '../../config/api';
|
|
import SigningOrderDiagram from '../../SigningOrderDiagram ';
|
|
import { IoCloseOutline } from 'react-icons/io5';
|
|
import { MdEditDocument } from "react-icons/md";
|
|
import { useSelector } from 'react-redux';
|
|
import { RootState } from '../../store/store';
|
|
import { SignTypeEnum } from '../../utlity/types';
|
|
import AuditModal from './AuditModal';
|
|
|
|
// Sample recipient data — you can replace this with actual API data later
|
|
const recipients = [
|
|
{
|
|
id: 1,
|
|
name: "Shanu.R",
|
|
email: "shanu.r@example.com",
|
|
signedAt: "5/1/2025 | 04:24:35 pm",
|
|
status: "Signed",
|
|
viewLink: "#"
|
|
},
|
|
{
|
|
id: 2,
|
|
name: "Amit B",
|
|
email: "amit.b@example.com",
|
|
signedAt: "5/1/2025 | 04:24:35 pm",
|
|
status: "Signed",
|
|
viewLink: "#"
|
|
}
|
|
];
|
|
|
|
const TransactionView = () => {
|
|
const { accountId, transactionId } = useParams<{ accountId: string, transactionId: string }>();
|
|
|
|
const navigate = useNavigate();
|
|
|
|
if (!accountId || !transactionId) {
|
|
window.location.href = "/";
|
|
return null;
|
|
}
|
|
|
|
const [transactionDetails, setTransactionDetails] = useState(null);
|
|
const [signingOrderModal, setSigningOrderModal] = useState({ open: false });
|
|
const user = useSelector((state: RootState) => state.user)
|
|
const location = useLocation();
|
|
const [isModalOpen, setIsModalOpen] = useState(false);
|
|
|
|
useEffect(() => {
|
|
// update whenever location changes
|
|
setIsModalOpen(location.search.includes("auditStatus"));
|
|
}, [location]);
|
|
|
|
const handleOpenAuditModal = () => {
|
|
const { pathname, search } = location;
|
|
let newSearch = search
|
|
? search.includes("auditStatus")
|
|
? search
|
|
: `${search}&auditStatus`
|
|
: "?auditStatus";
|
|
|
|
window.history.pushState({}, "", pathname + newSearch);
|
|
setIsModalOpen(true); // ✅ update state immediately
|
|
};
|
|
|
|
const closeModal = () => {
|
|
let search = location.search
|
|
.replace(/[?&]auditStatus\b/, "")
|
|
.replace(/^&/, "?")
|
|
.replace(/\?$/, "");
|
|
|
|
window.history.pushState({}, "", location.pathname + search);
|
|
setIsModalOpen(false); // ✅ update state immediately
|
|
};
|
|
|
|
console.log(transactionDetails, "line 37");
|
|
|
|
const fetchData = async () => {
|
|
try {
|
|
const [res1, res2] = await Promise.all([api.get(`/api/accounts/${accountId}/transactions/${transactionId}`),
|
|
api.get(`/api/accounts/${accountId}/transactions/${transactionId}/recipients`)])
|
|
setTransactionDetails({ transaction: res1.data, recipients: res2.data });
|
|
} catch (error) {
|
|
console.error("Error Gettign Transaction and its Recipient: ", error.message)
|
|
}
|
|
}
|
|
|
|
useEffect(() => {
|
|
fetchData()
|
|
}, [accountId, transactionId])
|
|
|
|
|
|
const handleNavigateToSign = (id: number) => {
|
|
window.open(`./../../../../sign/${id}/${SignTypeEnum.selfSign}`)
|
|
}
|
|
|
|
return (
|
|
<>
|
|
{transactionDetails &&
|
|
<>
|
|
<div className='mt-2 flex justify-center items-stretch w-full h-full bg-white rounded-sm'>
|
|
<div className="w-full px-6 py-4 rounded-tl-sm text-xs border-r-2 border-gray-200 overflow-y-auto">
|
|
{/* Header */}
|
|
<h1 className='text-lg font-semibold text-gray-800'>
|
|
Complete with Forms Made Easy: <span className='text-slate-600'>{transactionDetails?.transaction?.subject ?? "Untitled"}</span>
|
|
</h1>
|
|
|
|
<div className='mt-2'>
|
|
{/* new Date(transactionDetails?.transaction?.lastModifiedAt * 1000) */}
|
|
<p className='text-slate-600'>From <span className='text-blue-600'>{transactionDetails?.transaction.createdBy.firstName + " " + transactionDetails?.transaction.createdBy.lastName}</span></p>
|
|
<p className=''><span className='text-slate-600 font-semibold'>Last Change on</span> <span className='text-slate-400'>{(transactionDetails?.transaction?.lastModifiedAt)}</span></p>
|
|
<p className='text-slate-600'>Created at <span>{(transactionDetails?.transaction?.createdAt)}</span></p>
|
|
</div>
|
|
|
|
<p className='text-slate-600 text-[0.98rem] mt-2 font-semibold'>{transactionDetails?.transaction.status}</p>
|
|
|
|
{/* Buttons */}
|
|
<div className='my-2 flex gap-2'>
|
|
{/* <button
|
|
type="button"
|
|
aria-label="Sign the document"
|
|
className='cursor-pointer border border-gray-300 rounded-sm px-4 py-1 bg-[var(--primary)] text-white'
|
|
>
|
|
Sign
|
|
</button> */}
|
|
<button className='cursor-pointer border border-gray-300 rounded-sm px-4 py-1 hover:bg-gray-100 font-bold'>
|
|
More
|
|
</button>
|
|
|
|
{/* <Link className='cursor-pointer border text-green-700 border-gray-300 rounded-sm px-4 py-1 hover:bg-gray-100 font-bold' to={`/process-step-two/${accountId}/${transactionId}`}>
|
|
Edit
|
|
</Link> */}
|
|
<button className='cursor-pointer border text-green-700 border-gray-300 rounded-sm px-4 py-1 hover:bg-gray-100 font-bold' onClick={handleOpenAuditModal}>
|
|
Audit
|
|
</button>
|
|
|
|
</div>
|
|
|
|
<hr className='my-6 border-slate-200' />
|
|
|
|
{/* Recipients Section */}
|
|
{transactionDetails.recipients.length > 0 &&
|
|
<div>
|
|
<div className='flex justify-between items-center text-md'>
|
|
<h2 className='text-lg font-semibold text-gray-800 cursor-pointer'>Signers</h2>
|
|
<p className='font-bold flex items-center gap-1.5 text-slate-800 cursor-pointer' onClick={() => setSigningOrderModal({ ...signingOrderModal, open: true })}>
|
|
<LuGitGraph className='text-[0.9rem] text-[var(--primary)]' />
|
|
Signing Graph
|
|
</p>
|
|
</div>
|
|
|
|
{/* Recipients List */}
|
|
{transactionDetails.recipients.map((recipient, index) => (
|
|
<div key={index}>
|
|
<div className='mt-2 flex justify-between items-center'>
|
|
<div className='flex gap-8 items-center'>
|
|
{/* <p className='text-slate-600'>{index+1}</p> */}
|
|
<div>
|
|
<p className='text-slate-600 font-semibold'>{recipient.name}</p>
|
|
<p className='text-slate-600'>{recipient.email}</p>
|
|
{recipient.hostEmail !== null && recipient.hostEmail !== "" && <p className='text-xs'><i><b>Host: {recipient.hostEmail}</b></i></p>}
|
|
</div>
|
|
</div>
|
|
|
|
<div className='flex flex-col leading-4.5 items-start text-md text-slate-600'>
|
|
<div className='flex gap-3 items-center relative'>
|
|
<div className='flex gap-1 items-center relative'>
|
|
<AiFillSignature className='absolute left-[-30px] text-[1rem]' />
|
|
<p className='text-black font-semibold'>{recipient.status}</p>
|
|
</div>
|
|
{recipient.email === user.email && recipient.status !== "COMPLETED" &&
|
|
<p
|
|
onClick={() => handleNavigateToSign(recipient.id)}
|
|
className='text-white rounded-sm bg-[var(--primary)] px-3 py-1 font-semibold cursor-pointer'>
|
|
Sign
|
|
</p>}
|
|
</div>
|
|
{recipient.status !== "PENDING" && <p>on {recipient.completedAt}</p>}
|
|
{/* <a href={recipient.viewLink} className='text-blue-600 hover:underline'>
|
|
View signed location
|
|
</a> */}
|
|
</div>
|
|
</div>
|
|
<hr className='my-2 border-slate-200' />
|
|
</div>
|
|
))}
|
|
</div>
|
|
}
|
|
|
|
|
|
{transactionDetails?.transaction?.subject !== "" &&
|
|
<div>
|
|
<h2 className='text-lg mt-4 font-semibold text-gray-800 cursor-pointer'>Subject</h2>
|
|
<p className='mt-2 text-slate-600'>{`${transactionDetails?.transaction?.subject ? transactionDetails?.transaction?.subject : "Not Found"} `}</p>
|
|
</div>}
|
|
|
|
{transactionDetails?.transaction?.message !== "" &&
|
|
<div>
|
|
<h2 className='text-lg mt-4 font-semibold text-gray-800 cursor-pointer'>Message</h2>
|
|
<p className='mt-2 text-slate-600'>{`${transactionDetails?.transaction?.message ? transactionDetails?.transaction?.message : "Not Found"} `}</p>
|
|
</div>}
|
|
</div>
|
|
|
|
<div className='w-[210px] text-xs rounded-sm '>
|
|
|
|
<div className='p-4 flex flex-col gap-2'>
|
|
<p>abc.pdf</p>
|
|
<div className='border-3 rounded-sm border-slate-300 relative text-xs'>
|
|
<img style={{ width: "200px", height: "160px" }} src="https://images.unsplash.com/photo-1746469535771-71a672e8719f?q=80&w=870&auto=format&fit=crop&ixlib=rb-4.1.0&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D" alt="" />
|
|
<div className='bg-slate-100 p-2 absolute w-full bottom-[0px]'>
|
|
<p className='text-[0.7rem] text-slate-500 flex justify-between items-center'>
|
|
<span> 1 of 10</span>
|
|
<span className='cursor-pointer'><Link to={`/process-step-two/${accountId}/${transactionId}`}><MdEditDocument size={16} /></Link></span>
|
|
</p>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
{signingOrderModal.open &&
|
|
<div className="fixed top-0 left-0 inset-0 z-50 bg-black/50 backdrop-blur-sm flex justify-center items-center overflow-hidden">
|
|
<div className="flex-col relative justify-center items-center max-w-[80%] max-h-[60vh] h-full ">
|
|
<div className="pb-2 text-white flex justify-between items-center bg-[var(--primary)] px-2 py-3 rounded-t-lg">
|
|
<p className="font-semibold">Signing Order</p>
|
|
<div
|
|
onClick={() => setSigningOrderModal({ ...signingOrderModal, open: false })}
|
|
className="text-xl cursor-pointer hover:text-gray-200 transition-all">
|
|
<IoCloseOutline />
|
|
</div>
|
|
</div>
|
|
<span className='overflow-y-auto'>
|
|
<SigningOrderDiagram signers={transactionDetails.recipients} />
|
|
</span>
|
|
</div>
|
|
</div>
|
|
}
|
|
|
|
{isModalOpen && <AuditModal closeModal={closeModal} />}
|
|
</>
|
|
}
|
|
</>
|
|
);
|
|
};
|
|
|
|
export default TransactionView;
|