Merge pull request #538 from OpenSignLabs/staging

sync branch
This commit is contained in:
prafull-opensignlabs
2024-03-27 11:09:58 +05:30
committed by GitHub
38 changed files with 1146 additions and 500 deletions
+1 -1
View File
@@ -20,7 +20,7 @@ appName=open_sign_server
# A 12 character long random secret key that allows access to all the data. It is used in Parse dashboard config to view all the data in the database.
MASTER_KEY=XnAadwKxxByMr
# Mongodb URI to connect to
MONGODB_URI=mongodb://localhost:27017/OpenSignDB
MONGODB_URI=mongodb://mongo:27017/OpenSignDB
# Path on which APIs should be mounted. Do not change this. This variable shall be removed & value hardcoded in the source code in coming versions.
PARSE_MOUNT=/app
# Set it to the URL from where APIs will be accessible to the NodeJS functions, for local development it should be localhost:3000/api/app (use your local port number instead)
+1 -1
View File
@@ -1,6 +1,6 @@
build:
cp .env.local_dev .env
cd apps/OpenSign && npm install && npm run build
cd apps/OpenSign && cp ../../.env.local_dev .env && npm install && npm run build
docker compose up --build --force-recreate
run:
+1 -1
View File
@@ -21,5 +21,5 @@ EXPOSE 3000
# ENV NODE_ENV production
# Run the application
ENTRYPOINT npm run start-dev
ENTRYPOINT npm run start
+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",
@@ -4,6 +4,7 @@ import Parse from "parse";
import getReplacedHashQuery from "../../constant/getReplacedHashQuery";
import "../../styles/loader.css";
import { useNavigate } from "react-router-dom";
import Tooltip from "../../primitives/Tooltip";
const DashboardCard = (props) => {
const navigate = useNavigate();
@@ -335,7 +336,7 @@ const DashboardCard = (props) => {
props.Data && props.Data.Redirect_type
? "cursor-pointer"
: "cursor-default"
} w-full h-[140px] px-3 pt-4 pb-10 text-white rounded-md shadow overflow-hidden`}
} w-full h-[140px] px-3 pt-4 pb-10 text-white rounded-md shadow overflow-hidden relative`}
>
<div className="flex items-center justify-start gap-5">
<span className="rounded-full bg-black bg-opacity-20 w-[60px] h-[60px] self-start flex justify-center items-center">
@@ -352,6 +353,9 @@ const DashboardCard = (props) => {
</div>
</div>
</div>
<div className="text-xs absolute top-1 right-1">
<Tooltip id={props.Label} iconColor={"white"} message={props?.Data?.tourMessage} />
</div>
</div>
);
};
@@ -6,16 +6,18 @@ const buttonList = [
{
label: "Sign yourself",
redirectId: "sHAnZphf69",
redirectType: "Form"
redirectType: "Form",
icon: "fas fa-pen-nib"
},
{
label: "Request signature",
redirectId: "8mZzFxbG1z",
redirectType: "Form"
redirectType: "Form",
icon: "fa-solid fa-paper-plane"
}
];
const GetDashboard = (props) => {
const Button = ({ label, redirectId, redirectType }) => (
const Button = ({ label, redirectId, redirectType, icon }) => (
<div className={"bg-white rounded-md shadow w-full"}>
<Suspense
fallback={
@@ -33,7 +35,7 @@ const GetDashboard = (props) => {
}
>
<DashboardButton
Icon={"fa-solid fa-plus"}
Icon={icon}
Label={label}
Data={{ Redirect_type: redirectType, Redirect_id: redirectId }}
/>
@@ -136,6 +138,7 @@ const GetDashboard = (props) => {
label={btn.label}
redirectType={btn.redirectType}
redirectId={btn.redirectId}
icon={btn.icon}
/>
))}
</div>
@@ -8,19 +8,11 @@ import { saveAs } from "file-saver";
import { useNavigate } from "react-router-dom";
import Table from "react-bootstrap/Table";
import * as HoverCard from "@radix-ui/react-hover-card";
import SelectFolder from "../shared/fields/SelectFolder"; //check this one
import ModalUi from "../../primitives/ModalUi";
import { themeColor } from "../../constant/const";
import FolderModal from "../shared/fields/FolderModal";
function DriveBody({
pdfData,
setFolderName,
setDocId,
setIsLoading,
setPdfData,
isList,
setIsAlert
}) {
function DriveBody(props) {
const [rename, setRename] = useState("");
const [renameValue, setRenameValue] = useState("");
const inputRef = useRef(null);
@@ -50,28 +42,25 @@ function DriveBody({
name: data.Name,
objectId: data.objectId
};
setFolderName((prev) => [...prev, folderData]);
const loadObj = {
props.setFolderName((prev) => [...prev, folderData]);
props.setIsLoading({
isLoad: true,
message: "This might take some time"
};
setIsLoading(loadObj);
setDocId(data.objectId);
});
props.setDocId(data.objectId);
props.setPdfData([]);
props.setSkip(0);
};
//function for change doc name and update doc name in _document class
const handledRenameDoc = async (data) => {
setRename("");
const trimmedValue = renameValue.trim();
if (trimmedValue.length > 0) {
const updateName = {
Name: renameValue
};
const docId = data.objectId;
const docData = pdfData;
const docData = props.pdfData;
const updatedData = docData.map((item) => {
if (item.objectId === docId) {
// If the item's ID matches the target ID, update the name
@@ -80,8 +69,7 @@ function DriveBody({
// If the item's ID doesn't match, keep it unchanged
return item;
});
setPdfData(updatedData);
props.setPdfData(updatedData);
await axios
.put(
`${localStorage.getItem("baseUrl")}classes/${localStorage.getItem(
@@ -102,7 +90,7 @@ function DriveBody({
})
.catch((err) => {
console.log("Err ", err);
setIsAlert({
props.setIsAlert({
isShow: true,
alertMessage: "something went wrong"
});
@@ -199,13 +187,13 @@ function DriveBody({
.then((result) => {
const res = result.data;
if (res) {
const updatedData = pdfData.filter((x) => x.objectId !== docId);
setPdfData(updatedData);
const updatedData = props.pdfData.filter((x) => x.objectId !== docId);
props.setPdfData(updatedData);
}
})
.catch((err) => {
console.log("Err ", err);
setIsAlert({
props.setIsAlert({
isShow: true,
alertMessage: "something went wrong"
});
@@ -226,8 +214,8 @@ function DriveBody({
? true
: false
: selecFolderId
? false
: true;
? false
: true;
if (!checkExist) {
if (moveFolderId) {
updateData = {
@@ -262,10 +250,10 @@ function DriveBody({
// console.log("Listdata ", Listdata);
const res = Listdata.data;
if (res) {
const updatedData = pdfData.filter(
const updatedData = props.pdfData.filter(
(x) => x.objectId !== updateDocId
);
setPdfData(updatedData);
props.setPdfData(updatedData);
}
})
.catch((err) => {
@@ -292,14 +280,13 @@ function DriveBody({
//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;
@@ -314,18 +301,7 @@ function DriveBody({
status = "Completed";
} else if (isDecline) {
status = "Declined";
} else if (!signerExist || signerExist?.length === 0) {
status = "Draft";
} else if (
signerExist?.length > 0 &&
(!isPlaceholder || isPlaceholder?.length === 0)
) {
status = "Draft";
} else if (
signerExist?.length > 0 &&
isPlaceholder?.length > 0 &&
!signedUrl
) {
} else if (!signedUrl) {
status = "Draft";
} else if (isExpire) {
status = "Expired";
@@ -584,7 +560,7 @@ function DriveBody({
//component to handle type of document and render according to type
return (
<>
{isList ? (
{props.isList ? (
<div className="container" style={{ overflowX: "auto" }}>
<Table striped bordered hover>
<thead>
@@ -597,7 +573,7 @@ function DriveBody({
</tr>
</thead>
<tbody>
{pdfData.map((data, ind) => {
{props.pdfData.map((data, ind) => {
return (
<React.Fragment key={ind}>
{handleFolderData(data, ind, "table")}
@@ -609,7 +585,7 @@ function DriveBody({
</div>
) : (
<div className="pdfContainer">
{pdfData.map((data, ind) => {
{props.pdfData.map((data, ind) => {
return (
<div className="box" key={ind}>
{handleFolderData(data, ind, "list")}
@@ -618,13 +594,14 @@ function DriveBody({
})}
</div>
)}
{isOpenMoveModal && (
<SelectFolder
<FolderModal
onSuccess={handleMoveFolder}
isOpenModal={isOpenMoveModal}
folderCls={"contracts_Document"}
setIsOpenMoveModal={setIsOpenMoveModal}
setPdfData={setPdfData}
setPdfData={props.setPdfData}
/>
)}
<ModalUi
@@ -2,6 +2,7 @@ import React, { useEffect, useState } from "react";
import { themeColor } from "../../constant/const";
import ModalUi from "../../primitives/ModalUi";
import { radioButtonWidget } from "../../constant/Utils";
import PremiumAlertHeader from "../../primitives/PremiumAlertHeader";
function DropdownWidgetOption(props) {
const [dropdownOptionList, setDropdownOptionList] = useState([
"option-1",
@@ -144,21 +145,15 @@ function DropdownWidgetOption(props) {
};
return (
//props.showDropdown
<ModalUi
// styleClass={"dropdownModal"}
isOpen={props.showDropdown}
title={props.title}
showClose={false}
>
<div style={{ height: "100%", padding: 20 }}>
<ModalUi isOpen={props.showDropdown} title={props.title} showClose={false}>
<div className="h-full p-[15px]">
<form
onSubmit={(e) => {
e.preventDefault();
handleSaveOption();
}}
>
<div className="dropdownContainer">
<div>
<label style={{ fontSize: "13px", fontWeight: "600" }}>
Name<span style={{ color: "red", fontSize: 13 }}> *</span>
</label>
@@ -383,17 +378,22 @@ function DropdownWidgetOption(props) {
</div>
)}
</div>
{props.type === "checkbox" && !props.isSignYourself && (
<PremiumAlertHeader
message={
"Field validations are free in beta, this feature will incur a fee later."
}
/>
)}
<div
style={{
height: "1px",
backgroundColor: "#9f9f9f",
width: "100%",
marginTop: "15px",
marginBottom: "15px"
}}
className={`${
props.type === "checkbox" && !props.isSignYourself
? "mb-[15px]"
: "my-[15px]"
} w-full h-[1px] bg-[#9f9f9f]`}
></div>
<button
// onClick={() => handleSaveOption()}
disabled={dropdownOptionList.length === 0 && true}
style={{
background: themeColor,
@@ -4,6 +4,7 @@ import PlaceholderBorder from "./PlaceholderBorder";
import { Rnd } from "react-rnd";
import {
defaultWidthHeight,
handleCopyNextToWidget,
isMobile,
onChangeInput,
radioButtonWidget,
@@ -302,8 +303,14 @@ function Placeholder(props) {
props.setWidgetType(props.pos.type);
props.setCurrWidgetsDetails(props.pos);
};
//function ro set state value of onclick on widget's copy icon
//function to set required state value onclick on widget's copy icon
const handleCopyPlaceholder = (e) => {
e.stopPropagation();
//condition to handle text widget signer obj id and unique id
//when user click on copy icon of text widget in that condition text widget does not have any signerObjId
//in that case i have to save in tempSignerId as a unique id of previous select signer's unique id
//and on save or cancel button of copy all page popup i have set this temp signer Id in unique id
if (props.data && props?.pos?.type !== textWidget) {
props.setSignerObjId(props?.data?.signerObjId);
props.setUniqueId(props?.data?.Id);
@@ -312,9 +319,24 @@ function Placeholder(props) {
props.setSignerObjId(props?.data?.signerObjId);
props.setUniqueId(props?.data?.Id);
}
e.stopPropagation();
props.setIsPageCopy(true);
props.setSignKey(props.pos.key);
//checking widget's type and open widget copy modal for required widgets
if (
["signature", textWidget, "stamp", "initial"].includes(props.pos.type)
) {
props.setIsPageCopy(true);
props.setSignKey(props.pos.key);
} else {
//function to create new widget next to just widget
handleCopyNextToWidget(
props.pos,
props.pos.type,
props.xyPostion,
props.index,
props.setXyPostion,
props.data && props.data?.Id
);
}
};
//function to save date and format on local array onchange date and onclick format
@@ -3,6 +3,7 @@ import {
getMonth,
getYear,
isMobile,
onChangeHeightOfTextArea,
onChangeInput,
radioButtonWidget,
range,
@@ -285,6 +286,22 @@ function PlaceholderType(props) {
};
props.handleSaveDate(dateObj, isDateChange);
};
//handle height on enter press in text area
const handleEnterPress = (e) => {
const height = 15;
if (e.key === "Enter") {
//function to save height of text area
onChangeHeightOfTextArea(
height,
props.pos.type,
props.pos.key,
props.xyPostion,
props.index,
props.setXyPostion,
props.data && props.data?.Id
);
}
};
switch (type) {
case "signature":
return props.pos.SignUrl ? (
@@ -336,7 +353,7 @@ function PlaceholderType(props) {
<div key={ind} className="flex items-center text-center gap-0.5">
<input
style={{
width: props.pos.Width,
// width: props.pos.Width,
display: "flex",
justifyContent: "center",
marginTop: ind === 0 ? 0 : "5px"
@@ -443,7 +460,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) => {
@@ -741,6 +758,7 @@ function PlaceholderType(props) {
<textarea
placeholder="Enter label"
rows={1}
onKeyDown={handleEnterPress}
value={textValue}
onBlur={handleInputBlur}
onChange={(e) => {
+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>
+69 -44
View File
@@ -76,7 +76,6 @@ function SignPad({
};
//function for set signature url
const handleSignatureChange = () => {
// canvasRef.current.backgroundColor = 'rgb(165, 26, 26)'
setSignature(canvasRef.current.toDataURL());
setIsSignImg(canvasRef.current.toDataURL());
};
@@ -114,7 +113,6 @@ function SignPad({
setIsSignImg("");
onSaveSign(false, textWidth, textHeight);
} else {
setIsSignImg("");
onSaveSign();
}
}
@@ -153,6 +151,18 @@ function SignPad({
);
};
useEffect(() => {
if (canvasRef.current && isSignImg) {
canvasRef.current.fromDataURL(isSignImg);
}
const trimmedName = currentUserName.trim();
const firstCharacter = trimmedName.charAt(0);
const userName = isInitial ? firstCharacter : currentUserName;
setSignValue(userName);
setFontSelect("Fasthand");
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isSignPad]);
useEffect(() => {
const loadFont = async () => {
try {
@@ -186,7 +196,7 @@ function SignPad({
}, [isTab]);
//function for convert input text value in image
const convertToImg = async (fontStyle, text) => {
const convertToImg = async (fontStyle, text, color) => {
//get text content to convert in image
const textContent = text;
const fontfamily = fontStyle
@@ -199,6 +209,7 @@ function SignPad({
const span = document.createElement("span");
span.textContent = textContent;
span.style.font = `20px ${fontfamily}`; // here put your text size and font family
span.style.color = color ? color : penColor;
span.style.display = "hidden";
document.body.appendChild(span); // Replace 'container' with the ID of the container element
@@ -216,13 +227,9 @@ function SignPad({
canvasElement.width = width * pixelRatio;
canvasElement.height = height * pixelRatio;
// Render the content of the span onto the canvas
// ctx.fillStyle = "white"; // Set the background color of the canvas
// ctx.fillRect(0, 0, width*pixelRatio, height*pixelRatio);
// You can customize text styles if needed
ctx.font = font;
ctx.fillStyle = "black"; // Set the text color
ctx.fillStyle = color ? color : penColor; // Set the text color
ctx.textAlign = "center";
ctx.textBaseline = "middle";
ctx.scale(pixelRatio, pixelRatio);
@@ -235,6 +242,45 @@ function SignPad({
setSignature(dataUrl);
};
const PenColorComponent = (props) => {
return (
<div style={{ display: "flex", flexDirection: "row" }}>
{allColor.map((data, key) => {
return (
<i
style={{
margin: "5px",
color: data,
borderBottom:
key === 0 && penColor === "blue"
? "2px solid blue"
: key === 1 && penColor === "red"
? "2px solid red"
: key === 2 && penColor === "black"
? "2px solid black"
: "2px solid white"
}}
onClick={() => {
props?.convertToImg &&
props?.convertToImg(fontSelect, signValue, data);
if (key === 0) {
setPenColor("blue");
} else if (key === 1) {
setPenColor("red");
} else if (key === 2) {
setPenColor("black");
}
}}
key={key}
className="fa solid fa-pen-nib"
width={20}
height={20}
></i>
);
})}
</div>
);
};
return (
<div>
{isSignPad && (
@@ -457,6 +503,7 @@ function SignPad({
background: "rgb(255, 255, 255)",
objectFit: "contain"
}}
draggable="false"
src={isInitial ? myInitial : defaultSign}
/>
</div>
@@ -517,6 +564,7 @@ function SignPad({
alt="print img"
ref={imageRef}
src={image.src}
draggable="false"
style={{
objectFit: "contain",
height: "100%",
@@ -547,7 +595,7 @@ function SignPad({
<input
maxLength={isInitial ? 3 : 30}
style={{ fontFamily: fontSelect }}
style={{ fontFamily: fontSelect, color: penColor }}
type="text"
className="signatureInput"
placeholder="Your signature"
@@ -578,7 +626,8 @@ function SignPad({
<div
style={{
padding: "5px 10px 5px 10px",
fontSize: "20px"
fontSize: "20px",
color: penColor
}}
>
{signValue ? signValue : "Your signature"}
@@ -588,7 +637,15 @@ function SignPad({
})}
</div>
<div style={{ display: "flex", justifyContent: "flex-end" }}>
<div
style={{
display: "flex",
flexDirection: "row",
justifyContent: "space-between",
marginTop: "10px"
}}
>
<PenColorComponent convertToImg={convertToImg} />
<SaveBtn />
</div>
</div>
@@ -622,39 +679,7 @@ function SignPad({
marginTop: "10px"
}}
>
<div style={{ display: "flex", flexDirection: "row" }}>
{allColor.map((data, key) => {
return (
<i
style={{
margin: "5px",
color: data,
borderBottom:
key === 0 && penColor === "blue"
? "2px solid blue"
: key === 1 && penColor === "red"
? "2px solid red"
: key === 2 && penColor === "black"
? "2px solid black"
: "2px solid white"
}}
onClick={() => {
if (key === 0) {
setPenColor("blue");
} else if (key === 1) {
setPenColor("red");
} else if (key === 2) {
setPenColor("black");
}
}}
key={key}
className="fa solid fa-pen-nib"
width={20}
height={20}
></i>
);
})}
</div>
<PenColorComponent />
<SaveBtn />
</div>
</>
@@ -34,8 +34,11 @@ function WidgetComponent({
handleRoleChange,
handleOnBlur,
title,
isTemplateFlow
setSignersData,
sendInOrder,
isTemplateFlow,
setBlockColor,
blockColor
}) {
const [isSignersModal, setIsSignersModal] = useState(false);
@@ -233,14 +236,14 @@ function WidgetComponent({
(data) =>
data.type !== "dropdown" &&
data.type !== radioButtonWidget &&
data.type !== textWidget
data.type !== textInputWidget
);
const textWidgetData = widget.filter((data) => data.type !== textWidget);
const updateWidgets = isSignYourself
? filterWidgets
: isTemplateFlow
? textWidgetData
: widget;
? textWidgetData
: widget;
return (
<>
@@ -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={{
@@ -3,6 +3,7 @@ import ModalUi from "../../primitives/ModalUi";
import "../../styles/AddUser.css";
import RegexParser from "regex-parser";
import { textInputWidget } from "../../constant/Utils";
import PremiumAlertHeader from "../../primitives/PremiumAlertHeader";
const WidgetNameModal = (props) => {
const [formdata, setFormdata] = useState({
@@ -14,7 +15,7 @@ const WidgetNameModal = (props) => {
});
const [isValid, setIsValid] = useState(true);
const statusArr = ["Required", "Optional"];
const inputOpt = ["email", "number"];
const inputOpt = ["text", "email", "number"];
useEffect(() => {
if (props.defaultdata) {
@@ -70,7 +71,7 @@ const WidgetNameModal = (props) => {
return "/^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,}$/";
case "number":
return "/^\\d+$/";
case textInputWidget:
case "text":
return "/^[a-zA-Zs]+$/";
default:
return type;
@@ -98,7 +99,23 @@ const WidgetNameModal = (props) => {
handleClose={props.handleClose && props.handleClose}
title={"Widget info"}
>
<form onSubmit={handleSubmit} style={{ padding: 20 }}>
{(props.defaultdata?.type === textInputWidget ||
props.widgetName === textInputWidget) && (
<PremiumAlertHeader
message={
"Field validations are free in beta, this feature will incur a fee later."
}
/>
)}
<form
onSubmit={handleSubmit}
className={`${
props.defaultdata?.type === textInputWidget ||
props.widgetName === textInputWidget
? "pt-0"
: ""
} p-[20px]`}
>
<div className="form-section">
<label htmlFor="name" style={{ fontSize: 13 }}>
Name
@@ -170,25 +187,22 @@ const WidgetNameModal = (props) => {
name="defaultValue"
value={formdata.defaultValue}
onChange={(e) => handledefaultChange(e)}
onBlur={() =>
isValid === false &&
setFormdata({ ...formdata, defaultValue: "" })
}
autoComplete="off"
onBlur={() => {
if (isValid === false) {
setFormdata({ ...formdata, defaultValue: "" });
setIsValid(true);
}
}}
/>
{isValid === false ? (
<p style={{ color: "Red", fontSize: 8 }}>
{isValid === false && (
<div className="warning defaultvalueWarning" style={{ fontSize: 12 }}>
<i
className="fas fa-exclamation-circle"
style={{ color: "#fab005", fontSize: 15 }}
></i>{" "}
invalid default value
</p>
) : (
<p
style={{
color: "transparent",
fontSize: 10,
margin: "3px 8px"
}}
>
.
</p>
</div>
)}
</div>
</>
@@ -0,0 +1,234 @@
import React, { useEffect, useState } from "react";
import Parse from "parse";
import CreateFolder from "./CreateFolder";
import ModalUi from "../../../primitives/ModalUi";
const FolderModal = (props) => {
const [clickFolder, setClickFolder] = useState("");
const [folderList, setFolderList] = useState([]);
const [tabList, setTabList] = useState([]);
const [isLoader, setIsLoader] = useState(false);
const [isAdd, setIsAdd] = useState(false);
// below useEffect is called when user open popup
useEffect(() => {
if (props.isOpenModal) {
fetchFolder();
}
// eslint-disable-next-line
}, [props.isOpenModal]);
// `fetchFolder` is used to fetch of folder list created by user on basis of folderPtr or without folderPtr
const fetchFolder = async (folderPtr) => {
setIsLoader(true);
try {
const FolderQuery = new Parse.Query(props.folderCls);
if (folderPtr) {
FolderQuery.equalTo("Folder", folderPtr);
FolderQuery.equalTo("Type", "Folder");
} else {
FolderQuery.doesNotExist("Folder");
FolderQuery.equalTo("Type", "Folder");
}
const res = await FolderQuery.find();
if (res) {
const result = JSON.parse(JSON.stringify(res));
if (result) {
setFolderList(result);
setIsLoader(false);
}
setIsLoader(false);
}
} catch (error) {
setIsLoader(false);
}
};
// `handleSelect` is used to save pointer of folder selected by user and it's path in state
const handleSelect = (item) => {
setFolderList([]);
setClickFolder({ ObjectId: item.objectId, Name: item.Name });
if (tabList.length > 0) {
const tab = tabList.some((x) => x.objectId === item.objectId);
if (!tab) {
setTabList((tabs) => [...tabs, item]);
const folderPtr = {
__type: "Pointer",
className: props.folderCls,
objectId: item.objectId
};
fetchFolder(folderPtr);
}
} else {
setTabList((tabs) => [...tabs, item]);
const folderPtr = {
__type: "Pointer",
className: props.folderCls,
objectId: item.objectId
};
fetchFolder(folderPtr);
}
};
// `handleSubmit` is used to pass folderPtr to parent component
const handleSubmit = () => {
let url = "OpenSign™ Drive";
tabList.forEach((t) => {
url = url + " / " + t.Name;
});
if (props.onSuccess) {
props.onSuccess(clickFolder);
}
// SetIsOpen(false);
props.setIsOpenMoveModal(false);
};
// `handleCancel` is used to clear list of folder, close popup and folderUrl
const handleCancel = () => {
// SetIsOpen(false);
props.setIsOpenMoveModal(false);
setClickFolder({});
setFolderList([]);
setTabList([]);
};
// `handleCancel` is call when user click on folder name from path/tab in popup
const removeTabListItem = async (e, i) => {
e.preventDefault();
// setEditable(false);
if (!isAdd) {
setIsLoader(true);
let folderPtr;
if (i) {
setFolderList([]);
let list = tabList.filter((itm, j) => {
if (j <= i) {
return itm;
}
});
let _len = list.length - 1;
folderPtr = {
__type: "Pointer",
className: props.folderCls,
objectId: list[_len].objectId
};
setTabList(list);
} else {
setClickFolder({});
setFolderList([]);
setTabList([]);
}
fetchFolder(folderPtr);
}
};
// `handleCreate` is used to open folder creation form in popup
const handleCreate = () => {
setIsAdd(!isAdd);
};
// `handleAddFolder` is call when user folder created successfully and it fetch folder list on the basis of folderPtr or without folderPtr
const handleAddFolder = (newFolder) => {
props.setPdfData((prev) => [...prev, newFolder?.toJSON()]);
if (clickFolder && clickFolder.ObjectId) {
fetchFolder({
__type: "Pointer",
className: props.folderCls,
objectId: clickFolder.ObjectId
});
} else {
fetchFolder();
}
handleCreate();
};
return (
<div className="text-xs mt-2 ">
<ModalUi
title={"Select Folder"}
isOpen={props.isOpenModal}
handleClose={handleCancel}
>
<div className="w-full min-w-[300px] md:min-w-[500px] max-w-[500px] px-3">
<div className="py-2 text-[#ac4848] text-[14px] font-[500]">
<span
className="cursor-pointer"
title="OpenSign™ Drive"
onClick={(e) => removeTabListItem(e)}
>
OpenSign Drive /{" "}
</span>
{tabList &&
tabList.map((tab, i) => (
<React.Fragment key={`${tab.objectId}-${i}`}>
<span
className="cursor-pointer"
title={tab.Name}
onClick={(e) => removeTabListItem(e, i)}
>
{tab.Name}
</span>
{" / "}
</React.Fragment>
))}
<hr />
</div>
<div className="mt-2 mb-3">
<div className="max-h-[210px] overflow-auto">
{!isAdd &&
folderList.length > 0 &&
folderList.map((folder) => (
<div
key={folder.Name}
className="border-[1px] border-[#8a8a8a] px-2 py-2 mb-2 cursor-pointer "
onClick={() => handleSelect(folder)}
>
<div className="flex items-center gap-2">
<i
className="fa fa-folder text-[#33bbff] text-[1.4rem]"
aria-hidden="true"
></i>
<span className="font-semibold">{folder.Name}</span>
</div>
</div>
))}
</div>
{isAdd && (
<CreateFolder
parentFolderId={clickFolder && clickFolder.ObjectId}
folderCls={props.folderCls}
onSuccess={handleAddFolder}
/>
)}
{isLoader && (
<div className="flex justify-center">
<i className="fa-solid fa-spinner fa-spin-pulse text-[30px]"></i>
</div>
)}
</div>
</div>
<hr />
<div className="flex justify-between items-center py-[.75rem] px-[1.25rem]">
<div
className="text-[30px] cursor-pointer text-[#32a3ac]"
title="Save Here"
onClick={handleCreate}
>
{isAdd ? (
<i className="fa-solid fa-arrow-left" aria-hidden="true"></i>
) : (
<i className="fa-solid fa-square-plus" aria-hidden="true"></i>
)}
</div>
<div
className="text-[30px] cursor-pointer"
title="Save Here"
onClick={handleSubmit}
>
<i className="fas fa-save" aria-hidden="true"></i>
</div>
</div>
</ModalUi>
</div>
);
};
export default FolderModal;
@@ -3,6 +3,7 @@ import Select from "react-select";
import AddSigner from "../../AddSigner";
import Modal from "react-modal";
import Parse from "parse";
import Tooltip from "../../../primitives/Tooltip";
function arrayMove(array, from, to) {
array = array.slice();
array.splice(to < 0 ? array.length + to : to, 0, array.splice(from, 1)[0]);
@@ -107,6 +108,12 @@ const SignersInput = (props) => {
<label className="block">
Signers
{props.required && <span className="text-red-500 text-[13px]">*</span>}
<span className="absolute ml-1 text-xs z-50">
<Tooltip
id={"signer-tooltip"}
message={"Begin typing a contact's name to see suggested signers from your saved contacts or add new ones. Arrange the signing order by adding signers in the desired sequence. Use the '+' button to include signers and the 'x' to remove them. Each signer will receive an email prompt to sign the document in the order listed."}
/>
</span>
</label>
<div style={{ display: "flex", gap: 5 }}>
<div style={{ flexWrap: "wrap", width: "100%" }}>
+212 -15
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();
@@ -22,10 +51,12 @@ export const toDataUrl = (file) => {
});
};
//function for getting document details for getDrive
export const getDrive = async (documentId) => {
//function for getting document details for getDrive cloud function
export const getDrive = async (documentId, skip = 0, limit = 100) => {
const data = {
docId: documentId && documentId
docId: documentId && documentId,
limit: limit,
skip: skip
};
const driveDeatils = await axios
.post(`${localStorage.getItem("baseUrl")}functions/getDrive`, data, {
@@ -729,6 +760,82 @@ export const onChangeInput = (
}
};
//function to increase height of text area on press enter
export const onChangeHeightOfTextArea = (
height,
widgetType,
signKey,
xyPostion,
index,
setXyPostion,
userId
) => {
const isSigners = xyPostion.some((data) => data.signerPtr);
let filterSignerPos;
if (isSigners) {
if (userId) {
filterSignerPos = xyPostion.filter((data) => data.Id === userId);
}
const getPlaceHolder = filterSignerPos[0]?.placeHolder;
const getPageNumer = getPlaceHolder.filter(
(data) => data.pageNumber === index
);
if (getPageNumer.length > 0) {
const getXYdata = getPageNumer[0].pos;
const getPosData = getXYdata;
const addSignPos = getPosData.map((position) => {
if (position.key === signKey) {
return {
...position,
Height: position.Height
? position.Height + height
: defaultWidthHeight(widgetType).height + height
};
}
return position;
});
const newUpdateSignPos = getPlaceHolder.map((obj) => {
if (obj.pageNumber === index) {
return { ...obj, pos: addSignPos };
}
return obj;
});
const newUpdateSigner = xyPostion.map((obj) => {
if (obj.Id === userId) {
return { ...obj, placeHolder: newUpdateSignPos };
}
return obj;
});
setXyPostion(newUpdateSigner);
}
} else {
let getXYdata = xyPostion[index].pos;
const updatePosition = getXYdata.map((position) => {
if (position.key === signKey) {
return {
...position,
Height: position.Height
? position.Height + height
: defaultWidthHeight(widgetType).height + height
};
}
return position;
});
const updatePlaceholder = xyPostion.map((obj, ind) => {
if (ind === index) {
return { ...obj, pos: updatePosition };
}
return obj;
});
setXyPostion(updatePlaceholder);
}
};
export const addInitialData = (signerPos, setXyPostion, value, userId) => {
function widgetDataValue(type) {
switch (type) {
@@ -1078,8 +1185,8 @@ export const multiSignEmbed = async (
position.type === radioButtonWidget
? 10
: position.type === "checkbox"
? 10
: newUpdateHeight;
? 10
: newUpdateHeight;
const newHeight = ind ? (ind > 0 ? widgetHeight : 0) : widgetHeight;
if (signyourself) {
@@ -1148,13 +1255,14 @@ export const multiSignEmbed = async (
"email"
].includes(position.type);
if (position.type === "checkbox") {
let addYPosition, isCheck;
let addYPosition = 0,
isCheck;
if (position?.options?.values.length > 0) {
position?.options?.values.forEach((item, ind) => {
const checkboxRandomId = "checkbox" + randomId();
let yPosition;
const height = 10;
const height = 13;
if (
position?.options?.response &&
position?.options?.response?.length > 0
@@ -1175,14 +1283,14 @@ export const multiSignEmbed = async (
if (!position?.options?.isHideLabel) {
// below line of code is used to embed label with radio button in pdf
page.drawText(item, {
x: xPos(position) + 15,
x: xPos(position) + 17,
y: yPosition + 2,
size: height
});
}
checkbox.addToPage(page, {
x: xPos(position),
y: yPosition,
y: yPosition - 3,
width: height,
height: height
});
@@ -1203,19 +1311,21 @@ export const multiSignEmbed = async (
} else if (position?.options?.defaultValue) {
textContent = position?.options?.defaultValue;
}
const fixedWidth = scaleWidth; // Set your fixed width
const isNewOnEnterLineExist = textContent.includes("\n");
// Function to break text into lines based on the fixed width
const breakTextIntoLines = (textContent, width) => {
const NewbreakTextIntoLines = (textContent, width) => {
const lines = [];
let currentLine = "";
for (const word of textContent.split(" ")) {
//get text line width
const lineWidth = font.widthOfTextAtSize(
`${currentLine} ${word}`,
fontSize
);
//check text content line width is less or equal to container width
if (lineWidth <= width) {
currentLine += ` ${word}`;
} else {
@@ -1226,7 +1336,33 @@ export const multiSignEmbed = async (
lines.push(currentLine.trim());
return lines;
};
const lines = breakTextIntoLines(textContent, fixedWidth);
// Function to break text into lines based on when user go next line on press enter button
const breakTextIntoLines = (textContent, width) => {
const lines = [];
for (const word of textContent.split("\n")) {
const lineWidth = font.widthOfTextAtSize(`${word}`, fontSize);
//checking string length to container width
//if string length is less then container width it means user press enter button
if (lineWidth <= width) {
lines.push(word);
}
//else adjust text content according to width and send it in new line
else {
const newLine = NewbreakTextIntoLines(word, width);
lines.push(...newLine);
}
}
return lines;
};
//check if text content have `\n` string it means user press enter to go next line and handle condition
//else auto adjust text content according to container width
const lines = isNewOnEnterLineExist
? breakTextIntoLines(textContent, fixedWidth)
: NewbreakTextIntoLines(textContent, fixedWidth);
// Set initial y-coordinate for the first line
const labelDefaultHeight = defaultWidthHeight(position.type).height;
@@ -1327,9 +1463,7 @@ export const multiSignEmbed = async (
}
});
}
const pdfBytes = await pdfDoc.saveAsBase64({ useObjectStreams: false });
return pdfBytes;
};
@@ -1569,3 +1703,66 @@ export const getYear = (date) => {
const newYear = new Date(date).getFullYear();
return newYear;
};
//function to create/copy widget next to already dropped widget
export const handleCopyNextToWidget = (
position,
widgetType,
xyPostion,
index,
setXyPostion,
userId
) => {
const isSigners = xyPostion.some((data) => data.signerPtr);
let filterSignerPos;
//get position of previous widget and create new widget next to that widget on same data except
// xPosition and key
let newpos = position;
const calculateXPosition =
parseInt(position.xPosition) +
defaultWidthHeight(widgetType).width +
resizeBorderExtraWidth();
const newId = randomId();
newpos = { ...newpos, xPosition: calculateXPosition, key: newId };
//if condition to create widget in request-sign flow
if (isSigners) {
if (userId) {
filterSignerPos = xyPostion.filter((data) => data.Id === userId);
}
const getPlaceHolder = filterSignerPos[0]?.placeHolder;
const getPageNumer = getPlaceHolder.filter(
(data) => data.pageNumber === index
);
const getXYdata = getPageNumer[0].pos;
getXYdata.push(newpos);
if (getPageNumer.length > 0) {
const newUpdateSignPos = getPlaceHolder.map((obj) => {
if (obj.pageNumber === index) {
return { ...obj, pos: getXYdata };
}
return obj;
});
const newUpdateSigner = xyPostion.map((obj) => {
if (obj.Id === userId) {
return { ...obj, placeHolder: newUpdateSignPos };
}
return obj;
});
setXyPostion(newUpdateSigner);
}
} else {
// else condition to create widget in sign-yourself flow
let getXYdata = xyPostion[index].pos;
getXYdata.push(newpos);
const updatePlaceholder = xyPostion.map((obj, ind) => {
if (ind === index) {
return { ...obj, pos: getXYdata };
}
return obj;
});
setXyPostion(updatePlaceholder);
}
};
+19 -8
View File
@@ -18,7 +18,8 @@ export default function reportJson(id) {
btnIcon: "fa fa-plus",
redirectUrl: "draftDocument"
}
]
],
helpMsg: "This are documents you have started but have not finalized for sending."
};
// Need your sign report
case "4Hhwbp482K":
@@ -33,7 +34,9 @@ export default function reportJson(id) {
btnIcon: "fa fa-eye",
redirectUrl: "pdfRequestFiles"
}
]
],
helpMsg:
"This is a list of documents that are waiting for your signature"
};
// In progess report
case "1MwEuxLEkF":
@@ -48,7 +51,9 @@ export default function reportJson(id) {
btnIcon: "fa fa-eye",
redirectUrl: "pdfRequestFiles"
}
]
],
helpMsg:
"This is a list of documents you've sent to other parties for signature."
};
// completed documents report
case "kQUoW4hUXz":
@@ -63,7 +68,8 @@ export default function reportJson(id) {
btnIcon: "fa fa-eye",
redirectUrl: "draftDocument"
}
]
],
helpMsg: "This is a list of documents that have been signed by all parties."
};
// declined documents report
case "UPr2Fm5WY3":
@@ -78,7 +84,8 @@ export default function reportJson(id) {
btnIcon: "fa fa-eye",
redirectUrl: "draftDocument"
}
]
],
helpMsg: "This is a list of documents that have been declined by one of the signers."
};
// Expired Documents report
case "zNqBHXHsYH":
@@ -93,7 +100,8 @@ export default function reportJson(id) {
btnIcon: "fa fa-eye",
redirectUrl: "draftDocument"
}
]
],
helpMsg: "This is a list of documents that have reached their expiration date."
};
// Recently sent for signatures report show on dashboard
case "d9k3UfYHBc":
@@ -153,7 +161,8 @@ export default function reportJson(id) {
btnIcon: "fa-solid fa-trash"
}
],
form: "ContactBook"
form: "ContactBook",
helpMsg: "This is a list of contacts/signers added by you. These will appear as suggestions when you try to add signers to a new document."
};
// template report
case "6TeaPr321t":
@@ -175,7 +184,9 @@ export default function reportJson(id) {
btnIcon: "fa fa-plus",
redirectUrl: "template"
}
]
],
helpMsg:
"This is a list of templates that are available to you for creating documents. You can click the 'use' button to create a new document using a template, modify the document & add signers in the next step."
};
default:
return null;
+4 -1
View File
@@ -270,7 +270,10 @@ const HomeLayout = () => {
<div className="flex md:flex-row flex-col z-50">
<Sidebar isOpen={isOpen} closeSidebar={closeSidebar} />
<div className="relative h-screen flex flex-col justify-between w-full overflow-y-auto">
<div
id="renderList"
className="relative h-screen flex flex-col justify-between w-full overflow-y-auto"
>
<div className="bg-[#eef1f5] p-3">{<Outlet />}</div>
<div className="z-30">
<Footer />
+14 -5
View File
@@ -35,7 +35,7 @@ const Forms = (props) => {
const [formData, setFormData] = useState({
Name: "",
Description: "",
Note: "Please review and sign this document",
Note: "",
TimeToCompleteDays: 15,
SendinOrder: "false"
});
@@ -51,6 +51,7 @@ const Forms = (props) => {
};
useEffect(() => {
handleReset();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [props.title]);
const handleFileInput = (e) => {
setpercentage(0);
@@ -165,7 +166,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);
@@ -233,9 +236,12 @@ const Forms = (props) => {
setFormData({
Name: "",
Description: "",
Note: "Please review and sign this document",
Note:
props.title === "Sign Yourself"
? "Note to myself"
: "Please review and sign this document",
TimeToCompleteDays: 15,
SendinOrder: "false"
SendinOrder: "true"
});
setFileUpload([]);
setpercentage(0);
@@ -324,7 +330,10 @@ const Forms = (props) => {
</div>
<div className="text-xs mt-2">
<label className="block">
Title<span className="text-red-500 text-[13px]">*</span>
{props.title === "New Template"
? "Template Title"
: "Document Title"}
<span className="text-red-500 text-[13px]">*</span>
</label>
<input
name="Name"
+9 -1
View File
@@ -5,6 +5,8 @@ import Alert from "../primitives/Alert";
import ModalUi from "../primitives/ModalUi";
import { rejectBtn, submitBtn } from "../constant/const";
import { openInNewTab } from "../constant/Utils";
import PremiumAlertHeader from "../primitives/PremiumAlertHeader";
import Tooltip from "../primitives/Tooltip";
function GenerateToken() {
const [parseBaseUrl] = useState(localStorage.getItem("baseUrl"));
@@ -115,7 +117,13 @@ function GenerateToken() {
</div>
) : (
<div className="bg-white flex flex-col justify-center shadow rounded">
<h1 className="ml-4 mt-3 mb-2 font-semibold">API Token</h1>
<PremiumAlertHeader />
<h1 className="ml-4 mt-3 mb-2 font-semibold">
API Token{" "}
<Tooltip
url={"https://docs.opensignlabs.com/docs/API-docs/opensign-api-v-1"}
/>
</h1>
<ul className="w-full flex flex-col p-2 text-sm">
<li
className={`flex justify-between items-center border-y-[1px] border-gray-300 break-all py-2`}
+17 -85
View File
@@ -13,7 +13,6 @@ const ManageSign = () => {
const [signName, setSignName] = useState("");
const [signature, setSignature] = useState("");
const [warning, setWarning] = useState(false);
const [nameWarning, setNameWarning] = useState(false);
const [isvalue, setIsValue] = useState(false);
const allColor = ["blue", "red", "black"];
const [isLoader, setIsLoader] = useState(true);
@@ -105,9 +104,7 @@ const ManageSign = () => {
e.preventDefault();
const isUrl = image.includes("https");
if (!signName) {
setNameWarning(true);
} else if (!isvalue) {
if (!isvalue) {
setWarning(true);
setTimeout(() => setWarning(false), 1000);
} else {
@@ -222,26 +219,13 @@ const ManageSign = () => {
setIsValue(true);
};
return (
<div
style={{
backgroundColor: "white",
maxHeight: "500px",
overflow: "auto"
}}
>
<div className="relative h-full bg-white flex shadow rounded overflow-auto">
{isLoader && (
<div className="absolute bg-black bg-opacity-75 inset-0 z-50 flex justify-center items-center">
<div style={{ height: "300px" }}>
<div
style={{
marginLeft: "45%",
marginTop: "150px",
fontSize: "45px",
color: "#3dd3e0"
}}
className="loader-37"
></div>
</div>
<div className="absolute bg-black bg-opacity-75 z-50 w-full h-full flex justify-center items-center">
<div
style={{ color: "#3dd3e0" }}
className="loader-37 text-[45px]"
></div>
</div>
)}
{isSuccess && (
@@ -269,61 +253,9 @@ const ManageSign = () => {
paddingBottom: 8
}}
>
Manage Signature
My Signature
</div>
<div style={{ display: "flex", flexDirection: "column" }}>
<label
htmlFor="signName"
style={{
fontSize: 12,
position: "relative",
paddingBottom: 5
}}
>
Signer&apos;s Name
{nameWarning && (
<div
className="warning nameWarning"
style={{ fontSize: 12 }}
onAnimationEnd={() => setNameWarning(false)}
>
<i
className="fas fa-exclamation-circle"
style={{ color: "#fab005", fontSize: 15 }}
></i>{" "}
Please fill input field
</div>
)}
</label>
<input
id="signName"
className="signInputManage"
value={signName}
style={{
border: "1px solid #ccc",
borderRadius: 4,
height: 35,
padding: "6px 12px",
marginBottom: 10,
fontSize: 12
}}
required
onFocus={() => setNameWarning(false)}
onChange={(e) => setSignName(e.target.value)}
/>
<label
htmlFor="imgoverview"
style={{
fontSize: 12,
paddingBottom: 5
}}
>
Signature/Image
</label>
</div>
<div className="signBlock">
<div className="signBlock mt-4">
<div>
<div style={{ position: "relative" }}>
<div
@@ -429,10 +361,10 @@ const ManageSign = () => {
key === 0 && penColor === "blue"
? "2px solid blue"
: key === 1 && penColor === "red"
? "2px solid red"
: key === 2 && penColor === "black"
? "2px solid black"
: "2px solid white"
? "2px solid red"
: key === 2 && penColor === "black"
? "2px solid black"
: "2px solid white"
}}
onClick={() => {
if (key === 0) {
@@ -537,10 +469,10 @@ const ManageSign = () => {
key === 0 && initialPen === "blue"
? "2px solid blue"
: key === 1 && initialPen === "red"
? "2px solid red"
: key === 2 && initialPen === "black"
? "2px solid black"
: "2px solid white"
? "2px solid red"
: key === 2 && initialPen === "black"
? "2px solid black"
: "2px solid white"
}}
onClick={() => {
if (key === 0) {
+71 -84
View File
@@ -7,8 +7,8 @@ import { useNavigate } from "react-router-dom";
import Title from "../components/Title";
import Parse from "parse";
import ModalUi from "../primitives/ModalUi";
const DriveBody = React.lazy(() =>
import("../components/opensigndrive/DriveBody")
const DriveBody = React.lazy(
() => import("../components/opensigndrive/DriveBody")
);
const Loader = () => {
return (
@@ -51,6 +51,9 @@ function Opensigndrive() {
const [folderName, setFolderName] = useState([]);
const [isAlert, setIsAlert] = useState({ isShow: false, alertMessage: "" });
const [isNewFol, setIsNewFol] = useState(false);
const [skip, setSkip] = useState(0);
const limit = 100;
const [loading, setLoading] = useState(false);
const currentUser =
localStorage.getItem(
`Parse/${localStorage.getItem("parseAppId")}/currentUser`
@@ -61,103 +64,82 @@ function Opensigndrive() {
const jsonCurrentUser = JSON.parse(currentUser);
useEffect(() => {
if (docId) {
getPdfFolderDocumentList();
} else {
getPdfDocumentList();
}
getPdfDocumentList();
// eslint-disable-next-line
}, [docId]);
//function for get all pdf document list
const getPdfDocumentList = async () => {
const load = {
isLoad: true,
message: "This might take some time"
};
let driveDetails;
setIsLoading(load);
const getPdfDocumentList = async (disbaleLoading) => {
setLoading(true);
if (!disbaleLoading) {
setIsLoading({ isLoad: true, message: "This might take some time" });
}
try {
driveDetails = await getDrive();
if (driveDetails) {
if (driveDetails.length > 0) {
setPdfData(driveDetails);
sortApps(null, null, driveDetails);
}
const data = [
{
name: "OpenSign™ Drive",
objectId: ""
}
];
setFolderName(data);
const loadObj = {
isLoad: false
};
setIsLoading(loadObj);
} else if (driveDetails === "Error: Something went wrong!") {
const loadObj = {
isLoad: false
};
const driveDetails = await getDrive(docId, skip, limit);
if (driveDetails && driveDetails === "Error: Something went wrong!") {
setHandleError("Error: Something went wrong!");
setIsLoading(loadObj);
} else if (driveDetails && driveDetails.length > 0) {
setSkip((prevSkip) => prevSkip + limit);
sortApps("Date", "Decending", driveDetails, true);
}
if (!docId) {
setFolderName([{ name: "OpenSign™ Drive", objectId: "" }]);
}
} catch (e) {
setIsAlert({
isShow: true,
alertMessage: "something went wrong"
});
}
};
//function for get parent folder document list
const getPdfFolderDocumentList = async () => {
const load = {
isLoad: true,
message: "This might take some time"
};
setIsLoading(load);
let driveDetails;
try {
driveDetails = await getDrive(docId);
if (driveDetails) {
if (driveDetails.length > 0) {
setPdfData(driveDetails);
sortApps(null, null, driveDetails);
} else {
setPdfData([]);
}
const loadObj = {
isLoad: false
};
setIsLoading(loadObj);
} else if (driveDetails === "Error: Something went wrong!") {
const loadObj = {
isLoad: false
};
setHandleError("Error: Something went wrong!");
setIsLoading(loadObj);
}
} catch (e) {
setIsAlert({
isShow: true,
alertMessage: "something went wrong!"
} finally {
setLoading(false);
setIsLoading({
isLoad: false
});
}
};
//function for get all pdf document list
const getParentFolder = async () => {
setIsFolder(true);
//function to fetch drive details list on scroll bottom
const handleScroll = () => {
//get document of render openSign-drive component using id
const documentList = document.getElementById("renderList");
//documentList.clientHeight property returns the height of an element's content area, including padding but not including borders, margins, or scrollbars.
//documentList.scrollHeight property returns the entire height of an element,including the parts that are not visible due to overflow..
// documentList.scrollTop property show height of element, how much the content has been scrolled from the top.
// When the sum of scrollTop and clientHeight is equal to scrollHeight, it means that the user has scrolled to the bottom of the div.
if (
documentList &&
documentList.scrollTop + documentList.clientHeight >=
documentList.scrollHeight
) {
//disableLoading is used disable initial loader
const disableLoading = true;
// If the fetched data length is less than the limit, it means there's no more data to fetch
if (!loading && pdfData.length % 100 === 0) {
getPdfDocumentList(disableLoading);
}
}
};
//useEffect is used to call handleScroll function on scrolling event
useEffect(() => {
const documentList = document.getElementById("renderList");
if (documentList) {
documentList.addEventListener("scroll", handleScroll);
return () => {
documentList.removeEventListener("scroll", handleScroll);
};
}
// eslint-disable-next-line
}, [loading]); // Add/remove scroll event listener when loading changes
//function for handle folder name path
const handleRoute = (index) => {
setPdfData([]);
setSkip(0);
const updateFolderName = folderName.filter((x, i) => {
if (i <= index) {
return x;
}
});
setFolderName(updateFolderName);
const getLastId = updateFolderName[updateFolderName.length - 1];
setDocId(getLastId.objectId);
@@ -244,18 +226,19 @@ function Opensigndrive() {
}
};
const sortApps = (type, order, driveDetails) => {
const sortApps = (type, order, driveDetails, isInitial) => {
const selectedSortType = type ? type : selectedSort ? selectedSort : "Date";
const sortOrder = order ? order : sortingOrder ? sortingOrder : "Decending";
let sortingData = driveDetails;
if (selectedSortType === "Name") {
sortingApp(sortingData, "Name", sortOrder);
sortingApp(driveDetails, "Name", sortOrder);
} else if (selectedSortType === "Date") {
sortingApp(sortingData, "Date", sortOrder);
sortingApp(driveDetails, "Date", sortOrder);
}
if (isInitial) {
setPdfData([...pdfData, ...driveDetails]);
} else {
setPdfData(driveDetails);
}
setPdfData(sortingData);
};
//function for handle auto scroll on folder path
@@ -322,6 +305,7 @@ function Opensigndrive() {
// Cleanup the event listener when the component unmounts
document.removeEventListener("click", closeMenuOnOutsideClick);
};
// eslint-disable-next-line
}, [isShowSort, isNewFol]);
const handleFolderTab = (folderData) => {
@@ -563,7 +547,7 @@ function Opensigndrive() {
>
<span
className="dropdown-item itemColor"
onClick={() => getParentFolder()}
onClick={() => setIsFolder(true)}
>
<i
style={{ marginRight: "5px" }}
@@ -761,13 +745,16 @@ function Opensigndrive() {
setFolderName={setFolderName}
setIsLoading={setIsLoading}
setDocId={setDocId}
getPdfFolderDocumentList={getPdfFolderDocumentList}
getPdfDocumentList={getPdfDocumentList}
isDocId={docId}
setPdfData={setPdfData}
isList={isList}
setIsAlert={setIsAlert}
setSkip={setSkip}
/>
{loading && (
<div style={{ textAlign: "center" }}>Loading...</div>
)}
</React.Suspense>
)}
</>
+17 -18
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 (
@@ -1034,7 +1026,7 @@ function PlaceHolderSign() {
selector: '[data-tut="reactourSecond"]',
content: () => (
<TourContentWithBtn
message={`Drag the signature or stamp placeholder onto the PDF to choose your desired signing location.`}
message={`Drag or click on a field to add it to the document.`}
isChecked={handleDontShow}
/>
),
@@ -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">
+3
View File
@@ -12,6 +12,7 @@ const Report = () => {
const [List, setList] = useState([]);
const [isLoader, setIsLoader] = useState(true);
const [reportName, setReportName] = useState("");
const [reporthelp, setReportHelp] = useState("");
const [actions, setActions] = useState([]);
const [heading, setHeading] = useState([]);
const [isNextRecord, setIsNextRecord] = useState(false);
@@ -53,6 +54,7 @@ const Report = () => {
setHeading(json.heading);
setReportName(json.reportName);
setForm(json.form);
setReportHelp(json?.helpMsg)
const currentUser = Parse.User.current().id;
const headers = {
@@ -156,6 +158,7 @@ const Report = () => {
isMoreDocs={isMoreDocs}
docPerPage={docPerPage}
form={form}
report_help={reporthelp}
/>
) : (
<PageNotFound prefix={"Report"} />
+4 -5
View File
@@ -26,7 +26,7 @@ import {
contactBook,
randomId,
getDate,
textInputWidget
textWidget
} from "../constant/Utils";
import { useParams } from "react-router-dom";
import Tour from "reactour";
@@ -342,8 +342,7 @@ function SignYourSelf() {
return {
name: "checkbox"
};
case textInputWidget:
case textWidget:
return {
name: "text"
};
@@ -528,7 +527,6 @@ function SignYourSelf() {
checkSigned = requiredWidgets[i]?.options?.response;
if (!checkSigned) {
const checkSignUrl = requiredWidgets[i]?.pos?.SignUrl;
let checkDefaultSigned = requiredWidgets[i]?.options?.defaultValue;
if (!checkSignUrl) {
if (!checkDefaultSigned) {
@@ -587,11 +585,12 @@ function SignYourSelf() {
flag,
containerWH
);
// console.log('pdf',pdfBytes)
// console.log("pdf", pdfBytes);
//function for call to embed signature in pdf and get digital signature pdf
await signPdfFun(pdfBytes, documentId);
}
}
// console.log("signyourself", xyPostion);
//function for get digital signature
const signPdfFun = async (base64Url, documentId) => {
let singleSign = {
+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
+21 -7
View File
@@ -6,6 +6,8 @@ import dp from "../assets/images/dp.png";
import Title from "../components/Title";
import sanitizeFileName from "../primitives/sanitizeFileName";
import axios from "axios";
import PremiumAlertHeader from "../primitives/PremiumAlertHeader";
import Tooltip from "../primitives/Tooltip";
function UserProfile() {
const navigate = useNavigate();
@@ -250,7 +252,7 @@ function UserProfile() {
<span className="font-semibold">Email:</span>{" "}
<span>{UserProfile && UserProfile.email}</span>
</li>
<li className="flex justify-between items-center border-y-[1px] border-gray-300 py-2 break-all">
<li className="flex justify-between items-center border-t-[1px] border-gray-300 py-2 break-all">
<span className="font-semibold">Is Email verified:</span>{" "}
<span>
{UserProfile && UserProfile.emailVerified
@@ -258,10 +260,17 @@ function UserProfile() {
: "Not verified"}
</span>
</li>
<li className="flex justify-between items-center border-y-[1px] border-gray-300 py-2 break-all">
<span className="font-semibold">Disable DocumentId :</span>{" "}
<span>
<label className="relative inline-flex items-center cursor-pointer mb-0">
<li className="border-y-[1px] border-gray-300 break-all">
<div className="flex justify-between items-center py-2">
<span className="font-semibold">
Disable DocumentId :{" "}
<Tooltip url={"https://docs.opensignlabs.com/docs/help/Settings/disabledocumentid"} />{" "}
</span>{" "}
<label
className={`${
editmode ? "cursor-pointer" : ""
} relative inline-flex items-center mb-0`}
>
<input
disabled={editmode ? false : true}
checked={isDisableDocId}
@@ -270,9 +279,14 @@ function UserProfile() {
value=""
className="sr-only peer"
/>
<div className="w-9 h-5 bg-gray-200 peer-focus:outline-none peer-focus:ring-4 peer-focus:ring-blue-300 dark:peer-focus:ring-blue-800 rounded-full peer dark:bg-gray-700 peer-checked:after:translate-x-full rtl:peer-checked:after:-translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:start-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-4 after:w-4 after:transition-all dark:border-gray-600 peer-checked:bg-blue-600"></div>
<div className="w-9 h-5 bg-gray-200 peer-focus:outline-none peer-focus:ring-1 peer-focus:ring-black rounded-full peer dark:bg-gray-700 peer-checked:after:translate-x-full rtl:peer-checked:after:-translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:start-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-4 after:w-4 after:transition-all dark:border-black peer-checked:bg-blue-600"></div>
</label>
</span>
</div>
<PremiumAlertHeader
message={
"Disable documentId is free in beta, this feature will incur a fee later."
}
/>
</li>
</ul>
<div className="flex justify-center pt-2 pb-3 md:pt-3 md:pb-4">
+9 -1
View File
@@ -6,6 +6,8 @@ import ModalUi from "../primitives/ModalUi";
import { rejectBtn, submitBtn } from "../constant/const";
import { openInNewTab } from "../constant/Utils";
import Parse from "parse";
import PremiumAlertHeader from "../primitives/PremiumAlertHeader";
import Tooltip from "../primitives/Tooltip";
function Webhook() {
const [parseBaseUrl] = useState(localStorage.getItem("baseUrl"));
@@ -101,7 +103,13 @@ function Webhook() {
</div>
) : (
<div className="bg-white flex flex-col justify-center shadow rounded">
<h1 className="ml-4 mt-3 mb-2 font-semibold">Webhook</h1>
<PremiumAlertHeader />
<h1 className="ml-4 mt-3 mb-2 font-semibold">
Webhook{" "}
<Tooltip
url={"https://docs.opensignlabs.com/docs/API-docs/get-webhook"}
/>
</h1>
<ul className="w-full flex flex-col p-2 text-sm">
<li
className={`flex justify-between items-center border-y-[1px] border-gray-300 break-all py-2`}
@@ -6,6 +6,7 @@ import ModalUi from "./ModalUi";
import AddSigner from "../components/AddSigner";
import { modalSubmitBtnColor, modalCancelBtnColor } from "../constant/const";
import Alert from "./Alert";
import Tooltip from "./Tooltip";
const ReportTable = ({
ReportName,
List,
@@ -15,7 +16,8 @@ const ReportTable = ({
setIsNextRecord,
isMoreDocs,
docPerPage,
form
form,
report_help
}) => {
const navigate = useNavigate();
const [currentPage, setCurrentPage] = useState(1);
@@ -255,7 +257,14 @@ const ReportTable = ({
</Alert>
)}
<div className="flex flex-row items-center justify-between my-2 mx-3 text-[20px] md:text-[23px]">
<div className="font-light">{ReportName}</div>
<div className="font-light">
{ReportName}{" "}
{report_help && (
<span className="text-xs md:text-[13px] font-normal">
<Tooltip message={report_help} />
</span>
)}
</div>
{ReportName === "Templates" && (
<i
onClick={() => navigate("/form/template")}
@@ -0,0 +1,11 @@
import React from "react";
const PremiumAlertHeader = ({ message }) => (
<marquee className="bg-yellow-300 text-black px-2">
{message
? message
: "Currently free in Beta, this feature will incur a fee later."}
</marquee>
);
export default PremiumAlertHeader;
+28 -10
View File
@@ -1,18 +1,36 @@
import React from "react";
// import * as HoverCard from "@radix-ui/react-hover-card";
// import "../styles/opensigndrive.css";
import { Tooltip as ReactTooltip } from "react-tooltip";
const Tooltip = ({ children, message }) => (
<>
<a data-tooltip-id="my-tooltip" data-tooltip-content={message}>
import { openInNewTab } from "../constant/Utils";
const Tooltip = ({ children, id, message, url, iconColor }) =>
url ? (
<button onClick={() => openInNewTab(url)} className="text-center">
{children ? (
children
) : (
<i className="fa-solid fa-question border-[1.5px] px-1.5 py-0.5 rounded-full border-[#33bbff] text-xs text-[#33bbff]"></i>
<i className="fa-solid fa-question text-xs border-[1.5px] px-1 rounded-full border-[#33bbff] text-[#33bbff]"></i>
)}
</a>
<ReactTooltip id="my-tooltip" className="max-w-[200px]" />
</>
);
</button>
) : (
<>
<a
data-tooltip-id={id ? id : "my-tooltip"}
data-tooltip-content={message}
className="z-50"
>
{children ? (
children
) : (
<i
className="fa-solid fa-question text-xs border-[1.5px] px-1 rounded-full border-[#33bbff] text-[#33bbff]"
style={{
borderColor: iconColor ? iconColor : "#33bbff",
color: iconColor ? iconColor : "#33bbff"
}}
></i>
)}
</a>
<ReactTooltip id={id ? id : "my-tooltip"} className="max-w-[200px]" />
</>
);
export default Tooltip;
+58
View File
@@ -101,4 +101,62 @@
.validationlist {
background-position: right 1rem top 50%;
}
}
.warning {
position: absolute;
padding: 5px;
border: 1px solid black;
background: white;
width: 250px;
border-radius: 5px;
animation: inAnimation 1s ease-in-out;
z-index: 1;
}
.defaultvalueWarning {
position: absolute;
left: 20px;
}
.defaultvalueWarning::before {
content: "";
width: 0px;
height: 0px;
border-style: outset;
border-width: 0px 10px 10px 10px;
border-color: transparent transparent black transparent;
display: inline-block;
position: absolute;
top: -29%;
left: 0;
right: 75%;
margin: 0 auto;
}
@keyframes inAnimation {
0% {
opacity: 0;
visibility: hidden;
}
25% {
opacity: 1;
visibility: visible;
}
50% {
opacity: 1;
visibility: visible;
}
75% {
opacity: 1;
visibility: visible;
}
100% {
opacity: 1;
visibility: visible;
}
}
-21
View File
@@ -18,27 +18,6 @@
z-index: 1;
}
.nameWarning {
position: absolute;
bottom: -70px;
left: 10px;
}
.nameWarning::before {
content: "";
width: 0px;
height: 0px;
border-style: outset;
border-width: 0px 10px 10px 10px;
border-color: transparent transparent black transparent;
display: inline-block;
position: absolute;
top: -29%;
left: 0;
right: 75%;
margin: 0 auto;
}
.signWarning {
bottom: 70px;
left: 50px;
+2 -9
View File
@@ -15,15 +15,8 @@
border-width: 0.2px;
}
.dropdownContainer {
display: flex;
flex-direction: column;
overflow-y: scroll;
max-height: 250px;
}
.dropdownContainer::-webkit-scrollbar {
display: none;
.dragCursor {
cursor: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAAsTAAALEwEAmpwYAAAASElEQVR4nGNgwAMkJSUbpKSkOvCpIaT5PxSTbogUQjMYMwxeIIXmVFIxA8UGDDyQGg0DnIDi6JKUlCxHMqCeZAOghjSAMD5FAKfeaURdUFxCAAAAAElFTkSuQmCC"), auto;
}
.react-datepicker__input-container {
@@ -2,7 +2,8 @@ import axios from 'axios';
export default async function getDrive(request) {
const serverUrl = process.env.SERVER_URL;
const appId = process.env.APP_ID;
const limit = request.params.limit;
const skip = request.params.skip;
const classUrl = serverUrl + '/classes/contracts_Document';
const docId = request.params.docId;
try {
@@ -16,9 +17,9 @@ export default async function getDrive(request) {
if (userId) {
let url;
if (docId) {
url = `${classUrl}?where={"Folder":{"__type":"Pointer","className":"contracts_Document","objectId":"${docId}"},"CreatedBy":{"__type":"Pointer","className":"_User","objectId":"${userId}"},"IsArchive":{"$ne":true}}&include=ExtUserPtr,Signers,Folder`;
url = `${classUrl}?where={"Folder":{"__type":"Pointer","className":"contracts_Document","objectId":"${docId}"},"CreatedBy":{"__type":"Pointer","className":"_User","objectId":"${userId}"},"IsArchive":{"$ne":true}}&include=ExtUserPtr,Signers,Folder&skip=${skip}&limit=${limit}`;
} else {
url = `${classUrl}?where={"Folder":{"$exists":false},"CreatedBy":{"__type":"Pointer","className":"_User","objectId":"${userId}"},"IsArchive":{"$ne":true}}&include=ExtUserPtr,Signers`;
url = `${classUrl}?where={"Folder":{"$exists":false},"CreatedBy":{"__type":"Pointer","className":"_User","objectId":"${userId}"},"IsArchive":{"$ne":true}}&include=ExtUserPtr,Signers&skip=${skip}&limit=${limit}`;
}
try {
const res = await axios.get(url, {
@@ -11,11 +11,7 @@ export default function reportJson(id, userId) {
IsCompleted: { $ne: true },
IsDeclined: { $ne: true },
IsArchive: { $ne: true },
$or: [
{ Signers: { $eq: [] } },
{ Signers: null },
{ Signers: { $exists: true }, Placeholders: null },
],
SignedUrl: { $exists: false },
CreatedBy: {
__type: 'Pointer',
className: '_User',
@@ -33,6 +29,7 @@ export default function reportJson(id, userId) {
'Signers.Phone',
],
};
// Need your sign report
case '4Hhwbp482K':
return {
@@ -273,11 +270,7 @@ export default function reportJson(id, userId) {
IsCompleted: { $ne: true },
IsDeclined: { $ne: true },
IsArchive: { $ne: true },
$or: [
{ Signers: { $eq: [] } },
{ Signers: null },
{ Signers: { $exists: true }, Placeholders: null },
],
SignedUrl: { $exists: false },
CreatedBy: {
__type: 'Pointer',
className: '_User',