Merge branch 'staging' of https://github.com/OpenSignLabs/OpenSign into feat_bulksend

This commit is contained in:
prafull-opensignlabs
2024-05-24 11:34:58 +05:30
12 changed files with 250 additions and 62 deletions
@@ -17,7 +17,7 @@ function DefaultSignature({ defaultSignImg, xyPostion, setDefaultSignAlert }) {
};
return (
<div>
<div data-tut="reactourThird">
<div
style={{
background: themeColor,
@@ -331,7 +331,7 @@ function Header({
user exist than show finish button else no
*/}
{currentSigner && (
<>
<div className="flex items-center" data-tut="reactourFifth">
{decline && (
<div
onClick={() => handleDeclinePdfAlert()}
@@ -372,7 +372,7 @@ function Header({
<DropdownMenu.Root>
<DropdownMenu.Trigger asChild>
<div
className="border-none font-[650] text-[18px] pl-2"
className="border-none font-[650] text-[18px] px-2 ml-[4px]"
style={{ color: themeColor }}
>
<i
@@ -399,7 +399,7 @@ function Header({
</DropdownMenu.Content>
</DropdownMenu.Portal>
</DropdownMenu.Root>
</>
</div>
)}
</div>
)}
@@ -464,8 +464,8 @@ function Header({
{completeBtnTitle
? completeBtnTitle
: isMailSend
? "Sent"
: "Send"}
? "Sent"
: "Send"}
</button>
</div>
</>
@@ -505,7 +505,7 @@ function Header({
</button>
</div>
) : (
<div className="flex">
<div className="flex" data-tut="reactourFifth">
<button
onClick={() => navigate(-1)}
type="button"
@@ -334,6 +334,7 @@ function RenderPdf({
{successEmail && <Alert type={"success"}>Email sent successfully!</Alert>}
{isMobile && scale ? (
<div
data-tut="reactourForth"
style={{
border: "0.1px solid #ebe8e8",
marginTop: isGuestSigner && "30px"
@@ -517,6 +518,7 @@ function RenderPdf({
noScrollX={pdfNewWidth < pdfOriginalWidth ? false : true}
>
<div
data-tut="reactourForth"
style={{
border: "0.1px solid #ebe8e8",
width: pdfOriginalWidth
@@ -6,6 +6,7 @@ import { textInputWidget } from "../../constant/Utils";
import PremiumAlertHeader from "../../primitives/PremiumAlertHeader";
import Upgrade from "../../primitives/Upgrade";
import { isEnableSubscription } from "../../constant/const";
import Tooltip from "../../primitives/Tooltip";
const WidgetNameModal = (props) => {
const [formdata, setFormdata] = useState({
@@ -140,16 +141,21 @@ const WidgetNameModal = (props) => {
props.widgetName === textInputWidget) && (
<>
<div className="form-section">
<label
htmlFor="textvalidate"
className={
!props.isSubscribe && isEnableSubscription && "disabled"
}
style={{ fontSize: 13 }}
>
Validation
</label>
{!props.isSubscribe && isEnableSubscription && <Upgrade />}
<div className="flex items-center gap-1">
<label
htmlFor="textvalidate"
className={
!props.isSubscribe && isEnableSubscription && "disabled"
}
style={{ fontSize: 13 }}
>
Validation
</label>
<Tooltip
url={"https://www.w3schools.com/jsref/jsref_obj_regexp.asp"}
/>
{!props.isSubscribe && isEnableSubscription && <Upgrade />}
</div>
<div
className={
!props.isSubscribe && isEnableSubscription && "disabled"
@@ -165,7 +171,7 @@ const WidgetNameModal = (props) => {
<input
className="relative z-20 w-[87%] md:w-[92%] p-2.5 border-[1px] border-[#d1d5db] rounded-l-md outline-none text-xs"
name="textvalidate"
placeholder="Enter custom expression"
placeholder="Enter custom regular expression"
value={formdata.textvalidate}
onChange={(e) => handleChange(e)}
// onBlur={() => handleBlurRegex()}
+8 -8
View File
@@ -19,11 +19,12 @@ export async function fetchSubscription(
isGuestSign = false
) {
try {
const extClass = localStorage.getItem("Extand_Class");
const Extand_Class = localStorage.getItem("Extand_Class");
const extClass = Extand_Class && JSON.parse(Extand_Class);
// console.log("extClass ", extClass);
let extUser;
if (extClass) {
const jsonSender = JSON.parse(extClass);
extUser = jsonSender[0].objectId;
if (extClass && extClass.length > 0) {
extUser = extClass[0].objectId;
} else {
extUser = extUserId;
}
@@ -171,11 +172,10 @@ export const contractUsers = async (email) => {
.then((Listdata) => {
const json = Listdata.data;
let data = [];
if (json && json.result) {
data.push(json.result);
return data;
}
return data;
})
.catch((err) => {
console.log("Err ", err);
@@ -1351,8 +1351,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) {
+2 -1
View File
@@ -52,7 +52,8 @@ const plans = [
"API Access",
"100 API signatures included",
"DocumentId removal from signed docs",
"Custom email templates"
"Custom email templates",
"Auto reminders"
]
},
{
+2 -1
View File
@@ -136,7 +136,8 @@ function GuestLogin() {
`Parse/${parseId}/currentUser`,
JSON.stringify(_user)
);
if (contractUserDetails) {
// console.log("contractUserDetails ", contractUserDetails);
if (contractUserDetails && contractUserDetails.length > 0) {
localStorage.setItem(
"Extand_Class",
JSON.stringify(contractUserDetails)
+197 -6
View File
@@ -28,7 +28,8 @@ import {
fetchSubscription,
convertPdfArrayBuffer,
contractUsers,
handleSendOTP
handleSendOTP,
contactBook
} from "../constant/Utils";
import Loader from "../primitives/LoaderWithMsg";
import HandleError from "../primitives/HandleError";
@@ -39,6 +40,7 @@ import Title from "../components/Title";
import DefaultSignature from "../components/pdf/DefaultSignature";
import ModalUi from "../primitives/ModalUi";
import VerifyEmail from "../components/pdf/VerifyEmail";
import TourContentWithBtn from "../primitives/TourContentWithBtn";
function useQuery() {
return new URLSearchParams(useLocation().search);
}
@@ -68,6 +70,8 @@ function PdfRequestFiles() {
const [selectWidgetId, setSelectWidgetId] = useState("");
const [otpLoader, setOtpLoader] = useState(false);
const [isCelebration, setIsCelebration] = useState(false);
const [requestSignTour, setRequestSignTour] = useState(true);
const [tourStatus, setTourStatus] = useState([]);
const [isLoading, setIsLoading] = useState({
isLoad: true,
message: "This might take some time"
@@ -85,6 +89,8 @@ function PdfRequestFiles() {
const [isAlert, setIsAlert] = useState({ isShow: false, alertMessage: "" });
const [unSignedWidgetId, setUnSignedWidgetId] = useState("");
const [expiredDate, setExpiredDate] = useState("");
const [signerUserId, setSignerUserId] = useState();
const [isDontShow, setIsDontShow] = useState(false);
const [defaultSignAlert, setDefaultSignAlert] = useState({
isShow: false,
alertMessage: ""
@@ -114,6 +120,7 @@ function PdfRequestFiles() {
const [isEmailVerified, setIsEmailVerified] = useState(true);
const [isVerifyModal, setIsVerifyModal] = useState(false);
const [otp, setOtp] = useState("");
const [contractName, setContractName] = useState("");
const divRef = useRef(null);
const isMobile = window.innerWidth < 767;
const rowLevel =
@@ -439,7 +446,62 @@ function PdfRequestFiles() {
}, 5000);
}
}
//checking if condition current user already sign or owner does not exist as a signer or document has been declined by someone or document has been expired
//then stop to display tour message
if (
(checkAlreadySign &&
checkAlreadySign[0] &&
checkAlreadySign.length > 0) ||
!currUserId ||
declined ||
currDate > expireUpdateDate
) {
setRequestSignTour(false);
} else {
//else condition to check current user exist in contracts_Users class and check tour message status
//if not then check user exist in contracts_Contactbook class and check tour message status
const currentUser = JSON.parse(JSON.stringify(Parse.User.current()));
const currentUserEmail = currentUser.email;
const res = await contractUsers(currentUserEmail);
if (res === "Error: Something went wrong!") {
setHandleError("Error: Something went wrong!");
} else if (res[0] && res?.length) {
setContractName("_Users");
currUserId = res[0].objectId;
setSignerUserId(currUserId);
const tourstatus = res[0].TourStatus && res[0].TourStatus;
if (tourstatus && tourstatus.length > 0) {
setTourStatus(tourstatus);
const checkTourRequestSign = tourstatus.filter(
(data) => data.requestSign
);
if (checkTourRequestSign && checkTourRequestSign.length > 0) {
setRequestSignTour(checkTourRequestSign[0].requestSign);
}
}
} else if (res?.length === 0) {
const res = await contactBook(currUserId);
if (res === "Error: Something went wrong!") {
setHandleError("Error: Something went wrong!");
} else if (res[0] && res.length) {
setContractName("_Contactbook");
const objectId = res[0].objectId;
setSignerUserId(objectId);
const tourstatus = res[0].TourStatus && res[0].TourStatus;
if (tourstatus && tourstatus.length > 0) {
setTourStatus(tourstatus);
const checkTourRequestSign = tourstatus.filter(
(data) => data.requestSign
);
if (checkTourRequestSign && checkTourRequestSign.length > 0) {
setRequestSignTour(checkTourRequestSign[0].requestSign);
}
}
} else if (res.length === 0) {
setHandleError("Error: User does not exist!");
}
}
}
setIsUiLoading(false);
} else {
alert("No data found!");
@@ -1141,6 +1203,134 @@ function PdfRequestFiles() {
alertMessage: ""
});
};
const handleDontShow = (isChecked) => {
setIsDontShow(isChecked);
};
//function to close tour and save tour status
const closeRequestSignTour = async () => {
setRequestSignTour(false);
if (isDontShow) {
let updatedTourStatus = [];
if (tourStatus.length > 0) {
updatedTourStatus = [...tourStatus];
const requestSignIndex = tourStatus.findIndex(
(obj) => obj["requestSign"] === false || obj["requestSign"] === true
);
if (requestSignIndex !== -1) {
updatedTourStatus[requestSignIndex] = { requestSign: true };
} else {
updatedTourStatus.push({ requestSign: true });
}
} else {
updatedTourStatus = [{ requestSign: true }];
}
try {
await axios.put(
`${localStorage.getItem("baseUrl")}classes/${localStorage.getItem(
"_appName"
)}${contractName}/${signerUserId}`,
{
TourStatus: updatedTourStatus
},
{
headers: {
"Content-Type": "application/json",
"X-Parse-Application-Id": localStorage.getItem("parseAppId"),
"X-Parse-Session-Token": localStorage.getItem("accesstoken")
}
}
);
} catch (e) {
console.log("update tour messages error", e);
}
}
};
const requestSignTourFunction = () => {
const tourConfig = [
{
selector: '[data-tut="reactourFirst"]',
content: () => (
<TourContentWithBtn
message={`List of signers who still need to sign the document .`}
isChecked={handleDontShow}
/>
),
position: "top",
style: { fontSize: "13px" }
},
{
selector: '[data-tut="reactourForth"]',
content: () => (
<TourContentWithBtn
message={`Click any of the placeholders appearing on the document to sign. You will then see options to draw your signature, type it, or upload an image .`}
isChecked={handleDontShow}
/>
),
position: "top",
style: { fontSize: "13px" }
},
{
selector: '[data-tut="reactourFifth"]',
content: () => (
<TourContentWithBtn
message={`Click the Back, Decline, or Finish buttons to navigate your document. Use the ellipsis menu for additional options, including the Download button .`}
isChecked={handleDontShow}
/>
),
position: "top",
style: { fontSize: "13px" }
}
];
const signedByStep = {
selector: '[data-tut="reactourSecond"]',
content: () => (
<TourContentWithBtn
message={`List of signers who have already signed the document .`}
isChecked={handleDontShow}
/>
),
position: "top",
style: { fontSize: "13px" }
};
//checking if signed by user component exist then add signed step
const signedBy =
signedSigners.length > 0
? [...tourConfig.slice(0, 0), signedByStep, ...tourConfig.slice(0)]
: tourConfig;
//checking if default signature component exist then add defaultSign step
const defaultSignStep = {
selector: '[data-tut="reactourThird"]',
content: () => (
<TourContentWithBtn
message={`You can click "Auto Sign All" to automatically sign at all the locations meant to be signed by you. Make sure that you review the document properly before you click this button .`}
isChecked={handleDontShow}
/>
),
position: "top",
style: { fontSize: "13px" }
};
//index is handle is signed by exist then 2 else 1 to add tour step
const index = signedSigners.length > 0 ? 2 : 1;
const defaultSignTour = defaultSignImg
? [...signedBy.slice(0, index), defaultSignStep, ...signedBy.slice(index)]
: signedBy;
if (isMobile) {
tourConfig.shift();
}
return (
<Tour
onRequestClose={closeRequestSignTour}
steps={isMobile ? tourConfig : defaultSignTour}
isOpen={true}
closeWithMask={false}
rounded={5}
/>
);
};
return (
<DndProvider backend={HTML5Backend}>
<Title title={"Request Sign"} />
@@ -1209,6 +1399,7 @@ function PdfRequestFiles() {
}}
ref={divRef}
>
{requestSignTour && requestSignTourFunction()}
<ModalUi
headerColor={"#dc3545"}
isOpen={isAlert.isShow}
@@ -1496,7 +1687,7 @@ function PdfRequestFiles() {
className="autoSignScroll"
>
{signedSigners.length > 0 && (
<>
<div data-tut="reactourSecond">
<div
style={{ background: themeColor }}
className="signedStyle"
@@ -1556,11 +1747,11 @@ function PdfRequestFiles() {
);
})}
</div>
</>
</div>
)}
{unsignedSigners.length > 0 && (
<>
<div data-tut="reactourFirst">
<div
style={{
background: themeColor,
@@ -1626,7 +1817,7 @@ function PdfRequestFiles() {
);
})}
</div>
</>
</div>
)}
{defaultSignImg && !alreadySign && (
<DefaultSignature
+1 -4
View File
@@ -1804,10 +1804,7 @@ function PlaceHolderSign() {
{!isSubscribe && isEnableSubscription && (
<div className="mt-2">
<Upgrade
message="Upgrade to customize Email"
newWindow={true}
/>
<Upgrade message="Upgrade to customize Email" />
</div>
)}
</div>
@@ -560,6 +560,7 @@ const ReportTable = (props) => {
};
const handleResendMail = async (e, doc, user) => {
e.preventDefault();
console.log("first");
setActLoader({ [user.objectId]: true });
const url = `${localStorage.getItem("baseUrl")}functions/sendmailv3`;
const headers = {
@@ -1017,20 +1018,22 @@ const ReportTable = (props) => {
onSubmit={(e) =>
handleResendMail(e, item, user)
}
className="w-full flex flex-col gap-2 p-3 text-black"
className="w-full flex flex-col gap-2 p-3 text-black relative"
>
<div className="absolute right-5 text-xs z-40">
<Tooltip
id={`${user.objectId}_help`}
message={
"You can use following variables which will get replaced with their actual values:- {{document_title}}, {{sender_name}}, {{sender_mail}}, {{sender_phone}}, {{receiver_name}}, {{receiver_email}}, {{receiver_phone}}, {{expiry_date}}, {{company_name}}, {{signing_url}}."
}
/>
</div>
<div>
<label
className="text-xs ml-1"
htmlFor="mailsubject"
>
Subject{" "}
<Tooltip
id={isNextStep[user.objectId]}
message={
"You can use following variables which will get replaced with their actual values:- {{document_title}}, {{sender_name}}, {{sender_mail}}, {{sender_phone}}, {{receiver_name}}, {{receiver_email}}, {{receiver_phone}}, {{expiry_date}}, {{company_name}}, {{signing_url}}."
}
/>
</label>
<input
id="mailsubject"
@@ -1051,12 +1054,6 @@ const ReportTable = (props) => {
htmlFor="mailbody"
>
Body{" "}
<Tooltip
id={isNextStep[user.objectId]}
message={
"You can use following variables which will get replaced with their actual values:- {{document_title}}, {{sender_name}}, {{sender_mail}}, {{sender_phone}}, {{receiver_name}}, {{receiver_email}}, {{receiver_phone}}, {{expiry_date}}, {{company_name}}, {{signing_url}}."
}
/>
</label>
<EditorToolbar containerId="toolbar1" />
<ReactQuill
+3 -10
View File
@@ -1,20 +1,13 @@
import React from "react";
import { useNavigate } from "react-router-dom";
import { openInNewTab } from "../constant/Utils";
function Upgrade({ message, newWindow }) {
const navigation = useNavigate();
function Upgrade({ message }) {
return (
<sup>
<span
onClick={() => {
if (newWindow) {
const url = window.location.origin + "/subscription";
openInNewTab(url);
} else {
navigation("/subscription");
}
const url = window.location.origin + "/subscription";
openInNewTab(url);
}}
className="text-blue-800 text-sm cursor-pointer underline"
>
@@ -128,7 +128,7 @@ export default async function createContact(request, response) {
userId: userRes.objectId,
};
await axios.post(roleurl, body, { headers: headers });
contactQuery.set('CreatedBy', userPtr.objectId);
contactQuery.set('CreatedBy', userPtr);
contactQuery.set('UserId', {
__type: 'Pointer',
className: '_User',