SME Update 5-22

This commit is contained in:
Bhagwan Singh 2026-05-22 16:26:09 +05:30
parent 7612f6a3d3
commit dcb1934cfa
35 changed files with 1806 additions and 400 deletions

View File

@ -66,7 +66,7 @@ const CompletionScreen: React.FC<CompletionScreenProps> = ({
<span className="text-md font-semibold text-[var(--mainPrimaryBtn)]"> <span className="text-md font-semibold text-[var(--mainPrimaryBtn)]">
Sign made easy Sign made easy
</span> */} </span> */}
<img class="py-2 w-[140px]" alt="" src="/sme-colored.png"></img> <img class="py-2 w-[140px]" alt="" src="/sme-colored.png" />
</div> </div>
</header> </header>
@ -81,7 +81,7 @@ const CompletionScreen: React.FC<CompletionScreenProps> = ({
<p <p
onClick={() => { handleDownloadDocument(recipientId || "") }} onClick={() => { handleDownloadDocument(recipientId || "") }}
className="text-white bg-[var(--mainPrimaryBtn)] px-3 py-1 rounded-sm mt-4 cursor-pointer"> className="text-white bg-[var(--mainPrimaryBtn)] px-3 py-2 rounded-sm mt-4 cursor-pointer">
Download Signed Documents Download Signed Documents
</p> </p>
@ -113,7 +113,8 @@ const CompletionScreen: React.FC<CompletionScreenProps> = ({
</div> </div>
{/* Apps Section */} {/* Apps Section */}
<section className="relative z-10 top-6 2xl:top-10 w-full"> {/* <section className="relative z-10 top-6 2xl:top-10 w-full"> */}
<section className="relative z-10 w-full mt-6 2xl:mt-10 pb-6">
<h2 className="text-xl font-semibold text-gray-800 mb-4 2xl:text-2xl"> <h2 className="text-xl font-semibold text-gray-800 mb-4 2xl:text-2xl">
Boost Efficiency with Our Apps Boost Efficiency with Our Apps
</h2> </h2>
@ -128,7 +129,9 @@ const CompletionScreen: React.FC<CompletionScreenProps> = ({
<div className="w-full"> <div className="w-full">
<div className="w-full flex flex-col justify-start items-start sm:flex-row sm:justify-between sm:items-center"> <div className="w-full flex flex-col justify-start items-start sm:flex-row sm:justify-between sm:items-center">
<img src="/forms-made-easy.png" className="w-[40%] 2xl:w-[60%]" alt="Docs Made Easy" /> <img src="/forms-made-easy.png" className="w-[40%] 2xl:w-[60%]" alt="Docs Made Easy" />
<button className="text-xs sm:text-sm md:text-md sm:mt-0 bg-blue-600 text-white px-5 py-2 rounded-md hover:bg-blue-700 transition"> <button
onClick={() => window.open("https://formsmadeasy.com/", "_blank")}
className="cursor-pointer text-xs sm:text-sm md:text-md sm:mt-0 bg-blue-600 text-white px-5 py-2 rounded-md hover:bg-blue-700 transition">
Try Now Try Now
</button> </button>
</div> </div>
@ -146,7 +149,9 @@ const CompletionScreen: React.FC<CompletionScreenProps> = ({
<div className="w-full"> <div className="w-full">
<div className="w-full flex flex-col justify-start items-start sm:flex-row sm:justify-between sm:items-center"> <div className="w-full flex flex-col justify-start items-start sm:flex-row sm:justify-between sm:items-center">
<img src="/docs-made-easy.png" className="w-[40%] 2xl:w-[60%]" alt="Docs Made Easy" /> <img src="/docs-made-easy.png" className="w-[40%] 2xl:w-[60%]" alt="Docs Made Easy" />
<button className="text-xs sm:text-sm md:text-md sm:mt-0 bg-blue-600 text-white px-5 py-2 rounded-md hover:bg-blue-700 transition"> <button
onClick={() => window.open("https://docsmadeasy.com/", "_blank")}
className="cursor-pointer text-xs sm:text-sm md:text-md sm:mt-0 bg-blue-600 text-white px-5 py-2 rounded-md hover:bg-blue-700 transition">
Try Now Try Now
</button> </button>
</div> </div>

View File

@ -1,103 +1,150 @@
import React, { useState } from 'react' import React, { useEffect, useState } from 'react'
import { MdKeyboardArrowDown, MdOutlineNavigateNext } from 'react-icons/md'; import { MdKeyboardArrowUp, MdMoreVert } from 'react-icons/md';
import { DocumentType } from './SignDocument'; import { DocumentType } from './SignDocument';
import { GrFormPrevious } from 'react-icons/gr'; import { DroppedItem } from '../../utlity/types';
import { PiTagSimpleFill } from 'react-icons/pi';
interface DocumentPagesInterface { interface DocumentPagesInterface {
selectedDocuments: DocumentType[] | null; selectedDocuments: DocumentType[] | null;
setDropDownTogglePage: React.Dispatch<React.SetStateAction<string>>; setDropDownTogglePage: React.Dispatch<React.SetStateAction<string>>;
dropDownTogglePage: string; dropDownTogglePage: string;
droppedItems: Record<string, DroppedItem[]> | null;
} }
const DocumentPages = ({ selectedDocuments, setDropDownTogglePage, dropDownTogglePage }: DocumentPagesInterface) => { const DocumentPages = ({ selectedDocuments, setDropDownTogglePage, dropDownTogglePage, droppedItems }: DocumentPagesInterface) => {
const [activePage, setActivePage] = useState<string | null>(
selectedDocuments?.[0] ? `${selectedDocuments[0].documentId}_0` : null
)
const [documentPagesTagsMapping, setDocumentPagesTagsMapping] = useState<Record<string, Record<number, [number, string]>>>({})
useEffect(() => {
const documentId = selectedDocuments?.[0] ? `${selectedDocuments[0].documentId}_0` : null
if (documentId === null) return;
const key = 'pageDropDown' + 0;
setDropDownTogglePage(dropDownTogglePage === key ? '' : key);
setActivePage(`${documentId}_0`);
window.location.hash = documentId + "0";
}, [selectedDocuments])
const toggleDoc = (indx: number, documentId: string) => {
const key = 'pageDropDown' + indx;
setDropDownTogglePage(dropDownTogglePage === key ? '' : key);
setActivePage(`${documentId}_0`);
window.location.hash = documentId + "0";
}
const goToPage = (documentId: string, pageIndex: number) => {
const key = `${documentId}_${pageIndex}`;
setActivePage(key);
window.location.hash = documentId + "" + pageIndex;
}
useEffect(() => {
setTimeout(() => {
let documentPagesTagsMappingLocal: Record<string, Record<number, [number, string]>> = {}
droppedItems && Object.keys(droppedItems).length > 0 && droppedItems.forEach(item => {
const documentId = item.documentId;
const pageIndex = item.pageIndex;
if (!documentPagesTagsMappingLocal[documentId]) {
documentPagesTagsMappingLocal[documentId] = {};
}
documentPagesTagsMappingLocal[documentId][pageIndex] = documentPagesTagsMappingLocal[documentId][pageIndex] ? [documentPagesTagsMappingLocal[documentId][pageIndex][0] + 1, "#005AB5"] : [1, "#005AB5"]
})
setDocumentPagesTagsMapping(documentPagesTagsMappingLocal)
}, 300)
}, [droppedItems])
const [currentPage, setCurrentPage] = useState(0);
return ( return (
<div className="flex flex-col gap-2 pb-[6rem]"> <div className="flex flex-col w-full px-2 gap-2 pb-[6rem] bg-slate-100">
{selectedDocuments && selectedDocuments.length && selectedDocuments.map((document, indx) => { {selectedDocuments && selectedDocuments.length > 0 && selectedDocuments.map((document, indx) => {
const isOpen = dropDownTogglePage === 'pageDropDown' + indx;
return (
<div key={indx} className="border-b border-slate-200">
{/* Document Header */}
<div
onClick={() => toggleDoc(indx, document.documentId)}
className="flex items-start justify-between px-3 py-2.5 cursor-pointer hover:bg-slate-50 transition-colors"
>
<div className="flex-1 min-w-0">
<p className="text-[13px] font-medium text-slate-800 leading-tight truncate max-w-[16ch]">
{document.name}
</p>
<p className="text-[11px] text-slate-400 mt-0.5">
{document.pages?.length} {document.pages?.length === 1 ? 'page' : 'pages'}
</p>
</div>
<MdKeyboardArrowUp
size={20}
className={`text-slate-400 mt-0.5 flex-shrink-0 transition-transform duration-200 ${isOpen ? '' : 'rotate-180'}`}
/>
</div>
{/* Page Thumbnails - DocuSign style */}
{isOpen && (
<div className="flex flex-col px-2">
{document.pages?.map((page, pageIndex) => {
const key = `${document.documentId}_${pageIndex}`;
const isActive = activePage === key;
return ( return (
<div <div
key={indx} key={pageIndex}
onClick={() => goToPage(document.documentId, pageIndex)}
// tabIndex={1} className="flex flex-col items-center py-2 px-1 rounded-md cursor-pointer hover:bg-slate-50 transition-colors"
// onBlur={() => setDropDownTogglePage('')}
> >
<div {/* Big Thumbnail */}
onClick={() => { <div className={`relative sm:p-2 lg:p-4 bg-slate-100 rounded border flex items-center justify-center overflow-hidden
setDropDownTogglePage(`${(dropDownTogglePage == 'pageDropDown' + indx) ? '' : 'pageDropDown' + indx}`); ${isActive ? 'border-blue-500 border-2' : 'border-slate-200'}`}
setCurrentPage(0); >
window.location.hash = document.documentId + "" + 0; <img
} src={page.src}
} alt={`Page ${pageIndex + 1}`}
className={`p-2 ${(dropDownTogglePage == 'pageDropDown' + indx) ? "bg-[var(--primary)]" : "bg-slate-200"} flex flex-col justify-center items-center gap-2 rounded`}> className="w-[140px] h-[180px] h-full object-cover"
draggable="false"
/>
{/* Page number + dots */}
<div className="absolute bottom-0 bg-slate-800 flex items-center justify-between w-full pl-4 pr-2 py-3 bg-white">
<span className="text-[13px] text-slate-500">{pageIndex + 1}</span>
{ {
document.pages != undefined && <img // <>
key={indx} // <button
className='rounded-sm shadow' // onClick={(e) => { e.stopPropagation(); /* open context menu */ }}
src={document.pages[0].src} // className="text-slate-400 hover:text-slate-600 hover:bg-slate-100 rounded p-0.5"
alt="" /> // >
// <MdMoreVert size={18} />
// </button>
// </>
} }
<div
className={`flex items-center justify-between w-full text-xs rounded-sm cursor-pointer ${(dropDownTogglePage == 'pageDropDown' + indx) ? "text-slate-100" : "text-slate-600"}`}>
<p className={`whitespace-nowrap overflow-hidden text-ellipsis max-w-[18ch]`}>{document.name}</p>
<MdKeyboardArrowDown className={`${(dropDownTogglePage == 'pageDropDown' + indx) ? 'rotate-180' : ''}`} />
</div>
</div> </div>
{documentPagesTagsMapping[document.documentId]?.[pageIndex] && (
{ <div className='absolute top-4 right-2 rotate-180'>
dropDownTogglePage == 'pageDropDown' + indx && {/* <span className='absolute text-white text-[10px] font-medium text-slate-500 top-[50%] left-[50%] transform -translate-x-1/2 -translate-y-1/2 pointer-events-none select-none rotate-180'>
<div className="my-1 flex justify-center items-center gap-2 flex-wrap"> {documentPagesTagsMapping[document.documentId][pageIndex][0]}
{/* Prev Button */} </span> */}
<a <PiTagSimpleFill style={{ fontSize: '1.8rem', color: documentPagesTagsMapping[document.documentId][pageIndex][1] }} />
href={"#" + document.documentId + (currentPage - 1)}
className={`cursor-pointer px-2 py-1 shadow rounded-sm text-sm bg-slate-200 hover:border-[var(--primary)] transition-all duration-300 ${currentPage == 0 ? "pointer-events-none opacity-50" : ""
}`}
onClick={(e) => {
if (currentPage == 0) e.preventDefault();
else setCurrentPage(currentPage - 1);
}}
>
<GrFormPrevious />
</a>
{/* Page Selector */}
<select
className="w-[58px] py-1 text-xs text-slate-400 outline-none"
value={currentPage}
onChange={(e) => {
const newPage = Number(e.target.value);
setCurrentPage(newPage);
window.location.hash = document.documentId + "" + newPage;
}}
>
{document.pages != undefined && document.pages.map((_, pageIndex) => (
<option key={pageIndex} value={pageIndex}>
{pageIndex + 1}
</option>
))}
</select>
<span className="text-xs text-slate-600">
/ {document.pages?.length}
</span>
{/* Next Button */}
<a
href={"#" + document.documentId + (currentPage + 1)}
className={`cursor-pointer px-2 py-1 shadow rounded-sm text-sm bg-slate-200 hover:border-[var(--primary)] transition-all duration-300 ${currentPage == document.pages?.length - 1 ? "pointer-events-none opacity-50" : ""
}`}
onClick={(e) => {
if (currentPage == document.pages?.length - 1) e.preventDefault();
else setCurrentPage(currentPage + 1);
}}
>
<MdOutlineNavigateNext />
</a>
</div> </div>
} )}
</div>
</div>
)
})}
</div>
)}
</div> </div>
) )

View File

@ -23,7 +23,7 @@ import DocumentPages from "./DocumentPages";
import FieldList from "../process/processStepTwo/FieldList"; import FieldList from "../process/processStepTwo/FieldList";
import typeToIconMap from "../../helper/iconMap"; import typeToIconMap from "../../helper/iconMap";
import RightSideBar from "../process/processStepTwo/rightSideBar/RightSideBar"; import TagProperties from "../process/processStepTwo/tagProperties/TagProperties";
import TagsCon from "./TagsCon"; import TagsCon from "./TagsCon";
import ConsentScreen from "./ConsentScreen"; import ConsentScreen from "./ConsentScreen";
import SignButton from "./SignButton"; import SignButton from "./SignButton";
@ -165,13 +165,14 @@ const SignDocument = () => {
const [tagJson, setTagJson] = useState<DroppedItem[] | []>([]); const [tagJson, setTagJson] = useState<DroppedItem[] | []>([]);
const [transaction, setTransaction] = useState<TransactionType | null>(null); const [transaction, setTransaction] = useState<TransactionType | null>(null);
const [recSignStatus, setRecSignStatus] = useState<string | null>(null); const [recSignStatus, setRecSignStatus] = useState<string | null>(null);
const [droppedItems, setDroppedItems] = useState<DroppedItem[]>([]); const [droppedItems, setDroppedItems] = useState<DroppedItem[]>([]);
const [signTagId, setSignTagId] = useState<number>(0) const [signTagId, setSignTagId] = useState<number>(0)
const [showConfetti, setShowConfetti] = useState<boolean>(false) const [showConfetti, setShowConfetti] = useState<boolean>(false)
const [pageScales, setPageScales] = useState<Record<string, { x: number; y: number }>>({}); const [pageScales, setPageScales] = useState<Record<string, { x: number; y: number }>>({});
const [requiredFilled, setRequiredFilled] = useState<boolean>(false); const [requiredFilled, setRequiredFilled] = useState<boolean>(false);
const [horizontalFormLoader, setHorizontalFormLoader] = useState<{ requiredFilled: number, totalRequired: number | null }>({ requiredFilled: 0, totalRequired: null }); const [horizontalFormLoader, setHorizontalFormLoader] = useState<{ requiredFilled: number, totalRequired: number | null }>({ requiredFilled: 0, totalRequired: null });
@ -1036,7 +1037,7 @@ const SignDocument = () => {
{/* <div className="lg:col-span-2 bg-slate-100 h-full p-4 overflow-y-auto"></div> */} {/* <div className="lg:col-span-2 bg-slate-100 h-full p-4 overflow-y-auto"></div> */}
<div <div
id="tags-list-client-side" id="tags-list-client-side"
className={`${isPages ? "block" : "hidden"} sm:block absolute w-[220px] left-0 top-[42px] border-r-2 sm:border-none border-slate-200 z-10 sm:static sm:w-auto sm:col-span-2 h-full p-4 overflow-y-auto ${recipient?.type != "CC" ? "bg-white" : ""} ${!provideTags && isFields ? "hidden" : ""}`}> className={`${isPages ? "block" : "hidden"} bg-slate-100 sm:block absolute w-[220px] left-0 top-[42px] border-r-2 sm:border-none border-slate-200 z-10 sm:static sm:w-auto sm:col-span-2 h-full overflow-y-auto ${recipient?.type != "CC" ? "bg-slate-100" : ""} ${!provideTags && isFields ? "hidden" : ""}`}>
<div <div
@ -1074,7 +1075,9 @@ const SignDocument = () => {
<DocumentPages <DocumentPages
selectedDocuments={selectedDocuments} selectedDocuments={selectedDocuments}
setDropDownTogglePage={setDropDownTogglePage} setDropDownTogglePage={setDropDownTogglePage}
dropDownTogglePage={dropDownTogglePage} /> dropDownTogglePage={dropDownTogglePage}
droppedItems={droppedItems}
/>
</div> </div>
{/* Pages - END */} {/* Pages - END */}
@ -1222,7 +1225,7 @@ const SignDocument = () => {
{provideTags && selectedTag && selectedTag != null && "key" in selectedTag && "index" in selectedTag && {provideTags && selectedTag && selectedTag != null && "key" in selectedTag && "index" in selectedTag &&
<div className={`col-span-2 ${provideTags ? "" : "order-first"} h-full ${!isFields ? "bg-white" : isFields && provideTags ? "bg-white" : ""} overflow-y-auto`}> <div className={`col-span-2 ${provideTags ? "" : "order-first"} h-full ${!isFields ? "bg-white" : isFields && provideTags ? "bg-white" : ""} overflow-y-auto`}>
{/* Right Side Attributes Section if providedTags - START */} {/* Right Side Attributes Section if providedTags - START */}
<RightSideBar <TagProperties
droppedItems={droppedItems} droppedItems={droppedItems}
selectedTag={droppedItems[selectedTag.index]} selectedTag={droppedItems[selectedTag.index]}
setDroppedItems={setDroppedItems} setDroppedItems={setDroppedItems}

View File

@ -6,6 +6,12 @@ import { RootState } from '../../store/store';
import api from '../../config/api'; import api from '../../config/api';
import TransactionChoiceModal from './TransactionChoiceModal'; import TransactionChoiceModal from './TransactionChoiceModal';
import { resetRecipients } from '../../store/slices/selectedRecipientsSlice'; import { resetRecipients } from '../../store/slices/selectedRecipientsSlice';
import { IoMdArrowDropdown } from 'react-icons/io';
import { MdArrowRight } from 'react-icons/md';
import { FaDesktop } from 'react-icons/fa';
import { GrTemplate } from 'react-icons/gr';
import { HiOutlineTemplate, HiTemplate } from 'react-icons/hi';
import { LuLayoutTemplate } from 'react-icons/lu';
const StartNow = () => { const StartNow = () => {
@ -141,11 +147,11 @@ const StartNow = () => {
}} }}
/>} />}
<div <div
onClick={() => fileInputRef.current?.click()} // onClick={() => fileInputRef.current?.click()}
onDrop={handleDrop} onDrop={handleDrop}
onDragOver={handleDragOver} onDragOver={handleDragOver}
id='start-now-upload-con' id='start-now-upload-con'
className="group col-span-1 cursor-pointer border-1 border-dashed border-[#D3D3D3] rounded-lg bg-[var(--documentUploadBg)] transition-all duration-200 p-4 flex flex-col items-center justify-center gap-2 text-center shadow-sm hover:shadow-md"> className="group col-span-1 cursor-pointer border-1 border-dashed border-[#D3D3D3] rounded-lg bg-[var(--documentUploadBg)] transition-all duration-200 p-4 flex flex-col items-center justify-center gap-2 text-center shadow-sm">
{/* Upload icon */} {/* Upload icon */}
<svg className="mt-4" width="44" height="46" viewBox="0 0 62 46" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg className="mt-4" width="44" height="46" viewBox="0 0 62 46" fill="none" xmlns="http://www.w3.org/2000/svg">
@ -163,12 +169,72 @@ const StartNow = () => {
PDF, DOCX (max 10MB) PDF, DOCX (max 10MB)
</p> </p>
<button {/* <button
className="text-xs mt-4 mb-6 text-white bg-[var(--dashBoardStartNowBtn)] px-8 py-2 rounded shadow-xs transition-colors duration-150 focus:outline-none focus:ring-1 focus:ring-blue-400 cursor-pointer" className="text-xs mt-4 mb-6 text-white bg-[var(--dashBoardStartNowBtn)] px-8 py-2 rounded shadow-xs transition-colors duration-150 focus:outline-none focus:ring-1 focus:ring-blue-400 cursor-pointer"
> >
Upload File Upload File
</button> */}
<div className="relative inline-block group mt-4 mb-6">
<button
className="flex text-lg font-medium items-center gap-1 text-xs text-white bg-[var(--dashBoardStartNowBtn)] pl-3 pr-2 py-2 rounded shadow-xs transition-colors duration-150 focus:outline-none focus:ring-1 focus:ring-blue-400 cursor-pointer"
>
<p>Upload File</p>
<IoMdArrowDropdown className='text-lg' />
</button> </button>
{/* Dropdown */}
<div
className="
absolute
left-0
top-full
mt-1
min-w-[180px]
bg-white
border
border-slate-200
rounded-md
shadow-lg
overflow-hidden
z-50
opacity-0
invisible
translate-y-[-5px]
scale-[0.98]
transition-all
duration-200
ease-out
group-hover:opacity-100
group-hover:visible
group-hover:translate-y-0
group-hover:scale-100
"
>
<button
onClick={() => fileInputRef.current?.click()}
className="w-full text-left px-4 py-2.5 text-xs hover:bg-slate-50 cursor-pointer flex gap-2 items-center"
>
<FaDesktop className='text-[var(--primary)]' />
<span>Desktop</span>
</button>
<button
className="w-full text-left px-4 py-2.5 text-xs hover:bg-slate-50 border-t border-slate-100 cursor-pointer flex gap-2 items-center"
>
<HiTemplate className='text-[var(--primary)]' />
<span
onClick={() => navigate("/templates")}
>Use a Template</span>
</button>
</div>
</div>
{filesError && <span className="text-red-500 text-xs">{filesError}</span>} {filesError && <span className="text-red-500 text-xs">{filesError}</span>}
<input <input

View File

@ -91,83 +91,380 @@ const TransactionSentSuccess = () => {
</div> </div>
{/* Content */} {/* Content */}
<div className="flex flex-col p-4 justify-center items-center w-full"> <div className="relative z-10 w-full px-4 py-6 sm:px-6">
<div className="mx-auto w-full max-w-6xl">
{/* Success banner */}
<div className="flex flex-col justify-center items-center gap-3 mb-4 p-4 rounded-md">
{/* <div className="w-12 h-12 bg-gradient-to-br from-teal-500 to-green-400 rounded-full flex items-center justify-center shadow-sm">
<FaCheck className="text-white text-2xl" />
</div> */}
<div className="max-w-7xl mx-auto">
<svg width="70" height="70" viewBox="0 0 114 109" fill="none" xmlns="http://www.w3.org/2000/svg"> {/* Success Hero */}
<path d="M114 54.3837L101.095 40.1811L103.115 21.1673L84.2441 17.2075L74.6181 0.631836L57 8.43682L54.6221 54.3837L57 100.33L74.6183 108.135L84.2441 91.56L103.115 87.5999L101.095 68.5861L114 54.3837Z" fill="#0ED678" /> {/* Success Hero */}
<path d="M39.3816 0.631836L29.7558 17.2075L10.8847 21.1675L12.9053 40.1811L0 54.3837L12.9053 68.5861L10.8847 87.5999L29.7558 91.56L39.3819 108.135L57.0001 100.33V8.43682L39.3816 0.631836Z" fill="#04EB84" />
<path d="M80.8957 42.013L75.9064 37.5986L57 58.5946L54.6221 63.8124L57 68.5499L80.8957 42.013Z" fill="#F7F0EB" />
<path d="M37.1311 49.3916L32.4023 54.0791L52.2842 73.7868L57.0003 68.5495V58.5942L52.0082 64.138L37.1311 49.3916Z" fill="#FFFBF5" />
</svg>
<div>
<p className="text-lg sm:text-4xl font-bold text-[var(--mainPrimaryBtn)]">
Your Document is sent for signature
</p>
<p className="text-lg text-slate-600 text-center font-medium">All signers will be notified via email</p>
</div>
</div>
{/* Main two-column layout */}
<div className="flex flex-col lg:flex-row gap-4 mt-6 lg:justify-center lg:items-stretch">
{/* Left: Document Preview */}
<div className="flex-1 bg-slate-100 rounded-md overflow-hidden">
<div className="w-full h-[50vh] overflow-auto">
<div className="py-2 bg-white px-4 shadow-sm rounded-sm sticky top-0 z-10 border-1 border-gray-200">
<p className="text-sm font-medium">Quick Preview</p>
</div>
<div className="px-4">
<DocumentContainer isPreview={true} aId={accountId} tId={transactionId} />
</div>
</div>
</div>
{/* Right: Signers list + actions */}
<div <div
className={`flex-1 transition-all rounded-lg duration-700 ${isVisible ? 'translate-y-0 opacity-100' : 'translate-y-8 opacity-0' className="
}`} relative
overflow-hidden
bg-white
border
border-slate-200
rounded-md
px-6
py-8
lg:px-10
lg:py-10
"
> >
<div className="p-4 h-full bg-white rounded-md shadow-2xl border-1 border-gray-100">
<div className='overflow-auto'> {/* subtle background */}
<SignersList signers={selectedRecipients} /> <div
className="
absolute
right-[-80px]
top-[-80px]
w-[220px]
h-[220px]
bg-emerald-50
rounded-full
blur-3xl
opacity-70
"
/>
<div
className="
relative
z-10
flex
flex-col
lg:flex-row
items-start
lg:items-center
justify-between
gap-8
"
>
{/* Left */}
<div className="flex-1">
{/* success badge */}
<div
className="
inline-flex
items-center
gap-2
px-4
py-2
rounded-full
bg-emerald-50
border
border-emerald-100
mb-5
"
>
<div
className="
w-5
h-5
rounded-full
bg-emerald-500
flex
items-center
justify-center
"
>
<FaCheck className="text-white text-[11px]" />
</div> </div>
<div className="flex flex-col sm:flex-row gap-2 max-h-max relative top-auto bottom-0"> <span
<Link className="
to="/" text-sm
className="flex-1 p-2 text-center rounded-md bg-slate-200 hover:shadow-sm transition-all duration-150" font-medium
text-emerald-700
"
> >
Back to Home Transaction Sent Successfully
</Link> </span>
</div>
{/* title */}
<h1
className="
text-3xl
sm:text-4xl
font-bold
text-slate-900
leading-tight
tracking-[-0.02em]
max-w-3xl
"
>
Your document has been sent for signature
</h1>
{/* description */}
<p
className="
mt-4
text-base
leading-relaxed
text-slate-600
max-w-2xl
"
>
All recipients will receive an email notification with
secure access to review and sign the document.
</p>
{/* actions */}
<div
className="
mt-7
flex
flex-col
sm:flex-row
gap-3
"
>
{/* <button
onClick={() => {
handleDownloadDocument(recipientId || "")
}}
className="
h-11
px-5
rounded-sm
bg-[var(--mainPrimaryBtn)]
text-white
font-medium
hover:opacity-90
transition
shadow-sm
cursor-pointer
"
>
Download Documents
</button> */}
<button
onClick={() => {
window.location.href = "/"
}}
className="
h-11
px-5
rounded-sm
bg-white
text-slate-700
font-medium
hover:opacity-90
transition
shadow-sm
cursor-pointer
"
>
Back To Home
</button>
{selfUserSign.sign && ( {selfUserSign.sign && (
<p <button
onClick={() => signSessionRedirect(accountId, transactionId, selfUserSign.recipientId + "")} onClick={() =>
className="flex-1 p-2 text-center rounded-md bg-[var(--mainPrimaryBtn)] text-white hover:shadow-sm transition-all duration-150 cursor-pointer" signSessionRedirect(
accountId,
transactionId,
selfUserSign.recipientId + ""
)
}
className="
h-11
px-5
rounded-sm
border
border-slate-300
bg-[var(--primary)]
text-white
font-medium
opacity-80
hover:opacity-100
transition
cursor-pointer
"
> >
Sign Now Sign Now
</p> </button>
)} )}
</div> </div>
</div>
{/* Right */}
<div
className="
hidden
lg:flex
items-center
justify-center
shrink-0
"
>
<div
className="
w-[260px]
h-[220px]
rounded-md
bg-gradient-to-br
from-emerald-50
to-blue-50
border
border-slate-200
flex
items-center
justify-center
relative
overflow-hidden
"
>
<img
src="/completion-screen-1.png"
alt=""
className="
w-44
object-contain
relative
z-10
"
/>
</div>
</div>
</div>
</div>
{/* Main Section */}
<div
className="
grid
grid-cols-1
xl:grid-cols-[1.3fr_0.7fr]
gap-6
mt-6
items-start
"
>
{/* Preview */}
<div
className="
bg-white
rounded-md
border
border-slate-200
shadow-sm
overflow-hidden
"
>
{/* Header */}
<div
className="
flex
items-center
justify-between
px-5
py-4
border-b
border-slate-200
bg-slate-50
"
>
<div>
<p className="font-semibold text-slate-800">
Document Preview
</p>
<p className="text-sm text-slate-500">
Review the final document before signing
</p>
</div>
</div>
{/* Preview Body */}
<div className="h-[70vh] overflow-auto bg-slate-100">
<div className="px-4 py-4">
{
selectedRecipients.length > 0 &&
selectedDocuments.length > 0 && (
<DocumentContainer
isPreview={true}
aId={accountId}
tId={transactionId}
/>
)
}
</div>
</div>
</div>
{/* Right Sidebar */}
<div
className={`
transition-all
duration-700
${isVisible
? 'translate-y-0 opacity-100'
: 'translate-y-8 opacity-0'
}
`}
>
<div
className="
sticky
top-5
bg-white
rounded-md
border
border-slate-200
shadow-sm
overflow-hidden
"
>
{/* Header */}
<div
className="
px-5
py-4
border-b
border-slate-200
bg-slate-50
"
>
<p className="font-semibold text-slate-800">
Signers
</p>
<p className="text-sm text-slate-500 mt-1">
Recipients who will receive this document
</p>
</div> </div>
{/* Body */}
<div className="max-h-[70vh] overflow-auto p-4">
{/* Optional: Signing Order */} <SignersList signers={selectedRecipients} />
{/* <div className="mt-4">
<SigningOrderDiagram signers={selectedRecipients} /> </div>
</div> */} </div>
</div> </div>
</div> </div>
</div> </div>

View File

@ -5,7 +5,7 @@ import { RootState } from "../../../store/store";
import { useEffect, useRef, useState } from "react"; import { useEffect, useRef, useState } from "react";
import { useNavigate, useParams, useSearchParams } from "react-router-dom"; import { useNavigate, useParams, useSearchParams } from "react-router-dom";
import { addRecipientAsync, fetchRecipients, recipientType, removeAllRecipients, removeRecipientAsync, resetRecipients, toggleToTemplates, TRANSACTION_TEMPLATE_TOGGLE, updateRecipientOrder } from "../../../store/slices/selectedRecipientsSlice"; import { addRecipientAsync, fetchRecipients, recipientType, removeAllRecipients, removeRecipientAsync, resetRecipients, setSigningOrder, toggleToTemplates, TRANSACTION_TEMPLATE_TOGGLE, updateRecipientOrder } from "../../../store/slices/selectedRecipientsSlice";
import { TRANSACTION_STATUS_ENUMS, updateTransactionStatus } from "../../../store/slices/transactionSlice"; import { TRANSACTION_STATUS_ENUMS, updateTransactionStatus } from "../../../store/slices/transactionSlice";
import { hideLoader, showLoader } from "../../../store/slices/loaderSlice"; import { hideLoader, showLoader } from "../../../store/slices/loaderSlice";
import Loader from "../../dashboard/Loader"; import Loader from "../../dashboard/Loader";
@ -78,6 +78,8 @@ const ProcessStepOne = () => {
const selectedDocuments = useSelector((state: RootState) => state.documents.documents) const selectedDocuments = useSelector((state: RootState) => state.documents.documents)
const selectedRecipients = useSelector((state: RootState) => state.selectedRecipients.recipients) const selectedRecipients = useSelector((state: RootState) => state.selectedRecipients.recipients)
const signingOrder = useSelector((state: RootState) => state.selectedRecipients.signingOrder)
const [showSelfSignWarnModal, setShowSelfSignWarnModal] = useState(false) const [showSelfSignWarnModal, setShowSelfSignWarnModal] = useState(false)
const [editRecipientCurrentEmail, setEditRecipientCurrentEmail] = useState('') const [editRecipientCurrentEmail, setEditRecipientCurrentEmail] = useState('')
@ -87,7 +89,6 @@ const ProcessStepOne = () => {
const [templateName, setTemplateName] = useState('Untitled'); const [templateName, setTemplateName] = useState('Untitled');
const [isTyping, setIsTyping] = useState(false); const [isTyping, setIsTyping] = useState(false);
const [signingOrder, setSigningOrder] = useState(false)
// Optional: use a timer to detect pause in typing // Optional: use a timer to detect pause in typing
let typingTimeout = null; let typingTimeout = null;
@ -142,6 +143,12 @@ const ProcessStepOne = () => {
if (submitError.duplicateEmailSameRoutingOrder) setSubmitError(prev => ({ ...prev, duplicateEmailSameRoutingOrder: false })) if (submitError.duplicateEmailSameRoutingOrder) setSubmitError(prev => ({ ...prev, duplicateEmailSameRoutingOrder: false }))
if (submitError.documents) setSubmitError(prev => ({ ...prev, documents: false })) if (submitError.documents) setSubmitError(prev => ({ ...prev, documents: false }))
if (submitError.recipients) setSubmitError(prev => ({ ...prev, recipients: false })) if (submitError.recipients) setSubmitError(prev => ({ ...prev, recipients: false }))
// Handle Signing Order for template and for transction - fill on refresh based on recipients routing order or prefill - START
let isSigningOrder = signingOrder;
selectedRecipients && selectedRecipients.length > 1 && selectedRecipients.some(recipient => recipient.routingOrder && recipient.routingOrder > 1) ? isSigningOrder = true : "";
isSigningOrder && handleSigningOrderChange(isSigningOrder);
// Handle Signing Order for template and for transction - fill on refresh based on recipients routing order or prefill - END
}, [selectedRecipients]) }, [selectedRecipients])
const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms)); const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
@ -392,6 +399,9 @@ const ProcessStepOne = () => {
useEffect(() => { useEffect(() => {
validationCheck() validationCheck()
selectedRecipients.forEach((recipient, index) => {
})
}, [selectedDocuments, selectedRecipients, emailDtls]) }, [selectedDocuments, selectedRecipients, emailDtls])
const handleSelfSignCb = (e: React.ChangeEvent<HTMLInputElement>) => { const handleSelfSignCb = (e: React.ChangeEvent<HTMLInputElement>) => {
@ -415,10 +425,10 @@ const ProcessStepOne = () => {
) )
} }
function handleSigningOrderChange() { function handleSigningOrderChange(checked?: boolean) {
setSigningOrder(!signingOrder) dispatch(setSigningOrder(checked ?? !signingOrder))
if (!signingOrder) { if (checked ?? !signingOrder) {
selectedRecipients.forEach((recipient, index) => { selectedRecipients.forEach((recipient, index) => {
const id = recipient.id || recipient.uuid || "1" const id = recipient.id || recipient.uuid || "1"
dispatch(updateRecipientOrder({ id, routingOrder: index + 1 })); dispatch(updateRecipientOrder({ id, routingOrder: index + 1 }));
@ -431,7 +441,6 @@ const ProcessStepOne = () => {
} }
} }
return ( return (
<> <>
<div className={`relative w-full h-screen bg-[var(--mainViewBg)] ${globalLoader.isLoading ? 'pointer-events-none overflow-hidden' : ''}`}> <div className={`relative w-full h-screen bg-[var(--mainViewBg)] ${globalLoader.isLoading ? 'pointer-events-none overflow-hidden' : ''}`}>
@ -538,7 +547,8 @@ const ProcessStepOne = () => {
{selectedRecipients.length > 1 && <div className="flex items-center"> {selectedRecipients.length > 1 && <div className="flex items-center">
<input <input
// checked={signType == SignTypeEnum.selfSign} // checked={signType == SignTypeEnum.selfSign}
onChange={handleSigningOrderChange} onChange={(e) => handleSigningOrderChange(e.target.checked)}
checked={signingOrder}
id="default-checkbox" type="checkbox" value="" className="w-4 h-4 checked:bg-[var(--mainPrimaryBtn)] bg-gray-100 border-gray-300 rounded-sm outline-none" id="default-checkbox" type="checkbox" value="" className="w-4 h-4 checked:bg-[var(--mainPrimaryBtn)] bg-gray-100 border-gray-300 rounded-sm outline-none"
/> />
<label htmlFor="default-checkbox" className="ms-2 text-sm flex gap-1 text-slate-600"> <label htmlFor="default-checkbox" className="ms-2 text-sm flex gap-1 text-slate-600">
@ -561,7 +571,6 @@ const ProcessStepOne = () => {
// setEditRecipientsModal={setEditRecipientsModal} // setEditRecipientsModal={setEditRecipientsModal}
submitError={submitError} submitError={submitError}
isModal={false} isModal={false}
signingOrder={signingOrder}
/> />
</> </>
} }

View File

@ -53,7 +53,6 @@ type CardsData = {
transactionTemplateToggle: string; transactionTemplateToggle: string;
editRecipientCurrentEmail: string; editRecipientCurrentEmail: string;
setEditRecipientCurrentEmail: (email: string) => void; setEditRecipientCurrentEmail: (email: string) => void;
signingOrder: boolean;
}; };
const RecipientCards = ({ const RecipientCards = ({
@ -63,12 +62,13 @@ const RecipientCards = ({
editRecipient, editRecipient,
editRecipientCurrentEmail, editRecipientCurrentEmail,
setEditRecipientCurrentEmail, setEditRecipientCurrentEmail,
signingOrder // signing order - Routing order
}: CardsData) => { }: CardsData) => {
const templateId = useParams<{ templateId: string }>().templateId; const templateId = useParams<{ templateId: string }>().templateId;
let { accountId, transactionId } = useParams<{ accountId: string; transactionId: string }>(); let { accountId, transactionId } = useParams<{ accountId: string; transactionId: string }>();
const dispatch = useDispatch<AppDispatch>(); const dispatch = useDispatch<AppDispatch>();
const signingOrder = useSelector((state: RootState) => state.selectedRecipients.signingOrder)
if (!transactionId) { if (!transactionId) {
transactionId = templateId; transactionId = templateId;
} }
@ -388,7 +388,6 @@ const RecipientCards = ({
recipient={recipient} recipient={recipient}
idx={idx} idx={idx}
editRecipientCurrentEmail={editRecipientCurrentEmail} editRecipientCurrentEmail={editRecipientCurrentEmail}
signingOrder={signingOrder}
> >
<div <div
key={recipient.email + idx} key={recipient.email + idx}

View File

@ -20,10 +20,9 @@ interface RecipientsConIntProps {
isModal: boolean; isModal: boolean;
submitError?: FormSubmitInt; submitError?: FormSubmitInt;
setEditRecipientsModal?: React.Dispatch<boolean>; setEditRecipientsModal?: React.Dispatch<boolean>;
signingOrder?: boolean;
} }
const RecipientsCon: React.FC<RecipientsConIntProps> = ({ transactionTemplateToggle, accountId, transactionId, userData, submitError, isModal = true, setEditRecipientsModal, signingOrder }) => { const RecipientsCon: React.FC<RecipientsConIntProps> = ({ transactionTemplateToggle, accountId, transactionId, userData, submitError, isModal = true, setEditRecipientsModal }) => {
const dispatch = useDispatch<AppDispatch>() const dispatch = useDispatch<AppDispatch>()
const topRef = useRef<HTMLDivElement | null>(null); const topRef = useRef<HTMLDivElement | null>(null);
@ -145,7 +144,6 @@ const RecipientsCon: React.FC<RecipientsConIntProps> = ({ transactionTemplateTog
editRecipient={editRecipient} editRecipient={editRecipient}
setEditRecipientCurrentEmail={setEditRecipientCurrentEmail} setEditRecipientCurrentEmail={setEditRecipientCurrentEmail}
editRecipientCurrentEmail={editRecipientCurrentEmail} editRecipientCurrentEmail={editRecipientCurrentEmail}
signingOrder={signingOrder}
/> />
{submitError && submitError.showError && <p {submitError && submitError.showError && <p
className="text-xs text-right px-2 pt-2 text-red-500"> className="text-xs text-right px-2 pt-2 text-red-500">

View File

@ -4,9 +4,10 @@ import { MdDragIndicator } from "react-icons/md";
import { useSelector } from "react-redux"; import { useSelector } from "react-redux";
import { RootState } from "../../../store/store"; import { RootState } from "../../../store/store";
const SortableRecipient = ({ recipient, idx, editRecipientCurrentEmail, children, signingOrder }: any) => { const SortableRecipient = ({ recipient, idx, editRecipientCurrentEmail, children }: any) => {
const selectedRecipientsLen = useSelector((state: RootState) => state.selectedRecipients.recipients.length); const selectedRecipientsLen = useSelector((state: RootState) => state.selectedRecipients.recipients.length);
const signingOrder = useSelector((state: RootState) => state.selectedRecipients.signingOrder)
const { const {
attributes, attributes,
@ -35,10 +36,10 @@ const SortableRecipient = ({ recipient, idx, editRecipientCurrentEmail, children
{selectedRecipientsLen && <div {selectedRecipientsLen && <div
{...attributes} {...attributes}
{...listeners} {...listeners}
className="cursor-grab text-gray-500 flex items-center pr-2 absolute top-0 left-0 z-50" className="cursor-grab text-gray-500 flex items-center pr-2 absolute top-0 left-0 z-10 h-full"
> >
{signingOrder && <MdDragIndicator className="absolute top-[59px] left-[70px] text-xl text-gray-300" />} {signingOrder && <MdDragIndicator className="absolute top-[50%] left-[70px] transform -translate-y-1/2 text-gray-300" />}
</div>} </div>}

View File

@ -4,7 +4,7 @@ import { RootState } from '../../../store/store'
import api from '../../../config/api'; import api from '../../../config/api';
import RightSideBar, { MASKTYPE, TEXT } from './rightSideBar/RightSideBar'; import TagProperties, { MASKTYPE, TEXT } from './tagProperties/TagProperties';
import { DroppedItem, TagsTypes } from '../../../utlity/types'; import { DroppedItem, TagsTypes } from '../../../utlity/types';
import { useNavigate, useParams } from 'react-router-dom'; import { useNavigate, useParams } from 'react-router-dom';
@ -16,6 +16,7 @@ import typeToIconMap from '../../../helper/iconMap';
import Loader from '../../dashboard/Loader'; import Loader from '../../dashboard/Loader';
import { hideLoader, showLoader } from '../../../store/slices/loaderSlice'; import { hideLoader, showLoader } from '../../../store/slices/loaderSlice';
import DroppedItemsTagsCon from './DroppedItemsTagsCon'; import DroppedItemsTagsCon from './DroppedItemsTagsCon';
import DocumentPages from './documentPages/DocumentPages';
export interface DroppedItemsObj { export interface DroppedItemsObj {
[key: string]: DroppedItem[] [key: string]: DroppedItem[]
@ -42,6 +43,7 @@ interface DocumentContainerProps {
isPreview?: boolean; isPreview?: boolean;
aId?: string; aId?: string;
tId?: string; tId?: string;
selectedRecipientIndx?: number;
} }
let copiedTags: DroppedItem[] = [] let copiedTags: DroppedItem[] = []
@ -57,7 +59,7 @@ export interface DocumentContainerRef {
const DocumentContainer = forwardRef<DocumentContainerRef, DocumentContainerProps>( const DocumentContainer = forwardRef<DocumentContainerRef, DocumentContainerProps>(
({ transactionSaving, setTransactionSaving, isSend, isPreview = false, aId = null, tId = null }, ref) => { ({ transactionSaving, setTransactionSaving, isSend, isPreview = false, aId = null, tId = null, selectedRecipientIndx = 0 }, ref) => {
let transactionTemplateToggle = TRANSACTION_TEMPLATE_TOGGLE.TRANSACTIONS; let transactionTemplateToggle = TRANSACTION_TEMPLATE_TOGGLE.TRANSACTIONS;
const dispatch = useDispatch(); const dispatch = useDispatch();
const params = useParams<{ accountId?: string; transactionId?: string; templateId?: string }>(); const params = useParams<{ accountId?: string; transactionId?: string; templateId?: string }>();
@ -256,7 +258,7 @@ const DocumentContainer = forwardRef<DocumentContainerRef, DocumentContainerProp
let tagsData = groupByRecipientId(updatedTags); let tagsData = groupByRecipientId(updatedTags);
const responses = await sendTagsToRecipients(tagsData, accountId, transactionId); const responses = await sendTagsToRecipients(tagsData, accountId, transactionId);
if(responses && "status" in responses && responses?.status !== 200) return if (responses && "status" in responses && responses?.status !== 200) return
// return // return
// SENDING Transaction - START // SENDING Transaction - START
if (!save) { if (!save) {
@ -410,16 +412,16 @@ const DocumentContainer = forwardRef<DocumentContainerRef, DocumentContainerProp
setDroppedItems(dataToSet); setDroppedItems(dataToSet);
setDroppedItemsCounter((prev) => prev + 1); setDroppedItemsCounter((prev) => prev + 1);
setTimeout(() => { // setTimeout(() => {
const tagIndex = key in droppedItems ? droppedItems[key].length : 0; // const tagIndex = key in droppedItems ? droppedItems[key].length : 0;
setSelectedTag({ key, index: tagIndex }) // setSelectedTag({ key, index: tagIndex })
}, 1000) // }, 1000)
}; };
// HANDLE DROP - END // HANDLE DROP - END
useEffect(() => { useEffect(() => {
if (!hotKeysObj.isActive) return if (!hotKeysObj.isActive) return
if((!selectedTag?.key || `${selectedTag?.key}` in droppedItems && droppedItems[selectedTag?.key].length == 0)) return; if ((!selectedTag?.key || `${selectedTag?.key}` in droppedItems && droppedItems[selectedTag?.key].length == 0)) return;
const docCon = document.getElementById("doc-con"); const docCon = document.getElementById("doc-con");
@ -571,6 +573,7 @@ const DocumentContainer = forwardRef<DocumentContainerRef, DocumentContainerProp
// src={`http://10.10.1.99:8083/api/v1/accounts/${accountId}/transactions/${transactionId}/documents/${doc.id}/pages/${page.id}`} // src={`http://10.10.1.99:8083/api/v1/accounts/${accountId}/transactions/${transactionId}/documents/${doc.id}/pages/${page.id}`}
src={page.src} src={page.src}
loading='lazy' loading='lazy'
data-doc-img="true"
/> />
<DroppedItemsTagsCon <DroppedItemsTagsCon
@ -616,7 +619,10 @@ const DocumentContainer = forwardRef<DocumentContainerRef, DocumentContainerProp
</div> </div>
{selectedTag && selectedTag != null && "key" in selectedTag && "index" in selectedTag && <RightSideBar
{/* Note Check Here - Work on Tag Propertieres Apppearrrrrrrrrrrrrrrr ------------------------- */}
<>
{selectedTag && selectedTag != null && "key" in selectedTag && "index" in selectedTag && droppedItems[selectedTag.key] && droppedItems[selectedTag.key][selectedTag.index] && <TagProperties
droppedItems={droppedItems} droppedItems={droppedItems}
selectedTag={droppedItems[selectedTag.key][selectedTag.index]} selectedTag={droppedItems[selectedTag.key][selectedTag.index]}
setDroppedItems={setDroppedItems} setDroppedItems={setDroppedItems}
@ -625,8 +631,13 @@ const DocumentContainer = forwardRef<DocumentContainerRef, DocumentContainerProp
signClient={false} signClient={false}
emptySelectedTag={() => setSelectedTag(null)} emptySelectedTag={() => setSelectedTag(null)}
/> />
} }
</>
{selectedDocuments && selectedDocuments.length > 0 && <DocumentPages
droppedItems={droppedItems}
/>}
{/* Center Doc Container - END */} {/* Center Doc Container - END */}
</> </>
) )

View File

@ -201,6 +201,26 @@ const DroppedItemsTagsCon = forwardRef(({
// let timeOut = null // let timeOut = null
const handleMouseMove = (event: MouseEvent) => { const handleMouseMove = (event: MouseEvent) => {
// AUTO SCROLL ON DRAG - START
// const scrollContainer = document.getElementById("doc-con");
// const rect = scrollContainer.getBoundingClientRect();
// const threshold = 80; // distance from edge
// const scrollSpeed = 15;
// // mouse near bottom
// if (event.clientY > rect.bottom - threshold) {
// scrollContainer.scrollTop += scrollSpeed;
// }
// // mouse near top
// else if (event.clientY < rect.top + threshold) {
// scrollContainer.scrollTop -= scrollSpeed;
// }
// AUTO SCROLL ON DRAG - END
// Get new X and Y positions relative to the container // Get new X and Y positions relative to the container
let clientX = event.clientX let clientX = event.clientX
let clientY = event.clientY let clientY = event.clientY
@ -214,8 +234,8 @@ const DroppedItemsTagsCon = forwardRef(({
let top = clientY - containerRect.top - offsetY; let top = clientY - containerRect.top - offsetY;
// Restrict movement within container bounds // Restrict movement within container bounds
left = Math.max(0, Math.min(left, conScrollWidth - elementRect.width)); // left = Math.max(0, Math.min(left, conScrollWidth - elementRect.width));
top = Math.max(0, Math.min(top, conScrollHeight - elementRect.height)); // top = Math.max(0, Math.min(top, conScrollHeight - elementRect.height));
// Save in ref (fast, no re-render) // Save in ref (fast, no re-render)
positionRef.current = { left, top }; positionRef.current = { left, top };
@ -224,10 +244,6 @@ const DroppedItemsTagsCon = forwardRef(({
targetElement!.style.top = `${top}px`; targetElement!.style.top = `${top}px`;
targetElement!.style.left = `${left}px`; targetElement!.style.left = `${left}px`;
console.log(top, left, "top, left Line 283")
// Apply the movement to the element first // Apply the movement to the element first
// setDroppedItems(prevState => ({ // setDroppedItems(prevState => ({
// ...prevState, // ...prevState,
@ -247,8 +263,83 @@ const DroppedItemsTagsCon = forwardRef(({
} }
}; };
const handleMouseUp = () => { const handleMouseUp = (e: MouseEvent) => {
// CRITICAL: Ensure group borders are properly positioned after drag ends let hoveredDocId: any = ""
let hoveredPageIndex: string = ""
let { left, top } = positionRef.current;
const [currentDocumentId, currentPageIndex] = key.split("_");
let tagDocumentUpdate = {
update: false,
updatingPage: false,
updatingDocument: false,
pageIndex: 0,
documentId: ""
}
// N: Adding pointer event to none get page not tag itselef from elementFromPoint
targetElement.style.pointerEvents = "none";
const el = document.elementFromPoint(
e.clientX,
e.clientY
) as HTMLElement | null;
const hoveredContainer = el?.closest(".drop-page-in")
targetElement.style.pointerEvents = "auto";
// N: Adding pointer event to auto changing back the changes
if (hoveredContainer) {
const imgElem: HTMLElement | null = hoveredContainer.querySelector("[data-doc-img]");
hoveredDocId = imgElem ? imgElem.getAttribute("id") || "" : ""
hoveredDocId = hoveredDocId.split("-");
hoveredPageIndex = hoveredDocId[hoveredDocId.length - 1];
hoveredDocId.pop()
hoveredDocId = hoveredDocId.join("-");
}
if (hoveredContainer === null || hoveredDocId != currentDocumentId) { // NOT WORKING STILL MOVING THE TAG OUTSIDE THE COTEND DOCUMENT PAGE OVERFLOWING
targetElement!.style.top = `${droppedItems[key][index].dimensions.top}px`;
targetElement!.style.left = `${droppedItems[key][index].dimensions.left}px`;
document.removeEventListener("pointermove", handleMouseMove);
document.removeEventListener("pointerup", handleMouseUp);
return;
}
if (key in droppedItems && index in droppedItems[key]) {
// const tag = droppedItems[key][index];
const documentId = droppedItems[key][index]["documentId"];
const pageIndex = droppedItems[key][index]["pageIndex"];
// const targetElemTag = e.target.closest("[data-doc-id]")
tagDocumentUpdate.pageIndex = pageIndex
tagDocumentUpdate.documentId = documentId
// if (targetElemTag) {
// let hoveredDocId = targetElemTag.getAttribute("data-doc-id");
// if (hoveredDocId) {
if (hoveredDocId == documentId) {
if (hoveredPageIndex != pageIndex) {
tagDocumentUpdate.updatingPage = true;
tagDocumentUpdate.pageIndex = hoveredPageIndex;
tagDocumentUpdate.documentId = documentId;
}
} else {
tagDocumentUpdate.updatingDocument = true;
tagDocumentUpdate.updatingPage = true;
tagDocumentUpdate.pageIndex = hoveredPageIndex;
tagDocumentUpdate.documentId = hoveredDocId
}
// }
// }
}
if (groupElements != null && groupElements.length && groupId) { if (groupElements != null && groupElements.length && groupId) {
// Multiple approaches to ensure the positioning works: // Multiple approaches to ensure the positioning works:
@ -270,18 +361,120 @@ const DroppedItemsTagsCon = forwardRef(({
}); });
} }
const { left, top } = positionRef.current; // const tempDroppedItems = (droppedItems: any) => ({
console.log(top, left, "HandleTag Mouse Up top, left Line 283") // ...droppedItems,
// [key]: Array.isArray(droppedItems[key])
// ? droppedItems[key].map((item, i) =>
// i == index ? { ...item, dimensions: { ...item.dimensions, left, top }, update: true } : item
// )
// : [],
// })
setDroppedItems(prevState => ({ // setDroppedItems(prevState => ({
// ...prevState,
// [key]: Array.isArray(prevState[key])
// ? prevState[key].map((item, i) =>
// i == index ? { ...item, documentId: tagDocumentUpdate.documentId, pageIndex: tagDocumentUpdate.pageIndex, dimensions: { ...item.dimensions, left, top }, update: true } : item
// )
// : [],
// }));
setDroppedItems(prevState => {
// check documentId match
const isSameDocument =
currentDocumentId == tagDocumentUpdate.documentId;
if (!isSameDocument) return prevState
// check pageIndex match
const isSamePage = currentPageIndex == tagDocumentUpdate.pageIndex;
// same document + same page
if (isSameDocument && isSamePage) {
left = Math.max(0, Math.min(left, conScrollWidth - elementRect.width));
top = Math.max(0, Math.min(top, conScrollHeight - elementRect.height));
return {
...prevState, ...prevState,
[key]: Array.isArray(prevState[key]) [key]: Array.isArray(prevState[key])
? prevState[key].map((item, i) => ? prevState[key].map((item, i) =>
i == index ? { ...item, dimensions: { ...item.dimensions, left, top }, update: true } : item i === index
? {
...item,
documentId:
tagDocumentUpdate.documentId,
pageIndex:
tagDocumentUpdate.pageIndex,
dimensions: {
...item.dimensions,
left,
top,
},
update: true,
}
: item
) )
: [], : [],
})); };
}
// left = left - containerRect.left
const newContainerRect = hoveredContainer?.getBoundingClientRect() as DOMRect;
left = e.clientX - newContainerRect.left - offsetX;
top = e.clientY - newContainerRect.top - offsetY;
left = Math.max(
0,
Math.min(left, newContainerRect.width - elementRect.width)
);
top = Math.max(
0,
Math.min(top, newContainerRect.height - elementRect.height)
);
// move to another page/document
const targetKey = `${tagDocumentUpdate.documentId}_${tagDocumentUpdate.pageIndex}`;
const currentItems = Array.isArray(prevState[key])
? [...prevState[key]]
: [];
const movedItem = currentItems[index];
if (!movedItem) return prevState;
// remove from old array
currentItems.splice(index, 1);
// updated item
const updatedItem = {
...movedItem,
documentId: tagDocumentUpdate.documentId,
pageIndex: tagDocumentUpdate.pageIndex,
dimensions: {
...movedItem.dimensions,
left,
top,
},
update: true,
};
return {
...prevState,
// old container updated
[key]: currentItems,
// new container updated
[targetKey]: [
...(Array.isArray(prevState[targetKey])
? prevState[targetKey]
: []),
updatedItem,
],
};
});
document.removeEventListener("pointermove", handleMouseMove); document.removeEventListener("pointermove", handleMouseMove);
document.removeEventListener("pointerup", handleMouseUp); document.removeEventListener("pointerup", handleMouseUp);
@ -364,8 +557,6 @@ const DroppedItemsTagsCon = forwardRef(({
// HANDLE EXPAND TAG - END // HANDLE EXPAND TAG - END
const removeGroupBorders = (groupId: string) => { const removeGroupBorders = (groupId: string) => {
console.log(`Removing borders for group: ${groupId}`);
const borderPositions = ['top', 'right', 'bottom', 'left']; const borderPositions = ['top', 'right', 'bottom', 'left'];
borderPositions.forEach(position => { borderPositions.forEach(position => {
@ -391,7 +582,7 @@ const DroppedItemsTagsCon = forwardRef(({
return; return;
} }
const containerRect = targetContainer.getBoundingClientRect(); let containerRect = targetContainer.getBoundingClientRect();
// Use consistent offset values // Use consistent offset values
const offsetX = 10; const offsetX = 10;
@ -405,6 +596,17 @@ const DroppedItemsTagsCon = forwardRef(({
}); });
}; };
useEffect(() => {
if (!selectedTag) {
const borderElems = document.querySelectorAll("[border-group]")
if (borderElems && borderElems.length) {
borderElems.forEach(elem => {
elem.style.display = "none";
})
}
}
}, [selectedTag])
const handleGroupBorderPosition = (groupElements: NodeListOf<HTMLElement>, containerRect: DOMRect, offsetX: number, offsetY: number) => { const handleGroupBorderPosition = (groupElements: NodeListOf<HTMLElement>, containerRect: DOMRect, offsetX: number, offsetY: number) => {
if (!groupElements || groupElements.length == 0) return; if (!groupElements || groupElements.length == 0) return;
@ -473,6 +675,7 @@ const DroppedItemsTagsCon = forwardRef(({
// Position the border element // Position the border element
borderElement.style.position = 'absolute'; borderElement.style.position = 'absolute';
borderElement.style.display = 'block';
borderElement.style.pointerEvents = position == 'bottom' ? 'auto' : 'none'; borderElement.style.pointerEvents = position == 'bottom' ? 'auto' : 'none';
switch (position) { switch (position) {
@ -594,12 +797,21 @@ const DroppedItemsTagsCon = forwardRef(({
<React.Fragment key={index}> <React.Fragment key={index}>
<div <div
// key={index} // key={index}
tabIndex={0}
draggable="false" draggable="false"
data-tag-id={index} data-tag-id={index}
data-drop-key={pageIdkey} data-drop-key={pageIdkey}
data-tag-selected={index == selectedTag?.index} data-tag-selected={index == selectedTag?.index}
className="absolute rounded dropped-item" className="absolute rounded dropped-item z-50"
onClick={() => handleTagClick(pageIdkey, index)} onClick={(e) => {
e.currentTarget.focus();
handleTagClick(pageIdkey, index)
}}
onBlur={(e: React.FocusEvent) => {
const nextElement = e.relatedTarget as HTMLElement | null;
if (nextElement?.hasAttribute("data-type-group") || nextElement?.closest("[data-tag-properties]")) return;
setSelectedTag(null);
}}
style={{ style={{
left: `${item.dimensions.left * scaleX}px`, left: `${item.dimensions.left * scaleX}px`,
top: `${item.dimensions.top * scaleY}px`, top: `${item.dimensions.top * scaleY}px`,
@ -671,15 +883,22 @@ const DroppedItemsTagsCon = forwardRef(({
{ {
"group" in item && item.group && "isGroupExist" in item && !item.isGroupExist && "group" in item && item.group && "isGroupExist" in item && !item.isGroupExist &&
<> <>
<div className="group-border" border-group={item.group} border-pos="top"></div> <div className="group-border z-50" border-group={item.group} border-pos="top"></div>
<div className="group-border" border-group={item.group} border-pos="right"></div> <div className="group-border z-50" border-group={item.group} border-pos="right"></div>
<div className="group-border bottom-border" border-group={item.group} border-pos="bottom"> <div className="group-border z-50 bottom-border" border-group={item.group} border-pos="bottom">
<div <button
onClick={() => handleAddGroupItemClicked(pageIdkey, item)}> data-type-group="true"
onClick={() => handleAddGroupItemClicked(pageIdkey, item)}
onBlur={(e: React.FocusEvent) => {
const nextElement = e.relatedTarget as HTMLElement | null;
if (nextElement?.hasAttribute("groupp")) return;
setSelectedTag(null);
}}
>
<IoAdd /> <IoAdd />
</button>
</div> </div>
</div> <div className="group-border z-50" border-group={item.group} border-pos="left"></div>
<div className="group-border" border-group={item.group} border-pos="left"></div>
</> </>
} }

View File

@ -132,7 +132,7 @@ const FieldList: React.FC<FieldListProps> = ({ color, recipientId, recipientIndx
const priorityOrder = ['SIGNATURE', 'INITIALS', 'STAMP']; const priorityOrder = ['SIGNATURE', 'INITIALS', 'STAMP'];
const data = res.data.filter((obj, indx) => { const data = res.data.filter((obj, indx) => {
if (obj.type == "STAMP" || obj.type == "RADIO" || obj.type == "CHECKBOX" || obj.type == "ATTACHMENT" || obj.type == "SELECT") return false if (obj.type == "STAMP" || obj.type == "RADIO" || obj.type == "CHECKBOX" || obj.type == "ATTACHMENT" || obj.type == "SELECT") return true
return true return true
}); });
@ -316,12 +316,13 @@ const FieldList: React.FC<FieldListProps> = ({ color, recipientId, recipientIndx
if (!IconComponent) return null; if (!IconComponent) return null;
return ( return (
// <div key={index} draggable={true} onDragStart={(e) => handleDragStart(e, field.label, recipientId)} className="p-1 flex justify-start items-center gap-2 rounded-sm cursor-grab hover:bg-slate-200 transition-all duration-300"> <>
{/* <div key={index} draggable={true} onDragStart={(e) => handleDragStart(e, field.label, recipientId)} className="p-1 flex justify-start items-center gap-2 rounded-sm cursor-grab hover:bg-slate-200 transition-all duration-300"> */}
<div <div
draggable={true} draggable={true}
key={index} key={index}
onPointerDown={(e) => handlePointerDown(e, field.type.toLowerCase(), recipientId, recipientIndx, field)} onPointerDown={(e) => handlePointerDown(e, field.type.toLowerCase(), recipientId, recipientIndx, field)}
className={`border-[1px] flex flex-col justify-center bg-white p-2 w-[75px] h-[75px] items-center gap-2 rounded-sm cursor-grab hover:bg-slate-200 transition-all duration-300`} className={`border-[1px] flex w-full justify-left bg-white items-center gap-1 rounded-sm cursor-grab hover:bg-slate-200 transition-all duration-300`}
style={{ borderColor: color }} style={{ borderColor: color }}
> >
@ -336,6 +337,8 @@ const FieldList: React.FC<FieldListProps> = ({ color, recipientId, recipientIndx
{field.type.toLowerCase()} {field.type.toLowerCase()}
</p> </p>
</div> </div>
{/* </div> */}
</>
) )
} }

View File

@ -6,13 +6,10 @@ import FieldList from './FieldList';
import { addColorsToRecipient, fetchRecipients, recipientType, toggleToTemplates, TRANSACTION_TEMPLATE_TOGGLE } from '../../../store/slices/selectedRecipientsSlice'; import { addColorsToRecipient, fetchRecipients, recipientType, toggleToTemplates, TRANSACTION_TEMPLATE_TOGGLE } from '../../../store/slices/selectedRecipientsSlice';
import { IoCheckmark } from 'react-icons/io5' import { IoCheckmark } from 'react-icons/io5'
import { MdKeyboardArrowDown, MdOutlineNavigateNext } from 'react-icons/md' import { MdKeyboardArrowDown } from 'react-icons/md'
import { useParams } from 'react-router-dom'; import { useParams } from 'react-router-dom';
import { fetchDocuments } from '../../../store/slices/documentSlice'; import { fetchDocuments } from '../../../store/slices/documentSlice';
import { GrFormPrevious } from 'react-icons/gr'; import { FaAngleUp } from 'react-icons/fa';
import { LuTableOfContents } from 'react-icons/lu';
import { IoIosDocument, IoMdClose } from 'react-icons/io';
import { FaAngleUp, FaChevronRight } from 'react-icons/fa';
interface LeftSideBarProps { interface LeftSideBarProps {
isFields: boolean; isFields: boolean;
@ -23,7 +20,7 @@ interface LeftSideBarProps {
handleSetIsPages: (setTo: boolean) => void; handleSetIsPages: (setTo: boolean) => void;
} }
const LeftSideBar: React.FC<LeftSideBarProps> = ({ isFields, isPages, selectedRecipientIndx, setSelectedRecipient, handleSetIsFields, handleSetIsPages }) => { const LeftSideBar: React.FC<LeftSideBarProps> = ({ isFields, selectedRecipientIndx, setSelectedRecipient, handleSetIsFields, handleSetIsPages }) => {
let transactionTemplateToggle = TRANSACTION_TEMPLATE_TOGGLE.TRANSACTIONS; let transactionTemplateToggle = TRANSACTION_TEMPLATE_TOGGLE.TRANSACTIONS;
const dispatch = useDispatch(); const dispatch = useDispatch();
@ -44,8 +41,6 @@ const LeftSideBar: React.FC<LeftSideBarProps> = ({ isFields, isPages, selectedRe
const selectedRecipients = useSelector((state: RootState) => state.selectedRecipients.recipients) const selectedRecipients = useSelector((state: RootState) => state.selectedRecipients.recipients)
const selectedDocuments = useSelector((state: RootState) => state.documents.documents) const selectedDocuments = useSelector((state: RootState) => state.documents.documents)
const [currentPage, setCurrentPage] = useState(0);
useEffect(() => { useEffect(() => {
if (selectedRecipients.length == 0) { if (selectedRecipients.length == 0) {
dispatch(fetchRecipients({ accountId, transactionId })); dispatch(fetchRecipients({ accountId, transactionId }));
@ -60,7 +55,6 @@ const LeftSideBar: React.FC<LeftSideBarProps> = ({ isFields, isPages, selectedRe
// For Recipient // For Recipient
const [dropDownToggle, setDropDownToggle] = useState(false); const [dropDownToggle, setDropDownToggle] = useState(false);
const [dropDownTogglePage, setDropDownTogglePage] = useState<string>("");
const selectedRecipient = selectedRecipients[selectedRecipientIndx]; const selectedRecipient = selectedRecipients[selectedRecipientIndx];
@ -93,17 +87,17 @@ const LeftSideBar: React.FC<LeftSideBarProps> = ({ isFields, isPages, selectedRe
<div className='col-span-2'> <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)]"} `}> 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 {/* <div
onClick={() => { onClick={() => {
handleSetIsPages(false) // handleSetIsPages(false)
handleSetIsFields(false) handleSetIsFields(false)
}} }}
className='absolute sm:hidden top-[5px] right-[10px] bg-white rounded-full text-2xl z-50'> className='absolute sm:hidden top-[5px] right-[10px] bg-white rounded-full text-2xl z-50'>
<IoMdClose /> <IoMdClose />
</div> </div> */}
<div className='hidden sm:flex flex-col md:flex-row justify-between items-center bg-slate-100 p-1.5 rounded-2xl border-1 border-gray-200 mb-3'> {/* <div className='hidden sm:flex flex-col md:flex-row justify-between items-center bg-slate-100 p-1.5 rounded-2xl border-1 border-gray-200 mb-3'>
<div <div
onClick={() => handleSetIsFields(!isFields)} onClick={() => handleSetIsFields(!isFields)}
className={`flex items-center gap-1 cursor-pointer text-xs w-full justify-center py-1.5 rounded-2xl ${isFields ? 'bg-[var(--primary)] text-white' : ''} transition-all duration-300`}> className={`flex items-center gap-1 cursor-pointer text-xs w-full justify-center py-1.5 rounded-2xl ${isFields ? 'bg-[var(--primary)] text-white' : ''} transition-all duration-300`}>
@ -112,12 +106,12 @@ const LeftSideBar: React.FC<LeftSideBarProps> = ({ isFields, isPages, selectedRe
</div> </div>
<div <div
onClick={() => handleSetIsPages(!isPages)} // onClick={() => handleSetIsPages(!isPages)}
className={`flex items-center gap-1 cursor-pointer text-xs w-full justify-center py-1.5 rounded-2xl ${isPages ? 'bg-[var(--primary)] text-white' : ''} transition-all duration-300`}> className={`flex items-center gap-1 cursor-pointer text-xs w-full justify-center py-1.5 rounded-2xl ${isPages ? 'bg-[var(--primary)] text-white' : ''} transition-all duration-300`}>
<IoIosDocument /> <IoIosDocument />
<p className='tracking-wide'>Pages</p> <p className='tracking-wide'>Pages</p>
</div> </div>
</div> </div> */}
{selectedRecipients.length > 0 && isFields && <> {selectedRecipients.length > 0 && isFields && <>
@ -191,99 +185,6 @@ const LeftSideBar: React.FC<LeftSideBarProps> = ({ isFields, isPages, selectedRe
{/* Fields List and Recipeint - End */} {/* Fields List and Recipeint - End */}
</>} </>}
{isPages && <>
{/* Pages - START */}
<div className='flex flex-col gap-2'>
{selectedDocuments.length && selectedDocuments.map((document, indx) => {
return (
<div
key={indx}
// tabIndex={1}
// onBlur={() => setDropDownTogglePage('')}
>
<div
onClick={() => {
setDropDownTogglePage(`${(dropDownTogglePage == 'pageDropDown' + indx) ? '' : 'pageDropDown' + indx}`);
setCurrentPage(0);
window.location.hash = document.id + "" + 0;
}}
className={`p-2 ${(dropDownTogglePage == 'pageDropDown' + indx) ? "bg-[var(--primary)]" : "bg-slate-200"} flex flex-col justify-center items-center gap-2 rounded`}>
{
document.pages != undefined && <img
key={indx}
className='rounded-sm shadow'
src={"data:image/png;base64," + document.thumbnail}
alt="" />
}
<div
className='flex items-center justify-between w-full text-xs rounded-sm cursor-pointer text-slate-100'>
<p className='whitespace-nowrap overflow-hidden text-ellipsis max-w-[18ch]'>{document.name}</p>
<MdKeyboardArrowDown className={`${(dropDownTogglePage == 'pageDropDown' + indx) ? 'rotate-180' : ''}`} />
</div>
</div>
{
dropDownTogglePage == 'pageDropDown' + indx &&
<div className="my-1 flex justify-center items-center gap-2 flex-wrap">
{/* Prev Button */}
<a
href={"#" + document.id + (currentPage - 1)}
className={`cursor-pointer px-2 py-1 shadow rounded-sm text-sm bg-slate-200 hover:border-[var(--primary)] transition-all duration-300 ${currentPage == 0 ? "pointer-events-none opacity-50" : ""
}`}
onClick={(e) => {
if (currentPage == 0) e.preventDefault();
else setCurrentPage(currentPage - 1);
}}
>
<GrFormPrevious />
</a>
{/* Page Selector */}
<select
className="w-[58px] py-1 text-xs text-slate-400 outline-none"
value={currentPage}
onChange={(e) => {
const newPage = Number(e.target.value);
setCurrentPage(newPage);
window.location.hash = document.id + "" + newPage;
}}
>
{document.pages != undefined && document.pages.map((_, pageIndex) => (
<option key={pageIndex} value={pageIndex}>
{pageIndex + 1}
</option>
))}
</select>
<span className="text-xs text-slate-600">
/ {document.pages?.length}
</span>
{/* Next Button */}
<a
href={"#" + document.id + (currentPage + 1)}
className={`cursor-pointer px-2 py-1 shadow rounded-sm text-sm bg-slate-200 hover:border-[var(--primary)] transition-all duration-300 ${currentPage == document.pages?.length - 1 ? "pointer-events-none opacity-50" : ""
}`}
onClick={(e) => {
if (currentPage == document.pages?.length - 1) e.preventDefault();
else setCurrentPage(currentPage + 1);
}}
>
<MdOutlineNavigateNext />
</a>
</div>
}
</div>
)
})}
</div>
{/* Pages - END */}
</>}
</div> </div>
} }
{/* Left Side Bar - END */} {/* Left Side Bar - END */}
@ -292,9 +193,9 @@ const LeftSideBar: React.FC<LeftSideBarProps> = ({ isFields, isPages, selectedRe
<div> <div>
{/* Left Side Pages */} {/* Left Side Pages */}
{!isPages && <div {<div
onClick={() => { onClick={() => {
handleSetIsPages(true) // handleSetIsPages(true)
handleSetIsFields(false) 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'> 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'>
@ -314,7 +215,7 @@ const LeftSideBar: React.FC<LeftSideBarProps> = ({ isFields, isPages, selectedRe
{!isFields && <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' 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={() => { onClick={() => {
handleSetIsPages(false) // handleSetIsPages(false)
handleSetIsFields(true) handleSetIsFields(true)
}} }}
> >

View File

@ -3,9 +3,9 @@ import React, { useEffect, useState } from 'react'
import { useDispatch, useSelector } from 'react-redux' import { useDispatch, useSelector } from 'react-redux'
import { RootState } from '../../../store/store' import { RootState } from '../../../store/store'
import { FaCheck, FaSignature } from 'react-icons/fa' import { FaCheck, FaPaste, FaRegCopy, FaSave, FaSignature } from 'react-icons/fa'
import { IoIosDocument } from 'react-icons/io' import { IoIosDocument } from 'react-icons/io'
import { LuTableOfContents } from 'react-icons/lu' import { LuRedo2, LuTableOfContents, LuUndo2 } from 'react-icons/lu'
import { MdKeyboardArrowDown } from 'react-icons/md' import { MdKeyboardArrowDown } from 'react-icons/md'
import { RiResetRightLine } from 'react-icons/ri' import { RiResetRightLine } from 'react-icons/ri'
import { fetchRecipients, recipientType, toggleToTemplates, TRANSACTION_TEMPLATE_TOGGLE } from '../../../store/slices/selectedRecipientsSlice' import { fetchRecipients, recipientType, toggleToTemplates, TRANSACTION_TEMPLATE_TOGGLE } from '../../../store/slices/selectedRecipientsSlice'
@ -15,6 +15,9 @@ import { Bounce, toast } from 'react-toastify'
import { FiChevronDown } from 'react-icons/fi' import { FiChevronDown } from 'react-icons/fi'
import RecipientsCon from '../processStepOne/RecipientsCon' import RecipientsCon from '../processStepOne/RecipientsCon'
import MessageCon from '../processStepOne/MessageCon' import MessageCon from '../processStepOne/MessageCon'
import { IoCopy } from 'react-icons/io5'
import { FaRegPaste } from 'react-icons/fa6'
import ConfirmSendModal from './TransactionSendConfirmationModal'
interface NavTwoProps { interface NavTwoProps {
transactionSaving?: boolean; transactionSaving?: boolean;
@ -75,6 +78,11 @@ const NavTwo: React.FC<NavTwoProps> = ({
const [showActionsDropDown, setShowActionsDropDown] = useState<boolean>(false); const [showActionsDropDown, setShowActionsDropDown] = useState<boolean>(false);
const [editRecipientsModal, setEditRecipientsModal] = useState<boolean>(false); const [editRecipientsModal, setEditRecipientsModal] = useState<boolean>(false);
const [messageModal, setMessageModal] = useState<boolean>(false); const [messageModal, setMessageModal] = useState<boolean>(false);
const [confirmSendModalOpen, setConfirmSendModalOpen] = useState({
"open": false,
"confirm": false,
"loading": false
});
console.log(editRecipientsModal) console.log(editRecipientsModal)
@ -184,6 +192,7 @@ const NavTwo: React.FC<NavTwoProps> = ({
// setIsSend(!isSend); // setIsSend(!isSend);
// setTimeout(() => { // setTimeout(() => {
setConfirmSendModalOpen({ open: false, confirm: false, loading: false })
if (transactionTemplateToggle == TRANSACTION_TEMPLATE_TOGGLE.TRANSACTIONS) { if (transactionTemplateToggle == TRANSACTION_TEMPLATE_TOGGLE.TRANSACTIONS) {
if (isSignSend) { if (isSignSend) {
// SELF SIGN CHECK HERE // SELF SIGN CHECK HERE
@ -198,6 +207,10 @@ const NavTwo: React.FC<NavTwoProps> = ({
// }, 1000) // }, 1000)
} }
useEffect(() => {
if (confirmSendModalOpen.confirm) handleSend()
}, [confirmSendModalOpen])
const zoom = (percentage: number, contentClass: string) => { const zoom = (percentage: number, contentClass: string) => {
const content = document.querySelector(`.${contentClass}`); const content = document.querySelector(`.${contentClass}`);
if (content) { if (content) {
@ -227,9 +240,16 @@ const NavTwo: React.FC<NavTwoProps> = ({
return ( return (
<> <>
<ConfirmSendModal
confirmSendModalOpen={confirmSendModalOpen.open}
confirmSendModalOnClose={() => { setConfirmSendModalOpen({ open: false, confirm: false, loading: false }) }}
confirmSendModalOnConfirm={() => { setConfirmSendModalOpen({ open: true, confirm: true, loading: true }) }}
confirmSendModalLoading={confirmSendModalOpen.loading}
documents={selectedDocuments}
recipients={selectedRecipients}
/>
{/* Nav Two - Oprions - START */} {/* Nav Two - Oprions - START */}
<div className='flex justify-between items-center px-4 py-1 bg-white'> <div className='relative flex justify-between items-center px-4 py-1 bg-white'>
{/* Left Side */} {/* Left Side */}
<div className='flex justify-start items-center gap-2'> <div className='flex justify-start items-center gap-2'>
@ -244,14 +264,37 @@ const NavTwo: React.FC<NavTwoProps> = ({
<div <div
tabIndex={1} tabIndex={1}
onBlur={() => setIsFitDd(false)} onBlur={() => setIsFitDd(false)}
className='absolute gap-2 flex items-center left-[50%] top-[50%] translate-x-[-50%] translate-y-[-50%] z-50'
> >
<div className='flex'>
<p className='p-2 hover:bg-slate-100 cursor-pointer rounded-sm'>
<LuUndo2 className='text-sm text-slate-600' />
</p>
<p className='p-2 hover:bg-slate-100 cursor-pointer rounded-sm'>
<LuRedo2 className='text-sm text-slate-600' />
</p>
</div>
<div className='w-full h-[20px] border-[1px] border-slate-200'></div>
<div className='flex'>
<p className='p-2 hover:bg-slate-100 cursor-pointer rounded-sm'>
<FaRegCopy className='text-sm text-slate-600' />
</p>
<p className='p-2 hover:bg-slate-100 cursor-pointer rounded-sm'>
<FaRegPaste className='text-sm text-slate-600' />
</p>
</div>
<div className='w-full h-[20px] border-[1px] border-slate-200'></div>
<div className='border border-slate-400 rounded-sm ml-1'>
<div <div
onClick={() => setIsFitDd(!isFitDd)} onClick={() => setIsFitDd(!isFitDd)}
className='hidden sm:flex md:flex items-center gap-1 cursor-pointer text-xs px-2 py-1 rounded-sm'> className='hidden sm:flex md:flex items-center gap-1 cursor-pointer text-xs px-2 py-1 rounded-sm'
<p className='tracking-wide'>Fit</p> >
<p className='tracking-wide'>{fitObj[selFitIndx]?.label}</p>
<MdKeyboardArrowDown style={{ transform: `${isFitDd ? "rotate(180deg)" : ""}` }} /> <MdKeyboardArrowDown style={{ transform: `${isFitDd ? "rotate(180deg)" : ""}` }} />
</div> </div>
@ -273,6 +316,8 @@ const NavTwo: React.FC<NavTwoProps> = ({
</div>} </div>}
</div> </div>
</div>
{/* {!isPreview && <div className='hidden sm:flex items-center gap-1 cursor-pointer text-xs px-2 py-1 rounded-sm'> {/* {!isPreview && <div className='hidden sm:flex items-center gap-1 cursor-pointer text-xs px-2 py-1 rounded-sm'>
<RiResetRightLine <RiResetRightLine
@ -402,7 +447,7 @@ const NavTwo: React.FC<NavTwoProps> = ({
{!isPreview && <> {!isPreview && <>
<div <div
id='send-sign-dash-btn' id='send-sign-dash-btn'
onClick={() => handleSend()} onClick={() => setConfirmSendModalOpen({ open: true, confirm: false, loading: false })}
className='flex items-center gap-2 cursor-pointer text-xs px-4 py-2 ml-3 rounded-sm bg-[var(--primary)]'> className='flex items-center gap-2 cursor-pointer text-xs px-4 py-2 ml-3 rounded-sm bg-[var(--primary)]'>
<p className='tracking-wide text-white'> <p className='tracking-wide text-white'>
{transactionTemplateToggle == TRANSACTION_TEMPLATE_TOGGLE.TEMPLATES ? "Save" : "Send"} {transactionTemplateToggle == TRANSACTION_TEMPLATE_TOGGLE.TEMPLATES ? "Save" : "Send"}

View File

@ -99,7 +99,7 @@ const ProcessStepTwo = () => {
<div className='sm:grid sm:grid-cols-12 sm:gap-1 mt-1 h-[calc(100vh-46px)] overflow-hidden'> <div className='sm:grid sm:grid-cols-12 sm:gap-1 mt-1 h-[calc(100vh-46px)] overflow-hidden'>
<LeftSideBar isFields={isFields} isPages={isPages} selectedRecipientIndx={selectedRecipient} setSelectedRecipient={setSelectedRecipient} handleSetIsFields={handleSetIsFields} handleSetIsPages={handleSetIsPages} /> <LeftSideBar isFields={isFields} isPages={isPages} selectedRecipientIndx={selectedRecipient} setSelectedRecipient={setSelectedRecipient} handleSetIsFields={handleSetIsFields} handleSetIsPages={handleSetIsPages} />
<DocumentContainer ref={docRef} transactionSaving={transactionSaving} setTransactionSaving={setTransactionSaving} isSend={isSend} setIsSend={setIsSend} /> <DocumentContainer ref={docRef} selectedRecipientIndx={selectedRecipient} transactionSaving={transactionSaving} setTransactionSaving={setTransactionSaving} isSend={isSend} setIsSend={setIsSend} />
</div> </div>
{globalLoader.isLoading && <Loader />} {globalLoader.isLoading && <Loader />}

View File

@ -0,0 +1,351 @@
import React, { useEffect } from "react";
import { IoClose } from "react-icons/io5";
import { FiFileText, FiUsers } from "react-icons/fi";
interface Recipient {
id: string;
name: string;
email: string;
color?: string;
}
interface DocumentItem {
id: string;
name: string;
}
interface ConfirmSendModalProps {
confirmSendModalOpen: boolean;
confirmSendModalOnClose: () => void;
confirmSendModalOnConfirm: () => void;
confirmSendModalLoading?: boolean;
documents: DocumentItem[];
recipients: Recipient[];
}
const ConfirmSendModal = ({
confirmSendModalOpen,
confirmSendModalOnClose,
confirmSendModalOnConfirm,
confirmSendModalLoading = false,
documents,
recipients,
}: ConfirmSendModalProps) => {
// Close on ESC
useEffect(() => {
const handleEsc = (e: KeyboardEvent) => {
if (e.key === "Escape") {
confirmSendModalOnClose();
}
};
if (confirmSendModalOpen) {
document.addEventListener("keydown", handleEsc);
}
return () => {
document.removeEventListener("keydown", handleEsc);
};
}, [confirmSendModalOpen]);
// Prevent body scroll when modal open
useEffect(() => {
if (confirmSendModalOpen) {
document.body.style.overflow = "hidden";
} else {
document.body.style.overflow = "auto";
}
return () => {
document.body.style.overflow = "auto";
};
}, [confirmSendModalOpen]);
if (!confirmSendModalOpen) return null;
return (
<div
onClick={confirmSendModalOnClose}
className="
fixed
inset-0
z-[9999]
flex
items-center
justify-center
bg-black/50
backdrop-blur-sm
p-3
sm:p-4
"
>
{/* Modal */}
<div
onClick={(e) => e.stopPropagation()}
className="
w-full
max-w-xl
max-h-[90vh]
flex
flex-col
rounded-sm
bg-white
shadow-2xl
overflow-hidden
animate-in
fade-in
zoom-in
duration-200
"
>
{/* Header */}
<div className="flex items-start justify-between border-b border-slate-200 p-5 sm:p-6">
<div className="pr-4">
<h2 className="text-xl sm:text-2xl font-semibold text-slate-800">
Review & Send
</h2>
<p className="mt-1 text-sm text-slate-500 leading-relaxed">
Please review the documents and recipient details
before sending this transaction for signing.
</p>
</div>
<button
onClick={confirmSendModalOnClose}
className="
shrink-0
p-2
rounded-sm
hover:bg-slate-100
transition
cursor-pointer
"
>
<IoClose className="text-xl text-slate-500" />
</button>
</div>
{/* Body */}
<div className="flex-1 overflow-y-auto p-4 sm:p-6 space-y-5">
{/* Documents */}
<div className="rounded-sm border border-slate-200 overflow-hidden">
<div className="flex items-center gap-2 border-b border-slate-200 bg-slate-50 px-4 py-3">
<FiFileText className="text-slate-600" />
<h3 className="font-medium text-slate-800">
Documents ({documents.length})
</h3>
</div>
<div className="max-h-[220px] overflow-y-auto p-3 space-y-2">
{documents.map((document) => (
<div
key={document.id}
className="
flex
items-center
gap-3
rounded-sm
border
border-slate-200
bg-white
px-3
py-3
"
>
<div
className="
flex
items-center
justify-center
w-10
h-10
rounded-sm
bg-slate-100
shrink-0
"
>
<FiFileText className="text-slate-600" />
</div>
<div className="min-w-0">
<p className="text-sm font-medium text-slate-800 truncate">
{document.name}
</p>
</div>
</div>
))}
</div>
</div>
{/* Recipients */}
<div className="rounded-sm border border-slate-200 overflow-hidden">
<div className="flex items-center gap-2 border-b border-slate-200 bg-slate-50 px-4 py-3">
<FiUsers className="text-slate-600" />
<h3 className="font-medium text-slate-800">
Recipients ({recipients.length})
</h3>
</div>
<div className="max-h-[260px] overflow-y-auto p-3 space-y-2">
{recipients.map((recipient) => (
<div
key={recipient.id}
className="
flex
items-center
justify-between
gap-3
rounded-sm
border
border-slate-200
bg-white
px-3
py-3
"
>
<div className="flex items-center gap-3 min-w-0">
<div
className="w-3 h-3 rounded-full shrink-0"
style={{
background:
recipient.color || "#6366f1",
}}
/>
<div className="min-w-0">
<p className="text-sm font-medium text-slate-800 truncate">
{recipient.name}
</p>
<p className="text-xs text-slate-500 truncate">
{recipient.email}
</p>
</div>
</div>
<div
className="
shrink-0
rounded-full
bg-amber-50
border
border-amber-200
px-2.5
py-1
text-xs
font-medium
text-amber-700
"
>
Pending
</div>
</div>
))}
</div>
</div>
{/* Info */}
<div
className="
rounded-sm
border
border-indigo-100
bg-indigo-50
px-4
py-4
"
>
<p className="text-sm leading-relaxed text-indigo-900">
Once confirmed, recipients will receive an email
invitation to review and sign the documents.
</p>
</div>
</div>
{/* Footer */}
<div
className="
flex
flex-col-reverse
sm:flex-row
items-stretch
sm:items-center
justify-end
gap-3
border-t
border-slate-200
bg-white
p-4
sm:p-5
"
>
<button
onClick={confirmSendModalOnClose}
className="
w-full
sm:w-auto
text-sm
px-4
py-2.5
rounded-sm
border
border-slate-300
text-slate-700
hover:bg-slate-100
transition
cursor-pointer
font-medium
"
>
Continue Reviewing
</button>
<button
onClick={confirmSendModalOnConfirm}
disabled={confirmSendModalLoading}
className="
w-full
sm:w-auto
text-sm
px-5
py-2.5
rounded-sm
bg-[var(--primary)]
text-white
hover:opacity-100
transition
opacity-80
disabled:opacity-50
disabled:cursor-not-allowed
cursor-pointer
font-medium
"
>
{confirmSendModalLoading
? "Sending..."
: "Confirm & Send"}
</button>
</div>
</div>
</div>
);
};
export default ConfirmSendModal;

View File

@ -0,0 +1,245 @@
import React, { useEffect, useState } from 'react'
import { RootState } from '../../../../store/store';
import { useSelector } from 'react-redux';
import { DroppedItemsObj } from '../DocumentContainer';
import { MdKeyboardArrowUp, MdMoreVert } from 'react-icons/md';
import { PiTagSimpleFill } from 'react-icons/pi';
const DocumentPages: React.FC<{ droppedItems: DroppedItemsObj }> = ({ droppedItems }) => {
type RecipientPageTagInfo = {
color: string;
recipientIndex: number;
recipientTagCount?: number;
};
type DocumentTagsMapping = {
[documentId: string]: {
[pageIndex: string]: RecipientPageTagInfo[];
};
};
const selectedDocuments = useSelector((state: RootState) => state.documents.documents)
const [documentPagesTagsMapping, setDocumentPagesTagsMapping] = useState<DocumentTagsMapping>({});
const recipients = useSelector((state: RootState) => state.selectedRecipients.recipients);
const [openDocs, setOpenDocs] = useState<Record<string, boolean>>(
() => Object.fromEntries(selectedDocuments.map((d, i) => [d.id, i === 0]))
)
const [activePage, setActivePage] = useState<string | null>(
selectedDocuments[0] ? `${selectedDocuments[0].id}_0` : null
)
const toggleDoc = (docId: string) => {
setOpenDocs(prev => ({ ...prev, [docId]: !prev[docId] }))
}
const goToPage = (docId: string, pageIndex: number) => {
const key = `${docId}_${pageIndex}`
setActivePage(key)
window.location.hash = docId + "" + pageIndex
}
// useEffect(() => {
// setTimeout(() => {
// let documentPagesTagsMappingLocal: {
// [documentId: string]: {
// [pageIndex: number]: [number, string];
// };
// }[] = [];
// recipients && recipients.length && recipients.forEach((selectedRecipient, selectedRecipientIndx) => {
// const selectedRecipientColor = selectedRecipient.color
// let activeRecipientTagsCount = 0;
// droppedItems && Object.keys(droppedItems).length > 0 && Object.keys(droppedItems).forEach(key => {
// activeRecipientTagsCount = (droppedItems[key] && droppedItems[key].length) > 0 ? droppedItems[key].reduce((acc, item) => !item.isDeleted && item.recipientIndx == selectedRecipientIndx ? acc + 1 : acc, 0) : 0;
// if (activeRecipientTagsCount == 0) return;
// const [documentId, pageIndex] = key.split("_");
// if (!documentPagesTagsMappingLocal[documentId]) {
// documentPagesTagsMappingLocal[documentId] = {};
// }
// documentPagesTagsMappingLocal.push({ [documentId]: { [pageIndex]: [activeRecipientTagsCount, selectedRecipientColor] } })
// })
// })
// setDocumentPagesTagsMapping(documentPagesTagsMappingLocal)
// }, 300)
// // }, [droppedItems, selectedRecipientIndx])
// }, [droppedItems])
useEffect(() => {
setTimeout(() => {
let documentTagsMappingLocal: DocumentTagsMapping = {};
recipients &&
recipients.length &&
recipients.forEach((selectedRecipient, selectedRecipientIndx) => {
const selectedRecipientColor = selectedRecipient.color;
droppedItems &&
Object.keys(droppedItems).length > 0 &&
Object.keys(droppedItems).forEach(key => {
const activeRecipientTagsCount =
(droppedItems[key] && droppedItems[key].length) > 0
? droppedItems[key].reduce((acc, item) => {
return !item.isDeleted &&
item.recipientIndx == selectedRecipientIndx
? acc + 1
: acc;
}, 0)
: 0;
if (activeRecipientTagsCount === 0) return;
const [documentId, pageIndex] = key.split("_");
// Create document object
if (!documentTagsMappingLocal[documentId]) {
documentTagsMappingLocal[documentId] = {};
}
// Create page array
if (!documentTagsMappingLocal[documentId][pageIndex]) {
documentTagsMappingLocal[documentId][pageIndex] = [];
}
// Push recipient info
documentTagsMappingLocal[documentId][pageIndex].push({
color: selectedRecipientColor,
recipientIndex: selectedRecipientIndx,
recipientTagCount: activeRecipientTagsCount,
});
});
});
setDocumentPagesTagsMapping(documentTagsMappingLocal);
}, 300);
}, [droppedItems]);
return (
<div className="col-span-2 flex flex-col w-full overflow-y-auto">
{selectedDocuments.map((document, indx) => (
<div key={indx} className="border-b border-slate-200 pb-3 mb-1">
{/* Section label (if any) */}
{document.section && (
<p className="text-[10px] font-semibold uppercase tracking-wide text-slate-400 px-3 pt-2 pb-1">
{document.section}
</p>
)}
{/* Document Header */}
<div
onClick={() => toggleDoc(document.id)}
className="flex items-start justify-between px-3 py-2.5 cursor-pointer hover:bg-slate-50 transition-colors"
>
<div>
<p className="text-[13px] font-medium text-slate-800 leading-tight">{document.name}</p>
<p className="text-[11px] text-slate-400 mt-0.5">
{document.pages?.length} {document.pages?.length === 1 ? 'page' : 'pages'}
</p>
</div>
<MdKeyboardArrowUp
size={20}
className={`text-slate-400 mt-0.5 flex-shrink-0 transition-transform duration-200 ${openDocs[document.id] ? '' : 'rotate-180'}`}
/>
</div>
{/* Page Thumbnails */}
{openDocs[document.id] && (
<div className="flex flex-col px-2">
{document.pages?.map((_: any, pageIndex: number) => {
const key = `${document.id}_${pageIndex}`
const isActive = activePage === key
return (
<div
key={pageIndex}
onClick={() => goToPage(document.id, pageIndex)}
className="flex flex-col items-center py-2 px-1 rounded-md cursor-pointer hover:bg-slate-50 transition-colors"
>
{/* Big Thumbnail */}
<div className={`relative sm:p-2 lg:p-4 bg-slate-100 rounded border flex items-center justify-center overflow-hidden
${isActive ? 'border-blue-500 border-2' : 'border-slate-200'}`}
>
{document.thumbnail ? (
<img
src={"data:image/png;base64," + document.thumbnail}
alt={`Page ${pageIndex + 1}`}
className="w-[140px] h-[180px] h-full object-cover"
/>
) : (
<div className="text-slate-300 text-4xl">Document</div>
)}
{/* Page number + dots menu */}
<div className="absolute bottom-0 bg-slate-800 flex items-center justify-between w-full pl-4 pr-2 py-3 bg-white">
<span className="text-[13px] text-slate-500">{pageIndex + 1}</span>
{
// <>
// <button
// onClick={(e) => { e.stopPropagation(); /* open context menu */ }}
// className="text-slate-400 hover:text-slate-600 hover:bg-slate-100 rounded p-0.5"
// >
// <MdMoreVert size={18} />
// </button>
// </>
}
</div>
<div className='absolute top-4 left-2 h-[80%] overflow-y-auto overflow-x-hidden'>
{documentPagesTagsMapping[document.id]?.[pageIndex]?.length > 0 && (
<>
{documentPagesTagsMapping[document.id][pageIndex].map((tagInfo, indx) => (
<div key={indx} className='relative'>
{/* Count */}
{/* Optional */}
{/* <span className='absolute text-white text-[9px] font-medium top-[50%] left-[50%] transform -translate-x-1/2 -translate-y-1/2 pointer-events-none select-none z-10'>
{tagInfo.recipientTagCount}
</span> */}
{/* Tag */}
<PiTagSimpleFill
style={{
fontSize: '1.2rem',
color: tagInfo.color,
}}
/>
</div>
))}
</>
)}
</div>
</div>
</div>
)
})}
</div>
)}
</div>
))}
</div>
)
}
export default DocumentPages

View File

@ -19,6 +19,8 @@ import TextValueFIll from "./attributes/TextValueFIll";
import CurrTimeStampAttr from "./attributes/CurrTimeStampAttr"; import CurrTimeStampAttr from "./attributes/CurrTimeStampAttr";
import ToolTipAttr from "./attributes/ToolTipAttr"; import ToolTipAttr from "./attributes/ToolTipAttr";
import "./style.css";
export interface DroppedItemProps { export interface DroppedItemProps {
droppedItems: DroppedItem[] droppedItems: DroppedItem[]
selectedTag: DroppedItem selectedTag: DroppedItem
@ -91,7 +93,7 @@ const defaultStyles: StylesInterface = {
textSize: 14 textSize: 14
}; };
const RightSideBar: React.FC<DroppedItemProps> = ( const TagProperties: React.FC<DroppedItemProps> = (
{ {
droppedItems, droppedItems,
selectedTag, selectedTag,
@ -301,7 +303,10 @@ const RightSideBar: React.FC<DroppedItemProps> = (
return ( return (
<> <>
{/* Right Side Bar Start */} {/* Right Side Bar Start */}
<div className='absolute border-l-1 border-t-1 border-slate-300 rounded-tl-lg right-0 top-[48px] sm:rounded-none sm:top-0 sm:border-0 sm:relative sm:col-span-2 sm:w-full h-full bg-white text-sm overflow-y-auto'> <div
tabIndex={0}
data-tag-properties="true"
className='animate-slide-in-right z-50 absolute border-l-1 transition-all duration-600 border-t-1 border-slate-300 rounded-tl-lg right-0 top-[48px] sm:rounded-none sm:top-0 sm:border-0 sm:relative sm:col-span-2 sm:w-full h-full bg-white text-sm overflow-y-auto'>
<div className="flex flex-row-reverse justify-end items-center"> <div className="flex flex-row-reverse justify-end items-center">
<p className="p-2 font-semibold text-md"><span className="capitalize">{selectedTag.type.toLowerCase()}</span></p> <p className="p-2 font-semibold text-md"><span className="capitalize">{selectedTag.type.toLowerCase()}</span></p>
<p className="ml-2.5 cursor-pointer" > <p className="ml-2.5 cursor-pointer" >
@ -485,4 +490,4 @@ const RightSideBar: React.FC<DroppedItemProps> = (
) )
} }
export default RightSideBar export default TagProperties

View File

@ -1,6 +1,6 @@
import { MdKeyboardArrowDown } from 'react-icons/md'; import { MdKeyboardArrowDown } from 'react-icons/md';
import { DROPDOWNTOGGLE, MASKCHARACTER, MASKTYPE, NUMBER, SELECT, TEMPORAL, TEXT } from '../RightSideBar'; import { DROPDOWNTOGGLE, MASKCHARACTER, MASKTYPE, NUMBER, SELECT, TEMPORAL, TEXT } from '../TagProperties';
import { DroppedItem, TagsTypes } from '../../../../../utlity/types'; import { DroppedItem, TagsTypes } from '../../../../../utlity/types';
import { dateFormats, timeFormats } from '../../../../../utlity/dateTimeFormats'; import { dateFormats, timeFormats } from '../../../../../utlity/dateTimeFormats';
import { FaCommentsDollar } from 'react-icons/fa'; import { FaCommentsDollar } from 'react-icons/fa';

View File

@ -1,5 +1,5 @@
import { MdKeyboardArrowDown } from "react-icons/md"; import { MdKeyboardArrowDown } from "react-icons/md";
import { DROPDOWNTOGGLE, MASKCHARACTER, MASKTYPE } from "../RightSideBar"; import { DROPDOWNTOGGLE, MASKCHARACTER, MASKTYPE } from "../TagProperties";
import { LuAsterisk } from "react-icons/lu"; import { LuAsterisk } from "react-icons/lu";
import { GoDotFill } from "react-icons/go"; import { GoDotFill } from "react-icons/go";
import { IoClose } from "react-icons/io5"; import { IoClose } from "react-icons/io5";

View File

@ -1,5 +1,5 @@
import { useState } from "react"; import { useState } from "react";
import { DROPDOWNTOGGLE } from "./RightSideBar"; import { DROPDOWNTOGGLE } from "./TagProperties";
import { DroppedItemsObj } from "../DocumentContainer"; import { DroppedItemsObj } from "../DocumentContainer";
import { IoMdClose } from "react-icons/io"; import { IoMdClose } from "react-icons/io";
import { TagOptionAttrType } from "../../../../utlity/types"; import { TagOptionAttrType } from "../../../../utlity/types";

View File

@ -1,6 +1,6 @@
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { dateFormats, dateTimeFormats, monthFormats, timeFormats, weekFormats, yearFormats } from "../../../../../utlity/dateTimeFormats"; import { dateFormats, dateTimeFormats, monthFormats, timeFormats, weekFormats, yearFormats } from "../../../../../utlity/dateTimeFormats";
import { DROPDOWNTOGGLE, TEMPORAL } from "../RightSideBar"; import { DROPDOWNTOGGLE, TEMPORAL } from "../TagProperties";
import { MdKeyboardArrowDown } from "react-icons/md"; import { MdKeyboardArrowDown } from "react-icons/md";
import dayjs from "dayjs"; import dayjs from "dayjs";
import { DroppedItem } from "../../../../../utlity/types"; import { DroppedItem } from "../../../../../utlity/types";

View File

@ -0,0 +1,16 @@
/* globals.css */
@keyframes slideInRight {
from {
transform: translateX(100%);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
.animate-slide-in-right {
animation: slideInRight 0.3s ease-out forwards;
}

View File

@ -32,30 +32,30 @@ import { FaUser } from 'react-icons/fa';
const typeToIconMap = { const typeToIconMap = {
TEXT: TEXT:
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg width="20" height="20" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M36.6673 18.8333H25.0007C24.5586 18.8333 24.1347 19.0089 23.8221 19.3215C23.5096 19.634 23.334 20.058 23.334 20.5V22.8333C23.334 23.2753 23.5096 23.6993 23.8221 24.0118C24.1347 24.3244 24.5586 24.5 25.0007 24.5C25.4427 24.5 25.8666 24.3244 26.1792 24.0118C26.4917 23.6993 26.6673 23.2753 26.6673 22.8333V22.1666H29.1673V31.6666H27.334C26.892 31.6666 26.468 31.8422 26.1555 32.1548C25.8429 32.4674 25.6673 32.8913 25.6673 33.3333C25.6673 33.7753 25.8429 34.1993 26.1555 34.5118C26.468 34.8244 26.892 35 27.334 35H34.334C34.776 35 35.1999 34.8244 35.5125 34.5118C35.8251 34.1993 36.0007 33.7753 36.0007 33.3333C36.0007 32.8913 35.8251 32.4674 35.5125 32.1548C35.1999 31.8422 34.776 31.6666 34.334 31.6666H32.5007V22.1666H35.0007V22.8333C35.0007 23.2753 35.1762 23.6993 35.4888 24.0118C35.8014 24.3244 36.2253 24.5 36.6673 24.5C37.1093 24.5 37.5333 24.3244 37.8458 24.0118C38.1584 23.6993 38.334 23.2753 38.334 22.8333V20.5C38.334 20.058 38.1584 19.634 37.8458 19.3215C37.5333 19.0089 37.1093 18.8333 36.6673 18.8333Z" fill="#FBC249" /> <path d="M36.6673 18.8333H25.0007C24.5586 18.8333 24.1347 19.0089 23.8221 19.3215C23.5096 19.634 23.334 20.058 23.334 20.5V22.8333C23.334 23.2753 23.5096 23.6993 23.8221 24.0118C24.1347 24.3244 24.5586 24.5 25.0007 24.5C25.4427 24.5 25.8666 24.3244 26.1792 24.0118C26.4917 23.6993 26.6673 23.2753 26.6673 22.8333V22.1666H29.1673V31.6666H27.334C26.892 31.6666 26.468 31.8422 26.1555 32.1548C25.8429 32.4674 25.6673 32.8913 25.6673 33.3333C25.6673 33.7753 25.8429 34.1993 26.1555 34.5118C26.468 34.8244 26.892 35 27.334 35H34.334C34.776 35 35.1999 34.8244 35.5125 34.5118C35.8251 34.1993 36.0007 33.7753 36.0007 33.3333C36.0007 32.8913 35.8251 32.4674 35.5125 32.1548C35.1999 31.8422 34.776 31.6666 34.334 31.6666H32.5007V22.1666H35.0007V22.8333C35.0007 23.2753 35.1762 23.6993 35.4888 24.0118C35.8014 24.3244 36.2253 24.5 36.6673 24.5C37.1093 24.5 37.5333 24.3244 37.8458 24.0118C38.1584 23.6993 38.334 23.2753 38.334 22.8333V20.5C38.334 20.058 38.1584 19.634 37.8458 19.3215C37.5333 19.0089 37.1093 18.8333 36.6673 18.8333Z" fill="#FBC249" />
<path d="M22.5927 31.6667H18.3327V8.33333H28.3327V9.62833C28.3327 10.0704 28.5083 10.4943 28.8208 10.8068C29.1334 11.1194 29.5573 11.295 29.9993 11.295C30.4414 11.295 30.8653 11.1194 31.1779 10.8068C31.4904 10.4943 31.666 10.0704 31.666 9.62833V6.66667C31.666 6.22464 31.4904 5.80072 31.1779 5.48816C30.8653 5.17559 30.4414 5 29.9993 5H3.33268C2.89065 5 2.46673 5.17559 2.15417 5.48816C1.84161 5.80072 1.66602 6.22464 1.66602 6.66667V9.62833C1.66602 10.0704 1.84161 10.4943 2.15417 10.8068C2.46673 11.1194 2.89065 11.295 3.33268 11.295C3.77471 11.295 4.19863 11.1194 4.51119 10.8068C4.82375 10.4943 4.99935 10.0704 4.99935 9.62833V8.33333H14.9993V31.6667H10.7393C10.2973 31.6667 9.8734 31.8423 9.56084 32.1548C9.24828 32.4674 9.07268 32.8913 9.07268 33.3333C9.07268 33.7754 9.24828 34.1993 9.56084 34.5118C9.8734 34.8244 10.2973 35 10.7393 35H22.5927C23.0347 35 23.4586 34.8244 23.7712 34.5118C24.0838 34.1993 24.2593 33.7754 24.2593 33.3333C24.2593 32.8913 24.0838 32.4674 23.7712 32.1548C23.4586 31.8423 23.0347 31.6667 22.5927 31.6667Z" fill="#0D6EFD" /> <path d="M22.5927 31.6667H18.3327V8.33333H28.3327V9.62833C28.3327 10.0704 28.5083 10.4943 28.8208 10.8068C29.1334 11.1194 29.5573 11.295 29.9993 11.295C30.4414 11.295 30.8653 11.1194 31.1779 10.8068C31.4904 10.4943 31.666 10.0704 31.666 9.62833V6.66667C31.666 6.22464 31.4904 5.80072 31.1779 5.48816C30.8653 5.17559 30.4414 5 29.9993 5H3.33268C2.89065 5 2.46673 5.17559 2.15417 5.48816C1.84161 5.80072 1.66602 6.22464 1.66602 6.66667V9.62833C1.66602 10.0704 1.84161 10.4943 2.15417 10.8068C2.46673 11.1194 2.89065 11.295 3.33268 11.295C3.77471 11.295 4.19863 11.1194 4.51119 10.8068C4.82375 10.4943 4.99935 10.0704 4.99935 9.62833V8.33333H14.9993V31.6667H10.7393C10.2973 31.6667 9.8734 31.8423 9.56084 32.1548C9.24828 32.4674 9.07268 32.8913 9.07268 33.3333C9.07268 33.7754 9.24828 34.1993 9.56084 34.5118C9.8734 34.8244 10.2973 35 10.7393 35H22.5927C23.0347 35 23.4586 34.8244 23.7712 34.5118C24.0838 34.1993 24.2593 33.7754 24.2593 33.3333C24.2593 32.8913 24.0838 32.4674 23.7712 32.1548C23.4586 31.8423 23.0347 31.6667 22.5927 31.6667Z" fill="#0D6EFD" />
</svg> </svg>
, ,
SIGNATURE: SIGNATURE:
<svg width="40" height="28" viewBox="0 0 40 28" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg width="20" height="20" viewBox="0 0 40 28" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M24.0801 17.5987C21.5418 19.6325 18.4018 20.647 14.7059 20.647C13.0573 20.647 11.6623 20.2612 10.5509 19.6122C9.23383 20.2583 7.82453 20.647 6.47062 20.647C2.39344 20.647 0 18.453 0 14.7647C0 14.1149 0.52672 13.5882 1.17649 13.5882C1.82625 13.5882 2.35297 14.1149 2.35297 14.7647C2.35297 17.1057 3.64945 18.2941 6.47062 18.2941C7.1664 18.2941 7.89992 18.1431 8.62586 17.8792C7.57188 16.4205 7.22195 14.5556 7.68218 12.7146C8.42484 9.74382 10.361 7.70585 12.9412 7.70585C16.0278 7.70585 17.2912 11.6365 15.7581 14.7026C15.151 15.9167 14.1543 17.0938 12.9456 18.0758C13.4687 18.2168 14.0559 18.2941 14.7059 18.2941C17.8277 18.2941 20.4095 17.4789 22.4945 15.8528L24.1591 9.74928C24.2129 9.55178 24.3174 9.3717 24.4622 9.22694L33.2857 0.403428C33.7451 -0.0560254 34.49 -0.0560254 34.9495 0.403428C35.4089 0.862881 35.4089 1.6078 34.9495 2.06718L27.3366 9.68007C27.98 10.045 28.5577 10.4822 29.0671 10.9917C29.5766 11.5012 30.0138 12.0789 30.3787 12.7222L37.9916 5.10936C38.451 4.64991 39.1959 4.64991 39.6554 5.10936C40.1148 5.56874 40.1148 6.31366 39.6554 6.77311L30.8319 15.5966C30.6871 15.7414 30.507 15.8458 30.3095 15.8997L24.0801 17.5987ZM26.0741 11.6702L25.2062 14.8526L28.3886 13.9848C28.1148 13.4805 27.7864 13.0384 27.4034 12.6554C27.0204 12.2725 26.5784 11.9441 26.0741 11.6702ZM13.6536 13.6503C14.5119 11.9338 13.9092 10.0588 12.9412 10.0588C11.6096 10.0588 10.4722 11.2561 9.96492 13.2854C9.64672 14.5583 9.92117 15.831 10.7534 16.7749C12.0156 15.9148 13.0857 14.7862 13.6536 13.6503Z" fill="#0D6EFD" /> <path d="M24.0801 17.5987C21.5418 19.6325 18.4018 20.647 14.7059 20.647C13.0573 20.647 11.6623 20.2612 10.5509 19.6122C9.23383 20.2583 7.82453 20.647 6.47062 20.647C2.39344 20.647 0 18.453 0 14.7647C0 14.1149 0.52672 13.5882 1.17649 13.5882C1.82625 13.5882 2.35297 14.1149 2.35297 14.7647C2.35297 17.1057 3.64945 18.2941 6.47062 18.2941C7.1664 18.2941 7.89992 18.1431 8.62586 17.8792C7.57188 16.4205 7.22195 14.5556 7.68218 12.7146C8.42484 9.74382 10.361 7.70585 12.9412 7.70585C16.0278 7.70585 17.2912 11.6365 15.7581 14.7026C15.151 15.9167 14.1543 17.0938 12.9456 18.0758C13.4687 18.2168 14.0559 18.2941 14.7059 18.2941C17.8277 18.2941 20.4095 17.4789 22.4945 15.8528L24.1591 9.74928C24.2129 9.55178 24.3174 9.3717 24.4622 9.22694L33.2857 0.403428C33.7451 -0.0560254 34.49 -0.0560254 34.9495 0.403428C35.4089 0.862881 35.4089 1.6078 34.9495 2.06718L27.3366 9.68007C27.98 10.045 28.5577 10.4822 29.0671 10.9917C29.5766 11.5012 30.0138 12.0789 30.3787 12.7222L37.9916 5.10936C38.451 4.64991 39.1959 4.64991 39.6554 5.10936C40.1148 5.56874 40.1148 6.31366 39.6554 6.77311L30.8319 15.5966C30.6871 15.7414 30.507 15.8458 30.3095 15.8997L24.0801 17.5987ZM26.0741 11.6702L25.2062 14.8526L28.3886 13.9848C28.1148 13.4805 27.7864 13.0384 27.4034 12.6554C27.0204 12.2725 26.5784 11.9441 26.0741 11.6702ZM13.6536 13.6503C14.5119 11.9338 13.9092 10.0588 12.9412 10.0588C11.6096 10.0588 10.4722 11.2561 9.96492 13.2854C9.64672 14.5583 9.92117 15.831 10.7534 16.7749C12.0156 15.9148 13.0857 14.7862 13.6536 13.6503Z" fill="#0D6EFD" />
<path d="M0 26.1765C0 26.8262 0.52672 27.353 1.17649 27.353H21.1765C21.8263 27.353 22.353 26.8262 22.353 26.1765C22.353 25.5267 21.8263 25 21.1765 25H1.17649C0.52672 25 0 25.5267 0 26.1765Z" fill="#FBC249" /> <path d="M0 26.1765C0 26.8262 0.52672 27.353 1.17649 27.353H21.1765C21.8263 27.353 22.353 26.8262 22.353 26.1765C22.353 25.5267 21.8263 25 21.1765 25H1.17649C0.52672 25 0 25.5267 0 26.1765Z" fill="#FBC249" />
</svg> </svg>
, ,
INITIALS: <svg width="39" height="36" viewBox="0 0 39 36" fill="none" xmlns="http://www.w3.org/2000/svg"> INITIALS: <svg width="20" height="20" viewBox="0 0 39 36" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M5.91844 27.3C4.31844 27.3 3.10844 26.85 2.28844 25.95C1.46844 25.03 1.05844 23.72 1.05844 22.02V20.82H4.17844V22.26C4.17844 23.62 4.74844 24.3 5.88844 24.3C6.44844 24.3 6.86844 24.14 7.14844 23.82C7.44844 23.48 7.59844 22.94 7.59844 22.2C7.59844 21.32 7.39844 20.55 6.99844 19.89C6.59844 19.21 5.85844 18.4 4.77844 17.46C3.41844 16.26 2.46844 15.18 1.92844 14.22C1.38844 13.24 1.11844 12.14 1.11844 10.92C1.11844 9.26 1.53844 7.98 2.37844 7.08C3.21844 6.16 4.43844 5.7 6.03844 5.7C7.61844 5.7 8.80844 6.16 9.60844 7.08C10.4284 7.98 10.8384 9.28 10.8384 10.98V11.85H7.71844V10.77C7.71844 10.05 7.57844 9.53 7.29844 9.21C7.01844 8.87 6.60844 8.7 6.06844 8.7C4.96844 8.7 4.41844 9.37 4.41844 10.71C4.41844 11.47 4.61844 12.18 5.01844 12.84C5.43844 13.5 6.18844 14.3 7.26844 15.24C8.64844 16.44 9.59844 17.53 10.1184 18.51C10.6384 19.49 10.8984 20.64 10.8984 21.96C10.8984 23.68 10.4684 25 9.60844 25.92C8.76844 26.84 7.53844 27.3 5.91844 27.3ZM12.7905 6H17.5005L19.6005 21.03H19.6605L21.7605 6H26.4705V27H23.3505V11.1H23.2905L20.8905 27H18.1305L15.7305 11.1H15.6705V27H12.7905V6ZM28.9331 6H37.9331V9H32.2331V14.55H36.7631V17.55H32.2331V24H37.9331V27H28.9331V6Z" fill="#0D6EFD" /> <path d="M5.91844 27.3C4.31844 27.3 3.10844 26.85 2.28844 25.95C1.46844 25.03 1.05844 23.72 1.05844 22.02V20.82H4.17844V22.26C4.17844 23.62 4.74844 24.3 5.88844 24.3C6.44844 24.3 6.86844 24.14 7.14844 23.82C7.44844 23.48 7.59844 22.94 7.59844 22.2C7.59844 21.32 7.39844 20.55 6.99844 19.89C6.59844 19.21 5.85844 18.4 4.77844 17.46C3.41844 16.26 2.46844 15.18 1.92844 14.22C1.38844 13.24 1.11844 12.14 1.11844 10.92C1.11844 9.26 1.53844 7.98 2.37844 7.08C3.21844 6.16 4.43844 5.7 6.03844 5.7C7.61844 5.7 8.80844 6.16 9.60844 7.08C10.4284 7.98 10.8384 9.28 10.8384 10.98V11.85H7.71844V10.77C7.71844 10.05 7.57844 9.53 7.29844 9.21C7.01844 8.87 6.60844 8.7 6.06844 8.7C4.96844 8.7 4.41844 9.37 4.41844 10.71C4.41844 11.47 4.61844 12.18 5.01844 12.84C5.43844 13.5 6.18844 14.3 7.26844 15.24C8.64844 16.44 9.59844 17.53 10.1184 18.51C10.6384 19.49 10.8984 20.64 10.8984 21.96C10.8984 23.68 10.4684 25 9.60844 25.92C8.76844 26.84 7.53844 27.3 5.91844 27.3ZM12.7905 6H17.5005L19.6005 21.03H19.6605L21.7605 6H26.4705V27H23.3505V11.1H23.2905L20.8905 27H18.1305L15.7305 11.1H15.6705V27H12.7905V6ZM28.9331 6H37.9331V9H32.2331V14.55H36.7631V17.55H32.2331V24H37.9331V27H28.9331V6Z" fill="#0D6EFD" />
<path d="M0.398438 29.4H38.5931V31.8H0.398438V29.4Z" fill="#0D6EFD" /> <path d="M0.398438 29.4H38.5931V31.8H0.398438V29.4Z" fill="#0D6EFD" />
<rect y="29" width="39" height="3" fill="#FBC249" /> <rect y="29" width="39" height="3" fill="#FBC249" />
</svg>, </svg>,
TEMPORAL: TEMPORAL:
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg width="20" height="20" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M13.3333 3.33334V8.33334M26.6667 3.33334V8.33334M5.83333 15.15H34.1667M35 14.1667V28.3333C35 33.3333 32.5 36.6667 26.6667 36.6667H13.3333C7.5 36.6667 5 33.3333 5 28.3333V14.1667C5 9.16668 7.5 5.83334 13.3333 5.83334H26.6667C32.5 5.83334 35 9.16668 35 14.1667Z" stroke="#0D6EFD" stroke-width="2.5" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round" /> <path d="M13.3333 3.33334V8.33334M26.6667 3.33334V8.33334M5.83333 15.15H34.1667M35 14.1667V28.3333C35 33.3333 32.5 36.6667 26.6667 36.6667H13.3333C7.5 36.6667 5 33.3333 5 28.3333V14.1667C5 9.16668 7.5 5.83334 13.3333 5.83334H26.6667C32.5 5.83334 35 9.16668 35 14.1667Z" stroke="#0D6EFD" stroke-width="2.5" stroke-miterlimit="10" stroke-linecap="round" stroke-linejoin="round" />
<path d="M26.1583 22.8333H26.1733M26.1583 27.8333H26.1733M19.9916 22.8333H20.0083M19.9916 27.8333H20.0083M13.8233 22.8333H13.84M13.8233 27.8333H13.84" stroke="#FBC249" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" /> <path d="M26.1583 22.8333H26.1733M26.1583 27.8333H26.1733M19.9916 22.8333H20.0083M19.9916 27.8333H20.0083M13.8233 22.8333H13.84M13.8233 27.8333H13.84" stroke="#FBC249" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" />
</svg> </svg>
, ,
NUMBER: NUMBER:
<svg width="37" height="37" viewBox="0 0 37 37" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg width="20" height="20" viewBox="0 0 37 37" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M30.6157 2L21.8867 34.5769" stroke="#FBC249" stroke-width="3.38462" stroke-linecap="round" /> <path d="M30.6157 2L21.8867 34.5769" stroke="#FBC249" stroke-width="3.38462" stroke-linecap="round" />
<path d="M15.6528 2L6.92383 34.5769" stroke="#FBC249" stroke-width="3.38462" stroke-linecap="round" /> <path d="M15.6528 2L6.92383 34.5769" stroke="#FBC249" stroke-width="3.38462" stroke-linecap="round" />
<path d="M34.9991 13H3.69141" stroke="#0D6EFD" stroke-width="3.38462" stroke-linecap="round" /> <path d="M34.9991 13H3.69141" stroke="#0D6EFD" stroke-width="3.38462" stroke-linecap="round" />
@ -63,7 +63,7 @@ const typeToIconMap = {
</svg> </svg>
, ,
IMAGE: <FaRegImage />, IMAGE: <FaRegImage />,
STAMP: <svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg"> STAMP: <svg width="20" height="20" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_575_574)"> <g clip-path="url(#clip0_575_574)">
<mask id="mask0_575_574" maskUnits="userSpaceOnUse" x="0" y="0" width="40" height="40"> <mask id="mask0_575_574" maskUnits="userSpaceOnUse" x="0" y="0" width="40" height="40">
<path d="M0 3.8147e-06H40V40H0V3.8147e-06Z" fill="white" /> <path d="M0 3.8147e-06H40V40H0V3.8147e-06Z" fill="white" />
@ -82,13 +82,13 @@ const typeToIconMap = {
</svg> </svg>
, ,
CHECKBOX: CHECKBOX:
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg width="20" height="20" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M18.3341 26.25C18.1699 26.2504 18.0072 26.2182 17.8556 26.1553C17.704 26.0923 17.5664 25.9999 17.4507 25.8833L12.4507 20.8833C12.2299 20.6464 12.1097 20.333 12.1154 20.0091C12.1211 19.6853 12.2523 19.3763 12.4814 19.1473C12.7104 18.9183 13.0193 18.7871 13.3432 18.7814C13.667 18.7757 13.9804 18.8959 14.2174 19.1167L18.3341 23.2317L27.4507 14.1167C27.6877 13.8959 28.0011 13.7757 28.3249 13.7814C28.6488 13.7871 28.9577 13.9183 29.1868 14.1473C29.4158 14.3763 29.547 14.6853 29.5527 15.0091C29.5584 15.333 29.4382 15.6464 29.2174 15.8833L19.2174 25.8833C19.1018 25.9999 18.9641 26.0923 18.8125 26.1553C18.6609 26.2182 18.4982 26.2504 18.3341 26.25Z" fill="#FBC249" /> <path d="M18.3341 26.25C18.1699 26.2504 18.0072 26.2182 17.8556 26.1553C17.704 26.0923 17.5664 25.9999 17.4507 25.8833L12.4507 20.8833C12.2299 20.6464 12.1097 20.333 12.1154 20.0091C12.1211 19.6853 12.2523 19.3763 12.4814 19.1473C12.7104 18.9183 13.0193 18.7871 13.3432 18.7814C13.667 18.7757 13.9804 18.8959 14.2174 19.1167L18.3341 23.2317L27.4507 14.1167C27.6877 13.8959 28.0011 13.7757 28.3249 13.7814C28.6488 13.7871 28.9577 13.9183 29.1868 14.1473C29.4158 14.3763 29.547 14.6853 29.5527 15.0091C29.5584 15.333 29.4382 15.6464 29.2174 15.8833L19.2174 25.8833C19.1018 25.9999 18.9641 26.0923 18.8125 26.1553C18.6609 26.2182 18.4982 26.2504 18.3341 26.25Z" fill="#FBC249" />
<path d="M30.0007 37.9167H10.0007C7.90183 37.914 5.88973 37.0791 4.40564 35.595C2.92156 34.1109 2.08663 32.0988 2.08398 30V10C2.08663 7.90119 2.92156 5.88909 4.40564 4.405C5.88973 2.92091 7.90183 2.08599 10.0007 2.08334H30.0007C32.0995 2.08599 34.1116 2.92091 35.5957 4.405C37.0798 5.88909 37.9147 7.90119 37.9173 10V30C37.9147 32.0988 37.0798 34.1109 35.5957 35.595C34.1116 37.0791 32.0995 37.914 30.0007 37.9167ZM10.0007 4.58334C8.5646 4.58511 7.18788 5.15636 6.17244 6.1718C5.157 7.18724 4.58575 8.56396 4.58398 10V30C4.58575 31.4361 5.157 32.8128 6.17244 33.8282C7.18788 34.8437 8.5646 35.4149 10.0007 35.4167H30.0007C31.4367 35.4149 32.8134 34.8437 33.8289 33.8282C34.8443 32.8128 35.4156 31.4361 35.4173 30V10C35.4156 8.56396 34.8443 7.18724 33.8289 6.1718C32.8134 5.15636 31.4367 4.58511 30.0007 4.58334H10.0007Z" fill="#0D6EFD" /> <path d="M30.0007 37.9167H10.0007C7.90183 37.914 5.88973 37.0791 4.40564 35.595C2.92156 34.1109 2.08663 32.0988 2.08398 30V10C2.08663 7.90119 2.92156 5.88909 4.40564 4.405C5.88973 2.92091 7.90183 2.08599 10.0007 2.08334H30.0007C32.0995 2.08599 34.1116 2.92091 35.5957 4.405C37.0798 5.88909 37.9147 7.90119 37.9173 10V30C37.9147 32.0988 37.0798 34.1109 35.5957 35.595C34.1116 37.0791 32.0995 37.914 30.0007 37.9167ZM10.0007 4.58334C8.5646 4.58511 7.18788 5.15636 6.17244 6.1718C5.157 7.18724 4.58575 8.56396 4.58398 10V30C4.58575 31.4361 5.157 32.8128 6.17244 33.8282C7.18788 34.8437 8.5646 35.4149 10.0007 35.4167H30.0007C31.4367 35.4149 32.8134 34.8437 33.8289 33.8282C34.8443 32.8128 35.4156 31.4361 35.4173 30V10C35.4156 8.56396 34.8443 7.18724 33.8289 6.1718C32.8134 5.15636 31.4367 4.58511 30.0007 4.58334H10.0007Z" fill="#0D6EFD" />
</svg> </svg>
, ,
SELECT: SELECT:
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg width="20" height="20" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M20 34.1666C19.6667 34.1666 19.3333 34 19 33.8333L5.66666 23.8333C4.99999 23.3333 4.83332 22.1666 5.33332 21.5C5.83332 20.8333 6.99999 20.6666 7.66666 21.1666L20 30.3333L32.3333 21.1666C33 20.6666 34.1667 20.8333 34.6667 21.5C35.1667 22.1666 35 23.3333 34.3333 23.8333L21 33.8333C20.6667 34 20.3333 34.1666 20 34.1666Z" fill="#0D6EFD" /> <path d="M20 34.1666C19.6667 34.1666 19.3333 34 19 33.8333L5.66666 23.8333C4.99999 23.3333 4.83332 22.1666 5.33332 21.5C5.83332 20.8333 6.99999 20.6666 7.66666 21.1666L20 30.3333L32.3333 21.1666C33 20.6666 34.1667 20.8333 34.6667 21.5C35.1667 22.1666 35 23.3333 34.3333 23.8333L21 33.8333C20.6667 34 20.3333 34.1666 20 34.1666Z" fill="#0D6EFD" />
<path d="M33.3333 9.16665H6.66667C5.66667 9.16665 5 8.49998 5 7.49998C5 6.49998 5.66667 5.83331 6.66667 5.83331H33.3333C34.3333 5.83331 35 6.49998 35 7.49998C35 8.49998 34.3333 9.16665 33.3333 9.16665Z" fill="#FBC249" /> <path d="M33.3333 9.16665H6.66667C5.66667 9.16665 5 8.49998 5 7.49998C5 6.49998 5.66667 5.83331 6.66667 5.83331H33.3333C34.3333 5.83331 35 6.49998 35 7.49998C35 8.49998 34.3333 9.16665 33.3333 9.16665Z" fill="#FBC249" />
<path d="M33.3333 15.8333H6.66667C5.66667 15.8333 5 15.1667 5 14.1667C5 13.1667 5.66667 12.5 6.66667 12.5H33.3333C34.3333 12.5 35 13.1667 35 14.1667C35 15.1667 34.3333 15.8333 33.3333 15.8333Z" fill="#FBC249" /> <path d="M33.3333 15.8333H6.66667C5.66667 15.8333 5 15.1667 5 14.1667C5 13.1667 5.66667 12.5 6.66667 12.5H33.3333C34.3333 12.5 35 13.1667 35 14.1667C35 15.1667 34.3333 15.8333 33.3333 15.8333Z" fill="#FBC249" />
@ -96,31 +96,31 @@ const typeToIconMap = {
, ,
FILE: <FaFileAlt />, FILE: <FaFileAlt />,
ATTACHMENT: ATTACHMENT:
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg width="20" height="20" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M20.3329 19.6667L17.9829 22.0167C16.6829 23.3167 16.6829 25.4333 17.9829 26.7333C19.2829 28.0333 21.3996 28.0333 22.6996 26.7333L26.3996 23.0333C27.6482 21.7811 28.3493 20.085 28.3493 18.3167C28.3493 16.5484 27.6482 14.8522 26.3996 13.6C25.1474 12.3514 23.4513 11.6503 21.6829 11.6503C19.9146 11.6503 18.2185 12.3514 16.9663 13.6L12.9329 17.6333C12.4019 18.1639 11.9806 18.794 11.6932 19.4875C11.4058 20.181 11.2578 20.9243 11.2578 21.675C11.2578 22.4257 11.4058 23.169 11.6932 23.8625C11.9806 24.556 12.4019 25.1861 12.9329 25.7167" stroke="#FBC249" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" /> <path d="M20.3329 19.6667L17.9829 22.0167C16.6829 23.3167 16.6829 25.4333 17.9829 26.7333C19.2829 28.0333 21.3996 28.0333 22.6996 26.7333L26.3996 23.0333C27.6482 21.7811 28.3493 20.085 28.3493 18.3167C28.3493 16.5484 27.6482 14.8522 26.3996 13.6C25.1474 12.3514 23.4513 11.6503 21.6829 11.6503C19.9146 11.6503 18.2185 12.3514 16.9663 13.6L12.9329 17.6333C12.4019 18.1639 11.9806 18.794 11.6932 19.4875C11.4058 20.181 11.2578 20.9243 11.2578 21.675C11.2578 22.4257 11.4058 23.169 11.6932 23.8625C11.9806 24.556 12.4019 25.1861 12.9329 25.7167" stroke="#FBC249" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" />
<path d="M15.0007 36.6667H25.0007C33.334 36.6667 36.6673 33.3333 36.6673 25V15C36.6673 6.66668 33.334 3.33334 25.0007 3.33334H15.0007C6.66732 3.33334 3.33398 6.66668 3.33398 15V25C3.33398 33.3333 6.66732 36.6667 15.0007 36.6667Z" stroke="#0D6EFD" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" /> <path d="M15.0007 36.6667H25.0007C33.334 36.6667 36.6673 33.3333 36.6673 25V15C36.6673 6.66668 33.334 3.33334 25.0007 3.33334H15.0007C6.66732 3.33334 3.33398 6.66668 3.33398 15V25C3.33398 33.3333 6.66732 36.6667 15.0007 36.6667Z" stroke="#0D6EFD" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" />
</svg> </svg>
, ,
RADIO: RADIO:
<svg width="36" height="36" viewBox="0 0 36 36" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg width="20" height="20" viewBox="0 0 36 36" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M18 1.125C27.3139 1.125 34.875 8.68612 34.875 18C34.875 27.3139 27.3139 34.875 18 34.875C8.68612 34.875 1.125 27.3139 1.125 18C1.125 8.68612 8.68612 1.125 18 1.125ZM18 3.375C9.92812 3.375 3.375 9.92812 3.375 18C3.375 26.0719 9.92812 32.625 18 32.625C26.0719 32.625 32.625 26.0719 32.625 18C32.625 9.92812 26.0719 3.375 18 3.375Z" fill="#0D6EFD" /> <path fill-rule="evenodd" clip-rule="evenodd" d="M18 1.125C27.3139 1.125 34.875 8.68612 34.875 18C34.875 27.3139 27.3139 34.875 18 34.875C8.68612 34.875 1.125 27.3139 1.125 18C1.125 8.68612 8.68612 1.125 18 1.125ZM18 3.375C9.92812 3.375 3.375 9.92812 3.375 18C3.375 26.0719 9.92812 32.625 18 32.625C26.0719 32.625 32.625 26.0719 32.625 18C32.625 9.92812 26.0719 3.375 18 3.375Z" fill="#0D6EFD" />
<path d="M27 18C27 13.0331 22.9669 9 18 9C13.0331 9 9 13.0331 9 18C9 22.9669 13.0331 27 18 27C22.9669 27 27 22.9669 27 18Z" fill="#FBC249" /> <path d="M27 18C27 13.0331 22.9669 9 18 9C13.0331 9 9 13.0331 9 18C9 22.9669 13.0331 27 18 27C22.9669 27 27 22.9669 27 18Z" fill="#FBC249" />
</svg> </svg>
, ,
EMAIL: EMAIL:
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg width="20" height="20" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M13.8903 4.5H26.1098C28.845 4.49996 31.0496 4.49992 32.7836 4.73304C34.5838 4.97508 36.0994 5.49286 37.3034 6.6967C38.5072 7.90054 39.025 9.41628 39.267 11.2165C39.5 12.9504 39.5 15.1551 39.5 17.8903V22.1098C39.5 24.845 39.5 27.0496 39.267 28.7836C39.025 30.5838 38.5072 32.0994 37.3034 33.3034C36.0994 34.5072 34.5838 35.025 32.7836 35.267C31.0496 35.5 28.845 35.5 26.1098 35.5H13.8903C11.1551 35.5 8.95044 35.5 7.2165 35.267C5.41628 35.025 3.90054 34.5072 2.6967 33.3034C1.49287 32.0994 0.975079 30.5838 0.733047 28.7836C0.499925 27.0496 0.499959 24.845 0.500001 22.1098V17.8903C0.499959 15.1551 0.499925 12.9504 0.733047 11.2165C0.975079 9.41628 1.49287 7.90054 2.6967 6.6967C3.90054 5.49286 5.41628 4.97508 7.2165 4.73304C8.95044 4.49992 11.1551 4.49996 13.8903 4.5ZM7.61624 7.7063C6.1487 7.9036 5.37154 8.2645 4.81802 8.81802C4.2645 9.37154 3.9036 10.1487 3.7063 11.6162C3.50318 13.1269 3.5 15.1292 3.5 18V22C3.5 24.8708 3.50318 26.873 3.7063 28.3838C3.9036 29.8514 4.2645 30.6284 4.81802 31.182C5.37154 31.7356 6.1487 32.0964 7.61624 32.2938C9.12694 32.4968 11.1292 32.5 14 32.5H26C28.8708 32.5 30.873 32.4968 32.3838 32.2938C33.8514 32.0964 34.6284 31.7356 35.182 31.182C35.7356 30.6284 36.0964 29.8514 36.2938 28.3838C36.4968 26.873 36.5 24.8708 36.5 22V18C36.5 15.1292 36.4968 13.1269 36.2938 11.6162C36.0964 10.1487 35.7356 9.37154 35.182 8.81802C34.6284 8.2645 33.8514 7.9036 32.3838 7.7063C30.873 7.50318 28.8708 7.5 26 7.5H14C11.1292 7.5 9.12694 7.50318 7.61624 7.7063Z" fill="#0D6EFD" /> <path fill-rule="evenodd" clip-rule="evenodd" d="M13.8903 4.5H26.1098C28.845 4.49996 31.0496 4.49992 32.7836 4.73304C34.5838 4.97508 36.0994 5.49286 37.3034 6.6967C38.5072 7.90054 39.025 9.41628 39.267 11.2165C39.5 12.9504 39.5 15.1551 39.5 17.8903V22.1098C39.5 24.845 39.5 27.0496 39.267 28.7836C39.025 30.5838 38.5072 32.0994 37.3034 33.3034C36.0994 34.5072 34.5838 35.025 32.7836 35.267C31.0496 35.5 28.845 35.5 26.1098 35.5H13.8903C11.1551 35.5 8.95044 35.5 7.2165 35.267C5.41628 35.025 3.90054 34.5072 2.6967 33.3034C1.49287 32.0994 0.975079 30.5838 0.733047 28.7836C0.499925 27.0496 0.499959 24.845 0.500001 22.1098V17.8903C0.499959 15.1551 0.499925 12.9504 0.733047 11.2165C0.975079 9.41628 1.49287 7.90054 2.6967 6.6967C3.90054 5.49286 5.41628 4.97508 7.2165 4.73304C8.95044 4.49992 11.1551 4.49996 13.8903 4.5ZM7.61624 7.7063C6.1487 7.9036 5.37154 8.2645 4.81802 8.81802C4.2645 9.37154 3.9036 10.1487 3.7063 11.6162C3.50318 13.1269 3.5 15.1292 3.5 18V22C3.5 24.8708 3.50318 26.873 3.7063 28.3838C3.9036 29.8514 4.2645 30.6284 4.81802 31.182C5.37154 31.7356 6.1487 32.0964 7.61624 32.2938C9.12694 32.4968 11.1292 32.5 14 32.5H26C28.8708 32.5 30.873 32.4968 32.3838 32.2938C33.8514 32.0964 34.6284 31.7356 35.182 31.182C35.7356 30.6284 36.0964 29.8514 36.2938 28.3838C36.4968 26.873 36.5 24.8708 36.5 22V18C36.5 15.1292 36.4968 13.1269 36.2938 11.6162C36.0964 10.1487 35.7356 9.37154 35.182 8.81802C34.6284 8.2645 33.8514 7.9036 32.3838 7.7063C30.873 7.50318 28.8708 7.5 26 7.5H14C11.1292 7.5 9.12694 7.50318 7.61624 7.7063Z" fill="#0D6EFD" />
<path fill-rule="evenodd" clip-rule="evenodd" d="M6.75183 13.168C7.21135 12.4787 8.14267 12.2924 8.83195 12.7519L14.1756 16.3143C15.816 17.4079 16.9479 18.1601 17.8927 18.6516C18.8049 19.1261 19.4187 19.2889 19.9999 19.2889C20.5811 19.2889 21.1949 19.1261 22.1071 18.6516C23.0519 18.1601 24.1839 17.4079 25.8243 16.3143L31.1679 12.7519C31.8571 12.2924 32.7885 12.4787 33.2479 13.168C33.7075 13.8572 33.5213 14.7886 32.8319 15.2481L27.4191 18.8567C25.8647 19.893 24.5979 20.7374 23.4917 21.313C22.3365 21.914 21.2353 22.289 19.9999 22.289C18.7645 22.289 17.6634 21.914 16.5082 21.313C15.4019 20.7374 14.1351 19.893 12.5807 18.8566L7.16785 15.2481C6.47857 14.7886 6.29231 13.8572 6.75183 13.168Z" fill="#FBC249" /> <path fill-rule="evenodd" clip-rule="evenodd" d="M6.75183 13.168C7.21135 12.4787 8.14267 12.2924 8.83195 12.7519L14.1756 16.3143C15.816 17.4079 16.9479 18.1601 17.8927 18.6516C18.8049 19.1261 19.4187 19.2889 19.9999 19.2889C20.5811 19.2889 21.1949 19.1261 22.1071 18.6516C23.0519 18.1601 24.1839 17.4079 25.8243 16.3143L31.1679 12.7519C31.8571 12.2924 32.7885 12.4787 33.2479 13.168C33.7075 13.8572 33.5213 14.7886 32.8319 15.2481L27.4191 18.8567C25.8647 19.893 24.5979 20.7374 23.4917 21.313C22.3365 21.914 21.2353 22.289 19.9999 22.289C18.7645 22.289 17.6634 21.914 16.5082 21.313C15.4019 20.7374 14.1351 19.893 12.5807 18.8566L7.16785 15.2481C6.47857 14.7886 6.29231 13.8572 6.75183 13.168Z" fill="#FBC249" />
</svg> </svg>
, ,
NAME: NAME:
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg width="20" height="20" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M34.3169 36.6667C34.3169 30.2167 27.9003 25 20.0003 25C12.1003 25 5.68359 30.2167 5.68359 36.6667" stroke="#0D6EFD" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" /> <path d="M34.3169 36.6667C34.3169 30.2167 27.9003 25 20.0003 25C12.1003 25 5.68359 30.2167 5.68359 36.6667" stroke="#0D6EFD" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" />
<path d="M25.8919 17.5592C24.3291 19.122 22.2095 20 19.9993 20C17.7892 20 15.6696 19.122 14.1068 17.5592C12.544 15.9964 11.666 13.8768 11.666 11.6666C11.666 9.45651 12.544 7.33689 14.1068 5.77409C15.6696 4.21129 17.7892 3.33331 19.9993 3.33331C22.2095 3.33331 24.3291 4.21129 25.8919 5.77409C27.4547 7.33689 28.3327 9.45651 28.3327 11.6666C28.3327 13.8768 27.4547 15.9964 25.8919 17.5592Z" stroke="#FBC249" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" /> <path d="M25.8919 17.5592C24.3291 19.122 22.2095 20 19.9993 20C17.7892 20 15.6696 19.122 14.1068 17.5592C12.544 15.9964 11.666 13.8768 11.666 11.6666C11.666 9.45651 12.544 7.33689 14.1068 5.77409C15.6696 4.21129 17.7892 3.33331 19.9993 3.33331C22.2095 3.33331 24.3291 4.21129 25.8919 5.77409C27.4547 7.33689 28.3327 9.45651 28.3327 11.6666C28.3327 13.8768 27.4547 15.9964 25.8919 17.5592Z" stroke="#FBC249" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" />
</svg> </svg>
, ,
COMPANY: COMPANY:
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg"> <svg width="20" height="20" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M37.5 36.25H36.25V5C36.25 2.9375 34.5625 1.25 32.5 1.25H17.5C15.4375 1.25 13.75 2.9375 13.75 5V7.5H7.5C5.4375 7.5 3.75 9.1875 3.75 11.25V36.25H2.5C1.8125 36.25 1.25 36.8125 1.25 37.5C1.25 38.1875 1.8125 38.75 2.5 38.75H37.5C38.1875 38.75 38.75 38.1875 38.75 37.5C38.75 36.8125 38.1875 36.25 37.5 36.25ZM26.25 33.75H33.75V36.25H26.25V33.75ZM16.25 5C16.25 4.3125 16.8125 3.75 17.5 3.75H32.5C33.1875 3.75 33.75 4.3125 33.75 5V31.25H26.25V11.25C26.25 9.1875 24.5625 7.5 22.5 7.5H16.25V5ZM6.25 11.25C6.25 10.5625 6.8125 10 7.5 10H22.5C23.1875 10 23.75 10.5625 23.75 11.25V36.25H20V29.375C20.6875 29.375 21.25 28.8125 21.25 28.125C21.25 27.4375 20.6875 26.875 20 26.875H10C9.3125 26.875 8.75 27.4375 8.75 28.125C8.75 28.8125 9.3125 29.375 10 29.375V36.25H6.25V11.25ZM17.5 29.375V36.25H12.5V29.375H17.5Z" fill="#0D6EFD" /> <path d="M37.5 36.25H36.25V5C36.25 2.9375 34.5625 1.25 32.5 1.25H17.5C15.4375 1.25 13.75 2.9375 13.75 5V7.5H7.5C5.4375 7.5 3.75 9.1875 3.75 11.25V36.25H2.5C1.8125 36.25 1.25 36.8125 1.25 37.5C1.25 38.1875 1.8125 38.75 2.5 38.75H37.5C38.1875 38.75 38.75 38.1875 38.75 37.5C38.75 36.8125 38.1875 36.25 37.5 36.25ZM26.25 33.75H33.75V36.25H26.25V33.75ZM16.25 5C16.25 4.3125 16.8125 3.75 17.5 3.75H32.5C33.1875 3.75 33.75 4.3125 33.75 5V31.25H26.25V11.25C26.25 9.1875 24.5625 7.5 22.5 7.5H16.25V5ZM6.25 11.25C6.25 10.5625 6.8125 10 7.5 10H22.5C23.1875 10 23.75 10.5625 23.75 11.25V36.25H20V29.375C20.6875 29.375 21.25 28.8125 21.25 28.125C21.25 27.4375 20.6875 26.875 20 26.875H10C9.3125 26.875 8.75 27.4375 8.75 28.125C8.75 28.8125 9.3125 29.375 10 29.375V36.25H6.25V11.25ZM17.5 29.375V36.25H12.5V29.375H17.5Z" fill="#0D6EFD" />
<path d="M9.375 14.375H10.0375C10.725 14.375 11.2875 13.8125 11.2875 13.125C11.2875 12.4375 10.725 11.875 10.0375 11.875H9.375C8.6875 11.875 8.125 12.4375 8.125 13.125C8.125 13.8125 8.6875 14.375 9.375 14.375Z" fill="#FBC249" /> <path d="M9.375 14.375H10.0375C10.725 14.375 11.2875 13.8125 11.2875 13.125C11.2875 12.4375 10.725 11.875 10.0375 11.875H9.375C8.6875 11.875 8.125 12.4375 8.125 13.125C8.125 13.8125 8.6875 14.375 9.375 14.375Z" fill="#FBC249" />
<path d="M14.6758 14.375H15.3383C16.0258 14.375 16.5883 13.8125 16.5883 13.125C16.5883 12.4375 16.0258 11.875 15.3383 11.875H14.6758C13.9883 11.875 13.4258 12.4375 13.4258 13.125C13.4258 13.8125 13.9883 14.375 14.6758 14.375Z" fill="#FBC249" /> <path d="M14.6758 14.375H15.3383C16.0258 14.375 16.5883 13.8125 16.5883 13.125C16.5883 12.4375 16.0258 11.875 15.3383 11.875H14.6758C13.9883 11.875 13.4258 12.4375 13.4258 13.125C13.4258 13.8125 13.9883 14.375 14.6758 14.375Z" fill="#FBC249" />

View File

@ -185,7 +185,7 @@ input[type=number] {
/* pointer-events: none; */ /* pointer-events: none; */
} }
.bottom-border>div { .bottom-border>button {
position: absolute; position: absolute;
top: -10px; top: -10px;
left: 50%; left: 50%;
@ -197,7 +197,7 @@ input[type=number] {
border-radius: 5px; border-radius: 5px;
} }
.bottom-border>div:hover { .bottom-border>button:hover {
background-color: var(--primaryLight2); background-color: var(--primaryLight2);
opacity: 0.9; opacity: 0.9;
transition: all 0.3s ease-in-out; transition: all 0.3s ease-in-out;
@ -428,7 +428,7 @@ input[type=number] {
} }
} }
.bottom-border>div:hover { .bottom-border>button:hover {
background-color: var(--primaryLight2); background-color: var(--primaryLight2);
opacity: 0.9; opacity: 0.9;
transition: all 0.3s ease-in-out; transition: all 0.3s ease-in-out;

View File

@ -0,0 +1,185 @@
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
/* TYPES */
export interface Position {
top: number;
left: number;
width: number;
height: number;
}
export interface Mask {
maskType: string;
maskCharacter: string;
}
export interface Tag {
id: string;
label: string;
position: Position;
mask: Mask;
type: string;
subType: string | null;
error: string | null;
regex: string[];
format: string;
documentIndex: number;
pageNumber: number;
signerIndex: number;
style: Record<string, any>;
isDeleted?: boolean;
}
/* PAYLOAD TYPES */
interface AddTagPayload {
recipientId: string;
tag: Tag;
}
interface UpdateTagPayload {
recipientId: string;
tagId: string;
updatedData: Partial<Tag>;
}
interface RemoveTagPayload {
recipientId: string;
tagId: string;
}
interface ReplaceRecipientTagsPayload {
recipientId: string;
tags: Tag[];
}
/* STATE */
/*
STATE STRUCTURE
{
recipientId: {
tagId: Tag
}
}
*/
export interface LocalTagsState {
[recipientId: string]: {
[tagId: string]: Tag;
};
}
const initialState: LocalTagsState = {};
/* SLICE */
const localTagsSlice = createSlice({
name: "localTags",
initialState,
reducers: {
/* ------------------------------ ADD TAG ----------------------------- */
addTag: (state, action: PayloadAction<AddTagPayload>) => {
const { recipientId, tag } = action.payload;
if (!state[recipientId]) {
state[recipientId] = {};
}
state[recipientId][tag.id] = tag;
},
/* ---------------------------- UPDATE TAG ---------------------------- */
updateTag: (state, action: PayloadAction<UpdateTagPayload>) => {
const { recipientId, tagId, updatedData } = action.payload;
if (!state[recipientId]) return;
if (!state[recipientId][tagId]) return;
state[recipientId][tagId] = {
...state[recipientId][tagId],
...updatedData,
};
},
/* ---------------------------- REMOVE TAG ---------------------------- */
removeTag: (state, action: PayloadAction<RemoveTagPayload>) => {
const { recipientId, tagId } = action.payload;
if (!state[recipientId]) return;
delete state[recipientId][tagId];
// Optional cleanup
if (Object.keys(state[recipientId]).length === 0) {
delete state[recipientId];
}
},
/* ------------------------- REPLACE ALL TAGS ------------------------- */
replaceRecipientTags: (
state,
action: PayloadAction<ReplaceRecipientTagsPayload>
) => {
const { recipientId, tags } = action.payload;
state[recipientId] = {};
tags.forEach((tag) => {
state[recipientId][tag.id] = tag;
});
},
/* -------------------------- CLEAR RECIPIENT ------------------------- */
clearRecipientTags: (
state,
action: PayloadAction<string>
) => {
delete state[action.payload];
},
/* ----------------------------- CLEAR ALL ---------------------------- */
clearTags: () => {
return initialState;
},
},
});
/* EXPORTS */
export const {
addTag,
updateTag,
removeTag,
replaceRecipientTags,
clearRecipientTags,
clearTags,
} = localTagsSlice.actions;
export default localTagsSlice.reducer;

View File

@ -1,4 +1,4 @@
import { MASKCHARACTER } from "../components/process/processStepTwo/rightSideBar/RightSideBar"; import { MASKCHARACTER } from "../components/process/processStepTwo/tagProperties/TagProperties";
const type_all = (str: string, changeWith: string): string => { const type_all = (str: string, changeWith: string): string => {
return str.replace(/./g, changeWith); return str.replace(/./g, changeWith);