jai #1

Merged
jaipandey merged 6 commits from jai into master 2025-07-07 06:15:27 +00:00
3 changed files with 278 additions and 110 deletions
Showing only changes of commit faa4839fef - Show all commits

View File

@ -1,26 +1,27 @@
import React, { useEffect, useState } from 'react'; import React, { useEffect, useState } from 'react';
// import { X, Upload, Edit3, Image, Cloud, HardDrive, Droplets } from 'lucide-react'; // import { X, Upload, Edit3, Image, Cloud, HardDrive, Droplets } from 'lucide-react';
import { AiOutlineClose, AiOutlineUpload, AiOutlineEdit } from 'react-icons/ai'; import { AiOutlineClose, AiOutlineUpload, AiOutlineEdit } from 'react-icons/ai';
import { MdImage, MdCloudUpload, MdStorage } from 'react-icons/md'; import { MdImage, MdCloudUpload } from 'react-icons/md';
import api from '../../../config/api'; import api from '../../../config/api';
import DrawSignatureCanvas from './DrawSignatureCanvas';
interface AddSignatureModalProps { interface AddSignatureModalProps {
setShowAddSignature?: (value: boolean) => void; setShowAddSignature?: (value: boolean) => void;
} }
interface Account { interface Account {
id: number; id: number;
isDefault: boolean; isDefault: boolean;
name: string; name: string;
} }
interface UserData { interface UserData {
id: string; id: string;
firstName: string; firstName: string;
lastName: string; lastName: string;
email: string; email: string;
language: string; language: string;
account: Account; account: Account;
} }
const AddSignatureModal: React.FC<AddSignatureModalProps> = ({ setShowAddSignature }) => { const AddSignatureModal: React.FC<AddSignatureModalProps> = ({ setShowAddSignature }) => {
@ -28,6 +29,17 @@ const AddSignatureModal: React.FC<AddSignatureModalProps> = ({ setShowAddSignatu
const [fullName, setFullName] = useState(''); const [fullName, setFullName] = useState('');
const [initials, setInitials] = useState(''); const [initials, setInitials] = useState('');
const [signatures, setSignatures] = useState<{
fullSignature: string | null;
initials: string | null;
}>({ fullSignature: null, initials: null });
const handleSignatureChange = (data: { fullSignature: string | null; initials: string | null }) => {
setSignatures(data);
};
// Sample signature options for the CHOOSE tab // Sample signature options for the CHOOSE tab
const signatureOptions = [ const signatureOptions = [
{ id: 1, name: 'print', initials: 'LA', style: 'print' }, { id: 1, name: 'print', initials: 'LA', style: 'print' },
@ -39,6 +51,8 @@ const AddSignatureModal: React.FC<AddSignatureModalProps> = ({ setShowAddSignatu
]; ];
const [selectedSignature, setSelectedSignature] = useState<number>(1); const [selectedSignature, setSelectedSignature] = useState<number>(1);
console.log(selectedSignature);
// Store the actual file // Store the actual file
const [file1, setFile1] = useState<File | null>(null); const [file1, setFile1] = useState<File | null>(null);
@ -56,11 +70,11 @@ const AddSignatureModal: React.FC<AddSignatureModalProps> = ({ setShowAddSignatu
email: "", email: "",
language: "", language: "",
account: { account: {
id: 0, id: 0,
isDefault: false, isDefault: false,
name: "", name: "",
}, },
}); });
// Navigation items // Navigation items
const navigationItems = [ const navigationItems = [
@ -90,6 +104,38 @@ const AddSignatureModal: React.FC<AddSignatureModalProps> = ({ setShowAddSignatu
} }
}; };
const handleSubmit = async () => {
if (!signatures.fullSignature || !signatures.initials) {
alert('Please provide both signature and initials');
return;
}
// Example: Posting the base64 data
try {
const payload = {
signatureType: "DRAW",
// "signatureId": 0, this work on only the put req.
signatureBase64: signatures.fullSignature,
initialsBase64: signatures.initials,
firstName: user.firstName,
lastName: user.lastName,
font: ""
};
console.log('Submitting Signature:', payload);
let res = await api.post(`/api/user/signatures`,payload)
console.log("handleSubmit", res)
} catch (error) {
console.error("Error fetching user details:", error);
}
// fetch('/api/signature', { method: 'POST', body: JSON.stringify(payload) })
};
const renderContent = () => { const renderContent = () => {
switch (activeTab) { switch (activeTab) {
@ -149,51 +195,7 @@ const AddSignatureModal: React.FC<AddSignatureModalProps> = ({ setShowAddSignatu
case 'draw': case 'draw':
return ( return (
<div className="h-full flex flex-col"> <DrawSignatureCanvas onSignatureChange={handleSignatureChange} />
<div className="mb-6">
<h2 className="text-xl font-semibold text-gray-900 mb-2">Draw Signature</h2>
<p className="text-sm text-gray-600">Create your signature by drawing directly on the canvas</p>
</div>
<div className="flex-1 flex flex-col">
<div className="grid grid-cols-12 gap-6 mb-6">
{/* Draw area 1 */}
<div className="col-span-8">
<label className="block text-sm font-medium text-gray-700 mb-2">Full Signature</label>
<div className="border-2 border-dashed border-gray-300 rounded-lg h-48 flex items-center justify-center bg-gray-50 hover:bg-gray-100 cursor-pointer transition-colors">
<div className="text-center">
{/* <Edit3 className="mx-auto h-12 w-12 text-gray-400 mb-3" /> */}
<p className="text-sm text-gray-500">Click and drag to draw your signature</p>
<p className="text-xs text-gray-400 mt-1">Use mouse or touch to create your signature</p>
</div>
</div>
</div>
{/* Draw area 2 */}
<div className="col-span-4">
<label className="block text-sm font-medium text-gray-700 mb-2">Initials</label>
<div className="border-2 border-dashed border-gray-300 rounded-lg h-48 flex items-center justify-center bg-gray-50 hover:bg-gray-100 cursor-pointer transition-colors">
<div className="text-center">
{/* <Edit3 className="mx-auto h-8 w-8 text-gray-400 mb-2" /> */}
<p className="text-xs text-gray-500">Draw initials</p>
</div>
</div>
</div>
</div>
<div className="flex gap-3">
<button className="px-4 py-2 text-sm border border-gray-300 rounded-md hover:bg-gray-50 transition-colors flex items-center gap-2">
{/* <X className="w-4 h-4" /> */}
Clear
</button>
<button className="px-4 py-2 text-sm border border-gray-300 rounded-md hover:bg-gray-50 transition-colors">
Undo
</button>
<button className="px-4 py-2 text-sm border border-gray-300 rounded-md hover:bg-gray-50 transition-colors">
Redo
</button>
</div>
</div>
</div>
); );
case 'upload': case 'upload':
@ -261,19 +263,22 @@ const AddSignatureModal: React.FC<AddSignatureModalProps> = ({ setShowAddSignatu
}; };
const getUserDetails = async () => { const getUserDetails = async () => {
try { try {
let res = await api.get(`/api/user/details`) let res = await api.get(`/api/user/details`)
console.log("getuser", res) console.log("getuser", res)
setUser(res?.data) let currUser = res?.data
} catch (error) { setUser(currUser)
console.error("Error fetching user details:", error); 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(() => { useEffect(() => {
getUserDetails() getUserDetails()
}, []) }, [])
return ( return (
<div className="fixed inset-0 backdrop-blur-sm bg-opacity-50 flex items-center justify-center z-50 p-4"> <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"> <div className="bg-white rounded-md shadow-xl w-full max-w-[80vw] h-[80vh] flex relative">
@ -352,11 +357,11 @@ const AddSignatureModal: React.FC<AddSignatureModalProps> = ({ setShowAddSignatu
{/* Content */} {/* Content */}
<div className="flex-1 py-5 px-5 overflow-hidden "> <div className="flex-1 py-5 px-5 overflow-hidden ">
<button <button
onClick={() => setShowAddSignature?.(false)} onClick={() => setShowAddSignature?.(false)}
className="absolute top-2 right-3 p-2 hover:bg-gray-100 rounded-full transition-colors z-20" 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" /> <AiOutlineClose className="w-5 h-5 text-gray-500" />
</button> </button>
{renderContent()} {renderContent()}
</div> </div>
@ -368,7 +373,7 @@ const AddSignatureModal: React.FC<AddSignatureModalProps> = ({ setShowAddSignatu
> >
CANCEL CANCEL
</button> </button>
<button className="px-4 py-2 text-sm font-medium rounded-md bg-blue-600 text-white hover:bg-blue-700 transition-colors"> <button className="px-4 py-2 text-sm font-medium rounded-md bg-blue-600 text-white hover:bg-blue-700 transition-colors" onClick={handleSubmit}>
CREATE SIGNATURE CREATE SIGNATURE
</button> </button>
</div> </div>

View File

@ -0,0 +1,158 @@
import React, { useRef, useState } from 'react';
type Props = {
onSignatureChange: (data: { fullSignature: string | null; initials: string | null }) => void;
};
const DrawSignatureCanvas: React.FC<Props> = ({ onSignatureChange }) => {
const canvasRef1 = useRef<HTMLCanvasElement>(null);
const canvasRef2 = useRef<HTMLCanvasElement>(null);
const [isDrawing, setIsDrawing] = useState(false);
const [paths1, setPaths1] = useState<Array<Array<{ x: number; y: number }>>>([]);
const [paths2, setPaths2] = useState<Array<Array<{ x: number; y: number }>>>([]);
const [currentPath, setCurrentPath] = useState<Array<{ x: number; y: number }>>([]);
const [activeCanvas, setActiveCanvas] = useState<'full' | 'initials' | null>(null);
const handleMouseDown = (e: React.MouseEvent<HTMLCanvasElement>) => {
setIsDrawing(true);
const rect = e.currentTarget.getBoundingClientRect();
const point = { x: e.clientX - rect.left, y: e.clientY - rect.top };
setCurrentPath([point]);
setActiveCanvas(e.currentTarget === canvasRef1.current ? 'full' : 'initials');
};
const handleMouseMove = (e: React.MouseEvent<HTMLCanvasElement>) => {
if (!isDrawing) return;
const rect = e.currentTarget.getBoundingClientRect();
const point = { x: e.clientX - rect.left, y: e.clientY - rect.top };
const newPath = [...currentPath, point]; // <- Build new path
setCurrentPath(newPath); // <- Save for next event
drawPath(e.currentTarget as HTMLCanvasElement, newPath); // <- Draw current
};
const exportCanvasAsImage = (canvas: HTMLCanvasElement | null): string | null => {
if (!canvas) return null;
return canvas.toDataURL('image/png');
};
const handleMouseUp = () => {
setIsDrawing(false);
if (currentPath.length === 0 || !activeCanvas) return;
if (activeCanvas === 'full') {
const newPaths = [...paths1, currentPath];
setPaths1(newPaths);
const fullSignature = exportCanvasAsImage(canvasRef1.current);
onSignatureChange({ fullSignature, initials: exportCanvasAsImage(canvasRef2.current) });
} else {
const newPaths = [...paths2, currentPath];
setPaths2(newPaths);
const initials = exportCanvasAsImage(canvasRef2.current);
onSignatureChange({ fullSignature: exportCanvasAsImage(canvasRef1.current), initials });
}
setCurrentPath([]);
};
const drawPath = (canvas: HTMLCanvasElement | null, path: Array<{ x: number; y: number }>) => {
if (!canvas) return;
const ctx = canvas.getContext('2d');
if (!ctx) return;
const allPaths =
canvas === canvasRef1.current ? [...paths1, path] : [...paths2, path];
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.strokeStyle = '#000';
ctx.lineWidth = 2;
ctx.lineJoin = 'round';
ctx.lineCap = 'round';
allPaths.forEach((p) => {
if (p.length < 2) return;
ctx.beginPath();
ctx.moveTo(p[0].x, p[0].y);
for (let i = 1; i < p.length; i++) {
ctx.lineTo(p[i].x, p[i].y);
}
ctx.stroke();
});
};
const clearCanvas = () => {
if (canvasRef1.current) {
const ctx1 = canvasRef1.current.getContext('2d');
ctx1?.clearRect(0, 0, canvasRef1.current.width, canvasRef1.current.height);
}
if (canvasRef2.current) {
const ctx2 = canvasRef2.current.getContext('2d');
ctx2?.clearRect(0, 0, canvasRef2.current.width, canvasRef2.current.height);
}
setPaths1([]);
setPaths2([]);
onSignatureChange({ fullSignature: null, initials: null });
};
return (
<div className="h-full flex flex-col">
<div className="mb-6">
<h2 className="text-xl font-semibold text-gray-900 mb-2">Draw Signature</h2>
<p className="text-sm text-gray-600">Create your signature by drawing directly on the canvas</p>
</div>
<div className="flex-1 flex flex-col">
<div className="grid grid-cols-12 gap-6 mb-2">
{/* Full Signature Canvas */}
<div className="col-span-8">
<label className="block text-sm font-medium text-gray-700 mb-2">Full Signature</label>
<div className="border-2 border-dashed border-gray-300 rounded-lg bg-gray-50 hover:bg-gray-100 transition-colors">
<canvas
ref={canvasRef1}
className="w-full h-48 cursor-crosshair"
width={canvasRef1.current?.clientWidth || 600} // use real width
height={canvasRef1.current?.clientHeight || 180}
onMouseDown={handleMouseDown}
onMouseMove={handleMouseMove}
onMouseUp={handleMouseUp}
onMouseLeave={handleMouseUp}
/>
</div>
</div>
{/* Initials Canvas */}
<div className="col-span-4">
<label className="block text-sm font-medium text-gray-700 mb-2">Initials</label>
<div className="border-2 border-dashed border-gray-300 rounded-lg bg-gray-50 hover:bg-gray-100 transition-colors">
<canvas
ref={canvasRef2}
width={canvasRef2.current?.clientWidth || 300}
height={canvasRef2.current?.clientHeight || 180}
className="w-full h-48 cursor-crosshair"
onMouseDown={handleMouseDown}
onMouseMove={handleMouseMove}
onMouseUp={handleMouseUp}
onMouseLeave={handleMouseUp}
/>
</div>
</div>
</div>
{/* Controls */}
<div className="flex gap-4">
<button
className="px-4 py-2 text-sm border border-gray-300 rounded-md hover:bg-gray-50 transition-colors"
onClick={clearCanvas}
>
Clear
</button>
</div>
</div>
</div>
);
};
export default DrawSignatureCanvas;

View File

@ -1,38 +1,32 @@
import React, { useState } from 'react'; import React, { useEffect, useState } from 'react';
import { FiEdit3, FiTrash2, FiPlus, FiMoreVertical } from 'react-icons/fi'; import { FiEdit3, FiTrash2, FiPlus, FiMoreVertical } from 'react-icons/fi';
import AddSignatureModal from './AddSignatureModal'; import AddSignatureModal from './AddSignatureModal';
import api from '../../../config/api';
interface Signature { interface Signature {
id: string; id: string;
signature: string; email: string;
initial: string; signatureType: string;
createdAt: 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 SignatureManager: React.FC = () => {
const [signatures, setSignatures] = useState<Signature[]>([
{
id: '1',
signature: 'https://www.signwell.com/assets/vip-signatures/muhammad-ali-signature-3f9237f6fc48c3a04ba083117948e16ee7968aae521ae4ccebdfb8f22596ad22.svg', const [signatures, setSignatures] = useState<Signature[]>([]);
initial: 'AB',
createdAt: 'dca3e646-62cd-4cc...'
},
{
id: '2',
signature: '∼∼⌒∼∼⌒∼',
initial: 'AB',
createdAt: 'dca3e646-62cd-4cc...'
},
{
id: '3',
signature: 'Digital Signature',
initial: 'DS',
createdAt: 'dca3e646-62cd-4cc...'
}
]);
const [activeDropdown, setActiveDropdown] = useState<string | null>(null); const [activeDropdown, setActiveDropdown] = useState<string | null>(null);
const [showAddSignature , setShowAddSignature] = useState<boolean>(false) const [showAddSignature, setShowAddSignature] = useState<boolean>(false)
const handleEdit = (id: string) => { const handleEdit = (id: string) => {
console.log('Edit signature:', id); console.log('Edit signature:', id);
@ -48,6 +42,21 @@ const SignatureManager: React.FC = () => {
setActiveDropdown(activeDropdown === id ? null : id); setActiveDropdown(activeDropdown === id ? null : id);
}; };
const getAllSignatures = async () => {
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)
}
}
useEffect(() => {
getAllSignatures()
}, [])
return ( return (
<div className="min-h-screen bg-white p-3 rounded-md px-4 mb-6"> <div className="min-h-screen bg-white p-3 rounded-md px-4 mb-6">
<div className="max-w-[84vw] mx-auto"> <div className="max-w-[84vw] mx-auto">
@ -57,7 +66,7 @@ const SignatureManager: React.FC = () => {
<h1 className="text-[17px] font-medium text-gray-500 mb-2">Signatures</h1> <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> <p className="text-gray-300 text-sm">Add or update your name and signature styles.</p>
</div> </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={()=>setShowAddSignature((prev) => !prev)}> <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={() => setShowAddSignature((prev) => !prev)}>
<FiPlus className="" /> <FiPlus className="" />
ADD ADD
</button> </button>
@ -95,7 +104,7 @@ const SignatureManager: React.FC = () => {
{/* Signature Image */} {/* Signature Image */}
<div className="bg-white rounded-lg p-1 flex items-center justify-center "> <div className="bg-white rounded-lg p-1 flex items-center justify-center ">
<img <img
src="https://www.signwell.com/assets/vip-signatures/muhammad-ali-signature-3f9237f6fc48c3a04ba083117948e16ee7968aae521ae4ccebdfb8f22596ad22.svg" src={`data:image/png;base64,${signature.signatureBlob}`}
className="h-[50px] w-[250px] object-contain" className="h-[50px] w-[250px] object-contain"
alt="Signature" alt="Signature"
/> />
@ -103,7 +112,7 @@ const SignatureManager: React.FC = () => {
{/* Signature ID */} {/* Signature ID */}
<div className="text-xs text-gray-500 truncate"> <div className="text-xs text-gray-500 truncate">
{signature.createdAt} {signature.signatureHash}
</div> </div>
</div> </div>
</div> </div>
@ -120,7 +129,7 @@ const SignatureManager: React.FC = () => {
{/* Signature Image */} {/* Signature Image */}
<div className="bg-white rounded-lg p-1 flex items-center justify-center "> <div className="bg-white rounded-lg p-1 flex items-center justify-center ">
<img <img
src="https://www.signwell.com/assets/vip-signatures/muhammad-ali-signature-3f9237f6fc48c3a04ba083117948e16ee7968aae521ae4ccebdfb8f22596ad22.svg" src={`data:image/png;base64,${signature.initialsBlob}`}
className="h-[50px] object-contain" className="h-[50px] object-contain"
alt="DS Signature" alt="DS Signature"
/> />
@ -174,10 +183,6 @@ const SignatureManager: React.FC = () => {
</div> </div>
<h3 className="text-xl font-semibold text-gray-700 mb-2">No signatures yet</h3> <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> <p className="text-gray-500 mb-6">Create your first signature to get started.</p>
<button className="bg-blue-600 hover:bg-blue-700 text-white px-6 py-3 rounded-lg font-semibold flex items-center gap-2 mx-auto transition-all duration-200">
<FiPlus className="w-5 h-5" />
ADD SIGNATURE
</button>
</div> </div>
)} )}
</div> </div>
@ -191,7 +196,7 @@ const SignatureManager: React.FC = () => {
)} )}
{showAddSignature && ( {showAddSignature && (
<AddSignatureModal setShowAddSignature={setShowAddSignature}/> <AddSignatureModal setShowAddSignature={setShowAddSignature} />
)} )}
</div> </div>
); );