222 lines
11 KiB
TypeScript
222 lines
11 KiB
TypeScript
import React, { useEffect, useState } from 'react'
|
|
import { PiDotsThreeOutlineVerticalFill } from 'react-icons/pi';
|
|
import api from '../../config/api';
|
|
import { Transaction } from '../transactions/Transactions';
|
|
import { useLocation, useNavigate } from 'react-router-dom';
|
|
import { FaCheck } from 'react-icons/fa';
|
|
import LoaderUi from '../../helper/LoaderUi';
|
|
import logoRecord from '../../assets/record.svg';
|
|
|
|
|
|
|
|
|
|
const formatDate = (timestamp: number) => {
|
|
return new Date(timestamp * 1000).toLocaleString();
|
|
};
|
|
|
|
const statusColorMap = {
|
|
CREATED: "bg-blue-100 text-blue-600",
|
|
SENT: "bg-green-100 text-green-600",
|
|
COMPLETED: "bg-purple-100 text-purple-600",
|
|
EXPIRED: "bg-red-100 text-red-600",
|
|
};
|
|
|
|
|
|
const TransactionDataTable = ({activeAccountId}) => {
|
|
const navigate = useNavigate()
|
|
const loaction = useLocation()
|
|
console.log("loaction", loaction.pathname);
|
|
|
|
const [currentEmailId, setCurrentEmailId] = useState<string | null>('null');
|
|
const [selectedTransactions, setSelectedTransactions] = useState<number[]>([]);
|
|
const [loading, setLoading] = useState(false)
|
|
|
|
const fetchTransactions = async () => {
|
|
console.log("fetchTransactions")
|
|
setLoading(true)
|
|
try {
|
|
const response = await api.get(`/api/accounts/${activeAccountId}/transactions?sortBy=lastModifiedAt desc`);
|
|
setTransactions(response.data || []);
|
|
} catch (error) {
|
|
console.error("Error fetching transactions:", error);
|
|
} finally {
|
|
setLoading(false)
|
|
}
|
|
};
|
|
|
|
const [transactions, setTransactions] = useState<Transaction[]>([]);
|
|
|
|
const handleTransactionClick = (id: number) => {
|
|
navigate(`/accounts/${activeAccountId}/transactions/${id}`);
|
|
};
|
|
|
|
useEffect(() => {
|
|
if (activeAccountId) {
|
|
fetchTransactions();
|
|
}
|
|
}, [activeAccountId]);
|
|
|
|
useEffect(() => {
|
|
const userDetails = JSON.parse(sessionStorage.getItem('userDetails'));
|
|
setCurrentEmailId(userDetails?.email)
|
|
}, []);
|
|
|
|
|
|
function formatTimestamp(isoString, options = { timeZone: 'local' }) {
|
|
// Parse the ISO string to a Date object
|
|
const date = new Date(isoString);
|
|
|
|
if (isNaN(date)) {
|
|
return 'Invalid date';
|
|
}
|
|
|
|
// Return formatted date string
|
|
if (options.timeZone === 'UTC') {
|
|
return date.toUTCString(); // e.g. "Mon, 04 Aug 2025 13:46:46 GMT"
|
|
} else {
|
|
return date.toLocaleString(); // e.g. "8/4/2025, 9:46:46 AM" (based on local time zone)
|
|
}
|
|
}
|
|
|
|
function getStatusColorClass(status) {
|
|
return statusColorMap[status] || "bg-gray-100 text-gray-600";
|
|
}
|
|
|
|
|
|
|
|
return (
|
|
<>
|
|
|
|
<div className="bg-white rounded-xl shadow-sm border border-gray-100 overflow-hidden">
|
|
{/* Header */}
|
|
{location.pathname !== '/transactions' &&
|
|
<div className="flex justify-between items-center px-6 py-4 border-b border-gray-100">
|
|
<h2 className="text-sm font-semibold text-gray-800">Recent Transactions</h2>
|
|
<span className=' text-[var(--primary)] text-xs hover:underline cursor-pointer font-semibold'>View all</span>
|
|
</div>}
|
|
|
|
{/* Scrollable Table Container */}
|
|
<div className={`overflow-auto no-scrollbar ${location.pathname === '/transactions' ? "min-h-[76vh] max-h-[76vh]" : "min-h-[70vh] max-h-[70vh]"}`}>
|
|
<table className="w-full text-sm">
|
|
{/* Sticky Header */}
|
|
<thead className="bg-gray-50 sticky top-0 z-10">
|
|
<tr className="text-gray-600 text-xs uppercase tracking-wider border-b border-gray-200">
|
|
<th className="px-6 py-4 text-left font-semibold">Document</th>
|
|
<th className="px-6 py-4 text-center font-semibold">Status</th>
|
|
{location.pathname === '/transactions' && (
|
|
<>
|
|
<th className="px-6 py-4 text-center font-semibold">Signed By</th>
|
|
<th className="px-6 py-4 text-center font-semibold">Last Change</th>
|
|
<th className="px-6 py-4 text-center font-semibold">Expire After (Days)</th>
|
|
<th className="px-6 py-4 text-center font-semibold">Actions</th>
|
|
</>
|
|
)}
|
|
|
|
</tr>
|
|
</thead>
|
|
{loading ? (
|
|
<tbody>
|
|
<tr>
|
|
<td colSpan="6" className="relative h-100">
|
|
<LoaderUi />
|
|
</td>
|
|
</tr>
|
|
</tbody>
|
|
) : transactions.length > 0 ? (
|
|
<>
|
|
{/* Scrollable Body */}
|
|
<tbody className="bg-white divide-y divide-gray-100">
|
|
{transactions.map((tx) => (
|
|
<tr
|
|
key={tx.id}
|
|
onClick={() => handleTransactionClick(tx.id)}
|
|
className="hover:bg-gray-50 transition-colors duration-150 cursor-pointer"
|
|
>
|
|
<td className="px-6 py-4 text-left">
|
|
<div className="text-gray-800 font-medium truncate max-w-xs">
|
|
{tx?.subject || "Untitled"}
|
|
</div>
|
|
</td>
|
|
<td className="px-6 py-4 text-center">
|
|
<span
|
|
className={`inline-block px-3 py-1 rounded-full text-xs font-medium ${getStatusColorClass(
|
|
tx.status
|
|
)}`}
|
|
>
|
|
{tx.status}
|
|
</span>
|
|
</td>
|
|
|
|
{location.pathname === '/transactions' && (
|
|
<>
|
|
<td className="px-6 py-4">
|
|
<div className="flex items-center justify-center">
|
|
{tx.signedBy ? (
|
|
<div className="flex items-center gap-2">
|
|
<div
|
|
className="w-7 h-7 rounded-full flex items-center justify-center text-xs font-medium text-white flex-shrink-0"
|
|
style={{ backgroundColor: "#d8b4fe" }}
|
|
>
|
|
{tx.signedByInitials}
|
|
</div>
|
|
<span className="text-gray-700 truncate max-w-[120px]">
|
|
{tx.signedBy}
|
|
</span>
|
|
</div>
|
|
) : (
|
|
<span className="text-gray-400">-</span>
|
|
)}
|
|
</div>
|
|
</td>
|
|
<td className="px-6 py-4 text-center">
|
|
<span className="text-gray-600 whitespace-nowrap">
|
|
{formatTimestamp(tx.lastModifiedAt)}
|
|
</span>
|
|
</td>
|
|
<td className="px-6 py-4 text-center">
|
|
<span className="text-gray-700">
|
|
{tx.expireAfter ?? "-"}
|
|
</span>
|
|
</td>
|
|
<td className="px-6 py-4 text-center">
|
|
<button
|
|
className="text-gray-400 hover:text-gray-600 transition-colors duration-150 p-1 rounded-md hover:bg-gray-100"
|
|
onClick={(e) => {
|
|
e.stopPropagation();
|
|
// Handle actions menu
|
|
}}
|
|
>
|
|
<PiDotsThreeOutlineVerticalFill className="text-lg" />
|
|
</button>
|
|
</td>
|
|
</>
|
|
)}
|
|
|
|
|
|
</tr>
|
|
))}
|
|
</tbody>
|
|
</>) : (
|
|
<tbody>
|
|
<tr>
|
|
<td colSpan="6" className="text-center py-10 text-gray-500">
|
|
<div className="flex flex-col items-center justify-center">
|
|
<img
|
|
src={logoRecord}
|
|
alt="Record Not Found"
|
|
className="w-70 h-70 mb-4 opacity-70"
|
|
/>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
</tbody>
|
|
)}
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
</>
|
|
)
|
|
}
|
|
|
|
export default TransactionDataTable |