649 lines
30 KiB
TypeScript
649 lines
30 KiB
TypeScript
// import React from 'react'
|
|
// import { SaveType } from '../client/SignDocument';
|
|
|
|
// interface SignConProps {
|
|
// setOpenSign: React.Dispatch<React.SetStateAction<boolean>>;
|
|
// recipientId: string;
|
|
// saved: SaveType;
|
|
// setSaved: React.Dispatch<React.SetStateAction<SaveType>>;
|
|
// // save: () => void;
|
|
// }
|
|
|
|
// const SignCon: React.FC<SignConProps> = ({ setOpenSign, recipientId, saved, setSaved }) => {
|
|
// return (
|
|
// <div>SignCon</div>
|
|
// )
|
|
// }
|
|
|
|
// export default SignCon
|
|
|
|
import React, { useEffect, useState } from 'react';
|
|
// import api from '../../config/api';
|
|
import DrawSignatureCanvas from '../setting/Signature/DrawSignatureCanvas';
|
|
import { AiOutlineUpload, AiOutlineEdit } from 'react-icons/ai';
|
|
import { MdCloudUpload } from 'react-icons/md';
|
|
import { FaSpinner } from "react-icons/fa";
|
|
import { GrCheckboxSelected } from "react-icons/gr";
|
|
import { Bounce, toast } from "react-toastify";
|
|
import axios from 'axios';
|
|
import { SaveType } from '../client/SignDocument';
|
|
import { CiSaveDown1 } from 'react-icons/ci';
|
|
import api from '../../config/api';
|
|
|
|
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
|
|
}
|
|
|
|
interface AddSignatureModalProps {
|
|
// setShowAddSignature?: (value: boolean) => void;
|
|
// getAllSignatures: () => Promise<void>;
|
|
// editingSignature?: Signature | null;
|
|
|
|
recipientId?: string;
|
|
setOpenSign: React.Dispatch<React.SetStateAction<boolean>>;
|
|
saved: SaveType;
|
|
setSaved: React.Dispatch<React.SetStateAction<SaveType>>;
|
|
signTagId: boolean
|
|
}
|
|
|
|
interface Account {
|
|
id: number;
|
|
isDefault: boolean;
|
|
name: string;
|
|
}
|
|
|
|
interface UserData {
|
|
id: string;
|
|
firstName: string;
|
|
lastName: string;
|
|
email: string;
|
|
language: string;
|
|
account: Account;
|
|
}
|
|
|
|
interface SelectStyle {
|
|
id: number,
|
|
style: string
|
|
}
|
|
|
|
const AddSignatureModal: React.FC<AddSignatureModalProps> = ({
|
|
setOpenSign,
|
|
recipientId,
|
|
saved,
|
|
setSaved,
|
|
signTagId
|
|
}) => {
|
|
|
|
console.log("savedModal", saved)
|
|
const [activeTab, setActiveTab] = useState<'TYPED' | 'DRAW' | 'UPLOAD' | 'SAVED'>('TYPED');
|
|
const [fullName, setFullName] = useState('');
|
|
const [initials, setInitials] = useState('');
|
|
const [isSubmitting, setIsSubmitting] = useState<boolean>(false);
|
|
const [signatures, setSignatures] = useState<{
|
|
fullSignature: string | null;
|
|
initials: string | null;
|
|
}>({ fullSignature: null, initials: null });
|
|
|
|
const signatureOptions = [
|
|
{ id: 1, name: 'print', initials: 'LA', style: 'print' },
|
|
{ id: 2, name: 'cursive', initials: 'LA', style: 'cursive' },
|
|
{ id: 3, name: 'script', initials: 'LA', style: 'script' },
|
|
{ id: 4, name: 'elegant', initials: 'LA', style: 'elegant' },
|
|
{ id: 5, name: 'modern', initials: 'LA', style: 'modern' },
|
|
{ id: 6, name: 'classic', initials: 'LA', style: 'classic' },
|
|
];
|
|
|
|
// User data
|
|
const [user, setUser] = useState<UserData>({
|
|
id: "",
|
|
firstName: "",
|
|
lastName: "",
|
|
email: "",
|
|
language: "",
|
|
account: {
|
|
id: 0,
|
|
isDefault: false,
|
|
name: "",
|
|
},
|
|
});
|
|
|
|
console.log("User data", user)
|
|
|
|
const [selectedSignature, setSelectedSignature] = useState<SelectStyle>(
|
|
signatureOptions.length > 0 ? { id: signatureOptions[0].id, style: signatureOptions[0].style } : { id: 0, style: "" }
|
|
);
|
|
|
|
// Store the actual file
|
|
const [file1, setFile1] = useState<File | null>(null);
|
|
const [file2, setFile2] = useState<File | null>(null);
|
|
|
|
// Store the preview URL
|
|
const [preview1, setPreview1] = useState<string | null>(null);
|
|
const [preview2, setPreview2] = useState<string | null>(null);
|
|
|
|
const [preSaveSignatures, setPreSaveSignatures] = useState([])
|
|
|
|
|
|
// Navigation items
|
|
const navigationItems = [
|
|
{ id: 'TYPED', label: 'TYPED', icon: <GrCheckboxSelected className="w-4 h-4" /> },
|
|
{ id: 'DRAW', label: 'DRAW', icon: <AiOutlineEdit className="w-5 h-5" /> },
|
|
{ id: 'UPLOAD', label: 'UPLOAD', icon: <AiOutlineUpload className="w-5 h-5" /> },
|
|
{ id: 'SAVED', label: 'SAVED', icon: <CiSaveDown1 className="w-5 h-5" /> },
|
|
];
|
|
|
|
|
|
const handleSignatureChange = (data: { fullSignature: string | null; initials: string | null }) => {
|
|
setSignatures(data);
|
|
};
|
|
|
|
// Getting Signatures - START
|
|
useEffect(() => {
|
|
(async () => {
|
|
const res = await axios.get(`${import.meta.env.VITE_BASE_URI}/api/sign/signatures?recipientId=${recipientId}`, {
|
|
headers: {
|
|
"ngrok-skip-browser-warning": "69420"
|
|
}
|
|
})
|
|
if (res?.data) {
|
|
let preSavedSignatures = res?.data
|
|
setPreSaveSignatures(preSavedSignatures)
|
|
}
|
|
})()
|
|
}, [])
|
|
// Getting Signatures - END
|
|
|
|
// Cleanup preview URLs when component unmounts or image changes
|
|
useEffect(() => {
|
|
return () => {
|
|
if (preview1) URL.revokeObjectURL(preview1);
|
|
if (preview2) URL.revokeObjectURL(preview2);
|
|
};
|
|
}, [preview1, preview2]);
|
|
|
|
const handleImageChange = async (
|
|
e: React.ChangeEvent<HTMLInputElement>,
|
|
setFile: React.Dispatch<React.SetStateAction<File | null>>,
|
|
setPreview: React.Dispatch<React.SetStateAction<string | null>>,
|
|
type: "fullSignature" | "initials"
|
|
) => {
|
|
const file = e.target.files?.[0];
|
|
if (file) {
|
|
setFile(file);
|
|
const previewUrl = URL.createObjectURL(file);
|
|
setPreview(previewUrl);
|
|
|
|
// Convert to base64
|
|
const base64 = await convertToBase64(file);
|
|
|
|
// Update the appropriate signature
|
|
setSignatures((prev) => ({
|
|
...prev,
|
|
[type]: base64,
|
|
}));
|
|
}
|
|
};
|
|
|
|
const convertToBase64 = (file: File): Promise<string> => {
|
|
return new Promise((resolve, reject) => {
|
|
const reader = new FileReader();
|
|
reader.readAsDataURL(file);
|
|
reader.onload = () => resolve(reader.result as string);
|
|
reader.onerror = (error) => reject(error);
|
|
});
|
|
};
|
|
|
|
const handleSubmit = async () => {
|
|
setIsSubmitting(true);
|
|
console.log("handleSubmit Called")
|
|
if (!signatures.fullSignature || !signatures.initials) {
|
|
alert('Please provide both signature and initials');
|
|
setIsSubmitting(false);
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const payload = {
|
|
tagId: signTagId,
|
|
// signatureId: signatureId,
|
|
type: activeTab,
|
|
firstName: fullName.split(' ')[0] || user.firstName,
|
|
lastName: fullName.split(' ')[1] || user.lastName,
|
|
initials: "",
|
|
initialsBase64: signatures.initials,
|
|
signatureBase64: signatures.fullSignature,
|
|
font: selectedSignature.style
|
|
};
|
|
|
|
console.log("payload", payload)
|
|
// if (isEditMode && editingSignature) {
|
|
// // Update existing signature
|
|
// const updatePayload = {
|
|
// ...payload,
|
|
// signatureId: editingSignature.id
|
|
// };
|
|
// res = await axios.put(`${import.meta.env.VITE_BASE_URI}/api/user/signatures`, updatePayload);
|
|
// console.log("handleUpdate", res);
|
|
|
|
// toast.success("Signature Updated Successfully", {
|
|
// position: "top-center",
|
|
// autoClose: 500,
|
|
// hideProgressBar: false,
|
|
// closeOnClick: false,
|
|
// pauseOnHover: true,
|
|
// draggable: true,
|
|
// progress: undefined,
|
|
// theme: "light",
|
|
// transition: Bounce,
|
|
// });
|
|
// } else {
|
|
// Create new signature
|
|
let res = await axios.post(`${import.meta.env.VITE_BASE_URI}/api/sign/signatures?recipientId=${recipientId}`,
|
|
payload,
|
|
{
|
|
headers: {
|
|
"ngrok-skip-browser-warning": "69420"
|
|
}
|
|
});
|
|
console.log("handleSubmit", res);
|
|
|
|
toast.success("Signature Created Successfully", {
|
|
position: "top-center",
|
|
autoClose: 500,
|
|
hideProgressBar: false,
|
|
closeOnClick: false,
|
|
pauseOnHover: true,
|
|
draggable: true,
|
|
progress: undefined,
|
|
theme: "light",
|
|
transition: Bounce,
|
|
});
|
|
// }
|
|
|
|
if (res?.data) {
|
|
setSignatures({
|
|
fullSignature:"",
|
|
initials:"",
|
|
})
|
|
setSaved({
|
|
saveType: res.data.signatureType ?? null,
|
|
save: true,
|
|
signature: res.data.signatureBlob ?? null,
|
|
initial: res.data.initialsBlob ?? null,
|
|
});
|
|
setOpenSign(false);
|
|
}
|
|
|
|
} catch (error) {
|
|
console.error("Error submitting signature:", error);
|
|
// toast.error(isEditMode ? "Failed to update signature" : "Failed to create signature", {
|
|
// position: "top-center",
|
|
// autoClose: 2000,
|
|
// hideProgressBar: false,
|
|
// closeOnClick: false,
|
|
// pauseOnHover: true,
|
|
// draggable: true,
|
|
// progress: undefined,
|
|
// theme: "light",
|
|
// transition: Bounce,
|
|
// });
|
|
} finally {
|
|
setIsSubmitting(false);
|
|
}
|
|
};
|
|
|
|
const renderContent = () => {
|
|
switch (activeTab) {
|
|
case 'TYPED':
|
|
return (
|
|
<div className="h-full flex flex-col ">
|
|
<div className="mb-6">
|
|
<h2 className="text-xl font-semibold text-gray-900 mb-2">Choose Signature Style</h2>
|
|
<p className="text-sm text-gray-600">Select from pre-designed signature styles</p>
|
|
</div>
|
|
|
|
<div className="flex-1 overflow-y-auto">
|
|
<div className="space-y-4">
|
|
{signatureOptions.map((option) => (
|
|
<div key={option.id} className="flex items-center space-x-4 px-2 py-1 border border-gray-200 rounded-lg hover:bg-gray-50 transition-colors">
|
|
<input
|
|
type="radio"
|
|
id={`signature-${option.id}`}
|
|
name="signature"
|
|
checked={selectedSignature.id === option.id}
|
|
onChange={() => setSelectedSignature({ id: option.id, style: option.style })}
|
|
className="w-4 h-4 text-blue-600 focus:ring-blue-500"
|
|
/>
|
|
<div className="flex-1 grid grid-cols-2 gap-2">
|
|
<div className=" border-gray-300 rounded-lg px-2 py-1 bg-white ">
|
|
<div className="text-xs text-gray-500 mb-2">Signed by:</div>
|
|
<div className={`text-lg ${option.style === 'cursive' ? 'font-serif italic' :
|
|
option.style === 'script' ? 'font-mono' :
|
|
option.style === 'elegant' ? 'font-serif' :
|
|
option.style === 'modern' ? 'font-sans tracking-wide' :
|
|
'font-sans'
|
|
}`}>
|
|
{fullName || option.name}
|
|
</div>
|
|
<div className="text-xs text-gray-400 mt-2">
|
|
dca3e646-62cd-4cc...
|
|
</div>
|
|
</div>
|
|
<div className=" rounded-lg px-2 py-2 bg-white ">
|
|
<div className="text-xs text-gray-500 mb-2">DS</div>
|
|
<div className={`text-2xl ${option.style === 'cursive' ? 'font-serif italic' :
|
|
option.style === 'script' ? 'font-mono' :
|
|
option.style === 'elegant' ? 'font-serif' :
|
|
option.style === 'modern' ? 'font-sans tracking-wide' :
|
|
'font-sans'
|
|
}`}>
|
|
{initials || option.initials}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
|
|
case 'DRAW':
|
|
return (
|
|
<DrawSignatureCanvas
|
|
onSignatureChange={handleSignatureChange}
|
|
// initialSignatures={isEditMode ? signatures : undefined}
|
|
/>
|
|
);
|
|
|
|
case 'UPLOAD':
|
|
return (
|
|
<div className="h-full flex flex-col">
|
|
<div className="mb-6">
|
|
<h2 className="text-xl font-semibold text-gray-900 mb-2">Upload Signature</h2>
|
|
</div>
|
|
<div className="flex-1 overflow-y-auto">
|
|
<div>
|
|
<div className="grid grid-cols-12 gap-6">
|
|
{/* Upload 1 */}
|
|
<div className="col-span-8">
|
|
<label className="block text-sm font-medium text-gray-700 mb-2">Full Signature</label>
|
|
<label htmlFor="upload1" className=" border-2 border-dashed border-gray-300 rounded-lg h-62 flex items-center justify-center bg-gray-50 hover:bg-gray-100 cursor-pointer transition-colors">
|
|
{preview1 ? (
|
|
<img src={preview1} alt="Signature Preview" className="max-h-44 object-contain" />
|
|
) : (
|
|
<div className="text-center">
|
|
<MdCloudUpload className="mx-auto h-12 w-12 text-gray-400 mb-3" />
|
|
<p className="text-sm text-gray-500 mb-1">Click to upload or drag and drop</p>
|
|
<p className="text-xs text-gray-400">PNG, JPG, GIF up to 10MB</p>
|
|
</div>
|
|
)}
|
|
</label>
|
|
<input
|
|
id="upload1"
|
|
type="file"
|
|
accept="image/*"
|
|
className="hidden"
|
|
onChange={(e) => handleImageChange(e, setFile1, setPreview1, "fullSignature")}
|
|
/>
|
|
</div>
|
|
|
|
{/* Upload 2 */}
|
|
<div className="col-span-4">
|
|
<label className="block text-sm font-medium text-gray-700 mb-2">Initials</label>
|
|
<label htmlFor="upload2" className=" border-2 border-dashed border-gray-300 rounded-lg h-62 flex items-center justify-center bg-gray-50 hover:bg-gray-100 cursor-pointer transition-colors">
|
|
{preview2 ? (
|
|
<img src={preview2} alt="Initials Preview" className="max-h-44 object-contain" />
|
|
) : (
|
|
<div className="text-center">
|
|
<MdCloudUpload className="mx-auto h-8 w-8 text-gray-400 mb-2" />
|
|
<p className="text-xs text-gray-500">Upload initials</p>
|
|
</div>
|
|
)}
|
|
</label>
|
|
<input
|
|
id="upload2"
|
|
type="file"
|
|
accept="image/*"
|
|
className="hidden"
|
|
onChange={(e) => handleImageChange(e, setFile2, setPreview2, "initials")}
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
case 'SAVED':
|
|
return (
|
|
<div className="h-full flex flex-col ">
|
|
<div className="mb-6">
|
|
<h2 className="text-xl font-semibold text-gray-900 mb-2">Saved Signature's</h2>
|
|
{/* <p className="text-sm text-gray-600">Select from pre-designed signature styles</p> */}
|
|
</div>
|
|
|
|
<div className="flex-1 overflow-y-auto">
|
|
<div className="space-y-4">
|
|
{preSaveSignatures.map((signature) => (
|
|
<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">
|
|
<div className="text-sm text-gray-600 font-medium mb-1">
|
|
Signed by:
|
|
</div>
|
|
|
|
{/* Signature Image or Fallback Text */}
|
|
<div className="bg-white rounded-lg p-1 flex items-center justify-center">
|
|
{signature?.signatureBlob ? (
|
|
<img
|
|
src={`data:${signature?.signatureMimeType || "image/png"};base64,${signature?.signatureBlob}`}
|
|
className="h-[50px] w-[250px] object-contain"
|
|
alt="Signature"
|
|
/>
|
|
) : (
|
|
<div className="text-gray-400 italic text-sm">No Signature</div>
|
|
)}
|
|
</div>
|
|
|
|
{/* Name */}
|
|
<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">
|
|
<div className="text-sm text-gray-600 font-medium mb-1">
|
|
Initials
|
|
</div>
|
|
|
|
{/* Initials Image */}
|
|
<div className="bg-white rounded-lg p-1 flex items-center justify-center">
|
|
{signature?.initialsBlob ? (
|
|
<img
|
|
src={`data:image/png;base64,${signature?.initialsBlob}`}
|
|
className="h-[50px] object-contain"
|
|
alt="Initials"
|
|
/>
|
|
) : (
|
|
<div className="text-gray-400 italic text-sm">No Initials</div>
|
|
)}
|
|
</div>
|
|
|
|
{/* Optional initials hash or font */}
|
|
<div className="text-xs text-gray-500 truncate">
|
|
{signature?.initialsHash || " "}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
))}
|
|
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
|
|
default:
|
|
return null;
|
|
}
|
|
};
|
|
|
|
const getUserDetails = async () => {
|
|
try {
|
|
let res = await axios.get(`/api/user/details`, {
|
|
headers: {
|
|
"ngrok-skip-browser-warning": "69420",
|
|
}
|
|
});
|
|
console.log("getuser", res);
|
|
let currUser = res?.data;
|
|
setUser(currUser);
|
|
|
|
setFullName(currUser.firstName + " " + currUser.lastName);
|
|
setInitials(currUser.firstName.slice(0, 1) + currUser.lastName.slice(0, 1));
|
|
} catch (error) {
|
|
console.error("Error fetching user details:", error);
|
|
}
|
|
};
|
|
|
|
useEffect(() => {
|
|
getUserDetails();
|
|
}, []);
|
|
|
|
return (
|
|
<div className="fixed inset-0 backdrop-blur-sm bg-opacity-50 flex items-center justify-center z-50 p-4">
|
|
<div className="bg-white rounded-md shadow-xl w-full max-w-[80vw] h-[80vh] flex relative">
|
|
|
|
{/* Sidebar Navigation */}
|
|
<div className="w-64 bg-gray-50 rounded-l-lg border-r border-gray-200 flex flex-col">
|
|
{/* Header */}
|
|
<div className="px-4 py-4 border-b border-gray-200">
|
|
<div className="flex items-center space-x-3">
|
|
<div className='px-1 py-1'>
|
|
<h3 className="text-md font-semibold text-gray-900">Add Signature Sign</h3>
|
|
<p className="text-xs text-gray-500">Create your digital signature</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Form Inputs */}
|
|
<div className="px-6 py-2.5 border-b border-gray-200">
|
|
<div className="space-y-2">
|
|
<div>
|
|
<label className="block text-sm font-medium text-gray-700 mb-2">
|
|
Full Name
|
|
</label>
|
|
<input
|
|
type="text"
|
|
value={fullName}
|
|
onChange={(e) => setFullName(e.target.value)}
|
|
placeholder="Enter full name"
|
|
className="w-full px-3 py-2 text-sm border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
|
/>
|
|
</div>
|
|
|
|
<div>
|
|
<label className="block text-sm font-medium text-gray-700 mb-2">
|
|
Initials
|
|
</label>
|
|
<input
|
|
type="text"
|
|
value={initials}
|
|
onChange={(e) => setInitials(e.target.value)}
|
|
placeholder="Enter initials"
|
|
className="w-full px-3 py-2 text-sm border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Navigation Menu */}
|
|
<div className="flex-1 px-6 py-2.5">
|
|
<nav className="space-y-2">
|
|
{navigationItems.map((item) => (
|
|
<button
|
|
key={item.id}
|
|
onClick={() => setActiveTab(item.id as 'TYPED' | 'DRAW' | 'UPLOAD' | 'SAVED')}
|
|
className={`w-full flex items-center space-x-3 px-2 py-2 text-sm font-medium rounded-lg cursor-pointer transition-colors ${activeTab === item.id
|
|
? 'bg-blue-50 text-blue-700 border border-blue-200'
|
|
: 'text-gray-600 hover:bg-gray-100 hover:text-gray-900'
|
|
}`}
|
|
>
|
|
<span className={activeTab === item.id ? 'text-blue-600' : 'text-gray-400'}>
|
|
{item.icon}
|
|
</span>
|
|
|
|
<span>{item.label}</span>
|
|
</button>
|
|
))}
|
|
</nav>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
{/* Main Content Area */}
|
|
<div className="flex-1 flex flex-col ">
|
|
{/* Header with close button */}
|
|
|
|
{/* Content */}
|
|
<div className="flex-1 py-5 px-5 overflow-hidden ">
|
|
{/* <button
|
|
onClick={() => setShowAddSignature?.(false)}
|
|
className="absolute top-2 right-3 p-2 hover:bg-gray-100 rounded-full transition-colors z-20"
|
|
>
|
|
<AiOutlineClose className="w-5 h-5 text-gray-500" />
|
|
</button> */}
|
|
{renderContent()}
|
|
</div>
|
|
|
|
{/* Footer Buttons in Main Area */}
|
|
<div className="p-4 border-t border-gray-200 flex justify-end gap-3">
|
|
<button
|
|
className="cursor-pointer px-4 py-2 text-sm font-medium rounded-md border border-gray-300 text-gray-700 hover:bg-gray-50 transition-colors disabled:opacity-60 disabled:cursor-not-allowed"
|
|
onClick={() => setOpenSign?.(false)}
|
|
disabled={isSubmitting}
|
|
>
|
|
CANCEL
|
|
</button>
|
|
<button
|
|
className="cursor-pointer px-4 py-2 text-sm font-medium rounded-md bg-[var(--modalSubmitBG)] text-white hover:bg-[var(--modalSubmitBGHover)] transition-colors flex items-center gap-2 disabled:opacity-60 disabled:cursor-not-allowed"
|
|
onClick={handleSubmit}
|
|
disabled={isSubmitting}
|
|
>
|
|
{isSubmitting ? (
|
|
<>
|
|
<FaSpinner className="animate-spin" />
|
|
{/* {isEditMode ? 'UPDATING...' : 'CREATING...'} */}
|
|
</>
|
|
) : (
|
|
<>
|
|
CREATE SIGNATURE
|
|
{/* isEditMode ? 'UPDATE SIGNATURE' : 'CREATE SIGNATURE' */}
|
|
</>
|
|
)}
|
|
</button>
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default AddSignatureModal; |