260 lines
14 KiB
TypeScript
260 lines
14 KiB
TypeScript
import React, { useEffect, useState } from 'react';
|
|
import { FiEdit3, FiTrash2, FiPlus, FiMoreVertical } from 'react-icons/fi';
|
|
import AddSignatureModal from './AddSignatureModal';
|
|
import api from '../../../config/api';
|
|
import { Bounce, toast } from "react-toastify";
|
|
import { FaSpinner } from 'react-icons/fa';
|
|
|
|
interface Signature {
|
|
id: string;
|
|
email: string;
|
|
signatureType: string;
|
|
lastName: string;
|
|
firstName: string
|
|
font: string;
|
|
initialsBlob: string;
|
|
initialsStampedBlob: string;
|
|
initialsHash: string;
|
|
signatureHash: string;
|
|
signatureBlob: string;
|
|
signatureStampedBlob: string;
|
|
default: boolean
|
|
}
|
|
|
|
const SignatureManager: React.FC = () => {
|
|
const [loading, setLoading] = useState<boolean>(false)
|
|
const [signatures, setSignatures] = useState<Signature[]>([]);
|
|
const [activeDropdown, setActiveDropdown] = useState<string | null>(null);
|
|
const [showAddSignature, setShowAddSignature] = useState<boolean>(false);
|
|
const [editingSignature, setEditingSignature] = useState<Signature | null>(null);
|
|
|
|
const getAllSignatures = async () => {
|
|
setLoading(true)
|
|
try {
|
|
let res = await api.get("/api/user/signatures")
|
|
console.log("getAllSignatures", res.data)
|
|
let signatureData = res?.data
|
|
setSignatures(signatureData)
|
|
} catch (error) {
|
|
console.log("getAllSignatures", error)
|
|
} finally {
|
|
setLoading(false)
|
|
}
|
|
}
|
|
|
|
useEffect(() => {
|
|
getAllSignatures()
|
|
}, [])
|
|
|
|
const handleEdit = (id: string) => {
|
|
console.log('Edit signature:', id);
|
|
let editSignature = signatures.find((item) => item.id === id);
|
|
console.log("editSignature", editSignature);
|
|
|
|
if (editSignature) {
|
|
setEditingSignature(editSignature);
|
|
setShowAddSignature(true);
|
|
}
|
|
setActiveDropdown(null);
|
|
};
|
|
|
|
const handleDelete = async (id: string) => {
|
|
if (!id) return;
|
|
setLoading(true)
|
|
try {
|
|
let res = await api.delete(`/api/user/signatures/${id}`)
|
|
console.log(res.data)
|
|
toast.success("Signature Delete Successfull", {
|
|
position: "top-center",
|
|
autoClose: 500,
|
|
hideProgressBar: false,
|
|
closeOnClick: false,
|
|
pauseOnHover: true,
|
|
draggable: true,
|
|
progress: undefined,
|
|
theme: "light",
|
|
transition: Bounce,
|
|
});
|
|
await getAllSignatures();
|
|
} catch (error) {
|
|
console.log("delete signature", error)
|
|
} finally {
|
|
setActiveDropdown(null);
|
|
setLoading(false)
|
|
}
|
|
};
|
|
|
|
const toggleDropdown = (id: string) => {
|
|
setActiveDropdown(activeDropdown === id ? null : id);
|
|
};
|
|
|
|
const handleAddNew = () => {
|
|
setEditingSignature(null);
|
|
setShowAddSignature(true);
|
|
};
|
|
|
|
const handleModalClose = () => {
|
|
setShowAddSignature(false);
|
|
setEditingSignature(null);
|
|
};
|
|
|
|
return (
|
|
<>
|
|
{loading ? <div className=' flex items-center justify-center'>
|
|
<div className="p-12 text-center mt-8">
|
|
<div className="text-gray-400 mb-4">
|
|
<FaSpinner className="w-20 h-20 mx-auto animate-spin" />
|
|
</div>
|
|
<h3 className="text-xl font-semibold text-gray-700 mb-2">Loading...</h3>
|
|
</div>
|
|
</div> :
|
|
<div className="min-h-screen bg-white p-3 rounded-md px-4 mb-6">
|
|
<div className="max-w-[84vw] mx-auto">
|
|
<>
|
|
{/* Header */}
|
|
<div className="flex items-center justify-between mb-8">
|
|
<div>
|
|
<h1 className="text-[17px] font-medium text-gray-500 mb-2">Signatures</h1>
|
|
<p className="text-gray-300 text-sm">Add or update your name and signature styles.</p>
|
|
</div>
|
|
<button className="bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-lg font-semibold text-sm text-[14px] flex items-center cursor-pointer transition-all duration-200 shadow-lg hover:shadow-xl transform hover:-translate-y-0.5" onClick={handleAddNew}>
|
|
<FiPlus className="" />
|
|
ADD
|
|
</button>
|
|
</div>
|
|
|
|
{/* Table Card */}
|
|
<div className="bg-white rounded-md shadow-md overflow-hidden border border-gray-100">
|
|
{/* Table Header */}
|
|
<div className="bg-gray-50 border-b border-gray-200">
|
|
<div className="grid grid-cols-12 gap-4 px-6 py-4">
|
|
<div className="col-span-5">
|
|
<h3 className="text-sm font-semibold text-gray-700 uppercase tracking-wider">Signature</h3>
|
|
</div>
|
|
<div className="col-span-5">
|
|
<h3 className="text-sm font-semibold text-gray-700 uppercase tracking-wider">Initial</h3>
|
|
</div>
|
|
<div className="col-span-2">
|
|
<h3 className="text-sm font-semibold text-gray-700 uppercase tracking-wider text-right">Actions</h3>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Table Body */}
|
|
<div className="divide-y divide-gray-100">
|
|
{signatures.map((signature, index) => (
|
|
<div key={signature.id} className="grid grid-cols-12 gap-4 px-6 py-9 hover:bg-gray-50 transition-colors duration-150">
|
|
{/* Signature Column */}
|
|
<div className="col-span-5">
|
|
<div className="flex flex-col space-y-2 relative ps-6 border-l-3 border-blue-600 rounded-bl-2xl rounded-tl-2xl">
|
|
{/* Label */}
|
|
<div className="text-sm text-gray-600 font-medium mb-1">
|
|
Signed by:
|
|
</div>
|
|
|
|
{/* Signature Image */}
|
|
<div className="bg-white rounded-lg p-1 flex items-center justify-center ">
|
|
<img
|
|
src={`data:image/png;base64,${signature.signatureBlob}`}
|
|
className="h-[50px] w-[250px] object-contain"
|
|
alt="Signature"
|
|
/>
|
|
</div>
|
|
|
|
{/* Signature ID */}
|
|
<div className="text-xs text-gray-500 truncate">
|
|
{signature.signatureHash}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Initial Column */}
|
|
<div className="col-span-5">
|
|
<div className="flex flex-col space-y-2 relative ps-6 border-l-3 border-green-600 rounded-bl-2xl rounded-tl-2xl">
|
|
{/* Label */}
|
|
<div className="text-sm text-gray-600 font-medium mb-1">
|
|
DS
|
|
</div>
|
|
|
|
{/* Signature Image */}
|
|
<div className="bg-white rounded-lg p-1 flex items-center justify-center ">
|
|
<img
|
|
src={`data:image/png;base64,${signature.initialsBlob}`}
|
|
className="h-[50px] object-contain"
|
|
alt="DS Signature"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Actions Column */}
|
|
<div className="col-span-2 flex justify-between items-start pt-9 ps-8 ">
|
|
<div className="relative ">
|
|
<button
|
|
onClick={() => toggleDropdown(signature.id)}
|
|
className="bg-gray-100 hover:bg-gray-200 text-gray-700 px-1.5 py-1.5 text-[13px] rounded-lg font-semibold flex items-center transition-all duration-200 border border-gray-300 "
|
|
>
|
|
ACTIONS
|
|
<FiMoreVertical />
|
|
</button>
|
|
|
|
{/* Dropdown Menu */}
|
|
{activeDropdown === signature.id && (
|
|
<div className="absolute right-0 mt-0.5 w-28 bg-white rounded-lg shadow-xl border border-gray-200 z-10 py-1">
|
|
<button
|
|
onClick={() => handleEdit(signature.id)}
|
|
className="w-full px-4 py-1 text-left text-gray-500 hover:bg-blue-50 hover:text-blue-700 flex items-center gap-3 text-sm transition-colors duration-150"
|
|
>
|
|
<FiEdit3 />
|
|
Edit
|
|
</button>
|
|
<button
|
|
onClick={() => handleDelete(signature.id)}
|
|
className="w-full px-4 py-1 text-left text-gray-500 hover:bg-red-50 hover:text-red-700 flex items-center gap-3 text-sm transition-colors duration-150"
|
|
>
|
|
<FiTrash2 />
|
|
Delete
|
|
</button>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Empty State (if no signatures) */}
|
|
{signatures.length === 0 && (
|
|
<div className="bg-white rounded-xl shadow-lg p-12 text-center mt-8">
|
|
<div className="text-gray-400 mb-4">
|
|
<FiEdit3 className="w-16 h-16 mx-auto" />
|
|
</div>
|
|
<h3 className="text-xl font-semibold text-gray-700 mb-2">No signatures yet</h3>
|
|
<p className="text-gray-500 mb-6">Create your first signature to get started.</p>
|
|
</div>
|
|
)}
|
|
</>
|
|
</div>
|
|
|
|
{/* Click outside to close dropdown */}
|
|
{activeDropdown && (
|
|
<div
|
|
className="fixed inset-0 z-0"
|
|
onClick={() => setActiveDropdown(null)}
|
|
/>
|
|
)}
|
|
|
|
{showAddSignature && (
|
|
<AddSignatureModal
|
|
setShowAddSignature={handleModalClose}
|
|
getAllSignatures={getAllSignatures}
|
|
editingSignature={editingSignature}
|
|
/>
|
|
)}
|
|
</div>
|
|
}</>
|
|
);
|
|
};
|
|
|
|
export default SignatureManager; |