SME-Web/src/components/templates/Templates.tsx

238 lines
8.8 KiB
TypeScript

import { use, useEffect, useState } from "react";
import { PiDotsThreeOutlineVerticalFill } from "react-icons/pi";
import { Bounce, toast } from "react-toastify";
import api from "../../config/api";
import { IoMdAdd } from "react-icons/io";
import { useNavigate } from "react-router-dom";
import { useSelector } from "react-redux";
import { FaSpinner } from "react-icons/fa";
import LoaderUi from "../../helper/LoaderUi";
import logoRecord from '../../assets/record.svg';
const Templates = () => {
const [activeFilter, setActiveFilter] = useState("All");
const [loading, setLoading] = useState(false)
const [templates, setTemplates] = useState([])
const accountDetails = useSelector((state: any) => state.user.account);
const accountId = accountDetails.id || "";
const [showMenu, setShowMenu] = useState(null);
const [activeAccountId, setActiveAccountId] = useState<string | null>(
JSON.parse(sessionStorage.getItem('activeAccountId') || 'null')
);
const navigate = useNavigate();
const filters = ["All", "My Templates", "Shared"];
const handleAddTemplate = async () => {
console.log("handleAddTemplate called");
try {
const res = await api.post(`/api/accounts/${activeAccountId}/templates`);
navigate(`/process-step-one/${activeAccountId}/template/${res.data.id}`)
} catch (error) {
console.log(error)
}
}
const getAllTemplates = async () => {
setLoading(true)
try {
let res = await api.get(`/api/accounts/${activeAccountId}/templates?sortBy=lastModifiedAt desc`)
let templateData = res?.data
setTemplates(templateData)
} catch (error) {
console.log(error)
} finally {
setLoading(false)
}
}
useEffect(() => {
if (activeAccountId) {
getAllTemplates()
}
}, [activeAccountId])
const formatDate = (timestamp: number) => {
return new Date(timestamp * 1000).toLocaleString();
};
const handleUseClick = async (item: any) => {
try {
const payload = {
name: item?.name ?? "",
subject: item.subject,
message: item.message,
description: item.description,
expireAfter: item.expireAfter,
enableIdStamping: item.enableIdStamping,
allowViewAudit: item.allowViewAudit,
allowReassign: item.allowReassign,
enableSignatureIdStamping: item.enableSignatureIdStamping,
templateId: item.id,
};
let res = await api.post(`/api/accounts/${activeAccountId}/transactions`, payload)
console.log("handleUseClick", res.data)
// /process-step-one/:accountId/:transactionId
navigate(`/process-step-one/${activeAccountId}/${res?.data?.id}?useTemplate=true`);
} catch (error) {
console.log(error)
}
}
const handleEditClick = (templateId: string) => {
setShowMenu(null);
navigate(`/process-step-one/${accountId}/template/${templateId}`);
}
return (
<div className="p-3 text-sm">
{/* Header */}
<div className="flex items-center justify-between mb-3">
{/* Filter as pill buttons */}
<div className="flex gap-2">
{filters.map((filter) => (
<button
key={filter}
onClick={() => setActiveFilter(filter)}
className={`px-3 py-1 rounded-full text-xs border transition
${activeFilter === filter
? "bg-[var(--primary)] text-white"
: "border-gray-300 text-gray-600 hover:bg-gray-100"
}`}
>
{filter}
</button>
))}
</div>
<div
onClick={handleAddTemplate}
className="flex items-center gap-2 bg-[var(--primary)] px-3 py-1.5 rounded-sm cursor-pointer hover:opacity-90">
<IoMdAdd className="text-white" />
<button
className="text-white rounded text-sm hover:opacity-90">
Add Template
</button>
</div>
</div>
{/* Table */}
<div className="max-h-[77vh] pb-16 overflow-y-auto relative no-scrollbar">
<table className="min-w-full border border-gray-200 bg-white shadow rounded table-fixed">
<thead className="bg-gray-100 text-xs text-gray-700 uppercase tracking-wider sticky top-0 z-10">
<tr>
<th className="px-4 py-3 w-10 bg-gray-100">
<div className="w-4 h-4 border border-slate-300 rounded flex items-center justify-center">
{/* <FaCheck className="text-purple-600 text-xs" /> */}
</div>
</th>
<th className="px-4 py-2 text-center bg-gray-100">Name</th>
<th className="px-4 py-2 text-center bg-gray-100">Created</th>
<th className="px-4 py-2 text-center bg-gray-100">Last Updated</th>
<th className="px-4 py-2 text-center bg-gray-100">Use</th>
<th className="px-4 py-2 text-center bg-gray-100">Action</th>
</tr>
</thead>
{loading ? (
<tbody>
<tr>
<td colSpan="6" className="relative h-100">
<LoaderUi />
</td>
</tr>
</tbody>
) : templates.length > 0 ? (
<tbody className="divide-y divide-gray-200">
{templates.map((item, index) => {
return (
<tr key={index} className="hover:bg-gray-50 relative">
<td className="px-4 py-2">
<div className="w-4 h-4 border border-slate-300 rounded flex items-center justify-center">
{/* <FaCheck className="text-purple-600 text-xs" /> */}
</div>
</td>
<td className="px-4 py-2 text-sm text-center">{item.name || 'Untitled'}</td>
<td className="px-4 py-2 text-sm text-center">{formatDate(item.createdAt)}</td>
<td className="px-4 py-2 text-sm text-center">{formatDate(item.lastModifiedAt)}</td>
<td className="px-4 py-2 text-sm text-center" onClick={() => handleUseClick(item)}><span className="bg-[var(--lightGreyCol)] cursor-pointer border-gray-300 text-gray-600 hover:bg-gray-100 px-3 py-1 rounded-lg font-semibold text-[12px]">Use</span></td>
{/* Dropdown Trigger + Tooltip */}
<td className="px-4 py-2 text-center relative">
<button
onClick={() => setShowMenu(showMenu === index ? null : index)}
className="text-slate-400 hover:text-black focus:outline-none"
>
<PiDotsThreeOutlineVerticalFill className="text-lg" />
</button>
{showMenu === index && (
<div
className="absolute right-15 top-5 mt-2 w-32 bg-white border border-gray-200 rounded shadow-lg z-10 transition-opacity duration-300 ease-in-out"
tabIndex={0} // Make it focusable
onBlur={(e) => {
// Ensure it only closes if focus truly leaves the container
if (!e.currentTarget.contains(e.relatedTarget)) {
setShowMenu(null);
}
}}
>
<button
className="block w-full text-left px-4 py-2 text-sm text-gray-700 hover:bg-gray-100"
onClick={() => handleEditClick(item.id)}
>
Edit
</button>
<button
className="block w-full text-left px-4 py-2 text-sm text-red-600 hover:bg-red-100"
onClick={() => {
setShowMenu(null);
console.log('Delete clicked', item);
}}
>
Delete
</button>
</div>
)}
</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" // adjust size and appearance
/>
</div>
</td>
</tr>
</tbody>
)}
</table>
</div>
</div>
);
};
export default Templates;