Merge pull request #518 from OpenSignLabs/send_In_Order

feat: implement of draggable recipient's list to modify recipient's sequence when enable send-in-order during edit document
This commit is contained in:
prafull-opensignlabs
2024-03-22 18:08:34 +05:30
committed by GitHub
11 changed files with 233 additions and 90 deletions
+6
View File
@@ -8,6 +8,7 @@
"name": "open_sign",
"version": "0.1.0",
"dependencies": {
"@formkit/auto-animate": "^0.8.1",
"@radix-ui/themes": "^2.0.3",
"@react-pdf/renderer": "^3.4.0",
"@reduxjs/toolkit": "^2.2.1",
@@ -2984,6 +2985,11 @@
"resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.1.6.tgz",
"integrity": "sha512-OfX7E2oUDYxtBvsuS4e/jSn4Q9Qb6DzgeYtsAdkPZ47znpoNsMgZw0+tVijiv3uGNR6dgNlty6r9rzIzHjtd/A=="
},
"node_modules/@formkit/auto-animate": {
"version": "0.8.1",
"resolved": "https://registry.npmjs.org/@formkit/auto-animate/-/auto-animate-0.8.1.tgz",
"integrity": "sha512-0/Z2cuNXWVVIG/l0SpcHAWFhGdvLJ8DRvEfRWvmojtmRWfEy+LWNwgDazbZqY0qQYtkHcoEK3jBLkhiZaB/4Ig=="
},
"node_modules/@humanwhocodes/config-array": {
"version": "0.11.14",
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz",
+1
View File
@@ -3,6 +3,7 @@
"version": "0.1.0",
"private": true,
"dependencies": {
"@formkit/auto-animate": "^0.8.1",
"@radix-ui/themes": "^2.0.3",
"@react-pdf/renderer": "^3.4.0",
"@reduxjs/toolkit": "^2.2.1",
@@ -280,14 +280,13 @@ function DriveBody(props) {
//component to handle type of document and render according to type
const handleFolderData = (data, ind, listType) => {
let createddate, status, isDecline, signerExist, isComplete, isPlaceholder;
let createddate, status, isDecline, signerExist, isComplete;
if (data.Type !== "Folder") {
const expireDate = data.ExpiryDate && data.ExpiryDate.iso;
const createdDate = data.createdAt && data.createdAt;
createddate = new Date(createdDate).toLocaleDateString();
isComplete = data.IsCompleted && data.IsCompleted ? true : false;
isDecline = data.IsDeclined && data.IsDeclined;
isPlaceholder = data.Placeholders && data.Placeholders;
signerExist = data.Signers && data.Signers;
const signedUrl = data.SignedUrl;
@@ -443,7 +443,7 @@ function PlaceholderType(props) {
>
{/* Default/Title option */}
<option value="" disabled hidden>
{props.pos.options.name}
{props?.pos?.options?.name}
</option>
{props.pos?.options?.values.map((data, ind) => {
+126 -48
View File
@@ -1,36 +1,15 @@
import React, { useRef, useState } from "react";
import { darkenColor, getFirstLetter } from "../../constant/Utils";
import { useAutoAnimate } from "@formkit/auto-animate/react";
import {
color,
darkenColor,
getFirstLetter,
isMobile,
nameColor
} from "../../constant/Utils";
const RecipientList = (props) => {
const color = [
"#93a3db",
"#e6c3db",
"#c0e3bc",
"#bce3db",
"#b8ccdb",
"#ceb8db",
"#ffccff",
"#99ffcc",
"#cc99ff",
"#ffcc99",
"#66ccff",
"#ffffcc"
];
const nameColor = [
"#304fbf",
"#7d5270",
"#5f825b",
"#578077",
"#576e80",
"#6d527d",
"#cc00cc",
"#006666",
"#cc00ff",
"#ff9900",
"#336699",
"#cc9900"
];
const [animationParent] = useAutoAnimate();
const [isHover, setIsHover] = useState();
const [isEdit, setIsEdit] = useState(false);
//function for onhover signer name change background color
@@ -63,27 +42,94 @@ const RecipientList = (props) => {
return props.signerPos.some((x) => x.Id === Id);
};
//handle drag start
const handleDragStart = (e, id) => {
// `e.dataTransfer.setData('text/plain')`is used to set the data to be transferred during a drag operation.
// The first argument specifies the type of data being set, and the second argument is the actual data you want to transfer.
e.dataTransfer.setData("text/plain", id);
};
//handleDragOver prevents the default behavior of the dragover event, which is necessary for the drop event to be triggered.
const handleDragOver = (e) => {
e.preventDefault();
};
//handle draggable element drop and also used in mobile view on up and down key to chnage sequence of recipient's list
const handleChangeSequence = (e, ind, isUp, isDown) => {
e.preventDefault();
let draggedItemId;
let index = ind;
//if isUp true then set item's id and index in mobile view
if (isUp) {
draggedItemId = index;
index = index - 1;
}
//if isDown true then set item's id and index in mobile view
else if (isDown) {
draggedItemId = index;
index = index + 1;
} //else set id on drag element in desktop viiew
else {
//`e.dataTransfer.getData('text/plain')` is used to get data that you have saved.
draggedItemId = e.dataTransfer.getData("text/plain");
}
//convert string to number
const intDragId = parseInt(draggedItemId);
//get that item to change position
const draggedItem = props.signersdata.filter((_, ind) => ind === intDragId);
const remainingItems = props.signersdata.filter(
(_, ind) => ind !== intDragId
);
//splice method is used to replace or add new value in array at specific index
remainingItems.splice(index, 0, ...draggedItem);
//set current draggable recipient details,objectId,index,contract_className ... after replace recipient list
props.setSignersData(remainingItems);
props.setSignerObjId(remainingItems[index]?.objectId || "");
props.setIsSelectId(index);
props.setContractName(remainingItems[index]?.className || "");
props.setUniqueId(remainingItems[index]?.Id);
props.setRoleName(remainingItems[index]?.Role);
props.setBlockColor(remainingItems[index]?.blockColor);
};
return (
<>
{props.signersdata.length > 0 &&
props.signersdata.map((obj, ind) => {
return (
<div
ref={animationParent}
key={ind}
draggable={props.sendInOrder && !isMobile ? true : false}
onDragStart={(e) =>
props.sendInOrder && !isMobile && handleDragStart(e, ind)
}
onDragOver={(e) =>
props.sendInOrder && !isMobile && handleDragOver(e)
}
onDrop={(e) =>
props.sendInOrder && !isMobile && handleChangeSequence(e, ind)
}
data-tut="reactourFirst"
onMouseEnter={() => setIsHover(ind)}
onMouseLeave={() => setIsHover(null)}
key={ind}
className={props.sendInOrder && "dragCursor"}
style={
isHover === ind || props.isSelectListId === ind
? onHoverStyle(ind, obj.blockColor)
(!isMobile && isHover === ind) || props.isSelectListId === ind
? onHoverStyle(ind, obj?.blockColor)
: nonHoverStyle(ind)
}
onClick={() => {
onClick={(e) => {
e.preventDefault();
props.setSignerObjId(obj?.objectId || "");
props.setIsSelectId(ind);
props.setContractName(obj?.className || "");
props.setUniqueId(obj.Id);
props.setRoleName(obj.Role);
props.setBlockColor(obj?.blockColor);
if (props.handleModal) {
props.handleModal();
}
@@ -98,10 +144,9 @@ const RecipientList = (props) => {
}}
>
<div
className="signerStyle"
style={{
background: obj.blockColor
? darkenColor(obj.blockColor, 0.4)
background: obj?.blockColor
? darkenColor(obj?.blockColor, 0.4)
: nameColor[ind % nameColor.length],
width: 30,
height: 30,
@@ -113,6 +158,7 @@ const RecipientList = (props) => {
}}
>
<span
className={props.sendInOrder && "dragCursor"}
style={{
fontSize: "12px",
textAlign: "center",
@@ -140,13 +186,12 @@ const RecipientList = (props) => {
}}
>
{obj.Name ? (
<span className="userName" style={{ cursor: "default" }}>
{obj.Name}
</span>
<span className={"userName"}>{obj.Name}</span>
) : (
<>
<span
className="userName"
style={{ cursor: "pointer" }}
onClick={() => {
setIsEdit({ [obj.Id]: true });
props.setRoleName(obj.Role);
@@ -157,7 +202,8 @@ const RecipientList = (props) => {
ref={inputRef}
style={{
backgroundColor: "transparent",
width: "inherit"
width: "inherit",
padding: "3px"
}}
value={obj.Role}
onChange={(e) => props.handleRoleChange(e, obj.Id)}
@@ -173,25 +219,57 @@ const RecipientList = (props) => {
}}
/>
) : (
obj.Role
<span style={{ padding: "3px" }}>{obj.Role}</span>
)}
</span>
</>
)}
{obj.Name && (
<span className="useEmail" style={{ cursor: "default" }}>
{obj.Role}
</span>
)}
{obj.Name && <span className={"useEmail"}>{obj.Role}</span>}
</div>
</div>
{isMobile && props.sendInOrder && (
<div
style={{
display: "flex",
flexDirection: "row",
alignItems: "center",
gap: 5
}}
>
<div
onClick={(e) => {
if (ind !== 0) {
e.stopPropagation();
handleChangeSequence(e, ind, "up");
}
}}
style={{ color: ind === 0 ? "gray" : "black" }}
>
</div>
<div
onClick={(e) => {
if (ind !== props.signersdata.length - 1) {
e.stopPropagation();
handleChangeSequence(e, ind, null, "down");
}
}}
style={{
color:
ind === props.signersdata.length - 1 ? "gray" : "black"
}}
>
</div>
</div>
)}
{props.handleDeleteUser && (
<div
onClick={(e) => {
e.stopPropagation();
props.handleDeleteUser(obj.Id);
}}
style={{ cursor: "pointer" }}
style={{ cursor: "pointer", marginLeft: "5px" }}
>
<i className="fa-regular fa-trash-can"></i>
</div>
@@ -34,8 +34,11 @@ function WidgetComponent({
handleRoleChange,
handleOnBlur,
title,
isTemplateFlow
setSignersData,
sendInOrder,
isTemplateFlow,
setBlockColor,
blockColor
}) {
const [isSignersModal, setIsSignersModal] = useState(false);
@@ -256,9 +259,11 @@ function WidgetComponent({
<div
data-tut={dataTut}
style={{
background: isSelectListId
? color[isSelectListId % color.length]
: color[0],
background: blockColor
? blockColor
: isSelectListId
? color[isSelectListId % color.length]
: color[0],
padding: "10px 20px",
display: "flex",
alignItems: "center",
@@ -386,20 +391,25 @@ function WidgetComponent({
handleClose={handleModal}
>
{signersdata.length > 0 ? (
<RecipientList
signerPos={signerPos}
signersdata={signersdata}
isSelectListId={isSelectListId}
setSignerObjId={setSignerObjId}
setIsSelectId={setIsSelectId}
setContractName={setContractName}
setUniqueId={setUniqueId}
setRoleName={setRoleName}
handleDeleteUser={handleDeleteUser}
handleRoleChange={handleRoleChange}
handleOnBlur={handleOnBlur}
handleModal={handleModal}
/>
<div style={{ maxHeight: "600px", overflow: "auto" }}>
<RecipientList
signerPos={signerPos}
signersdata={signersdata}
isSelectListId={isSelectListId}
setSignerObjId={setSignerObjId}
setIsSelectId={setIsSelectId}
setContractName={setContractName}
setUniqueId={setUniqueId}
setRoleName={setRoleName}
handleDeleteUser={handleDeleteUser}
handleRoleChange={handleRoleChange}
handleOnBlur={handleOnBlur}
handleModal={handleModal}
sendInOrder={sendInOrder}
setSignersData={setSignersData}
setBlockColor={setBlockColor}
/>
</div>
) : (
<div
style={{
+29
View File
@@ -12,6 +12,35 @@ export const openInNewTab = (url) => {
window.open(url, "_blank", "noopener,noreferrer");
};
export const color = [
"#93a3db",
"#e6c3db",
"#c0e3bc",
"#bce3db",
"#b8ccdb",
"#ceb8db",
"#ffccff",
"#99ffcc",
"#cc99ff",
"#ffcc99",
"#66ccff",
"#ffffcc"
];
export const nameColor = [
"#304fbf",
"#7d5270",
"#5f825b",
"#578077",
"#576e80",
"#6d527d",
"#cc00cc",
"#006666",
"#cc00ff",
"#ff9900",
"#336699",
"#cc9900"
];
export const toDataUrl = (file) => {
return new Promise((resolve) => {
const reader = new FileReader();
+4 -2
View File
@@ -165,7 +165,9 @@ const Forms = (props) => {
"TimeToCompleteDays",
parseInt(formData?.TimeToCompleteDays)
);
const isChecked = formData.SendinOrder === "true" ? true : false;
}
if (props.title !== "Sign Yourself") {
const isChecked = formData.SendinOrder === "false" ? false : true;
object.set("SendinOrder", isChecked);
}
object.set("URL", fileupload);
@@ -235,7 +237,7 @@ const Forms = (props) => {
Description: "",
Note: "Please review and sign this document",
TimeToCompleteDays: 15,
SendinOrder: "false"
SendinOrder: "true"
});
setFileUpload([]);
setpercentage(0);
+16 -17
View File
@@ -27,7 +27,8 @@ import {
addWidgetOptions,
textInputWidget,
textWidget,
radioButtonWidget
radioButtonWidget,
color
} from "../constant/Utils";
import RenderPdf from "../components/pdf/RenderPdf";
import { useNavigate } from "react-router-dom";
@@ -80,6 +81,7 @@ function PlaceHolderSign() {
const [zIndex, setZIndex] = useState(1);
const [signKey, setSignKey] = useState();
const [tempSignerId, setTempSignerId] = useState("");
const [blockColor, setBlockColor] = useState("");
const [pdfLoadFail, setPdfLoadFail] = useState({
status: false,
type: "load"
@@ -106,20 +108,6 @@ function PlaceHolderSign() {
status: false,
message: ""
});
const color = [
"#93a3db",
"#e6c3db",
"#c0e3bc",
"#bce3db",
"#b8ccdb",
"#ceb8db",
"#ffccff",
"#99ffcc",
"#cc99ff",
"#ffcc99",
"#66ccff",
"#ffffcc"
];
const isMobile = window.innerWidth < 767;
const [, drop] = useDrop({
@@ -276,14 +264,17 @@ function PlaceHolderSign() {
});
setSignersData(updatedSigners);
setUniqueId(updatedSigners[0].Id);
setBlockColor(updatedSigners[0].blockColor);
} else {
const updatedSigners = documentData[0].Signers.map((x, index) => ({
...x,
Id: randomId(),
Role: "User " + (index + 1)
Role: "User " + (index + 1),
blockColor: color[index % color.length]
}));
setSignersData(updatedSigners);
setUniqueId(updatedSigners[0].Id);
setBlockColor(updatedSigners[0].blockColor);
}
} else {
setRoleName("User 1");
@@ -303,6 +294,7 @@ function PlaceHolderSign() {
setSignersData(updatedSigners);
setIsSelectId(0);
setUniqueId(updatedSigners[0].Id);
setBlockColor(updatedSigners[0].blockColor);
}
}
} else if (
@@ -1372,7 +1364,6 @@ function PlaceHolderSign() {
const closePopup = () => {
setIsAddUser({});
};
return (
<>
<Title title={state?.title ? state.title : "New Document"} />
@@ -1717,6 +1708,10 @@ function PlaceHolderSign() {
setUniqueId={setUniqueId}
setRoleName={setRoleName}
initial={true}
sendInOrder={pdfDetails[0].SendinOrder}
setSignersData={setSignersData}
blockColor={blockColor}
setBlockColor={setBlockColor}
/>
</div>
) : (
@@ -1735,6 +1730,10 @@ function PlaceHolderSign() {
setContractName={setContractName}
setUniqueId={setUniqueId}
setRoleName={setRoleName}
sendInOrder={pdfDetails[0].SendinOrder}
setSignersData={setSignersData}
blockColor={blockColor}
setBlockColor={setBlockColor}
// handleAddSigner={handleAddSigner}
/>
<div data-tut="reactourSecond">
+16 -1
View File
@@ -77,6 +77,7 @@ const TemplatePlaceholder = () => {
const [showDropdown, setShowDropdown] = useState(false);
const [widgetType, setWidgetType] = useState("");
const [isRadio, setIsRadio] = useState(false);
const [blockColor, setBlockColor] = useState("");
const [selectWidgetId, setSelectWidgetId] = useState("");
const [isNameModal, setIsNameModal] = useState(false);
const [pdfLoadFail, setPdfLoadFail] = useState({
@@ -223,6 +224,7 @@ const TemplatePlaceholder = () => {
});
setSignersData(updatedSigners);
setUniqueId(updatedSigners[0].Id);
setBlockColor(updatedSigners[0].blockColor);
} else {
const updatedSigners = documentData[0].Signers.map((x, index) => ({
...x,
@@ -231,6 +233,7 @@ const TemplatePlaceholder = () => {
}));
setSignersData(updatedSigners);
setUniqueId(updatedSigners[0].Id);
setBlockColor(updatedSigners[0].blockColor);
}
} else {
setRoleName("User 1");
@@ -249,6 +252,7 @@ const TemplatePlaceholder = () => {
setUniqueId(updatedSigners[0].Id);
setSignersData(updatedSigners);
setIsSelectId(0);
setBlockColor(updatedSigners[0].blockColor);
}
}
} else if (
@@ -446,7 +450,7 @@ const TemplatePlaceholder = () => {
setIsCheckbox(true);
} else if (dragTypeValue === radioButtonWidget) {
setIsRadio(true);
}
}
setCurrWidgetsDetails({});
setWidgetType(dragTypeValue);
setSignKey(key);
@@ -854,6 +858,7 @@ const TemplatePlaceholder = () => {
setIsModalRole(false);
setRoleName("");
setUniqueId(Id);
setBlockColor(color[index]);
setIsSelectId(index);
setIsMailSend(false);
};
@@ -870,9 +875,11 @@ const TemplatePlaceholder = () => {
if (index === signersdata.length - 1) {
setUniqueId(updateSigner[updateSigner.length - 1]?.Id || "");
setIsSelectId(index - 1 || 0);
setBlockColor(color[index - 1 || 0]);
} else {
setUniqueId(updateSigner[index]?.Id || "");
setIsSelectId(index);
setBlockColor(color[index]);
}
setSignerPos(updatePlaceholderUser);
@@ -1443,6 +1450,10 @@ const TemplatePlaceholder = () => {
title={"Roles"}
initial={true}
isTemplateFlow={true}
sendInOrder={pdfDetails[0].SendinOrder}
setSignersData={setSignersData}
blockColor={blockColor}
setBlockColor={setBlockColor}
/>
</div>
) : (
@@ -1466,6 +1477,10 @@ const TemplatePlaceholder = () => {
handleRoleChange={handleRoleChange}
handleOnBlur={handleOnBlur}
title={"Roles"}
sendInOrder={pdfDetails[0]?.SendinOrder}
setSignersData={setSignersData}
blockColor={blockColor}
setBlockColor={setBlockColor}
/>
<div data-tut="reactourSecond">
<WidgetComponent
+4
View File
@@ -15,6 +15,10 @@
border-width: 0.2px;
}
.dragCursor {
cursor: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAAsTAAALEwEAmpwYAAAASElEQVR4nGNgwAMkJSUbpKSkOvCpIaT5PxSTbogUQjMYMwxeIIXmVFIxA8UGDDyQGg0DnIDi6JKUlCxHMqCeZAOghjSAMD5FAKfeaURdUFxCAAAAAElFTkSuQmCC"), auto;
}
.dropdownContainer {
display: flex;
flex-direction: column;