This commit is contained in:
=bhagwanSingh 2025-10-15 11:46:00 +05:30
parent 68f0185c76
commit 5c12a25d51
8 changed files with 376 additions and 250 deletions

View File

@ -1,4 +1,4 @@
import { useEffect, useState } from "react";
import { useEffect, useRef, useState } from "react";
import axios from "axios"
import { RootState } from "../../store/store";
@ -154,6 +154,8 @@ const SignDocument = () => {
const { recipientId } = useParams();
const dispatch = useDispatch();
const imageRefs = useRef<{ [key: string]: HTMLImageElement | null }>({});
// Signer data - START
const [selectedDocuments, setSelectedDocuments] = useState<DocumentType[] | null>(null);
const [selectedTag, setSelectedTag] = useState<{ key: string, index: number } | null>(null);
@ -1065,7 +1067,10 @@ const SignDocument = () => {
<img
// style={{ "minWidth": page.width, "minHeight": page.height, "maxWidth": page.width, "maxHeight": page.height }}
style={{ "width": page.width, "height": page.height, "maxWidth": "100%", "maxHeight": "100%" }}
id={`${doc.documentId}-${page.index}`}
// style={{ "width": page.width, "height": page.height, "maxWidth": "100%", "maxHeight": "100%" }}
className={`w-[${page.width / 2}px] h-[${page.height / 2}px] sm:w-[${page.width}px] sm:h-[${page.height}px]`}
ref={(el) => (imageRefs.current[page.index] = el)}
draggable="false"
// onLoad={(imgElem) => {
// setPageScales((prev) => ({
@ -1084,6 +1089,9 @@ const SignDocument = () => {
{!hideTags && <>
<TagsCon
pageWidth={page.width}
pageHeight={page.height}
renderedWidth={imageRefs.current[page.index]?.offsetWidth || 0}
renderedHeight={imageRefs.current[page.index]?.offsetHeight || 0}
currDocTags={currDocTags}
pageIdkey={key}
provideTags={provideTags}
@ -1236,7 +1244,7 @@ const SignDocument = () => {
{!transparentTags && recipient?.type != "CC" && <>
<div className={`w-full shadow-[0_20px_35px_rgba(0,0,0,0.25)] bottom-0 left-0 bg-white px-4 py-2 flex justify-between items-center gap-4`}>
<div className={`fixed bottom-0 w-full shadow-[0_20px_35px_rgba(0,0,0,0.25)] bottom-0 left-0 bg-white px-4 py-2 flex justify-between items-center gap-4`}>
<p className='text-sm text-slate-600'>By Signing I agree Sign Made Easy <a href="" className="text-[var(--mainPrimaryBtn)]">Terms & Conditions.</a></p>
{/* <div className='flex items-center gap-8 cursor-pointer text-xs px-2 py-1 rounded-sm outline-none'>
<FaAngleLeft

View File

@ -15,6 +15,9 @@ import axios from 'axios';
interface TagsConProps {
pageWidth: number,
pageHeight: number,
renderedWidth: number,
renderedHeight: number,
currDocTags: DroppedItem[]
pageIdkey: string;
provideTags: boolean
@ -41,6 +44,9 @@ interface TagsConProps {
const TagsCon: React.FC<TagsConProps> = ({
pageWidth,
pageHeight,
renderedWidth,
renderedHeight,
currDocTags,
pageIdkey,
provideTags,
@ -309,7 +315,9 @@ const TagsCon: React.FC<TagsConProps> = ({
<>
{currDocTags.map((item, index) => {
if ((item.pageIndex != pageIndex) || (item.documentId != documentId)) return <></>;
console.log('pageWidth:', pageWidth, 'computed left:', (item.dimensions.left / pageWidth) * 100);
const scaleX = renderedWidth / Math.round(pageWidth);
const scaleY = renderedHeight / Math.round(pageHeight);
return (
<div
tabIndex={index + 1}
@ -322,21 +330,26 @@ const TagsCon: React.FC<TagsConProps> = ({
// onPointerDown={(e) => handleTagMouseDown(e, key, index)}
className="absolute rounded dropped-item text-sm"
// onClick={() => handleTagClick(key, index)}
style={{
left: `${item.dimensions.left * scaleX}px`,
top: `${item.dimensions.top * scaleY}px`,
width: `${item.dimensions.width * scaleX}px`,
height: `${item.dimensions.height * scaleY}px`,
border: `${((!hideTags && focusElement) && !transparentTags && focusElement.documentId == item.documentId && focusElement.focusElementIndx == index) ? '2px dashed #1c398e' : 'none'}`,
// display: `${hideTags ? 'none' : 'block'}`
}}
// style={{
// left: `${item.dimensions.left}px`,
// top: `${item.dimensions.top}px`,
// width: `${item.dimensions.width}px`,
// height: `${item.dimensions.height}px`,
// border: `${((!hideTags && focusElement) && !transparentTags && focusElement.documentId == item.documentId && focusElement.focusElementIndx == index) ? '2px dashed #1c398e' : 'none'}`,
// // display: `${hideTags ? 'none' : 'block'}`
// width: item.type == "radio" ? `${item.dimensions.width + 20}px` : `${item.dimensions.width}px`,
// height: item.type == "radio" ? `max-content` : `${item.dimensions.height}px`
// }}
// style={{
// left: `${((item.dimensions.left / pageWidth) * 100)}%`,
// top: item.dimensions.top,
// width: `${((item.dimensions.width / pageWidth) * 100)}%`,
// height: item.dimensions.height,
// }}
style={{
left: `${((item.dimensions.left / pageWidth) * 100)}%`,
top: item.dimensions.top,
width: `${((item.dimensions.width / pageWidth) * 100)}%`,
height: item.dimensions.height,
}}
>
{item.required && <div

View File

@ -51,7 +51,7 @@ const fetchedTags: string[] = []
// 👇 Create an interface for the exposed methods
export interface DocumentContainerRef {
handleTransactionProcess: () => void;
handleTransactionProcess: (isSave?: boolean, isNavigate?: boolean, navigateTo?: string) => void;
}
@ -84,6 +84,8 @@ const DocumentContainer = forwardRef<DocumentContainerRef, DocumentContainerProp
const childRef = useRef<any>(null);
const tagUndoRef = useRef<DroppedItem[] | []>([]);
const imageRefs = useRef<{ [key: string]: HTMLImageElement | null }>({});
const selectedRecipients = useSelector((state: RootState) => state.selectedRecipients.recipients);
// const selectedDocuments = useSelector((state: RootState) => state.selectedDocuments.selectedDocuments)
const selectedDocuments = useSelector((state: RootState) => state.documents.documents)
@ -233,12 +235,15 @@ const DocumentContainer = forwardRef<DocumentContainerRef, DocumentContainerProp
};
const handleTransactionProcess = async (save: boolean = false) => {
const handleTransactionProcess = async (save: boolean = false, isNavigate: boolean = false, navigateTo: string = "/") => {
if (TRANSACTION_TEMPLATE_TOGGLE.TEMPLATES === transactionTemplateToggle) save = true;
// if (TRANSACTION_TEMPLATE_TOGGLE.TEMPLATES === transactionTemplateToggle) save = true;
dispatch(showLoader("Saving Tags ..."))
if (Object.keys(droppedItems).length == 0) return;
if (Object.keys(droppedItems).length == 0) {
if (isNavigate) navigate(navigateTo);
dispatch(hideLoader());
};
// if (!save) return;
let updatedTags: any = [];
@ -260,10 +265,12 @@ const DocumentContainer = forwardRef<DocumentContainerRef, DocumentContainerProp
// window.location.href = `${import.meta.env.VITE_BASE_URI}/transaction-sent/${accountId}/${transactionId}`
}
dispatch(hideLoader())
} else {
navigate("/templates")
dispatch(hideLoader())
}
if (isNavigate) {
navigate(navigateTo);
}
dispatch(hideLoader());
// SENDING Transaction - END
}
@ -312,8 +319,20 @@ const DocumentContainer = forwardRef<DocumentContainerRef, DocumentContainerProp
// if (!field) return; // If not found, exit
const dropZone = event.currentTarget.getBoundingClientRect();
let x = event.clientX - dropZone.left - (dragElemData.width / 2);
let y = event.clientY - dropZone.top - (dragElemData.height / 2);
let clientX, clientY;
if (event.type.startsWith("touch") || event.nativeEvent?.changedTouches) {
const touch = event.nativeEvent?.changedTouches?.[0] || event.changedTouches?.[0];
clientX = touch.pageX;
clientY = touch.pageY;
} else {
clientX = event.clientX;
clientY = event.clientY;
}
let x = clientX - dropZone.left - (dragElemData.width / 2);
let y = clientY - dropZone.top - (dragElemData.height / 2);
// Handling Drop Over Right
// if (dropZone.width < x + dragElemData.width) {
@ -549,7 +568,7 @@ const DocumentContainer = forwardRef<DocumentContainerRef, DocumentContainerProp
// className={`w-[${(page.width / 2)}px] h-[${(page.height / 2)}px] sm:w-[${page.width}px] sm:h-[${page.height}px]`}
id={`${doc.id}-${page.index}`}
className={`w-[${page.width / 2}px] h-[${page.height / 2}px] sm:w-[${page.width}px] sm:h-[${page.height}px]`}
ref={(el) => (imageRefs.current[page.index] = el)}
draggable="false"
width={page.width}
height={page.height}
@ -561,6 +580,8 @@ const DocumentContainer = forwardRef<DocumentContainerRef, DocumentContainerProp
<DroppedItemsTagsCon
pageWidth={page.width}
pageHeight={page.height}
renderedWidth={imageRefs.current[page.index]?.offsetWidth || 0}
renderedHeight={imageRefs.current[page.index]?.offsetHeight || 0}
ref={childRef}
droppedItems={droppedItems}
pageIdkey={key}

View File

@ -19,8 +19,10 @@ import { Recipient } from '../../../store/slices/selectedRecipientsSlice';
import { DroppedItem } from '../../../utlity/types';
interface DroppedItemsTagsConProps {
pageWidth: number;
pageHeight: number;
pageWidth: number,
pageHeight: number,
renderedWidth: number,
renderedHeight: number,
droppedItems: DroppedItemsObj;
pageIdkey: string;
selectedTag: { key: string; index: number } | null;
@ -37,6 +39,8 @@ interface DroppedItemsTagsConProps {
const DroppedItemsTagsCon = forwardRef(({
pageWidth,
pageHeight,
renderedWidth,
renderedHeight,
droppedItems,
pageIdkey,
selectedTag,
@ -165,10 +169,17 @@ const DroppedItemsTagsCon = forwardRef(({
const containerRect = targetContainer.getBoundingClientRect();
const elementRect = targetElement.getBoundingClientRect();
let clientX = event.clientX
let clientY = event.clientY
if ((event as any).changedTouches?.[0]) {
clientX = (event as any).changedTouches[0].pageX;
clientY = (event as any).changedTouches[0].pageY;
}
// Calculate the initial offset (where the mouse was clicked inside the element)
const offsetX = event.clientX - elementRect.left;
const offsetY = event.clientY - elementRect.top;
const offsetX = event.clientX - elementRect.left;
positionRef.current = { left: event.clientX - containerRect.left - offsetX, top: event.clientY - containerRect.top - offsetY };
@ -191,8 +202,16 @@ const DroppedItemsTagsCon = forwardRef(({
// let timeOut = null
const handleMouseMove = (event: MouseEvent) => {
// Get new X and Y positions relative to the container
let left = event.clientX - containerRect.left - offsetX;
let top = event.clientY - containerRect.top - offsetY;
let clientX = event.clientX
let clientY = event.clientY
if ((event as any).changedTouches?.[0]) {
clientX = (event as any).changedTouches[0].pageX;
clientY = (event as any).changedTouches[0].pageY;
}
let left = clientX - containerRect.left - offsetX;
let top = clientY - containerRect.top - offsetY;
// Restrict movement within container bounds
left = Math.max(0, Math.min(left, conScrollWidth - elementRect.width));
@ -205,6 +224,10 @@ const DroppedItemsTagsCon = forwardRef(({
targetElement!.style.top = `${top}px`;
targetElement!.style.left = `${left}px`;
console.log(top, left, "top, left Line 283")
// Apply the movement to the element first
// setDroppedItems(prevState => ({
// ...prevState,
@ -248,6 +271,8 @@ const DroppedItemsTagsCon = forwardRef(({
}
const { left, top } = positionRef.current;
console.log(top, left, "HandleTag Mouse Up top, left Line 283")
setDroppedItems(prevState => ({
...prevState,
[key]: Array.isArray(prevState[key])
@ -292,10 +317,6 @@ const DroppedItemsTagsCon = forwardRef(({
const dropZone: DOMRect = targetContainer?.getBoundingClientRect() as DOMRect;
const droppedElemRect: DOMRect = targetElement?.getBoundingClientRect() as DOMRect;
console.log(dropZone, "dropZone Line 309")
console.log(droppedElemRect, "rect Line 309")
@ -565,6 +586,10 @@ const DroppedItemsTagsCon = forwardRef(({
const type = item.type.toUpperCase();
const IconComponent = typeToIconMap[type] || <FaQuestionCircle />;
const selectedRecipient = selectedRecipients.filter((obj, indx) => obj.id == item.recipientId);
const scaleX = renderedWidth / Math.round(pageWidth);
const scaleY = renderedHeight / Math.round(pageHeight);
return (
<React.Fragment key={index}>
<div
@ -575,26 +600,25 @@ const DroppedItemsTagsCon = forwardRef(({
data-tag-selected={index == selectedTag?.index}
className="absolute rounded dropped-item"
onClick={() => handleTagClick(pageIdkey, index)}
// style={{
// left: `${item.dimensions.left}px`,
// top: `${item.dimensions.top}px`,
// width: item.type == "radio" ? `${item.dimensions.width + 20}px` : `${item.dimensions.width}px`,
// height: item.type == "radio" ? `max-content` : `${item.dimensions.height}px`
// }}
style={{
left: `${item.dimensions.left * scaleX}px`,
top: `${item.dimensions.top * scaleY}px`,
width: item.type == "radio" ? `${item.dimensions.width + 20}px` : `${item.dimensions.width * scaleX}px`,
height: item.type == "radio" ? `max-content` : `${item.dimensions.height * scaleY}px`
}}
// style={{
// // left: `${((item.dimensions.left / pageWidth) * 100)}%`,
// // top: `${((item.dimensions.top / pageHeight) * 100)}%`,
// // width: `${(item.dimensions.width / pageWidth) * 100}%`,
// // height: `${(item.dimensions.height / pageHeight) * 100}%`,
// left: `${((item.dimensions.left / pageWidth) * 100)}%`,
// top: `${((item.dimensions.top / pageHeight) * 100)}%`,
// width: `${(item.dimensions.width / pageWidth) * 100}%`,
// height: `${(item.dimensions.height / pageHeight) * 100}%`,
left: `${((item.dimensions.left / pageWidth) * 100)}%`,
top: `${((item.dimensions.top / pageHeight) * 100)}%`,
width: `${(item.dimensions.width / pageWidth) * 100}%`,
height: `${(item.dimensions.height / pageHeight) * 100}%`,
}}
// }}
>
{/* If Selected Tag Recipient Info- START */}
{selectedTag?.key && selectedTag.key == pageIdkey && selectedTag.index == index &&
<div className='absolute flex justify-between items-center w-full max-w-max bg-white top-[-22px] shadow z-20 text-sm rounded-t-sm' >

View File

@ -90,6 +90,8 @@ const LeftSideBar: React.FC<LeftSideBarProps> = ({ isFields, isPages, selectedRe
return (
<>
{/* Left Side Bar - START */}
<div className='col-span-2'>
{
(isPages || isFields) && <div className={`sm:block p-4 absolute sm:top-[0] rounded-tr-md z-50 sm:relative sm:col-span-2 bg-white border-t-1 border-r-1 border-slate-300 sm:h-[calc(100vh-40px)] overflow-y-auto sm:w-auto ${isFields ? "h-[150px] bottom-0 overflow-x-hidden w-full" : "h-[calc(100vh-40px)]"} `}>
<div
@ -121,7 +123,7 @@ const LeftSideBar: React.FC<LeftSideBarProps> = ({ isFields, isPages, selectedRe
{selectedRecipients.length > 0 && isFields && <>
{/* Fields List and Recipeint - Start */}
{/* Side Bar showing Recipients */}
<div className='sm:relative sm:top-0 sm:left-0 absolute w-full'>
<div className='sm:relative sm:top-0 sm:left-0 fixed w-full'>
<div className='relative'
tabIndex={0}
@ -180,7 +182,7 @@ const LeftSideBar: React.FC<LeftSideBarProps> = ({ isFields, isPages, selectedRe
{/* Drag Drop Tags Start */}
<div
id='tags-con-dash-left'
className='mt-4 w-full'>
className='mt-2 w-full h-[400px] sm:h-auto'>
<FieldList color={selectedRecipient?.color || '#2d2d2d'} recipientId={selectedRecipient.id} recipientIndx={selectedRecipientIndx} />
</div>
@ -283,11 +285,51 @@ const LeftSideBar: React.FC<LeftSideBarProps> = ({ isFields, isPages, selectedRe
{/* Left Side Bar - END */}
{/* Small Devices - START */}
<div>
{/* Left Side Pages */}
{!isPages && <div
onClick={() => {
handleSetIsPages(true)
handleSetIsFields(false)
}}
className='sm:hidden fixed top-[8%] left-0 bg-[var(--primary)] z-50 flex items-center text-white rounded-tr-md rounded-br-md cursor-pointer opacity-50 hover:opacity-100 transition-all duration-300'>
<div className='py-2 pl-2 pr-1'>
<FaAngleUp className='rotate-90' />
<p className='flex flex-col justify-center items-center text-xs tracking-widest mt-1 pt-1 border-t-1 border-white'>
<span>P</span>
<span>A</span>
<span>G</span>
<span>E</span>
<span>S</span>
</p>
</div>
</div>}
{!isFields && <div
className='sm:hidden fixed bottom-0 left-0 rounded-tr-2xl rounded-tl-2xl bg-[var(--primary)] z-50 w-full flex justify-center text-white cursor-pointer opacity-50 hover:opacity-100 transition-all duration-300'
onClick={() => {
handleSetIsPages(false)
handleSetIsFields(true)
}}
>
<div className='flex py-2'>
<FaAngleUp />
<p className='flex justify-center items-center text-xs tracking-widest ml-1 pl-1 border-l-1 border-white'>
<span>T</span>
<span>A</span>
<span>G</span>
<span>S</span>
</p>
</div>
</div>}
</div>
{/* Small Devices - END */}
</div>
</>
)

View File

@ -28,7 +28,7 @@ interface NavTwoProps {
isPreview?: boolean
aId?: string | null
tId?: string | null
onSendClick?: () => void;
onSendClick?: (isSave?: boolean, navigate?: boolean, navigateTo?: string) => void;
}
@ -171,6 +171,7 @@ const NavTwo: React.FC<NavTwoProps> = ({
function handleSend(isSignSend: boolean = false) {
// dispatch(showLoader("Loading ..."));
if (onSendClick === undefined) return;
const transactionSendValidationObj = transactionSendValidation()
if (transactionSendValidationObj.oneSignerIsRequiredError) {
@ -185,13 +186,14 @@ const NavTwo: React.FC<NavTwoProps> = ({
// setTimeout(() => {
if (transactionTemplateToggle == TRANSACTION_TEMPLATE_TOGGLE.TRANSACTIONS) {
if (isSignSend) {
// SELF SIGN CHECK HERE
window.open(`./../../../sign/${selfUserSign.recipientId}`, "_self");
}
else {
onSendClick();
onSendClick(false); // false for not a save action
}
} else {
onSendClick();
onSendClick(true, true, "/templates"); // true for save action
}
// }, 1000)
}
@ -290,7 +292,7 @@ const NavTwo: React.FC<NavTwoProps> = ({
{/* Trigger */}
<button
onClick={() => setShowActionsDropDown((prev) => !prev)}
className="flex justify-center items-center bg-white px-4 py-2 text-xs font-medium text-gray-700 hover:bg-gray-50 ">
className="flex justify-center items-center bg-white px-4 py-2 text-xs font-medium text-gray-700 hover:bg-gray-50 cursor-pointer">
<p className='tracking-wide mr-1'>Actions</p>
<MdKeyboardArrowDown />
</button>
@ -303,12 +305,28 @@ const NavTwo: React.FC<NavTwoProps> = ({
{/* <button className="w-full text-left px-4 py-2 text-sm hover:bg-gray-100">
Save & Close
</button> */}
<button
onClick={() => {
if (onSendClick !== undefined) onSendClick(true, false); // true for save action
setShowActionsDropDown(false)
}}
className="cursor-pointer w-full text-left px-4 py-2 text-sm hover:bg-gray-100">
Save
</button>
<button
onClick={() => {
if (onSendClick !== undefined) onSendClick(true, true, transactionTemplateToggle === TRANSACTION_TEMPLATE_TOGGLE.TEMPLATES ? "/templates" : "/"); // true for save action
setShowActionsDropDown(false);
}}
className="cursor-pointer w-full text-left px-4 py-2 text-sm hover:bg-gray-100">
Save & Close
</button>
<button
onClick={() => {
setMessageModal(true)
setShowActionsDropDown(false)
}}
className="w-full text-left px-4 py-2 text-sm hover:bg-gray-100">
className="cursor-pointer w-full text-left px-4 py-2 text-sm hover:bg-gray-100">
Edit Message
</button>
<button
@ -316,7 +334,7 @@ const NavTwo: React.FC<NavTwoProps> = ({
setEditRecipientsModal(true)
setShowActionsDropDown(false)
}}
className="w-full text-left px-4 py-2 text-sm hover:bg-gray-100">
className="cursor-pointer w-full text-left px-4 py-2 text-sm hover:bg-gray-100">
Edit Recipients
</button>
{/* <button

View File

@ -89,8 +89,8 @@ const ProcessStepTwo = () => {
const docRef = useRef<DocumentContainerRef>(null);
const handleSendClick = () => {
docRef.current?.handleTransactionProcess();
const handleSendClick = (isSave = false, isNavigate = false, navigateTo = "/") => {
docRef.current?.handleTransactionProcess(isSave, isNavigate, navigateTo);
};
return (

View File

@ -158,9 +158,9 @@ const TransactionView = () => {
>
Sign
</button> */}
<button className='cursor-pointer border border-gray-300 rounded-sm px-4 py-1 hover:bg-gray-100 font-bold'>
{/* <button className='cursor-pointer border border-gray-300 rounded-sm px-4 py-1 hover:bg-gray-100 font-bold'>
More
</button>
</button> */}
{transactionDetails.transaction.status == "CREATED" && <Link className='cursor-pointer border text-green-700 border-gray-300 rounded-sm px-4 py-1 hover:bg-gray-100 font-bold' to={`/setup-transaction/${accountId}/${transactionId}`}>
Edit